一.什么是依赖注入,首先我认为是一种编程思想
简单来说,依赖注入就是为了控制反转和解耦的
当两个类或多个类 组合使用时,不可避免的会发生以下情况:
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()]);
}
...
}
public
interface
MovieFinder
{
List
findAll
()
;
}
可见
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
// 注明本类是Module
public
class
MyModule
{
@Provides
// 注明该方法是用来提供依赖对象的方法
public
B
provideB
(){
return
new
B
();
}
}
实现Component
@Component
(
modules
={
MyModule
.
class
})
// 指明Component查找Module的位置
public
interface
MyComponent
{
// 必须定义为接口,Dagger2框架将自动生成Component的实现类,对应的类名是Dagger×××××,这里对应的实现类是DaggerMyComponent
void
inject
(
A a
);
// 注入到A(Container)的方法,方法名一般使用inject
}
实现Container
A就是可以被注入依赖关系的容器。
public
A
{
@Inject
//标记b将被注入
B b
;
// 成员变量要求是包级可见,也就是说@Inject不可以标记为private类型。
public
void
init
(){
DaggerMyComponent
.
create
().
inject
(
this
);
// 将实现类注入
}
}
当调用A的init()方法时, b将会自动被赋予实现类的对象。
2.当bean 有有参构造器
需要参数的实例化对象
Person
的构造方法发生了变化,需要传入一个
Context
,代码如下:
public
class
Person
{
private
Context mContext;
public
Person(Context context){ mContext = context; Log.i(
"dagger"
,
"create"
); }}
这样的话,我们需要修改
MainModule
@Module
//提供依赖对象的实例
public
class
MainModule
{
private
Context mContext;
public
MainModule(Context context){ mContext = context; }
@Provides
Context providesContext(){
// 提供上下文对象
return
mContext; }
@Provides
// 关键字,标明该方法提供依赖对象
@Singleton
Person providerPerson(Context context){
return
new
Person(context); }}
- 修改providerPerson方法,传入Context对象。
- 添加providesContext(),用以提供Context对象。
看一下使用
// 构造桥梁对象
MainComponent component = DaggerMainComponent.builder().mainModule(
new
MainModule(
this
)).build();
//注入
component.inject(
this
);