Dagger系列:
Demo地址:
带着这两个疑问,开始本文章的话题。
@Provides可以认为是对@Inject的补充。对于@Inject不能满足的情况,可以使用@Provides注解方法来满足依赖性,该方法的定义返回类型满足了其依赖关系。不管是接口还是第三方库的类,甚至是相关的配置对象,都可以通过@Provides方法来提供了,以弥补@Inject的盲区。
例如:每当需要StudentBean实例时,都会调用provideStudent方法:
@Provides
static StudentBean provideStudent() {
return new StudentBean();
}
甚至,@Provides方法本身可能也拥有自身的依赖关系。例如,每当需要StudentBean实例时,都会需要依赖注入AreaBean实例。
@Provides
static StudentBean provideStudent(AreaBean area) {
return new StudentBean(area);
}
按照惯例,对于@Provides方法的命名以provide为前缀,例如, provideXx.
值得注意的是,@Provides方法本身是不能独立存在的,它必须依附于一个Module。所谓的Module是具有@Module注解的类。关于@Moduel是什么?后续即将讲解。
@Module源码:
public @interface Module {
/**
* 本身依赖的其他的Module
*/
Class>[] includes() default {};
/**
* 注入该模块的子组件
* 一个子组件中,可以注入多个Module
*/
@Beta
Class>[] subcomponents() default {};
}
官方对于@Module的解释是这样的: Annotates a class that contributes to the object graph.,翻译过来就是,其注解的类为Dagger提供依赖关系。
之前已经提到了,@Inject可以提供依赖关系,但是其不是万能的。如果我们所需要的提供的构造函数没有使用@Inject注解,比如,第三方库里的类,我们并没有权限修改源码。这时,Module类可以在不修改源码构造函数的情况下,提供依赖关系。即使是可以用@Inject注解的,依然可以通过Module提供依赖关系。
这里,我们需要明确的一个概念是@Module注解的类,是向Dagger提供依赖关系。具体该如何使用,后续更精彩!
@Compoent部分源码解析:
public @interface Component {
/**
* 注入的Module列表,用于组件的实现
* 这些Module可能依赖于其他的Module,这里只需要列出注入的Module即可
*/
Class>[] modules() default {};
/**
* 可用于依赖关系的组件列表
*/
Class>[] dependencies() default {};
}
不管@Inejct还是@Provide方法,形成了由它们的依赖关系链接做组成的IOC容器。这时,应用程序应可以通过一个桥接调用IOC中实例,以完成依赖的注入的,那就不得不提@Component。@Component一般用来注解接口,被注解的接口在编译时会产生相应的实例,作为提供依赖和所需依赖之间的桥梁,把相关依赖注入到其中。
编译时,被@Component注解的接口在编译时会产生相应的实例的名称一般以Dagger为前缀。比如,接口名称为StudentComponent,那么编译时生成的实例名称为DaggerStudentComponent.
@Component(modules = DripCoffeeModule.class)
interface CoffeeShop {
CoffeeMaker maker();
}
在编译时,已经实现了@Component注解的接口,我们可以通过调用该实现的builder()方法,获取相应的Builder实例,并使用返回的Builder设置依赖关系,然后调用build()方法创建一个新的实例。
CoffeeShop coffeeShop = DaggerCoffeeShop.builder()
.dripCoffeeModule(new DripCoffeeModule())
.build();
注意:
如果您的@Component不是top_level类,则生成的组件的名称将包含其包围类型的名称,并加上下划线。 例如,这段代码:
class Foo {
static class Bar {
@Component
interface BazComponent {}
}
}
将生成一个名为DaggerFoo_Bar_BazComponent的组件。
如果所有的依赖关系在没有设置的情况下创建@Component实例,也就是说Builder可以自动创建实例,此时我们调用create方法来创建过一个@Component实例,而不用Builder
@Component(modules = DripCoffeeModule.class)
interface CoffeeShop {
CoffeeMaker maker();
}
public class CoffeeApp {
public static void main(String[] args) {
CoffeeShop coffeeShop = DaggerCoffeeShop.create();
coffeeShop.maker().brew();
}
}
Dagger2要实现一个完整的依赖注入,必不可少的元素有三种,Module,Component,Container。
1.实现@Module和@Provides注解
Module其实就是依赖关系的加工厂,我们只需在添加制造依赖的方法即可。现创建两个Module:OrangeModule和FruitModule,而FruitModule又依赖于OrangeModule。
@Module
public class OrangeModule {
OrangeBean orangeBean;
// 有参构造函数
// 标明该Module必须初始化
// 如果未初始化,编译时不会报错,
// 但,在运行时报错“IllegalStateException”,提示初始化该Module
public OrangeModule(OrangeBean orangeBean) {
this.orangeBean = orangeBean;
}
// 表明该方法是用来提供依赖对象的特殊方法
// 提供依赖对象 OrangeBean
@Provides
public OrangeBean provideOrange() {
return this.orangeBean;
}
}
// 注明本类属于Module
@Module(includes = OrangeModule.class)
public class FruitModule {
// 表明该方法是用来提供依赖对象的特殊方法
// 提供依赖对象 AppleBean
@Provides
public AppleBean provideApple() {
return new AppleBean("红富士", 4.55);
}
// 表明该方法是用来提供依赖对象的特殊方法
// 提供依赖对象 Fruits
// @Provides
@Provides
public Fruits provideFruits(AppleBean appleBean, OrangeBean orangeBean) {
return new Fruits(appleBean, orangeBean);
}
}
注意:
Component就是一个将Module生成的实例注入Container中的注入器。下面创建FruitComponent,其注入的Module为FruitModule。
// modules 指明Component在哪些Module中查找依赖
@Component(modules = {FruitModule.class})
public interface FruitComponent {
void inject(FruitActivity activity);
AppleBean makeApple();
}
注意:
3. 创建Component实例
创建Component实例,我们就可以从Container中提取注入的实例。
public class FruitActivity extends AppCompatActivity {
***
@Inject
OrangeBean orangeBean;
@Inject
AppleBean appleBean;
@Inject
Fruits fruits;
@Override
protected void onCreate(Bundle savedInstanceState) {
FruitComponent fruitComponent = DaggerFruitComponent.builder()
.orangeModule(new OrangeModule(new OrangeBean("贡菊", 6.88, "江西南昌")))
.build();
fruitComponent.inject(this);
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
****
// orangeBean:OrangeBean{name='贡菊', price=6.88, area='江西南昌'}
Log.d("test", "orangeBean:" + orangeBean.toString());
// appleBean:AppleBean{name='红富士', price=4.55}
Log.d("test", "appleBean:" + appleBean.toString());
// fruits:Fruits{appleBean=AppleBean{name='红富士', price=4.55}, orangeBean=OrangeBean{name='贡菊', price=6.88, area='江西南昌'}}
Log.d("test", "fruits:" + fruits.toString());
AppleBean apple = fruitComponent.makeApple();
// apple:AppleBean{name='红富士', price=4.55}
Log.d("test", "apple:" + apple.toString());
tvFruit.setText(fruits.toString());
}
}
注意:
一个Component可以包含多个Module或者Component,这样Component获取依赖时候会自动从多个Module中查找获取,但是,Module间不能有重复方法。
添加多个Module有两种方法
在Component中的modules属性中注入多个Module
@Component(modules={ModuleA.class,ModuleB.class,...})
public interface FruitComponent{
...
}
在Component中的dependencies属性中注入多个依赖的Component
@Component(dependencies={ComponentA.class,ComponentB.class,...})
public interface FruitComponent{
...
}
在Module中注入多个Module
@Module(includes={ModuleA.class,ModuleB.class,...})
public class FruitModule{
...
}
注入的Module,不管是属于Component本身还是注入的Module的依赖,如果其构造函数为有参构造函数,必须进行初始化。
DaggerFruitComponent.builder()
.orangeModule(new OrangeModule(new OrangeBean("贡菊", 6.88, "江西南昌")))
.build();
在Component中,可以声明方法,比如,makeApple(),直接提供注入对象实例。如果看过Dagger 编译过的DaggerFruitComponent源码的,可以知道,在编译时,针对每一个@Provides方法创建Provider<T>实例。在调用makeApple()方法时,实际上是调用的相应Provider的get()方法,获取相应的实例。有兴趣的,可以跟踪源码看看。
public AppleBean makeApple() {
return provideAppleProvider.get();
}
不管是@Injdec还是@Module都提供了依赖对象,它们注入的优先级是怎样的呢?Dagger 2有自己的依赖规则:
步骤2:若存在创建类方法,查看该方法是否存在参数
步骤3:若不存在创建类方法,则查找Inject注解的构造函数,看构造函数是否存在参数
到这,Dagger 2的基本用法算是结束了,Dagger 2的功能何其强大,远远不如此,仍需继续学习。后续将解决前面提到的“依赖迷失”。
本文中很多地方都是重复提到,每一次重复都是一次血泪史,教训太深刻。多一遍查看,给自己敲响警钟,不要再犯这二笔的错误。