javaee论坛

普通会员

225648

帖子

344

回复

358

积分

楼主
发表于 2019-10-30 08:39:14 | 查看: 128 | 回复: 1

依赖注入框架Dagger2详解(一),依赖注入和控制反转的深入理解依赖注入框架Dagger2详解(二),Java依赖注入标准JSR-330规范依赖注入框架Dagger2详解(三),Java注解处理器APT入门依赖注入框架Dagger2详解(四),初级篇依赖注入框架Dagger2详解(五),中级篇依赖注入框架Dagger2详解(六),高级篇

什么是Dagger2

Dagger2是一种依赖注入的框架,能够在编译时自动生成出一些代码,这些代码可以帮助对应的实例初始化。,它是鼎鼎大名的Square公司旗下又一把利刃,还有一把黄油刀,叫做ButterKnife。

Dagger2起源于Dagger,是一款基于Java注解来实现的完全在编译阶段完成依赖注入的开源库,主要用于模块间解耦、提高代码的健壮性和可维护性。Dagger2在编译阶段通过apt利用Java注解自动生成Java代码,然后结合手写的代码来自动帮我们完成依赖注入的工作。

起初Square公司受到Guice的启发而开发了Dagger,但是Dagger这种半静态半运行时的框架还是有些性能问题,还是基于反射来生成的,这无论在大型的服务端应用还是在Android应用上都不是最优方案。因此Google工程师Fork了Dagger项目,对它进行了改造。于是变演变出了今天我们要讨论的Dagger2,所以说Dagger2其实就是高配版的Dagger。

依赖注入是面向对象编程的一种设计模式,其目的是为了降低程序耦合,这个耦合就是类之间的依赖引起的。

举个例子:我们在写面向对象程序时,往往会用到组合,即在一个类中引用另一个类,从而可以调用引用的类的方法完成某些功能,就像下面这样

publicclassClassA{...ClassBb;...publicClassA(){b=newClassB();}publicvoiddo(){...b.doSomething();...}}

这个时候就产生了依赖问题,ClassA依赖于ClassB,必须借助ClassB的方法,才能完成一些功能。这样看好像并没有什么问题,但是我们在ClassA的构造方法里面直接创建了ClassB的实例,问题就出现在这,在ClassA里直接创建ClassB实例,违背了单一职责原则,ClassB实例的创建不应由ClassA来完成;其次耦合度增加,扩展性差,如果我们想在实例化ClassB的时候传入参数,那么不得不改动ClassA的构造方法,不符合开闭原则。因此我们需要一种注入方式,将依赖注入到宿主类(或者叫目标类)中,从而解决上面所述的问题。依赖注入有一下几种方式:

