模块解耦神器-Dagger2的使用和详解
1 Dagger2简介
Dagger2提供给Java和Android使用,主要用于模块间解耦、提高代码的健壮性和可维护性。它是一款依赖注入框架,使用了IOC(控制反转)的思想,在编译阶段使用APT利用Java注解生成Java代码,然后结合部分手写代码来完整依赖注入工作。
运行前需要先编译一次项目,目的是用APT生成中间代码。Dagger2不使用反射,在编译阶段生成代码,所以不会程序性能有影响。
IOC(控制反转)和依赖注入的区别:
控制反转是一种在软件工程中解耦合的思想,调用类只依赖接口,而不依赖具体的实现类,减少了耦合。 控制权交给了容器,在运行的时候才由容器决定将具体的实现动态的“注入”到调用类的对象中。 依赖注入是一种设计模式,可以作为控制反转的一种实现方式。
Dagger2官网:https://dagger.dev/
Dagger2 Github:https://github.com/google/dagger
2 Dagger2原理解析

可以使用用户在网上买东西,然后通过快递配送来理解Dagger2的使用原理。用户购买一本书(@Inject
注解),然后卖家(Module)进行打包,接着快递员(Component)配送,然后用户就收到了这本书。这样就达到了解耦的目的,只需要使用@Inject
注解,就能获取到该对象。
3 Dagger2中的注解
Dagger2通过注解使用APT生成代码,所以首先要知道Dagger2中常用注解的使用。
3.1 @Inject
@Inject
有两种用处,一是用来标注需要依赖获取的对象,比如:
public class FirstActivity extends AppCompatActivity {
@Inject
Book book;
...
}
就表示在当前类中,需要依赖Book。
二是用来标注构造方法,Dagger2通过@Inject
注解可以在需要这个类实例的时候来找到这个构造函数并把相关实例构造出来,以此来为被@Inject
标记了的变量提供依赖,同时会在build下生成对应的xxx_Factory类,后面示例中会详解。
public class Book {
@Inject
public Book() {
}
}
3.2 @Module
@Module
用来标注提供依赖对象的类,也就是“包裹”Module类,Module类中会有一个方法来提供依赖对象,在这个方法中可以对注入对象的有参构造函数传入参数或者进行其他处理。
3.3 @Provides
@Provides
用来标注Module类中的提供依赖对象的方法进行标注,该方法在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了@Inject 的变量赋值。
被@Module
和@Provides
标注的类在build下会生成对应的xxxModule_xxxFactory类。
3.4 @Component
@Component
用来标注接口,该接口提供了方法用来传入业务层,是业务层和Module之间的连接器。标注后会在build下生成DaggerxxxComponent类。
3.5 @Singleton
默认情况下,@Inject
获取到的依赖对象是非单例的,要想实现单例,需要用@Singleton
对Module中的provide方法和Conponent接口进行标注。
4 Dagger2使用
引入依赖:
implementation 'com.google.dagger:dagger:2.41'
annotationProcessor 'com.google.dagger:dagger-compiler:2.41'
4.1 获取到的依赖注入对象是非单例
创建商品Book。
public class Book {
@Inject
public Book() {
}
}
创建包裹Module。
@Module
public class BookModule {
@Provides
public Book provideBook() {
return new Book();
}
}
创建快递员Component。
@Component(modules = BookModule.class)
public interface BookComponent {
void injectFirstActivity(FirstActivity activity);
void injectSecondActivity(SecondActivity activity);
}
创建两个FirstActivity,通过@Inject
获取两个依赖注入对象Book,并打印两个Book的hasCode。
public class FirstActivity extends AppCompatActivity {
private static final String TAG = "zhangmushui";
@Inject
Book book;
@Inject
Book book2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
//DaggerBookComponent是由dagger-compiler通过APT自动生成的
DaggerBookComponent.builder()
.bookModule(new BookModule())
.build().injectFirstActivity(this);
//另个Book的hashCode不一样,说明Dagger2提供的Book默认是非单例的
Log.d(TAG, "book: " + book.hashCode());
Log.d(TAG, "book2: " + book2.hashCode());
findViewById(R.id.button).setOnClickListener(v ->
startActivity(new Intent(this, SecondActivity.class)));
}
}
创建SecondActivity
public class SecondActivity extends AppCompatActivity {
private static final String TAG = "zhangmushui";
@Inject
Book book;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
DaggerBookComponent.builder()
.bookModule(new BookModule())
.build().injectSecondActivity(this);
Log.d(TAG, "book: " + book.hashCode());
}
}
控制台可以看到,两个页面中的3个Book对象的hasCode都不同,可见通过Dagger2默认获取到的依赖注入对象是非单例的。
2022-04-20 17:00:04.388 6649-6649/cn.zhangmushui.dagger D/zhangmushui: book2: 94427313
2022-04-20 17:00:16.220 6649-6649/cn.zhangmushui.dagger D/zhangmushui: book: 63721347
2022-04-20 18:46:50.724 6649-6649/cn.zhangmushui.dagger D/zhangmushui: book: 194950979
4.2 获取到的依赖注入对象是局部单例
首先在Module的provide方法上加上@Singleton
,需要注意一点的是,之前版本的Dagger2需要在Module类上也添加@Singleton
注解,新版本的已经不需要。
/**
* 想要提供的对象是单例,需要加上@Singleton注解
* 但是最新版本的Dagger2已经不需要在Module上添加@Singleton注解(不然编译会报错),只需要在下面的provider和Component上添加@Singleton即可。
*/
@Module
public class BookModule {
@Singleton
@Provides
public Book provideBook() {
return new Book();
}
}
然后在Component类上添加@Singleton
注解。
@Singleton
@Component(modules = BookModule.class)
public interface BookComponent {
void injectFirstActivity(FirstActivity activity);
void injectSecondActivity(SecondActivity activity);
}
重新编译后运行,可以看到FirstActivity中的两个Book的hashCode是相同的,SecondActivity和FirstActivity中的Book对象hashCode是不一样的,可见实现了局部单例。
2022-04-20 19:11:04.306 9367-9367/cn.zhangmushui.dagger D/zhangmushui: book: 29400920
2022-04-20 19:11:04.306 9367-9367/cn.zhangmushui.dagger D/zhangmushui: book2: 29400920
2022-04-20 19:11:06.047 9367-9367/cn.zhangmushui.dagger D/zhangmushui: book: 146490930
4.3 获取到的依赖注入对象是全局单例
在上边的基础上继续做修改,创建Application,在Application中实例化Component对象,并提供获取Component的方法。
public class MainApplication extends Application {
private BookComponent mBookComponent;
@Override
public void onCreate() {
super.onCreate();
mBookComponent = DaggerBookComponent.builder()
.bookModule(new BookModule())
.build();
}
public BookComponent getBookComponent() {
return mBookComponent;
}
}
然后在Activity中通过Application去获取Component。
public class FirstActivity extends AppCompatActivity {
private static final String TAG = "zhangmushui";
@Inject
Book book;
@Inject
Book book2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
//在Application中获取Component,实现全局单例
((MainApplication) getApplication()).getBookComponent().injectFirstActivity(this);
//两个Book的。hashCode一致。
Log.d(TAG, "book: " + book.hashCode());
Log.d(TAG, "book2: " + book2.hashCode());
findViewById(R.id.button).setOnClickListener(v ->
startActivity(new Intent(this, SecondActivity.class)));
}
}
public class SecondActivity extends AppCompatActivity {
private static final String TAG = "zhangmushui";
@Inject
Book book;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
((MainApplication) getApplication()).getBookComponent().injectSecondActivity(this);
//SecondActivity和FirstActivity中的Book的hashCode一致。
Log.d(TAG, "book: " + book.hashCode());
}
}
编译后运行查看日志,发现两个页面的3个Book的hashCode都是一样的,这就实现了全局单例。
2022-04-20 19:15:13.890 9544-9544/cn.zhangmushui.dagger D/zhangmushui: book: 29400920
2022-04-20 19:15:13.890 9544-9544/cn.zhangmushui.dagger D/zhangmushui: book2: 29400920
2022-04-20 19:15:23.048 9544-9544/cn.zhangmushui.dagger D/zhangmushui: book: 29400920
5 Dagger2原理解析
接下来以默认非单例的版本去分析Dagger2的原理。
所有用注解标注的类,Dagger2都会用APT在build下去生成对应的辅助类,这些辅助类的名字是根据dagger-compiler注解依赖库中的命名规则生成的。
手动创建 | APT生成 | 命名规则 | 注解 |
---|---|---|---|
FirstActivity | FirstActivity_MembersInjector | 类名_MembersInjector | @Inject |
SecondActivity | SecondActivity_MembersInjector | 类名_MembersInjector | @Inject |
BookComponent | DaggerBookComponent | Dagger对象名Component | |
@Component | |||
BookModule | BookModule_ProvideBookFactory | 对象名Module_方法名Factory | @Module @Provides |
Book | Book_Factory | 对象名_Factory | @Inject |
为了方便观看,原理直接写到注释里边,注释前边用数字标出,表示大概的调用顺序。
// Generated by Dagger (https://dagger.dev).
package cn.zhangmushui.dagger.demo1;
import cn.zhangmushui.dagger.demo1.activity.FirstActivity;
import cn.zhangmushui.dagger.demo1.activity.FirstActivity_MembersInjector;
import cn.zhangmushui.dagger.demo1.activity.SecondActivity;
import cn.zhangmushui.dagger.demo1.activity.SecondActivity_MembersInjector;
import dagger.internal.DaggerGenerated;
import dagger.internal.Preconditions;
@DaggerGenerated
@SuppressWarnings({
"unchecked",
"rawtypes"
})
public final class DaggerBookComponent implements BookComponent {
private final BookModule bookModule;
private final DaggerBookComponent bookComponent = this;
private DaggerBookComponent(BookModule bookModuleParam) {
this.bookModule = bookModuleParam;
}
public static Builder builder() {
//1 使用构造者模式实例化DaggerBookComponent对象
return new Builder();
}
public static BookComponent create() {
return new Builder().build();
}
@Override
public void injectFirstActivity(FirstActivity activity) {
injectFirstActivity2(activity);
}
@Override
public void injectSecondActivity(SecondActivity activity) {
injectSecondActivity2(activity);
}
private FirstActivity injectFirstActivity2(FirstActivity instance) {
//4 Activity中的@Inject生成injectBook方法,通过BookModule_ProvideBookFactory来获取依赖注入对象
FirstActivity_MembersInjector.injectBook(instance, BookModule_ProvideBookFactory.provideBook(bookModule));
FirstActivity_MembersInjector.injectBook2(instance, BookModule_ProvideBookFactory.provideBook(bookModule));
return instance;
}
private SecondActivity injectSecondActivity2(SecondActivity instance) {
SecondActivity_MembersInjector.injectBook(instance, BookModule_ProvideBookFactory.provideBook(bookModule));
return instance;
}
public static final class Builder {
private BookModule bookModule;
private Builder() {
}
//2 传入一个BookModule对象
public Builder bookModule(BookModule bookModule) {
this.bookModule = Preconditions.checkNotNull(bookModule);
return this;
}
//3 实例化DaggerBookComponent
public BookComponent build() {
if (bookModule == null) {
this.bookModule = new BookModule();
}
return new DaggerBookComponent(bookModule);
}
}
}
// Generated by Dagger (https://dagger.dev).
package cn.zhangmushui.dagger.demo1;
import dagger.internal.DaggerGenerated;
import dagger.internal.Factory;
import dagger.internal.Preconditions;
import dagger.internal.QualifierMetadata;
import dagger.internal.ScopeMetadata;
@ScopeMetadata
@QualifierMetadata
@DaggerGenerated
@SuppressWarnings({
"unchecked",
"rawtypes"
})
public final class BookModule_ProvideBookFactory implements Factory<Book> {
private final BookModule module;
public BookModule_ProvideBookFactory(BookModule module) {
this.module = module;
}
@Override
public Book get() {
return provideBook(module);
}
public static BookModule_ProvideBookFactory create(BookModule module) {
return new BookModule_ProvideBookFactory(module);
}
//5 通过BookModule拿到Book对象,并用Dagger2提供的Preconditions.checkNotNullFromProvides()方法来检查获取到的对象是否为空
public static Book provideBook(BookModule instance) {
return Preconditions.checkNotNullFromProvides(instance.provideBook());
}
}
// Generated by Dagger (https://dagger.dev).
package cn.zhangmushui.dagger.demo1.activity;
import cn.zhangmushui.dagger.demo1.Book;
import dagger.MembersInjector;
import dagger.internal.DaggerGenerated;
import dagger.internal.InjectedFieldSignature;
import dagger.internal.QualifierMetadata;
import javax.inject.Provider;
@QualifierMetadata
@DaggerGenerated
@SuppressWarnings({
"unchecked",
"rawtypes"
})
public final class FirstActivity_MembersInjector implements MembersInjector<FirstActivity> {
private final Provider<Book> bookProvider;
private final Provider<Book> book2Provider;
public FirstActivity_MembersInjector(Provider<Book> bookProvider, Provider<Book> book2Provider) {
this.bookProvider = bookProvider;
this.book2Provider = book2Provider;
}
public static MembersInjector<FirstActivity> create(Provider<Book> bookProvider,
Provider<Book> book2Provider) {
return new FirstActivity_MembersInjector(bookProvider, book2Provider);
}
@Override
public void injectMembers(FirstActivity instance) {
injectBook(instance, bookProvider.get());
injectBook2(instance, book2Provider.get());
}
@InjectedFieldSignature("cn.zhangmushui.dagger.demo1.activity.FirstActivity.book")
public static void injectBook(FirstActivity instance, Book book) {
//6 将获取到的Book对象赋值给Activity中的book
instance.book = book;
}
@InjectedFieldSignature("cn.zhangmushui.dagger.demo1.activity.FirstActivity.book2")
public static void injectBook2(FirstActivity instance, Book book2) {
instance.book2 = book2;
}
}
以上几处注释,就是Dagger2的核心原理,也就是通过Dagger生成辅助类,在业务层需要获取依赖对象的地方使用@Inject去标注对象,就可以交给Dagger2的这些辅助类去实例化对象,起到了解耦的作用,提高了代码的可读性和可维护性。
类图如下,白色的是手动创建的类,灰色的是APT生成的类。

完整源码下载:https://gitee.com/mushuicode/dagger
关注木水小站 (zhangmushui.cn)和微信公众号【木水Code】,及时获取更多最新文章。