Dagger2 神器入门(二)

2017 年 9 月 10 日 ImportNew

(点击上方公众号,可快速关注)


来源:小腊月,

www.jianshu.com/p/c673e6e73c8b

如有好文章投稿,请点击 → 这里了解详情


前言


Dagger2神器入门(一)中,我们了解了什么是依赖注入,那么在这一章中,我们将逐渐入门Dagger2。接下来我们会通过demo形式给大家展现Dagger2的神奇之处。


上文中通过”开车上班”的例了解了什么是”依赖注入”,那么我们继续这例子。我们知道车由发动机,轮子,车座等部件组成。那么,如果我们要造一辆车的话,可能需要这些部件。回归到代码中,我们new一个Car可能需要发动机,轮子,车座等对象。


原始依赖


首先,我们创建造车所需要的部件。

发动机


/**

 * 发动机

 */

public class Engine {

 

public Engine(){

    Log.d(Config.TAG,"new Engine()");

  }

}


车座


/**

 * 车座

 */

public class Seat {

    public Seat(){

        Log.d(Config.TAG,"new Seat()");

    }

}


轮子


/**

 * 轮子

 */

public class Wheel {

    public Wheel(){

        Log.d(Config.TAG,"new Wheel()");

    }

}


上面3个类用到了Config配置类,其实就是一个字符串。


public class Config {

    public static final String TAG = "TAG";

}


另话:把公共的部分抽取出来,也是代码规范的一部分。在今后的工作中,需要不断review自身代码,随着技术水平的提高,代码质量也需要不断提高。


上面的代码中,我们写了3个类,都是用来造车的构件。那么对于造车,对应于我们的代码就是new Car(),就是这么简单。但是Car可能需要Engine,Seat,Wheel 等组件。那么我们来造个车试试。


public class Car {

    private Engine engine;

    private Seat seat;

    private Wheel wheel;

    public Car() {

        engine = new Engine();

        seat = new Seat();

        wheel = new Wheel();

        Log.d(Config.TAG, "new Car()");

    }

}


按照正常逻辑,Car类应该是这样写。那么我们在new Car()试试。


06-27 12:44:53.726 18967-18967/com.bae.basicandext D/TAG: new Engine()

06-27 12:44:53.726 18967-18967/com.bae.basicandext D/TAG: new Seat()

06-27 12:44:53.726 18967-18967/com.bae.basicandext D/TAG: new Wheel()

06-27 12:44:53.726 18967-18967/com.bae.basicandext D/TAG: new Car()


就这样,我们把Car给new出来了,这样写也是没有问题的。

那么接下来我们用Dagger2的方式,来做做试试,先不管他们之间的区别,just do it。做出来效果之后再回过头来反思。


Dagger2依赖注入


下面让我们一步一步走下去:


1 在modle的build.gradle文件中添加


annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'

compile 'com.google.dagger:dagger:2.0.2' 

provided 'org.glassfish:javax.annotation:10.0-b28'


2 写一个Module类,管理上面的三个依赖。


@Module

public class CarModule {

    @Provides

    public Engine provideEngine(){

        return new Engine();

    }

 

    @Provides

    public Seat provideSeat(){

        return new Seat();

    }

 

    @Provides

    public Wheel provideWheel(){

        return new Wheel();

    }

}


3 写一个Component类,来连接Module和你的Car。


@Component(modules = CarModule.class)

public interface CarComponent {

  void inject(Car car);

}


4 重写Car类


public class Car {

 

  @Inject

  Engine engine;

  @Inject

  Seat seat;

  @Inject

  Wheel wheel;

//    private Engine engine;

//    private Seat seat;

//    private Wheel wheel;

 

  public Car() {

//        engine = new Engine();

//        seat = new Seat();

//        wheel = new Wheel();

    DaggerCarComponent

            .builder()

            .carModule(new CarModule())

            .build()

            .inject(this);

    Log.d(Config.TAG, "new Car()");

 

  }

}


看看输出


06-27 13:03:25.447 26227-26227/com.bae.basicandext D/TAG: new Engine()

06-27 13:03:25.447 26227-26227/com.bae.basicandext D/TAG: new Seat()

06-27 13:03:25.447 26227-26227/com.bae.basicandext D/TAG: new Wheel()

06-27 13:03:25.447 26227-26227/com.bae.basicandext D/TAG: new Car()


