I want to use Dagger in a project currently configured with Guice. I'm still very new to the concepts of DI, but I see that both Guice and Dagger use #Inject and #Provides notations. I have some understanding of the #Module and #Component annotations and how to set them up, but I'm wondering if the #Inject and and #Provides can basically be left as they are?
For example, let's say I have this in Guice:
public class ModuleA extends AbstractModule {
#Inject
public ModuleA() {
...
}
#Provides
#Singleton
protected InterfaceX() {
...
}
}
Could the following dagger implementation work the same, assuming there is also a component etc?
#Module
public class ModuleA {
#Inject
public ModuleA() {
...
}
#Provides
#Singleton
protected InterfaceX() {
...
}
}
One thing that confused me was that #Provides is used in Dagger to bind implementations to interfaces, and I'm not sure if that's what it is used for in Guice.
Again, I'm pretty new to this so any clarification would be really appreciated. Thanks!
NOTE: Have not used Dagger but can clarify on Guice side.
#Provides in Guice is for informing the Guice injector about which method to use to for creating the asked object at build time.
for eg : class Parent has implementation in 2 sub classes Child1 and Child2
Now you want to have a logic which defines when to inject Child1 and when to inject child2. This logic can be written in a method which can become the provider for Parent class impl. and to declare this provider method, #Provides annotation is used
class Parent {}
class Child1 extends Parent {}
class child2 extends Parent {}
GuieModule {
#Provides
Parent getParent() {
if(something)
return new Child1();
else
return new child2();
}
}
Related
I'm working on a project with Vert.x that uses Dagger for DI, and there's a class that's creating an unsustainable problem with how big the injection is done.
This is the class that I have:
public class ClassManager {
private List<ParentClass> all = new ArrayList<>();
#Inject
public ClassManager(ParentClass... classes) {
if (classes != null) {
all.addAll(Arrays.asList(classes));
}
}
//other methods down here
}
The problem being that, as of right now, we have 32 classes that extend from this abstract class ParentClass, so the injection right now is as this:
#Provides
#Singleton
public ClassManager provideClassManager(SubClassA a, SubClassB b, SubClassC c.... and so on) {
return new ClassManager(a, b, c...and so on);
}
I haven't found so far a better solution on how to do this injection with Dagger, but I do need that this ClassManager has access to all of the classes that extend from ParentClass. Is there any other better way? Maybe with other library?
You can use dagger multibindings and bind your subclasses into set, like this
#Module
public abstract class SubClassModule {
#Binds
#IntoSet
public abstract ParentClass bindsSubClassA(impl: SubClassA);
#Binds
#IntoSet
public abstract ParentClass bindsSubClassB(impl: SubClassB)
}
And in module with class manager pass set into constructor
#Provides
#Singleton
public ClassManager provideClassManager(Set<ParentClass> set) {
return new ClassManager(set);
}
I am looking into Dagger 2 (Java) right now and came across an issue right upon the start. Sadly, I was not able find a anything yet in the Dagger 2 Documentation or on Stackoverflow about it, so if you guys know some resources I would really appreciate it.
I prepared a minimal example within this repository here to explain whats my issue: https://github.com/stackoverflow-samples/dagger2-dependency-cycle
So we got an Application class that should be constructed
public class Application {
#Inject
public Application(SomeDependency one) {
}
public static void main(String[] args) {
DaggerApplicationComponent.create().build();
}
}
... with a dummy dependency
public class SomeDependency {
#Inject
public SomeDependency() {
}
}
And of course the Dagger classes/interfaces ...
.. a component interface:
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
Application build();
}
.. and a module:
#Module
abstract class ApplicationModule {
#Provides
static SomeDependency provideDepdendencyOne() {
return new SomeDependency();
}
}
What I dont get is why Dagger forces me to register SomeDepdendency with #Provides annotation and does not allow to register it via #Binds:
#Binds
abstract SomeDependency bindDepdendencyOne(SomeDependency one);
Whenever I change the code from #Provides to #Binds it gives me the following errors:
[Dagger/DependencyCycle] Found a dependency cycle:
io.github.codejanovic.dagger2.example.SomeDependency is injected at
io.github.codejanovic.dagger2.example.ApplicationModule.bindDepdendencyOne(one)
io.github.codejanovic.dagger2.example.SomeDependency is injected at
io.github.codejanovic.dagger2.example.Application(one)
io.github.codejanovic.dagger2.example.Application is provided at
io.github.codejanovic.dagger2.example.ApplicationComponent.build()
Being unable to #Bind an implementation makes absolutely no sense to me. What do I oversee?
Thanks in advance.
You're wrong in assuming that you need #Binds or #Provides in the first place. You can and should be using constructor injection—not the pattern, but the Dagger generates the code for me kind.
You already have the constructor annotated with #Inject, thus Dagger knows about the class and how to create it. There's nothing else to do.
public class SomeDependency {
#Inject
public SomeDependency() {
}
}
You don't need any #Provides, no #Binds, not even a #Module for this simple use case of yours. Your example should work right out of the box since both constructors are annotated with #Inject.
#Component
public interface ApplicationComponent {
Application build();
}
If you need to specify a scope you can add it on the class.
#Provides should be used for code that you can't use constructor injection for, or that needs additional setup. Of course you can create all the objects manually (as you're doing in your example) but this has no real benefit and will just produce a lot of boilerplate that can be avoided.
#Binds is to be used with implementations that you need to bind to an interface. Optimally you'd use constructor injection for the implementation as well, but you can as well add it to the component builder (#BindsInstance) or create it in a #Provides annotated method.
#Binds MyInterface bindMyImplementation(MyImplementation implementation);
If your class is marked with an #Inject constructor:
public class SomeDependency {
#Inject // <----
public SomeDependency() {
}
}
Then you need #Binds (or #Provides) only if you need to "bind" it as an implementation of an interface, or at least a different type than its concrete type.
Also, if your object has an #Inject constructor, you would not need to instantiate it in the module, because Dagger already knows how to instantiate it.
So to fix your code, all you need to do is:
// #Module
// abstract class ApplicationModule {
// #Provides
// static SomeDependency provideDepdendencyOne() {
// return new SomeDependency();
// }
// }
Solved.
To simplify problem prerequisites suppose the following sources set:
src/main/java/di
ApplicationComponent.java:
#PerApplication
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
void inject(MyApplication target);
}
ApplicationModule.java
#Module
public class ApplicationModule {
#Provides #PerApplication
CoffeeMaker provideCoffeeMaker(CoffeeMakerImpl impl) { return impl; }
}
src/debug/java/di
DebugApplicationComponent.java:
#PerApplication
#Component(modules = {DebugApplicationModule.class})
public interface DebugApplicationComponent extends ApplicationComponent {}
DebugApplicationModule.java
#Module(includes = ApplicationModule.class)
public class DebugApplicationModule {
#Provides #PerApplication
Heater provideHeater(HeaterImpl impl) { return impl; }
}
src/main/java/app
CoffeeMakerImpl.java
class CoffeeMakerImpl implements CoffeeMaker {
#Inject CoffeeMakerImpl(Heater heater) {}
}
In MyApplication.java
#Inject CoffeeMaker coffeeMaker;
DaggerDebugApplicationComponent.
builder().
applicationModule(new ApplicationModule(application)).
debugApplicationModule(new DebugApplicationModule()).
build().
inject(this);
When compiling the project I got error:
Error:(18, 10) error: Heater cannot be provided without an #Provides-annotated method.
app.Heater is injected at
app.CofeeMakerImpl.<init>(heater)
app.CofeeMakerImpl is injected at
app.MyApplication
app.MyApplication is injected at
di.ApplicationComponent.inject(target)
I expect, that due DebugApplicationModule includes ApplicationModule, and I pass to DebugApplicationComponent both of these modules, DebugApplicationComponent, should see both Heater and CoffeeMaker.
What could be a reason, why Heater is not accessible in injection chain?
P.S. Thanks to #DavidMedenjak, solution is quite simple: just make ApplicationComponent a plain interface, removing #PerApplication #Component(modules = {ApplicationModule.class}) line.
See his answer, there are some useful advices there.
I expect, that due DebugApplicationModule includes ApplicationModule [...] should see both Heater and CoffeeMaker.
DebugApplicationModule compiles fine, the problem is that your ApplicationComponent cannot compile, since it does not know how to inject MyApplication.
#PerApplication
#Component(modules = {ApplicationModule.class})
public interface ApplicationComponent {
void inject(MyApplication target);
}
Here you declare that this component knows how to inject MyApplication, but it has no access or knowledge about Heater, as you can see in the compile error:
error:(18, 10) error: Heater cannot be provided without an #Provides-annotated method.
app.Heater is injected at
app.CofeeMakerImpl.<init>(heater)
app.CofeeMakerImpl is injected at
app.MyApplication
app.MyApplication is injected at <- trying to inject MyApplication
di.ApplicationComponent.inject(target) <- ApplicationComponent has the problem
Make sure to always look very closely at the compile errors.
You only add this knowledget to your DebugApplicationModule, but ApplicationComponent also says it can inject the class, which it can't.
To solve this the obvious solutions would be to remove the inject() signature from your ApplicationComponent and move it to your DebugApplicationComponent, since it just doesn't know how to inject it. The full information to inject your Application is within your DebugApplicationComponent, and later maybe also ReleaseApplicationComponent.
Since you probably want an interface that your components implement, you could also just remove the #Component annotation from ApplicationComponent.
public interface ApplicationComponent {
void inject(MyApplication target);
}
Make it a plain interface, and Dagger will not try to generate an implementation.
You could also have a look at component builder methods, where you could bind / add different objects / modules to a component.
This is what I currently have and it works:
#FragmentScope
#Component(dependencies = {FacebookComponent.class},
modules = {FragmentFacebookLoginModule.class})
public interface FragmentFacebookLoginComponent {
void inject(FragmentFacebookLogin fragment);
}
Now I want to add another dependency. I changed it to this:
#Component(dependencies = {FacebookComponent.class, AnotherComponent.class},
modules = {FragmentFacebookLoginModule.class})
But now I get this error message:
FragmentFacebookLoginComponent depends on more than one scoped
component
How can I solve this? How can I have more than one dependencies?
If I remove the scope from one component I get this error message:
AnotherComponent (unscoped) cannot depend on scoped components
I found the answer here: https://stackoverflow.com/a/29619594/1016472
At the end I created a AppComponent with the right scope and let FacebookComponent and AnotherComponent extends this AppComponent.
FacebookComponent and AnotherComponent does not have it's own scope (I removed it).
Looks now like this:
#AppScope
#Component
public interface AppComponent {
}
#Component(modules = {FacebookModule.class})
public interface FacebookComponent extends AppComponent {
}
#Component(modules = {AnotherModule.class})
public interface AnotherComponent extends AppComponent {
}
#FragmentScope
#Component(dependencies = {FacebookComponent.class, AnotherComponent.class},
modules = {FragmentFacebookLoginModule.class})
public interface FragmentFacebookLoginComponent {
void inject(FragmentFacebookLogin fragment);
}
You can't use scoped components in a dependencies array (which is quite strange I have to say), only unscoped, or one scoped + other unscoped.
But you can deceive dagger with "proxy" interfaces:
#Component
#Singleton
interface ComponentA {
fun provideSomeA()
}
interface ProxyComponentA : ComponentA
#Component
#Singleton
interface ComponentB {
fun provideSomeB()
}
interface ProxyComponentB : ComponentB
#Component(dependencies = [ProxyComponentA::class, ProxyComponentB::class])
#OtherScope
interface ComponentC
But in your ComponentC builder you should use proxy components implementations, which could easily be achieved with Kotlin:
class ProxyComponentAImpl(private val delegate: ComponentA) : ProxyComponentA, ComponentA by delegate
class ProxyComponentBImpl(private val delegate: ComponentB) : ProxyComponentB, ComponentB by delegate
componentA = DaggerComponentA.builder()...
componentB = DaggerComponentB.builder()...
componentC = DaggerComponentC.builder()
.componentA(ProxyComponentAImpl(componentA))
.componentB(ProxyComponentBImpl(componentB))
Works on dagger version 2.13, don't know about others
Also you could use vice versa inheritance ComponentA : ProxyComponentA to eliminate the need to create ProxyComponentAImpl, but it's not a good design choice if your ComponentA lays for example in a different gradle module
The solution was inspired by that issue discussion:
https://github.com/google/dagger/issues/1225
What you want to be determined to be within the ApplicationScope should be all defined without a scope, and linked together under the application scope only in the ApplicationComponent under the given scope.
For example,
#Component(modules = {FacebookModule.class})
public interface FacebookComponent {
FacebookThing facebookThing(); //assuming this is with #Provides in FacebookModule with NO SCOPE
}
#Component(modules = {AnotherModule.class})
public interface AnotherComponent{
AnotherThing anotherThing(); //assuming this is with #Provides in AnotherModule with NO SCOPE
}
Then you can do
#AppScope
#Component(dependencies={AnotherComponent.class, FacebookComponent.class})
public interface AppComponent extends AnotherComponent, FacebookComponent {}
After which you can do
#FragmentScope
#Component(dependencies=AppComponent.class)
public interface FragmentComponent extends AppComponent {}
Please note that unscoped providers create a new instance on every inject call. If you need the scoping, you should bind the modules to the same component, but components should only depend on other components with the intention of subscoping.
Now Dagger supports a component that can depends on more than 1 scoped dependencies. Just update your dagger version to 2.27
https://github.com/google/dagger/issues/1414
api 'com.google.dagger:dagger:2.27'
kapt 'com.google.dagger:dagger-compiler:2.27'
Include on your module the dependency module like this:
#Module(includes = FacebookModule.class)
public class AnotherModule {...
I am wondering what the difference is between using #provides on a method and using bind() in my guice modules.
I usually override AbstractModule.configure() and bind all my implementations to my interfaces like this:
public class MyModule extends AbstractModule
{
#Override
protected void configure()
{
this.bind(myIface.class).to(myIfaceImpl.class);
this.bind(myOtherIface.class).to(myOtherIfaceImpl.class).asEagerSingleton();
}
...
}
However, I have noticed a pattern in the codebase I'm currently working with where implementations aren't bound explicitly they are being returned from providers like this:
public class MyModule extends AbstractModule
{
#Provides
#Singleton
myIface iFaceProvider()
{
return new myIfaceImpl();
}
...
}
Is there a reason to prefer one over the other? Are there cases that force a particular method?
If you do
bind(MyInterface.class).to(MyImplementation.class)
Guice creates the instance for you. This enables certiain things like AOP. If you do
#Provides
MyInterface provideMyInterface() {
return new MyImplementation();
}
then Guice didn't create the instance so AOP won't work. Also, it requires an accessible constructor for MyImplementation. Generally, this form is only used when you can't edit MyImplementation to make it Guice-compatible.
There's a third form:
#Provides
MyInterface provideMyInterface(MyImplementation impl) {
return impl;
}
which is almost totally equivalent to the bind(...).to(...) form. It is commonly used in frameworks like Dagger that do not have the bind syntax.