Dagger 2从浅到深(二)
来源:互联网 发布:电脑usb端口怎么设置 编辑:程序博客网 时间:2024/06/01 10:22
Dagger系列:
- Dagger 2从浅到深(一)
- Dagger 2从浅到深(二)
- Dagger 2从浅到深(三)
- Dagger 2从浅到深(四)
- Dagger 2从浅到深(五)
- Dagger 2从浅到深(六)
- Dagger 2从浅到深(七)
- Dagger 2应用于Android的完美扩展库-dagger.android
在Dagger 2从浅到深(一)中,了解到Dagger依赖注入的基本用法,心里有两个盲点:
- @Provides是什么呢?
- 注解的构造函数是有参构造函数,该如何传递参数?
带着这两个疑问,开始本文章的话题。
所涉及到的注解
@Provides
@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
@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提供依赖关系。具体该如何使用,后续更精彩!
@Component
@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的组件。
- 对于具有默认构造函数的Module,可以被忽略。如果没有设置,Builder将自动创建一个实例。
- 对于所有的@Provides方法都是静态的Module,没有必要创建Module实例
如果所有的依赖关系在没有设置的情况下创建@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。
- Container就是可以被注入的容器,具体对应上文例子中的Container,Container拥有需要被初始化的元素。需要被初始化的元素必须标上@Inject,只有被标上@Inject的元素才会被自动初始化。@Inject在Dagger2中一般标记构造方法与成员变量。
- Module 可以说就是依赖的原材料的制造工厂,所有需要被注入的元素的实现都是从Module生产的。
- 有了可以被注入的容器Container,也有了提供依赖对象的Module。我们必须将依赖对象注入到容器中,这个过程由Component来执行。Component将Module中产生的依赖对象自动注入到Container中。
依赖关系
- 在@Module中,由@Provides注解的方法,可以由@Component.modules直接饮用,或通过@Module.includes引用
- 任何类型的构造被@Inject注解的构造函数,不管是否与该组建的的生命周期相关联(是否被@Scope注释)
- 组件依赖关系所配置的方法
- 组件本身
- 任何包含的子组件的不合格建造者
- Provider或Lazy包装器用于上述任何绑定
- 任何上述绑定的延迟提供者(例如,Provider
简单示例
1.实现@Module和@Provides注解
Module其实就是依赖关系的加工厂,我们只需在添加制造依赖的方法即可。现创建两个Module:OrangeModule和FruitModule,而FruitModule又依赖于OrangeModule。
@Modulepublic 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); }}
注意:
- @Module注解类,表明该类是一个Module
- @Provides注解方法,表明方法是用来提供依赖对象的特殊方法
- 一个完整的Module必须拥有@Module与@Provides注解
- 实现Component
Component就是一个将Module生成的实例注入Container中的注入器。下面创建FruitComponent,其注入的Module为FruitModule。
// modules 指明Component在哪些Module中查找依赖@Component(modules = {FruitModule.class})public interface FruitComponent { void inject(FruitActivity activity); AppleBean makeApple();}
注意:
- @Component中使用modules,表明该Component在哪些注入的Module中查找依赖
- @Component中使用dependencie,表明该Component在哪些注入的Component中查找依赖
- 添加注入方法,一般使用inject
- 可以声明方法,提供注入的实例
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();
- 返回类型相同的@Provides 方法必须是唯一的。如果不唯一,Dagger 2不知道如何去区分,也就是所谓的“依赖迷失”。鉴于Java的多态性-重载,这种情况必然会出现。如何解决“依赖迷失”,后续介绍。
在Component中,可以声明方法,比如,makeApple(),直接提供注入对象实例。如果看过Dagger 编译过的DaggerFruitComponent源码的,可以知道,在编译时,针对每一个@Provides方法创建Provider<T>实例。在调用makeApple()方法时,实际上是调用的相应Provider的get()方法,获取相应的实例。有兴趣的,可以跟踪源码看看。
public AppleBean makeApple() { return provideAppleProvider.get();}
依赖规则
不管是@Injdec还是@Module都提供了依赖对象,它们注入的优先级是怎样的呢?Dagger 2有自己的依赖规则:
- 步骤1:查找Module中是否存在创建该类的方法。
步骤2:若存在创建类方法,查看该方法是否存在参数
- 步骤2.1:若存在参数,则按从步骤1开始依次初始化每个参数
- 步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
步骤3:若不存在创建类方法,则查找Inject注解的构造函数,看构造函数是否存在参数
- 步骤3.1:若存在参数,则从步骤1开始依次初始化每个参数
- 步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
总结
到这,Dagger 2的基本用法算是结束了,Dagger 2的功能何其强大,远远不如此,仍需继续学习。后续将解决前面提到的“依赖迷失”。
本文中很多地方都是重复提到,每一次重复都是一次血泪史,教训太深刻。多一遍查看,给自己敲响警钟,不要再犯这二笔的错误。
参考文档
- 浅析Dagger2的使用
- Android:dagger2让你爱不释手-基础依赖注入框架篇
- Android:dagger2让你爱不释手-重点概念讲解、融合篇
- Android:dagger2让你爱不释手-终结篇
- “一盘沙拉”带你入门Dagger2(一)之HelloWorld
- Dagger2图文完全教
- 为什么网上这么多dagger2教程,我还写了这篇文章。
- Android常用开源工具(1)-Dagger2入门
- Dagger 2从浅到深(二)
- Dagger 2从浅到深(一)
- Dagger 2从浅到深(三)
- Dagger 2从浅到深(四)
- Dagger 2从浅到深(五)
- Dagger 2从浅到深(六)
- Dagger 2从浅到深(七)
- Dagger 2
- Dagger入门二
- Dagger 2学习笔记
- Dagger 2 入门
- Dagger 2 before
- Dagger 2 初体验
- dagger 2 详解
- Dagger
- Dagger
- dagger
- dagger
- PHP buffer的机制
- 第八届蓝桥杯第十题
- 使用jsonp来实现跨域请求
- 求阶乘之和 1!+2!+3!+...+10!
- Android root检测方法小结
- Dagger 2从浅到深(二)
- static
- bzoj 2120 数颜色 待修改的莫队
- CSV导出大量数据
- node.js的generic-pool与mysql结合,mysql连接池
- Anjular组件化
- POJ 2236 Wireless Network(并差集)
- linux脚本使用-------安装madplay及MPlayer播放器脚本分析及命令参数前加“横杠”的背景解释
- crypto加密