I'm just playing around trying to get to know Dagger2 a bit better. I found a simple example on the web where a Bar
depends on a BarDatabase
:
public class Bar {
// Bar depends on BarDatabase
private BarDatabase db;
@Inject
public Bar(BarDatabase db) {
this.db = db;
}
public BarDatabase getBar() { return db; }
public Cocktail getCocktail(String name) {
String row = db.getCocktail(name);
return new Cocktail(row);
}
}
Dagger works just peachy with the following Module and Component:
package com.example.eflatt.di.barcomponent;
import com.example.eflatt.di.Bar;
import com.example.eflatt.di.module.BarModule;
import com.example.eflatt.di.testmodule.TestBarModule;
import javax.inject.Singleton;
import dagger.Component;
/**
* Created by eflatt on 4/26/16.
*/
@Singleton
@Component(modules = {BarModule.class, TestBarModule.class})
public interface BarComponent {
public Bar provideBar();
}
package com.example.eflatt.di.module;
import com.example.eflatt.di.Bar;
import com.example.eflatt.di.BarDatabase;
import com.example.eflatt.di.MockBarDatabase;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
/**
* Created by eflatt on 4/26/16.
*/
@Module
public class BarModule {
@Provides @Singleton
public Bar provideBar () {
return new Bar(new BarDatabase());
}
}
But I wanted to try injecting a MockBarDatabase() in my unittest, which is when things went off the rails.
Question 1: I decided to write a TestBarModule which new'd a MockBarDatabase() in the provideBar method (separate file called TestBarModule) - is this the wrong technique?
@Module
public class TestBarModule {
@Provides @Singleton
public Bar provideBar () {
return new Bar(new MockBarDatabase());
}
}
Question 2: If writing a separate Module to handle the Mock case is the right thing to do (I'm guessing it's not), then how do I deal with the fact that my IDE tells me:
Error:(15, 16) error: com.example.eflatt.di.Bar is bound multiple times: @Provides @Singleton com.example.eflatt.di.Bar com.example.eflatt.di.module.BarModule.provideBar() @Provides @Singleton com.example.eflatt.di.Bar com.example.eflatt.di.testmodule.TestBarModule.provideBar()
答案 0 :(得分:0)
这是错误的技术吗?
是
您所做的是,您将所有内容配置为使用构造函数注入
@Inject
public Bar(BarDatabase db) {
this.db = db;
}
然后你决定自己提供它,最重要的是不要使用匕首框架并自己处理事情
@Provides @Singleton
public Bar provideBar () {
// MockBarDatabase isn't supplied by dagger but by you.
return new Bar(new MockBarDatabase());
}
至少你应该提供自己的依赖。然后,您还将看到为模拟对象切换单个对象更加容易
// this would be a 'righter' way to do it
@Provides @Singleton
public Bar provideBar (MockBarDatabase db) {
return new Bar(db);
}
MockBarDatabase
必须通过上述示例中的其他@Provides
方法提供。但如前所述,您已经为构造函数注入配置了所有内容。所以使用它。您可以继续并省略任何new
来电:
// dagger will create the object for you!
@Provides @Singleton
public Bar provideBar (Bar bar) {
return bar;
}
这也是您实现界面的方式。或者只是充分利用构造函数注入,并且不要在模块中指定自己的方法。现在,如果您真的希望自己的酒吧成为单身人士,那么您必须适应您的课程:
@Singleton
public class Bar {
@Inject Bar(Db db) {/**/}
// ...
}
是的,你的模块只需提供BarDatabase
,如果它也不能被构造函数注入。
如果对象具有带注释的构造函数,并且还可以创建它们的依赖项(构造函数注入)或提供(那些@Provide
和{ {1}})。因此,如果您不能使用构造函数注入或需要限定符或其他设置,请使用它,并且仅使用模块。
我不知道您是如何创建此错误的,但我相信您尝试添加第二个模块,将模拟对象提供给同一个组件?
如果你想模拟模块尝试子类化模块,覆盖方法来提供对象。
匕首2上有很多很好的教程和样本,我强烈建议你深入研究它们。