码迷,mamicode.com
首页 > 其他好文 > 详细

使用Dagger2做静态注入, 对比Guice.

时间:2017-12-22 17:33:51      阅读:414      评论:0      收藏:0      [点我收藏+]

标签:name   不用   policy   blog   oid   span   component   如何   logs   

Dagger

依赖注入的诉求, 这边就不重复描述了, 在上文Spring以及Guice的IOC文档中都有提及, 既然有了Guice,

Google为啥还要搞个Dagger2出来重复造轮子呢? 因为使用动态注入, 虽然写法简单了, 耦合也降低了,

但是带来了调试不方便, 反射性能差等一些缺点.

而Dagger跟Guice最大的差异在于, 他是编译期注入的, 而不是运行时.

他生成的代码可以直观的调试, 也不是通过反射, 而是通过构建工厂类. 下面我们用代码来简单演示一下.

构建工程

既然Dagger是静态注入的, 那么他自然也跟其他动态注入框架工程有点区别,

编译时需要额外依赖dagger-compiler, dagger-producers等,

技术分享图片

不过运行时的jar只需要dagger以及javax.inject包即可.

好在Google为我们提供了pom文件, 我们只需要在idea里新建maven工程, 在pom文件中导入如下内容, 他会自动下载依赖.

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0"
 3          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5     <modelVersion>4.0.0</modelVersion>
 6 
 7     <groupId>com.maven.dagger2</groupId>
 8     <artifactId>com.maven.dagger2</artifactId>
 9     <version>1.0-SNAPSHOT</version>
10 
11     <dependencies>
12         <dependency>
13             <groupId>com.google.dagger</groupId>
14             <artifactId>dagger</artifactId>
15             <version>2.2</version>
16         </dependency>
17         <dependency>
18             <groupId>com.google.dagger</groupId>
19             <artifactId>dagger-compiler</artifactId>
20             <version>2.2</version>
21             <optional>true</optional>
22         </dependency>
23     </dependencies>
24 </project>

第一个注入程序

我们以一个打印系统为例, 打印业务类PrintJob, 里面有一份报表Reportpage待打印.

1 public class ReportPage{
2 
3     public void print(){
4         System.out.println("开始打印报表");
5     }
6 }
 1 public class PrintJob {
 2     // 需要打印的报表 
 3     public ReportPage reportPage;
 4 
 5     public void setReportPage(ReportPage reportPage) {
 6         this.reportPage = reportPage;
 7     }
 8 
 9     public void print() {
10         this.reportPage.print();
11     }
12 
13     public static void main(String[] args) throws InterruptedException {
14         // 初始化报表
15         ReportPage page = new ReportPage();
16         PrintJob job = new PrintJob();
17         job.setReportPage(page);
18         //执行打印
19         job.print();
20     }
21 }

在main函数中, 我们初始化了Printjob以及它里面的报表对象, 并执行打印.

下面我们通过Dagger注入的方式来写.

写法很简单, 跟Guice类似, 我们只需要在reportpage成员上加@Inject注解.

同时添加一个Component对象, 用来告诉Dagger, 应该注入到该类, 并扫描其中@Inject的成员

1 @Component
2 public interface PrintjobComponent {
3 
4     void inject(PrintJob job);
5 }

添加完Component以及@Inject注解后我们需要编译代码或者rebuild工程, 让Dagger为我们生成工厂类.

生成的代码位于target/generated-sources目录. 里面会有一个叫DaggerPrintjobComponent的类.

技术分享图片

idea会自动将当期路径标记成Classpath, 因此我们也不需要把他手动拷贝出来.

如果没有自动import, 可以右键pom.xml->Maven ->Reimport.

我们在Printjob的构造函数里加上DaggerPrintjobComponent.create().inject(this);来实现注入

 1 public class PrintJob {
 2 
 3     @Inject
 4     public ReportPage reportPage;
 5 
 6     public PrintJob() {
 7         DaggerPrintjobComponent.create().inject(this);
 8     }
 9 
10     public void print() {
11         this.reportPage.print();
12     }
13 
14     public static void main(String[] args) throws InterruptedException {
15         // 看上去清爽了一点
16         PrintJob job = new PrintJob();
17         job.print();
18     }
19 }
 1 public class ReportPage {
 2 
 3     @Inject
 4     public ReportPage() {
 5         System.out.println("初始化成功!!!");
 6     }
 7 
 8     public void print(){
 9         System.out.println("开始打印报表");
10     }
11 }

相比于一开始的非注入写法, 在外部是看不到赋值操作的.

有人会说, 那我直接在printjob的构造函数里new reportpage()不就行了, 为什么要这么费事呢.

原因很简单, 大型系统里, printjob只存在一个接口, 他无法, 也不需要直接new reportpage()对象.

下面演示如何注入接口对象.

注入接口对象

我们给reportpage增加一个接口, 并在printjob中修改为接口声明.