是不是达到了和之前一样的效果呢?是不是从第3步开始,就不知道为什么这样写了呢?


分析


我们看看CarModule类是用一个@Module注解的类,里面的方法是使用@Provides注解。什么意思呢?


@Moudle 表示该类能够管理并提供依赖;你需要造车,但是车依赖于发动机,轮胎以及车座,那么写一个@Module注解的类来帮你管理这些依赖。

@Provides 表示该方法提供依赖;通过这个注解的方法,能给你提供依赖,看代码应该清楚。


我们知道了管理并提供依赖的类,那么我们就可以通过它来直接使用依赖。但是Dagger2为了解耦,提供了一个中介,@Component注解,也就是我们的第4步。


@Component(modules = CarModule.class)

public interface CarComponent {

     void inject(Car car);

  }


我们要清楚,@Component就是一个中间人,里面存着依赖提供者和依赖需求者。在这里


@Component(modules = CarModule.class)


表示的是需要在CarModule类中去寻找依赖,void inject(Car car);这个方法是抽象的,表示需要将这些依赖应用到Car类。说白了就是Car类需要CarModule来提供依赖。


那么我们来看看@Component的官方文档。


* Annotates an interface or abstract class for which a fully-formed,     dependency-injected

 * implementation is to be generated from a set of {@linkplain #modules}.


说的是这个注解只能用于接口或者抽象类。将代码改成下面,输出也是一样的。


@Component(modules = CarModule.class)

public abstract class CarComponent {

    public abstract void inject(Car car);

}


在上面的步骤中,我们搞定了依赖提供者,中间人,现在我们要看看依赖需求者。在这我们的需求者是Car,也就是上面写的Car类。


我们用到了@Inject注解,


@Inject

Engine engine;


上面的代码表示engine这个属性你不用像一般情况去初始化(engine= new Engine()),它能给你自动寻找依赖。但是如果是这样肯定是不行的,还需要


DaggerCarComponent

       .builder()

       .carModule(new CarModule())

       .build()

       .inject(this);


DaggerCarComponent是apt工具帮我们生成的类,实现了CarComponent接口。

通过carModule()将我们的依赖提供者传入,通过inject()将我们的Car对象传入,这样就达到了中间人的目的。


@Generated("dagger.internal.codegen.ComponentProcessor")

public final class DaggerCarComponent implements CarComponent {

     private Provider<Engine> provideEngineProvider;

     private Provider<Seat> provideSeatProvider;

     private Provider<Wheel> provideWheelProvider;

     private MembersInjector<Car> carMembersInjector;

 

    private DaggerCarComponent(Builder builder) {  

        assert builder != null;

        initialize(builder);

    }

 

    private void initialize(final Builder builder) {  

        this.provideEngineProvider = CarModule_ProvideEngineFactory.create(builder.carModule);

        this.provideSeatProvider = CarModule_ProvideSeatFactory.create(builder.carModule);

        this.provideWheelProvider = CarModule_ProvideWheelFactory.create(builder.carModule);

        this.carMembersInjector = Car_MembersInjector.create(provideEngineProvider, provideSeatProvider, provideWheelProvider);

}

 

    public static Builder builder() {  

        return new Builder();

      }

      public static CarComponent create() {  

        return builder().build();

      }

     @Override

      public void inject(Car car) {  

          carMembersInjector.injectMembers(car);

      }

}


上面的代码是自动生成的。也就是你在CarComponent接口中加了@Component注解,然后注解处理器(dagger.internal.codegen.ComponentProcessor)就能帮你生成上面的代码,要不然程序如何知道你的注解是什么意思?


后记


如果对于注解相关知识不太了解,可以看看《Java编程思想》注解那一章。我提两点:


  1. 不是所有注解都用到了反射,只有@Retention(RUNTIME)才可能会用到反射。关于什么是反射,这里有涉及到类加载机制,不明白的可以翻翻我之前的blog。

  2. 自定义注解都需要注解处理器来处理的,不然你随便定义一个注解,谁能明白?就像上面的ComponentProcessor类一样,处理@Component注解。


这章主要是入门了Dagger2,下面我们会了解


  1. 为什么要使用Dagger2来替代文章一开头的写法?

  2. 如果@Inject注解的构造器有多个怎么办?

  3. 如果存在依赖链怎么办呢?


Tips


学习不要贪多,一点一点的消化,逐个击破,你会发现原来自己会的很多。比如讲Dagger的时候,我们会用到注解,那就得去了解一下注解相关知识;一提到注解很多人就会想到反射,那就要去看看反射的内容;反射里面涉及了类加载机制,又可以看看JVM相关的知识。学着学着,你会发现所有的内容都是相关的,这就是一种学习的境界了。不断的联想,不断的巩固,才能不断的提高。


看完本文有收获?请转发分享给更多人

关注「ImportNew」,看技术干货

登录查看更多
0

相关内容

【2020新书】实战R语言4,323页pdf
专知会员服务
98+阅读 · 2020年7月1日
一份简明有趣的Python学习教程,42页pdf
专知会员服务
76+阅读 · 2020年6月22日
《动手学深度学习》(Dive into Deep Learning)PyTorch实现
专知会员服务
116+阅读 · 2019年12月31日
【新书】傻瓜式入门深度学习,371页pdf
专知会员服务
183+阅读 · 2019年12月28日
【书籍】深度学习框架:PyTorch入门与实践(附代码)
专知会员服务
160+阅读 · 2019年10月28日
机器学习入门的经验与建议
专知会员服务
90+阅读 · 2019年10月10日
用 Python 开发 Excel 宏脚本的神器
私募工场
26+阅读 · 2019年9月8日
3 行代码 5 秒抠图的 AI 神器,根本无需 PS
大数据技术
20+阅读 · 2019年7月24日
告别 PS !3 行代码 5 秒搞定抠图的 AI 神器!
程序人生
6+阅读 · 2019年7月11日
手把手教你用Python做一个哄女友神器,小白可上手
网易智能菌
5+阅读 · 2019年6月15日
Windows开源无人机仿真工具:AirSim1.0 入门
无人机
25+阅读 · 2019年6月8日
已删除
将门创投
18+阅读 · 2019年2月18日
深度强化学习入门,这一篇就够了!
机器学习算法与Python学习
26+阅读 · 2018年8月17日
10行代码实现目标检测,请收下这份教程
极市平台
4+阅读 · 2018年6月27日
Python NLP入门教程
计算机与网络安全
8+阅读 · 2017年11月21日
Python NLP 入门教程
开源中国
14+阅读 · 2017年10月1日
Arxiv
3+阅读 · 2018年10月8日
Arxiv
16+阅读 · 2018年4月2日
Arxiv
5+阅读 · 2018年3月6日
VIP会员
相关VIP内容
【2020新书】实战R语言4,323页pdf
专知会员服务
98+阅读 · 2020年7月1日
一份简明有趣的Python学习教程,42页pdf
专知会员服务
76+阅读 · 2020年6月22日
《动手学深度学习》(Dive into Deep Learning)PyTorch实现
专知会员服务
116+阅读 · 2019年12月31日
【新书】傻瓜式入门深度学习,371页pdf
专知会员服务
183+阅读 · 2019年12月28日
【书籍】深度学习框架:PyTorch入门与实践(附代码)
专知会员服务
160+阅读 · 2019年10月28日
机器学习入门的经验与建议
专知会员服务
90+阅读 · 2019年10月10日
相关资讯
用 Python 开发 Excel 宏脚本的神器
私募工场
26+阅读 · 2019年9月8日
3 行代码 5 秒抠图的 AI 神器,根本无需 PS
大数据技术
20+阅读 · 2019年7月24日
告别 PS !3 行代码 5 秒搞定抠图的 AI 神器!
程序人生
6+阅读 · 2019年7月11日
手把手教你用Python做一个哄女友神器,小白可上手
网易智能菌
5+阅读 · 2019年6月15日
Windows开源无人机仿真工具:AirSim1.0 入门
无人机
25+阅读 · 2019年6月8日
已删除
将门创投
18+阅读 · 2019年2月18日
深度强化学习入门,这一篇就够了!
机器学习算法与Python学习
26+阅读 · 2018年8月17日
10行代码实现目标检测,请收下这份教程
极市平台
4+阅读 · 2018年6月27日
Python NLP入门教程
计算机与网络安全
8+阅读 · 2017年11月21日
Python NLP 入门教程
开源中国
14+阅读 · 2017年10月1日
Top
微信扫码咨询专知VIP会员