通过接口注入interfaceClassBInterface{voidsetB(ClassBb);}publicclassClassAimplementsClassBInterface{ClassBclassB;@overridevoidsetB(ClassBb){classB=b;}}通过set方法注入publicclassClassA{ClassBclassB;publicvoidsetClassB(ClassBb){classB=b;}}通过构造方法注入publicclassClassA{ClassBclassB;publicvoidClassA(ClassBb){classB=b;}}通过Java注解publicclassClassA{//此时并不会完成注入,还需要依赖注入框架的支持,如RoboGuice,Dagger2@injectClassBclassB;...publicClassA(){}}

在Dagger2中用的就是最后一种注入方式,通过注解的方式,将依赖注入到宿主类中,这样,ClassA的成员变量就自动初始化classB

我们再看另外一个例子,一个容器里面装的是苹果,不用Dagger2的情况下我们应该这么写:

publicclassContainer{Fruitf=newApple(color,size);...}

上面例子面临着一个问题,Container依赖了Apple实现,如果某一天需要修改Apple为Banana,那么你一定得改Container的代码。有没有一种方法可以不改Container呢?可以使用Dagger2,我们可以把代码改成

publicclassContainer{@InjectFruitf;...}

这样,Container的成员变量就自动初始化成Apple实例了,Container不用关心具体用哪个Fruit的实现,也不用关心到底用什么颜色多大的苹果。假如某一天要把苹果替换成香蕉,Container的代码是完全不需要改动的。从某种意义上说,Dagger2就是一个帮你写工厂代码的工具,当然Dagger2的功能比工厂模式更加强大。

Dagger2的三要素

Dagger2要实现一个完整的依赖注入,必不可少的元素有三种,Module,Component,Container

Container就是可以被注入的容器,具体对应上文例子中的Container,Container拥有需要被初始化的元素。需要被初始化的元素必须标上@Inject,只有被标上@Inject的元素才会被自动初始化。@Inject在Dagger2中一般标记构造方法与成员变量。Module可以说就是依赖的原材料的制造工厂,所有需要被注入的元素的实现都是从Module生产的。有了可以被注入的容器Container,也有了提供依赖对象的Module。我们必须将依赖对象注入到容器中,这个过程由Component来执行。Component将Module中产生的依赖对象自动注入到Container中。如何引入Dagger2配置apt插件(在build.gradle(Project:xxx)中添加如下代码)dependencies{classpath'com.android.tools.build:gradle:2.1.0'//添加apt插件classpath'com.neenbedankt.gradle.plugins:android-apt:1.8'}添加依赖(在build.gradle(Module:app)中添加如下代码)applyplugin:'com.android.application'//添加如下代码,应用apt插件applyplugin:'com.neenbedankt.android-apt'...dependencies{...compile'com.google.dagger:dagger:2.4'apt'com.google.dagger:dagger-compiler:2.4'//java注解compile'org.glassfish:javax.annotation:10.0-b28'...}基本用法实现Module

Module其实就是一个依赖的制造工厂。我们只需要为其添加制造依赖的方法即可。

@Module//1注明本类属于ModulepublicclassFruitModule{@Provides//2注明该方法是用来提供依赖对象的特殊方法publicFruitprovideFruit(){returnnewApple(Color.RED,Size.BIG);}}

(1)中添加注解@Module注明本类属于Module(2)中添加注解@Provides注明该方法是用来提供依赖对象的特殊方法一个完整的Module必须拥有@Module与@Provides注解

实现Component

Component就是一个将Module生成的实例注入Container中的注入器。我们来写一个水果注入器:

@Component(modules={FruitModule.class})//3指明Component在哪些Module中查找依赖publicinterfaceFruitComponent{//4接口,自动生成实现voidinject(Containercontainer);//5注入方法,在Container中调用}

(3)中@Component使用modules指向使用的Module的集合。(4)所有的Component都必须以接口形式定义。Dagger2框架将自动生成Component的实现类,对应的类名是Dagger×××××,这个例子中对应的实现类是DaggerFruitComponent(5)中添加注入方法,一般使用inject做为方法名,方法参数为对应的Container

实现Container

Container就是可以被注入依赖关系的容器。具体实现如下

publicContainer{@Inject//6添加@Inject,标记f可以被注入Fruitf;publicvoidinit(){DaggerFruitComponent.create().inject(this);//7使用FruitComponent的实现类注入}}

Container除了代码中(6)标记f需要被注入外,还需要代码中(7)调用Component的实现类将Module的生成的对象注入到f中。到此,当调用Container的init()方法时,Contianer中的f将会自动初始化成实现类Apple的对象。以后如果想更改Fruit的实现类,只需要在@Component中的modules指向不同的Module即可。而Container的代码完全不需要改动。因为Container已经不再依赖Apple实现了。这就是Dagger2的基本用法,下一篇我们将学习它的更高级一些的用法。


普通会员

0

帖子

304

回复

332

积分
沙发
发表于 2024-04-25 05:00:47

围观

您需要登录后才可以回帖 登录 | 立即注册

触屏版| 电脑版

技术支持 历史网 V2.0 © 2016-2017