依赖注入框架Dagger2详解(四),初级篇
发布日期:2021-07-01 03:06:16 浏览次数:2 分类:技术文章

本文共 4236 字,大约阅读时间需要 14 分钟。

什么是Dagger2

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

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

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

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

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

public class ClassA {    ...    ClassB b;    ...    public ClassA() {        b = new ClassB();    }        public void do() {        ...        b.doSomething();        ...    }}

这个时候就产生了依赖问题,ClassA依赖于ClassB,必须借助ClassB的方法,才能完成一些功能。这样看好像并没有什么问题,但是我们在ClassA的构造方法里面直接创建了ClassB的实例,问题就出现在这,在ClassA里直接创建ClassB实例,违背了单一职责原则,ClassB实例的创建不应由ClassA来完成;其次耦合度增加,扩展性差,如果我们想在实例化ClassB的时候传入参数,那么不得不改动ClassA的构造方法,不符合开闭原则。

因此我们需要一种注入方式,将依赖注入到宿主类(或者叫目标类)中,从而解决上面所述的问题。依赖注入有一下几种方式:

  • 通过接口注入
interface ClassBInterface {    void setB(ClassB b);}public class ClassA implements ClassBInterface {    ClassB classB;        @override    void setB(ClassB b) {        classB = b;    }}
  • 通过set方法注入
public class ClassA {    ClassB classB;        public void setClassB(ClassB b) {        classB = b;    }}
  • 通过构造方法注入
public class ClassA {    ClassB classB;        public void ClassA(ClassB b) {        classB = b;    }}
  • 通过Java注解
public class ClassA {    //此时并不会完成注入,还需要依赖注入框架的支持,如RoboGuice,Dagger2    @inject ClassB classB;        ...    public ClassA() {}}

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

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

public class Container{    Fruit f = new Apple(color,size);    ...}

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

可以使用Dagger2,我们可以把代码改成

public class Container{    @Inject    Fruit f;    ...}

这样,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)中添加如下代码)
apply plugin: 'com.android.application'//添加如下代码,应用apt插件apply plugin: '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  注明本类属于Modulepublic class FruitModule{    @Provides  //2 注明该方法是用来提供依赖对象的特殊方法    public Fruit provideFruit(){        return new Apple(Color.RED,Size.BIG);    }}

(1)中添加注解@Module注明本类属于Module

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

实现Component

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

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

(3)中@Component使用modules指向使用的Module的集合。

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

实现Container

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

public Container{     @Inject   //6 添加@Inject,标记f可以被注入     Fruit f;     public void init(){         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的基本用法,下一篇我们将学习它的更高级一些的用法。

转载地址:https://menxindiaolong.blog.csdn.net/article/details/90412107 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:依赖注入框架Dagger2详解(五),中级篇
下一篇:依赖注入框架Dagger2详解(三),Java注解处理器APT入门

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年05月07日 16时38分04秒