依赖注入的诉求, 这边就不重复描述了, 在上文Spring以及Guice的IOC文档中都有说起, 既然有了Guice,html
Google为啥还要搞个Dagger2出来重复造轮子呢? 由于使用动态注入, 虽然写法简单了, 耦合也下降了,java
可是带来了调试不方便, 反射性能差等一些缺点.apache
而Dagger跟Guice最大的差别在于, 他是编译期注入的, 而不是运行时.框架
他生成的代码能够直观的调试, 也不是经过反射, 而是经过构建工厂类. 下面咱们用代码来简单演示一下.maven
既然Dagger是静态注入的, 那么他天然也跟其余动态注入框架工程有点区别,ide
编译时须要额外依赖dagger-compiler, dagger-producers等,函数
不过运行时的jar只须要dagger以及javax.inject包便可.性能
好在Google为咱们提供了pom文件, 咱们只须要在idea里新建maven工程, 在pom文件中导入以下内容, 他会自动下载依赖.ui
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待打印.this
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便可.
默认对象都是每次都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中.
默认对象是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基本差很少, 各个注解概念也相似,
最大的区别就是非动态注入, 非反射实现, 而是编译期静态注入.