依赖注入--Dagger2

来源:互联网 发布:数据库系统包括 编辑:程序博客网 时间:2024/05/22 09:01
一.什么是依赖注入,首先我认为是一种编程思想
简单来说,依赖注入就是为了控制反转和解耦的
当两个类或多个类 组合使用时,不可避免的会发生以下情况:
public class MovieLister {
private MovieFinder finder;

public MovieLister() {
finder = new MovieFinderImpl();
}
public Movie[] moviesDirectedBy(String arg) {
List allMovies = finder.findAll();
for (Iterator it = allMovies.iterator(); it.hasNext();) {
Movie movie = (Movie) it.next();
if (!movie.getDirector().equals(arg)) it.remove();
}
return (Movie[]) allMovies.toArray(new Movie[allMovies.size()]);
}
...
}


publicinterfaceMovieFinder {
ListfindAll();
}

可见MovieFinder 接口 在MovieLister类的构造器中实例化 这样我们就可以说MovieFinder 接口是依赖于MovieLister的. 这样增加了代码的耦合性,非常的不好

由此引入了依赖注入这种设计思想,关键在于达到相同的需求还要降低耦合度

在不使用框架的情况下 依赖注入有以下几种常见的方式
1.通过构造函数注入(Contructor Injection)

这是我认为的最简单的依赖注入方式,我们修改一下上面代码中MovieList的构造函数,使得MovieFinderImpl的实现在MovieLister类之外创建。
这样,MovieLister就只依赖于我们定义的MovieFinder接口,而不依赖于MovieFinder的实现了。


public class MovieLister {
private MovieFinder finder;

public MovieLister(MovieFinder finder) {
this.finder = finder;
}
...
}

2.通过setter注入

类似的,我们可以增加一个setter函数来传入创建好的MovieFinder对象,这样同样可以避免在MovieFinder中hard init这个对象。

public class MovieLister {
s...
public void setFinder(MovieFinder finder) {
this.finder = finder;
}
}

3.接口注入

接口注入使用接口来提供setter方法,其实现方式如下。
首先要创建一个注入使用的接口。

public interface InjectFinder {
void injectFinder(MovieFinder finder);
}

之后,我们让MovieLister实现这个接口。

class MovieLister implements InjectFinder {
...
public void injectFinder(MovieFinder finder) {
this.finder = finder;
}
...
}

可以看到以上三种方法 MovieFinder 接口不再 在MovieLister 类里实例化了
MovieLister 类里只是持有MovieFinder 的引用,大大的降低了代码的耦合性

二.Dagger2

Dagger2是一种依赖注入的框架
1. Dagger2是什么?
Dagger2在Github主页上的自我介绍是:“A fast dependency injector for Android and Java“(一个提供给Android和Java使用的快速依赖注射器。)

2. 使用依赖注入的最大好处是什么?
没错,就是模块间解耦! 就拿当前Android非常流行的开发模式MVP来说,使用Dagger2可以将MVP中的V 层与P层进一步解耦,这样便可以提高代码的健壮性和可维护性。

3.Dagger2的用法
3.1. 注解
Dagger2 通过注解来生成代码,定义不同的角色,主要的注解如下:
@Module:Module类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的依赖。
@Provides:在Module中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。
@Inject:通常在需要依赖的地方使用这个注解。换句话说,你用它告诉Dagger这个类或者字段需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。
@Component:Component从根本上来说就是一个注入器,也可以说是@Inject和@Module的桥梁,它的主要作用就是连接这两个部分。将Module中产生的依赖对象自动注入到需要依赖实例的Container中。
@Scope:Dagger2可以通过自定义注解限定注解作用域,来管理每个对象实例的生命周期。
@Qualifier:当类的类型不足以鉴别一个依赖的时候,我们就可以使用这个注解标示。例如:在Android中,我们会需要不同类型的context,所以我们就可以定义 qualifier注解“@perApp”和“@perActivity”,这样当注入一个context的时候,我们就可以告诉 Dagger我们想要哪种类型的context。
3.2 结构
Dagger2要实现一个完整的依赖注入,必不可少的元素有三种:Module,Component,Container。
为了便于理解,其实可以把component想象成针管,module是注射瓶,里面的依赖对象是注入的药水,build方法是插进患者(Container),inject方法的调用是推动活塞。

简单来说:component(理解为注射器) 将Module中的依赖 注入到Container
3. 配置
  • 配置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' ... }

下面举一个简单的例子:

4. 简单的例子

4.1最简单的方式
实现Module
继续上面的例子:
@Module// 注明本类是ModulepublicclassMyModule{@Provides// 注明该方法是用来提供依赖对象的方法public BprovideB(){returnnew B();}}
实现Component
@Component(modules={MyModule.class})// 指明Component查找Module的位置publicinterfaceMyComponent{// 必须定义为接口,Dagger2框架将自动生成Component的实现类,对应的类名是Dagger×××××,这里对应的实现类是DaggerMyComponentvoidinject(A a);// 注入到A(Container)的方法,方法名一般使用inject}
实现Container
A就是可以被注入依赖关系的容器。
public A{@Inject//标记b将被注入 B b;// 成员变量要求是包级可见,也就是说@Inject不可以标记为private类型。publicvoidinit(){DaggerMyComponent.create().inject(this);// 将实现类注入}}
当调用A的init()方法时, b将会自动被赋予实现类的对象。

2.当bean 有有参构造器

需要参数的实例化对象
Person的构造方法发生了变化,需要传入一个Context,代码如下:
publicclassPerson {private Context mContext;public Person(Context context){ mContext = context; Log.i("dagger","create"); }}

这样的话,我们需要修改MainModule
@Module//提供依赖对象的实例publicclassMainModule {private Context mContext;public MainModule(Context context){ mContext = context; }@Provides Context providesContext(){// 提供上下文对象return mContext; }@Provides// 关键字,标明该方法提供依赖对象@Singleton Person providerPerson(Context context){returnnew Person(context); }}
  • 修改providerPerson方法,传入Context对象。
  • 添加providesContext(),用以提供Context对象。
看一下使用
// 构造桥梁对象 MainComponent component = DaggerMainComponent.builder().mainModule(new MainModule(this)).build();//注入 component.inject(this);




0 0
原创粉丝点击