Dagger依赖注入入门

cqpw8262 8年前

来自: https://segmentfault.com/a/1190000004537518

依赖注入

目的:将类之间的依赖关系交由第三方管理spring IOC就是很有名的依赖注入框架,但是这个框架基于反射来实现,对性能要求比较高,所以不适合android平台。dagger基于预编译的方式完成依赖注入。

Dagger使用步骤

在使用Dagger前,首先要导入相关的包,如果使用Maven进行项目构建,则添加如下依赖即可:

      <dependency>            <groupId>com.squareup.dagger</groupId>            <artifactId>dagger</artifactId>            <version>1.2.2</version>            <optional>true</optional>        </dependency>          <dependency>            <groupId>com.squareup.dagger</groupId>            <artifactId>dagger-compiler</artifactId>            <version>1.2.2</version>        </dependency>

注意这里第二个依赖dagger-compiler很重要,一开始没有加入这个依赖的时候,一直在报如下错误:

Module adapter for class XXXXX could not be loaded.Please ensure that code generation was run for this module.

原因是,dagger的依赖注入是通过预编译而非反射来完成的(但是dagger还有些地方用到了反射【具体是哪里还有待研究】,dagger2完全脱离了反射)。dagger-compiler库中的函数完成编译工程,在编译的过程中创建注入依赖所需要的类。

Dagger中基本的三个注解:@Inject,@Provides,@Moudle,最最简单的情况需要使用@Inject,@Moudle。

示例场景:进影院看电影,首先要入座Seat,然后开始看电影watchMovie入座---->Seat类

public class Seat {        @Inject      public Seat() {          System.out.println("*********prepare to seat************");      }        public void seat(){          System.out.println("<----------- seating ---------->");      }  }

看电影--->WatchMovie类

public class WatchMovie {      @Inject Seat seat;        public void watchMovie(){          seat.seat();          System.out.println("<----------- watching ---------->");      }  }

Module类(这个类的作用待会儿说,现在先照着做就好啦!)

@Module(          injects = MovieTest.class,          library = true  )  public class MovieModule {        }

测试类--->MovieTest

public class MovieTest {      @Inject WatchMovie watchMovie;        public void movie(){          watchMovie.watchMovie();      }      public static void main(String[] args) {          ObjectGraph objectGraph = ObjectGraph.create(new MovieModule());          MovieTest movieTest = objectGraph.get(MovieTest.class);          movieTest.movie();      }  }

运行结果:

*********prepare to seat************  <----------- seating ---------->  <----------- watching ---------->    Process finished with exit code 0

Dagger使用简介

在dagger中,依赖注入的完成是通过只使用注解的方式表明在编译时需要创建的类,使得你在类中用到的对象的引用不需要显示地实例化。告诉dagger的编译器需要在编译时创建类的方式有如下三种:

  1. 使用@Inject注解构造函数

  2. 使用@Inject注解类的属性

  3. 在@Module注解的类中提供构造对象的方法

在第一种方法中,如果@Inject注解的构造函数有参数,则这些参数所对应的类必须要么包含@Inject注解(可以是注解构造函数,也可以是注解类的属性),要么在@Moudle注解的类中@Provides了这个类。第二种方法中注解的类的属性也要满足这个条件。

那么,什么情况下使用@Module呐?Dagger官方文档中说,@Inject不能在以下三种情况下使用:

接口类型不能被构造,第三方的类不能被注释构造,可配置的对象必须被配置好

以第一种情况为例,在刚才的场景中增加一个步骤:离开,接口为ILeave:

public interface ILeave {      void leave();  }

接口的实现LeaveImpl:

public class LeaveImpl implements ILeave {        @Override      public void leave() {          System.out.println("<----------- leaving ---------->");      }  }

WatchMovie修改为如下:

public class WatchMovie {      @Inject Seat seat;      @Inject ILeave leave;        public void watchMovie(){          seat.seat();          System.out.println("<----------- watching ---------->");          leave.leave();      }  }

这里增加了接口类型ILeave的注入,此时是不能用@Inject注解接口的,因为无法创建一个接口;并且被注解的属性是ILeave类型,所以也不能去注解该接口的实现。此时就需要在@Module注解的类中,使用@Provides说明,当需要ILeave类型的对象时应当如何创建。故,在MovieModule中增加如下代码:

    @Provides      public ILeave provideLeave(){          return new LeaveImpl();      }

所有的@Provides只能出现在@Module所注解的类中,通过这种方式告诉程序,如果你需要ILeave类型的引用,就按照@Provides标注的方法中返回类型是ILeave的那个方法所提供的方式来获取。

完整的@Module代码:

@Module(          injects = MovieTest.class,          library = true  )  public class MovieModule {        @Provides      public ILeave provideLeave(){          return new LeaveImpl();      }  }
<p>@Module的injects参数表明,MovieTest.class这个类需要通过MovieModule来完成依赖注入。</p>

dagger依赖注入的最后一个步骤就是使用对象图ObjectGraph。本示例使用对象图的部分为:

ObjectGraph objectGraph = ObjectGraph.create(new MovieModule());  MovieTest movieTest = objectGraph.get(MovieTest.class);

ObjectGraph.create(new MovieModule())告诉编译器,MovieTest类中用到的依赖关系根据MovieModule生成,接下来就可以通过objectGraph.get(MovieTest.class)生成完成了依赖注入的MovieTest类啦。

Dagger的作用

  1. 控制反转本身的优点:可以解耦

  2. 方便测试

  3. 复用

You don't need a bunch of boilerplate just to swap the RpcCreditCardService out for a FakeCreditCardService.You can share the same AuthenticationModule across all of your apps. And you can run DevLoggingModule during development and ProdLoggingModule in production to get the right behavior in each situation.

</div>