I'm new to lombok and guice injection, I could get the general concept but I ran into some code which I don't understand and can't search due to the syntax. Following is the code, can someone help me understand this?
import com.google.inject.Inject;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
#AllArgsConstructor(access = AccessLevel.PRIVATE, onConstructor = #__({ #Inject }))
public class SomeClass {
...
}
Thanks!
This is going to add a constructor with all fields as parameters, with #Inject annotation and private modifier, so your code will be expanded to:
import com.google.inject.Inject;
public class SomeClass {
#Inject
private SomeClass() {
}
}
This is assuming there are no fields in the class. If you have some fields, then they will be added to the constructor, for example:
import com.google.inject.Inject;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
#AllArgsConstructor(access = AccessLevel.PRIVATE, onConstructor = #__({ #Inject }))
public class SomeClass {
private String name;
}
Will become:
import com.google.inject.Inject;
public class SomeClass {
private String name
#Inject
private SomeClass(String name) {
this.name = name;
}
}
Please note, that this won't work in Guice anyway, as it requires a constructor that is not private, per this documentation.
Also make sure Lombok retains any #Named annotations you have added!
Otherwise the code below for example will fail to inject:
#AllArgsConstructor(access = AccessLevel.PACKAGE, onConstructor = #__({#Inject}))
public class SomeClass {
#Named("example")
private String exampleString;
}
public class ExampleModule extends AbstractModule {
#Override
protected void configure() {
bind(String.class)
.annotatedWith(Names.named("example"))
.toInstance("Hello, world!");
}
}
See this answer: Lombok retain fields. You want to add
lombok.copyableAnnotations += com.google.inject.name.Named
to your lombok.config file.
Related
So i am new to Dagger 2 dependency injection. I have created a custom ViewModelFactory class which returns my ViewModel.
#Singleton
public class CustomViewModelFactory implements ViewModelProvider.Factory {
private final MyCatchesRepository repository;
#Inject
public CustomViewModelFactory(MyCatchesRepository repository) {
this.repository = repository;
}
#NonNull
#Override
#SuppressWarnings("unchecked")
public <T extends ViewModel> T create(#NonNull Class<T> modelClass) {
if (modelClass.isAssignableFrom(MyCatchViewModel.class)) {
return (T) new MyCatchViewModel(repository);
} else {
throw new IllegalArgumentException("ViewModel Not Found");
}
}
}
The CustomViewModel takes a MyCatchesRepository in the constructor and then creates the MyCatchViewModel.
How could i change this class so that i can use this ViewModelFactory to create different ViewModels with different constructor arguments (repositories)
This is the Module where the CustomViewModelFactory is created
#Module
public class RoomModule {
private final MyDatabase myDatabase;
public RoomModule(Application application) {
this.myDatabase = Room.databaseBuilder(application,
MyDatabase.class, AppConstants.DATABASE_NAME)
.build();
}
#Provides
#Singleton
MyCatchesRepository provideCatchesRepository(MyCatchDao myCatchDao) {
return new MyCatchesRepository(myCatchDao);
}
#Provides
#Singleton
MyCatchDao providesCatchDao(MyDatabase myDatabase) {
return myDatabase.myCatchDao();
}
#Provides
#Singleton
LuresRepository provideLureRepository(LureDao lureDao) {
return new LuresRepository(lureDao);
}
#Provides
#Singleton
LureDao provideLureDao(MyDatabase myDatabase) {
return myDatabase.lureDao();
}
#Provides
#Singleton
MyDatabase provideDatabase(Application application) {
return myDatabase;
}
#Provides
#Singleton
ViewModelProvider.Factory provideCatchesViewModelFactory(MyCatchesRepository catchesRepository) {
return new CustomViewModelFactory(catchesRepository);
}
}
ViewModelModule
#Module
public abstract class ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(MyCatchViewModel.class)
abstract ViewModel myCatchViewModel(MyCatchViewModel myCatchViewModel);
#Binds
#IntoMap
#ViewModelKey(FishingSpotViewModel.class)
abstract ViewModel fishingSpotViewModel(FishingSpotViewModel fishingSpotViewModel);
#Binds
abstract ViewModelProvider.Factory bindCustomViewModelFactory(CustomViewModelFactory customViewModelFactory);
}
The approach the Google team came up with in the architecture components samples is to use a custom annotation in order to provide ViewModel classes through dagger.
In Java the Annotation looks as follows.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import androidx.lifecycle.ViewModel;
import dagger.MapKey;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#MapKey
#interface ViewModelKey {
Class<? extends ViewModel> value();
}
This uses MapKey from Dagger, where any annotated ViewModel will be composed into a Map which can then be used in your ViewModelFactory.
In the Google samples the ViewModelFactory looks as follows, where using constructor injection you can access the map of ViewModel providers.
public class ViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModels;
#Inject
public ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> viewModels) {
this.viewModels = viewModels;
}
#NonNull
#Override
public <T extends ViewModel> T create(#NonNull Class<T> modelClass) {
Provider<ViewModel> viewModelProvider = viewModels.get(modelClass);
if (viewModelProvider == null) {
throw new IllegalArgumentException("model class " + modelClass + " not found");
}
//noinspection unchecked
return (T) viewModelProvider.get();
}
}
In your example your would end up with the following in order to provide the MyCatchViewModel. Other ViewModels could then be provided by following the same pattern.
#Module
public abstract class ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(MyCatchViewModel.class)
abstract ViewModel myCatchViewModel(MyCatchViewModel myCatchViewModel);
}
For a complete example you can check out the GithubBrowserSample sample from Google. https://github.com/googlesamples/android-architecture-components/blob/master/GithubBrowserSample/app/src/main/java/com/android/example/github/di/ViewModelModule.kt
Does anyone know if there is any easy way to use dependency injection within the FXML controllers of a Gluon Desktop (ParticleApplication) app? There is already #Inject used for things like
public class HomeController {
#Inject ParticleApplication app;
#Inject private ViewManager viewManager;
#Inject private StateManager stateManager;
(as part of the framework) but I would like to be able to #Inject my own objects.
EDIT: An answer suggested to use Gluon Ignite, but I'm still having trouble figuring it out. Here is some of my attempted code:
My ParticleApplication class:
package com.gluonapplication;
import com.gluonhq.ignite.guice.GuiceContext;
import com.gluonhq.particle.application.ParticleApplication;
import com.google.inject.AbstractModule;
import javafx.scene.Scene;
import java.util.Arrays;
public class GluonApplication extends ParticleApplication {
private GuiceContext context = new GuiceContext(this, () -> Arrays.asList(new GuiceModule()));
public GluonApplication() {
super("Gluon Desktop Application");
context.init();
System.out.println("From within GluonApplication Constructor: " +
context.getInstance(TestClassToInject.class).testDependancy.testString
);
}
#Override
public void postInit(Scene scene) {
setTitle("Gluon Desktop Application");
}
class GuiceModule extends AbstractModule {
#Override protected void configure() {
// Use just in time injection.
}
}
}
My controller class:
package com.gluonapplication.controllers;
import com.gluonapplication.TestClassToInject;
import com.gluonhq.particle.application.ParticleApplication;
import com.gluonhq.particle.state.StateManager;
import com.gluonhq.particle.view.ViewManager;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javax.inject.Inject;
public class PrimaryController {
#Inject ParticleApplication app;
#Inject private ViewManager viewManager;
#Inject private StateManager stateManager;
#Inject private TestClassToInject testClassToInject;
#FXML
private Label label;
public void initialize() {
}
public void postInit() {
System.out.println("From within controller's postInit() method: " +
testClassToInject.testDependancy.testString
);
}
public void dispose() {
}
}
And then my two classes created as the dependancies:
package com.gluonapplication;
import javax.inject.Inject;
public class TestClassToInject {
#Inject
public TestDependancy testDependancy;
public TestClassToInject() {
}
}
package com.gluonapplication;
public class TestDependancy {
public String testString = "This is a test string";
public TestDependancy() {
}
}
And finally my Gradle file:
apply plugin: 'java'
apply plugin: 'application'
repositories {
jcenter()
maven {
url 'http://nexus.gluonhq.com/nexus/content/repositories/releases'
}
}
mainClassName = 'com.gluonapplication.GluonApplication'
dependencies {
compile 'com.gluonhq:particle:1.1.0'
compile 'com.gluonhq:ignite-guice:1.0.0'
}
The "From within GluonApplication Constructor: " +
context.getInstance(TestClassToInject.class).testDependancy.testString prints out just fine, so I think I have the dependancy classes and Guice configured OK. However, the System.out.println("From within controller's postInit() method: " + testClassToInject.testDependancy.testString); doesn't work from within the controller because the testClassToInject is null at time of use.
You can use Gluon Ignite (http://gluonhq.com/open-source/ignite/) which supports dependency injection with Dagger, Guice and Spring. Gluon Ignite is an open source project, and you can find an example in the test directory on the repository website: https://bitbucket.org/gluon-oss/ignite/src/c85197b33852/src/test/java/com/gluonhq/ignite/?at=default
At the moment I have a Base class that contains a member I would like to inject. However, I would like the concrete type of this member to depend on the Subclass being instantiated. What I am aiming for is something along these lines:
public interface StringInterface {
public String getString();
}
public class HelloStringConcrete implements StringInterface {
public String getString() {
return "Hello";
}
}
public class WorldStringConcrete implements StringInterface {
public String getString() {
return "World";
}
}
public abstract class Base {
#Inject StringInterface member;
public Base() {
// Assume access to object graph
MyObjectGraph.get().inject(this);
}
public void printSomething() {
System.out.println(member.getString());
}
}
public class SubclassHello extends Base {}
public class SubclassWorld extends Base {}
#Module(injects = {SubclassHello.class})
public class HelloModule {
#Provides StringInterface provideStringInterface() {
return new HelloStringConcrete();
}
}
#Module(injects = {SubclassWorld.class})
public class WorldModule {
#Provides StringInterface provideStringInterface() {
return new WorldStringConcrete();
}
}
So now what I would like to do is something along the lines of:
#Module(
includes = {
HelloModule.class,
WorldModule.class
}
)
public class BigModule {}
// Somewhere in another piece of code...
objectGraph = ObjectGraph.create(new BigModule());
// In yet another piece of code...
SubclassHello hello = new SubclassHello();
SubclassWorld world = new SubclassWorld();
hello.printSomething();
world.printSomething();
// Hopefully would result in :
// Hello
// World
This type of setup won't work though, because including two modules with the same provider will result in a duplicate provider error at compile time. It would be cool to see a solution to this problem without introducing #Named or #Qualifer annotations, or using scoped graph extensions via graph.plus() because these strategies necessarily introduce coupling to the subclasses
This is possible but I think the code I've attached below is more coupled than using scoped graphs or annotations. Basically you can use constructor injection to inject concrete dependencies to your
SubclassHello and SubclassWorld.
public abstract class Base {
private final StringInterface member;
public Base(StringInterface member) {
this.member = member;
}
...
}
#Module(injects = {SubclassWorld.class})
public class WorldModule {
#Provides
WorldStringConcrete provideStringInterface() {
return new WorldStringConcrete();
}
}
public class SubclassWorld extends Base {
#Inject
public SubclassWorld(WorldStringConcrete worldStringConcrete) {
super(worldStringConcrete);
}
}
#Module(injects = {SubclassHello.class})
public class HelloModule {
#Provides
HelloStringConcrete provideStringInterface() {
return new HelloStringConcrete();
}
}
public class SubclassHello extends Base {
#Inject
public SubclassHello(HelloStringConcrete helloStringConcrete) {
super(helloStringConcrete);
}
}
// Somewhere in another piece of code...
ObjectGraph objectGraph = ObjectGraph.create(new BigModule());
// In yet another piece of code...
SubclassHello hello = objectGraph.get(SubclassHello.class);
SubclassWorld world = objectGraph.get(SubclassWorld.class);
I don't think there are other solutions. How could Dagger find out which StringInterface implementations should be injected to the concrete classes?
I have a hard time understanding the injection mechanism of Jersey. The JAX-RS Specification (http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-520005) states that injection via #Context is possible in Application subclasses, root resource classes and providers.
I now have a class that is instantiated at startup and has a method which is called on every request. Inside the method I need access to the current UriInfo object. The problem is, that this method is not called from my code. So I can't pass UriInfo directly to the method.
I actually want to do something like this:
public class MyClass implements ThirdPartyInterface {
// not possible because class is no Application subclass, root resource class or provider
#Context
private UriInfo uriInfo;
public void methodCallebByThirdPartyCode() {
Uri requestUri = uriInfo.getRequestUri();
// do something
}
}
I tried this. Obviously with no success:
public class MyClass implements ThirdPartyInterface {
private UriInfo uriInfo;
public MyClass(UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
public void methodCallebByThirdPartyCode() {
Uri requestUri = uriInfo.getRequestUri();
// do something
}
}
#Provider
#Produces(MediaType.WILDCARD)
public class MyBodyWriter implements MessageBodyWriter<MyView> {
#Context
private UriInfo uriInfo;
private MyClass myClass;
private ThirdPartyClass thirdPartyClass;
public MyBodyWriter() {
// uriInfo is null at this time :(
myClass = new MyClass(uriInfo);
thirdPartyClass = new ThirdPartyClass();
thirdPartyClass.register(myClass);
}
public void writeTo(final MyView view, final Class<?> type, /* and so on */) throws IOException, WebApplicationException {
// execute() calls MyClass#methodCallebByThirdPartyCode()
thirdPartyClass.execute();
}
}
The only workaround I can think of is this. I don't think it's very clean:
public class MyClass implements ThirdPartyInterface {
private UriInfo uriInfo;
public void setUriInfo(final UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
public void methodCallebByThirdPartyCode() {
Uri requestUri = uriInfo.getRequestUri();
// do something
}
}
#Provider
#Produces(MediaType.WILDCARD)
public class MyBodyWriter implements MessageBodyWriter<MyView> {
#Context
private UriInfo uriInfo;
private MyClass myClass;
private ThirdPartyClass thirdPartyClass;
public MyBodyWriter() {
myClass = new MyClass();
thirdPartyClass = new ThirdPartyClass();
thirdPartyClass.register(myClass);
}
public void writeTo(final MyView view, final Class<?> type, /* and so on */) throws IOException, WebApplicationException {
myClass.setUriInfo(uriInfo);
// execute() calls MyClass#methodCallebByThirdPartyCode()
thirdPartyClass.execute();
myClass.setUriInfo(null);
}
}
I hope there is a better solution, but maybe I'm completely on the wrong track.
Thanks!
Late answer, but a good question ... so lets go:
You can use a org.glassfish.hk2.api.Factory and javax.inject.Provider for injections. I don't know since which version this is available, so maybe you have to upgrade your jersery version. For the following samples i used jersey 2.12.
First you have to implement and register/bind a Factory for your MyClass:
MyClassFactory:
import javax.inject.Inject;
import javax.ws.rs.core.UriInfo;
import org.glassfish.hk2.api.Factory;
// ...
public class MyClassFactory implements Factory<MyClass> {
private final UriInfo uriInfo;
// we will bind MyClassFactory per lookup later, so
// the constructor will be called everytime we need the factory
// meaning, uriInfo is also per lookup
#Inject
public MyClassFactory(final UriInfo uriInfo) {
this.uriInfo = uriInfo;
}
#Override
public MyClass provide() {
return new MyClass(uriInfo)
}
#Override
public void dispose(UriInfo uriInfo) {
// ignore
}
}
Registration via ResourceConfig:
import org.glassfish.hk2.api.PerLookup;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
// ...
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(new AbstractBinder() {
#Override
protected void configure() {
bindFactory(MyClassFactory.class).to(MyClass.class).in(PerLookup.class);
// ... bind additional factories here
}
});
// ...
}
}
Now you are able to inject MyClass per lookup to providers, resources etc.
But Attention: Afaig there are two approaches and only one will work as eventually aspected for providers ...
import javax.inject.Inject;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
// ...
#Provider
#Produces("application/foo-bar")
public class MyBodyWriter implements MessageBodyWriter<MyView> {
// first approache - don't do it!
// will only injected once, cause MyBodyWriter is only instantiated once
#Inject
private MyClass myClass;
// second approache - works fine!
private final javax.inject.Provider<MyClass> provider;
// MyBodyWriter instantiate once
// get an inject provider here
#Inject
public MyBodyWriter(javax.inject.Provider<MyClass> myClassProvider) {
this.provider = myClassProvider;
}
#Override
public boolean isWriteable(Class<?> t, Type g, Annotation[] a, MediaType m) {
return t == MyView.class;
}
#Override
public long getSize(MyView t, Class<?> c, Type g, Annotation[] a, MediaType m) {
// deprecated by JAX-RS 2.0 and ignored by Jersey runtime
return 0;
}
#Override
public void writeTo(MyView v, Class<?> c, Type t, Annotation[] a, MediaType m, MultivaluedMap<String, Object> s, OutputStream o) throws IOException, WebApplicationException {
// attention: its not per lookup !!!
MyClass myClassDirectInjected = myClass;
System.out.println(myClassDirectInjected); // same instance everytime
// but this is ;)
MyClass myClassFromProvider = provider.get();
System.out.println(myClassFromProvider); // it's a new instance everytime
// ...
}
}
Hope this was somehow helpfull.
I'm new to CDI, tried searching for the usage, could not find anything and so posting the question. I'm trying to figure how I can inject an instance of a class with a parameterized constructor only using CDI. I'm not using Spring so, how it is done in spring does not help. Here is a sample I've created to show what's the issue. My #Inject will not work in this scenario.
public class A
{
public A(boolean deliverFromLocalWarehouse)
{
if(deliverFromLocalWarehouse)
{
wareHouseId = new Integer(10);
}
else
{
wareHouseId = new Integer(100);
}
}
public void deliver()
{
//get wareHouse address by Id and initiate delivery.
}
private Integer wareHouseId = null;
}
public class B
{
#Inject
private A a;
}
Thanks
Srikrishna Kalavacharla
If the constructor parameter should come from a bean, I think you can simply annotate it:
public A(#Inject boolean localWarehouse) { ...
and inject it with
#Inject A a;
If you want two different instances of A (with different constructor arguments), you could subclass them:
public AForLocalWarehouse extends A {
public AForLocalWarehouse() {
super(true);
}
}
and inject them with
#Inject AForLocalWarehouse a;
or use a producer method with qualifiers:
#Produces #LocalWarehouse
public A localWarehouse() { return new A(true); }
#Produces #RemoteWarehouse
public A remoteWarehouse() { return new A(false); }
and inject them with
#Inject #LocalWarehouse A a;
#Inject #RemoteWarehouse A a;