Reputation:
In this question I talk about Dagger2. Dagger2 consists basically of Components and Modules. Here is an example:
Assume I have a interface:
public interface MyCoolService {
void run();
}
and a possible implementation:
public class MyCoolServiceImpl {
@Override
void run() {}
}
I could link the implementation with the interface using Dagger2 generating:
@Component(modules = {MyModule.class})
@Singleton
public interface Component {
MyCoolService getMyCoolService();
}
and
@Module
public class MyModule {
@Provides @Singleton
MyCoolService provideMyCoolService() {
return new MyCoolServiceImpl();
}
}
This was a brief intro to Dagger2. Now assume I have the following interface:
public interface MySecondCoolService {
void doCoolStuff();
}
There is no implementation MySecondCoolServiceImpl
of MySecondCoolService
in code. Instead, I have an Annotations @JustForCoolStuff
to mark fields and methods. I created an Annotation processor which collects all these Annotations and generates MySecondCoolServiceImpl
which implements MySecondCoolService
.
I the compiler knows the new interface MySecondCoolService
before the annotation processor is running. So I could change my Component as:
@Component(modules = {MyModule.class})
@Singleton
public interface Component {
MyCoolService getMyCoolService();
MySecondCoolService getMySecondCoolService();
}
The problem is that I do not have an implementation yet in code and I do not know the name of the implementation of MySecondCoolService
which will be generated by a annotation processor. Therefore, I cannot wire the interface with the correct implemantation in MyModule
. What I can do is changing my annotation processor such that it generates a new module for me. My annotation processor could generate a module (MyGeneratedModule
) like this:
@Module
public class MyGeneratedModule {
@Provides @Singleton
MySecondCoolService provide MySecondCoolService() {
return new MySecondCoolServiceImpl();
}
}
Again MyGeneratedModule
is generated by an annotation processor. I do not have access to it before running the annotation processor also I do not know the name.
Here is the problem: The annotation processor somehow have to tell Dagger2 that there is a new module which Dagger2 should take into account. Since annotation processors cannot change files it cannot extend the @Component(modules = {MyModule.class})
annotation and change it into something like this: @Component(modules = {MyModule.class, MyGeneratedModule.class})
Is there a way to add MyGeneratedModule
programmatically to the dagger2 dependency graph? How can my Annotation Processor tell Dagger2 that there should be a new wiring between an interface and an implementation as I have described above?
Foray: I know that something like that can be done in Google Guice and Google Gin. A project which does that is GWTP. There you have a Presenter:
public class StartPagePresenter extends ... {
@NameToken("start")
public interface MyProxy extends ProxyPlace<StartPagePresenter> {
}
...
}
which has a @NameToken
annotation to a ProxyPlace
interface. In your AbstractPresenterModule
you wire the view with the presenter and the proxy:
public class ApplicationModule extends AbstractPresenterModule {
bindPresenter(StartPagePresenter.class,
StartPagePresenter.MyView.class, StartPageView.class,
StartPagePresenter.MyProxy.class);
...
}
As so can see no implementation of the MyProxy
interface is given. The implementation created by a Generator (similar to annotation processor but for GWT). There Generator generates the implementation of StartPagePresenter.MyProxy
and add it to the guide/gin system:
public class StartPagePresenterMyProxyImpl extends com.gwtplatform.mvp.client.proxy.ProxyPlaceImpl<StartPagePresenter> implements buddyis.mobile.client.app.start.StartPagePresenter.MyProxy, com.gwtplatform.mvp.client.DelayedBind {
private com.gwtplatform.mvp.client.ClientGinjector ginjector;
@Override
public void delayedBind(Ginjector baseGinjector) {
ginjector = (com.gwtplatform.mvp.client.ClientGinjector)baseGinjector;
bind(ginjector.getPlaceManager(),
ginjector.getEventBus());
presenter = new CodeSplitProvider<StartPagePresenter>( ginjector.getbuddyismobileclientappstartStartPagePresenter() );
...
}
}
Upvotes: 11
Views: 783
Reputation: 1965
Is there a way to add MyGeneratedModule programmatically to the dagger2 dependency graph?
Yes. Use a constructor argument in your module.
@Module
public class MyModule {
private final MyCoolService serviceImpl;
public MyModule(MyCoolService serviceImpl) {
this.serviceImpl = serviceImpl;
}
@Provides @Singleton
MyCoolService provideMyCoolService() {
return new MyCoolServiceImpl();
}
}
Initializing the serviceImpl is done when you build the graph:
DaggerComponent.builder().myModule(new MyModule(serviceImpl)).build();
Upvotes: 1
Reputation: 2956
Well, It seems that you will have to resort to reflection for this one...
@Module
public class MyGeneratedModule {
@Provides @Singleton
MySecondCoolService provide MySecondCoolService() {
try {
return (MySecondCoolService) Class.forName("package.MySecondCoolServiceImpl").newInstance();
} catch (Exception ex) { ... }
}
}
Upvotes: 0