1 public class ReportPage implements ReportPageProvider{
1 public interface ReportPageProvider {
2 
3     void print();
4 }
1 public class PrintJob {
2 
3     @Inject
4     public ReportPageProvider reportPage;

这个时候会发现, 运行注入报错了, 原因很简单, 我们@inject依然加载reportpage对象上,

此时他是一个接口, 接口是无法直接被实例化的.

因此我们需要引入Module对象来处理接口, 其实就是类似于一个工厂提供类.

1 @Module
2 public class ReportPageModule {
3 
4     @Provides
5     public ReportPageProvider createPage() {
6         return new ReportPage();
7     }
8 }

然后在component中引入module, 其他代码不用改, 依然直接new printjob().print()对象.

1 @Component(modules = ReportPageModule.class)
2 public interface PrintjobComponent {
3 
4     void inject(PrintJob job);
5 }

接口存在多个实现

我们给ReportpageProvider再增加一个子类NewReportPage, 修改Module, 增加一个方法, 构造NewReportPage.

 1 @Module
 2 public class ReportPageModule {
 3 
 4     @Provides
 5     public ReportPageProvider createPage() {
 6         return new ReportPage();
 7     }
 8 
 9     @Provides
10     public ReportPageProvider createNewReportPage() {
11         return new NewReportPage();
12     }
13 
14 }

这个时候直接编译是无法通过的, 返回类型的provider只能添加一个, 如果添加多个, dagger将报错, 存在多个提供类.

 技术分享图片

此时我们就要跟Guice里一样, 使用@Named注解来标识了

1     @Named("new")
2     public ReportPageProvider reportPage;

调用的时候也很简单

1     @Inject
2     @Named("new")
3     public ReportPageProvider reportPage;

同理, 也可以通过@Qualifier来自定义注解标识.

1 @Qualifier
2 @Retention(RetentionPolicy.RUNTIME)
3 public @interface NewReportMark {}

然后在调用的地方加上 @NewReportMark即可.

Scope生命周期

默认对象都是每次都new的, 如果想要单例实现, 则需要添加@Singleton.

在Component以及Module都加上Singleton注解.

1 @Singleton
2 @Component(modules = ReportPageModule.class)
3 public interface PrintjobComponent {
4 
5     void inject(PrintJob job);
6 }
1     @Provides
2     @Named("new")
3     @Singleton
4     public ReportPageProvider createNewReportPage() {
5         return new NewReportPage();
6     }

我们给Printjob中再增加一个reportpage对象, 并打印他们的hashcode.

 1     @Inject
 2     @Named("new")
 3     public ReportPageProvider reportPage;
 4 
 5     @Inject
 6     @Named("new")
 7     public ReportPageProvider reportPage2;
 8 
 9 ......
10 
11     PrintJob job = new PrintJob();
12     System.out.println(job.reportPage);
13     System.out.println(job.reportPage2);

加上Singleton注解后, 打印出来的hashcode是一致的了.

但是, 如果我们再new 一个Printjob, 打印他的reportpage.

1         PrintJob job = new PrintJob();
2         System.out.println(job.reportPage);
3         System.out.println(job.reportPage2);
4 
5         PrintJob job2 = new PrintJob();
6         System.out.println(job2.reportPage);
7         System.out.println(job2.reportPage2);

会发现前两个的hashcode跟后两个的不一样, 这就很蛋疼了. 他只是一个作用于当前component的伪单例.

那么如何实现真单例呢, 其实就是想办法把Component搞成单例的.

这样他里面的对象也都是同一个作用域下的单例了.

我们添加一个SingletonPrintjobComponent, 写法与PrintjobComponent一致.

编译后生成DaggerSingletonPrintjobComponent. 然后修改printjob构造函数中的注入.

DaggerPrintjobComponent.create().inject(this); 改成如下:

 1 public class PrintJob {
 2 
 3     private static SingletonPrintjobComponent component = DaggerSingletonPrintjobComponent.create();
 4 
 5     @Inject
 6     @Named("new")
 7     public ReportPageProvider reportPage;
 8 
 9     @Inject
10     @Named("new")
11     public ReportPageProvider reportPage2;
12 
13     public PrintJob() {
14         component.inject(this);
15     }
16 
17     public void print() {
18         this.reportPage.print();
19     }
20 
21     public static void main(String[] args) throws InterruptedException {
22         PrintJob job = new PrintJob();
23         System.out.println(job.reportPage);
24         System.out.println(job.reportPage2);
25 
26         PrintJob job2 = new PrintJob();
27         System.out.println(job2.reportPage);
28         System.out.println(job2.reportPage2);
29     }
30 }

这样的话, 多个printjob打印出来的reportpage就是一致的了, 因为都是位于同一个static的component中.

Lazy 延迟初始化

默认对象是inject的时候初始化, 如果使用Lazy封装一下, 则可以在get的时候再初始化.

1     @Inject
2     @Named("old")
3     public Lazy<ReportPageProvider> oldReportPage;
1         PrintJob job = new PrintJob();
2         Thread.sleep(3000);
3         // 对象会在get()方法调用的时候触发初始化
4         job.oldReportPage.get().print();

到这边就结束了, 可以看到Dagger使用上跟Guice基本差不多, 各个注解概念也类似,

最大的区别就是非动态注入, 非反射实现, 而是编译期静态注入.

 

使用Dagger2做静态注入, 对比Guice.

标签:name   不用   policy   blog   oid   span   component   如何   logs   

原文地址:http://www.cnblogs.com/xdecode/p/8086906.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!