在上一篇中,我们介绍了Dagger2的使用方式,这一篇,我们将会就Dagger2的核心原理实现进行解析,同时还会手写Dagger2的基本实现。
我们知道,使用Dagger2的时候,需要用到这两个关键的依赖:
arduino
// 导入API支持,包括注解
implementation 'com.google.dagger:dagger:2.28.3'
// 使用Dagger2的注解处理器APT
annotationProcessor 'com.google.dagger:dagger-compiler:2.4'
本篇文章主要是手写核心"com.google.dagger:dagger:2.28.3"。
Dagger2的核心实现
我们在使用Dagger2的时候,需要4个注解,因此我们首先写4个注解。
Component
less
@Target(TYPE)
@Retention(RUNTIME)
public @interface Component {
Class<?>[] modules() default {};
}
Inject
less
@Target({CONSTRUCTOR, FIELD})
@Retention(RUNTIME)
public @interface Inject {}
Module
less
@Target(TYPE)
@Retention(RUNTIME)
public @interface Module {}
Provides
less
@Target(METHOD)
@Retention(RUNTIME)
public @interface Provides {}
另外,我们知道,Dagger2支持单例,这个又是怎么实现的呢?其实是通过一个双重检查锁实现的。
这些逻辑是Javac的过程中生成的。
因此,我们也需要写一个同样的DoubleCheck类:
csharp
public final class DoubleCheck<T> implements Provider<T> { // 单例处理的类
private static final Object UNINITIALIZED = new Object(); // 为了单例的检查而已
private volatile Provider<T> provider; // 最上层实例化对象的 接口
private volatile Object instance = UNINITIALIZED;
private DoubleCheck(Provider<T> provider) {
this.provider = provider;
}
@Override // 单例,对象初始化一次
public T get() {
Object result = instance; // instance = new Student();
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
instance = result = provider.get(); // new Student();
provider = null;
}
}
}
return (T) result;
}
public static <T> Provider<T> provider(Provider<T> delegate) {
Preconditions.checkNotNull(delegate); // 工具类,检查
if (delegate instanceof DoubleCheck) {
return delegate;
}
return new DoubleCheck<T>(delegate);
}
}
对于Provider,他支持泛型,因为我们传入的对象可以是任意的对象。
csharp
public interface Provider<T> {
T get();
}
另外,注入的标准通过Javac编译之后:
依赖注入的标准MembersInjector,这里的泛型指的是我们要把对象注入到哪里?
csharp
//
依赖注入的标准
public interface MembersInjector<T> { // T == 我们要把对象注入到 MainActivity.this那里 MainActivity110那里
void injectMembers(T instance);
}
最后,我们还需要一个工具类来做一些安全检查:
typescript
public class Preconditions {
public static <T> T checkNotNull(T value) {
if (null == value) {
throw new NullPointerException("value is null exception..");
}
return value;
}
public static <T> T checkNotNull(T value, String errorMessage) {
if (null == value) {
throw new IllegalArgumentException(errorMessage);
}
return value;
}
}
对于module里边的类,通过Module注解,我们需要在编译期之后,生成需要的类。
我们看一下生成类的内部细节:
从上图来看,我们还需要这样的一个接口来做module内容处理:
csharp
public interface Factory<T> extends Provider<T> {
}
到目前为止,我们手写的整体结构如下:
我们已经把Dagger2的基础逻辑写好了,那么应该如何使用呢?
例如,我们创建一个Student类,这是我们希望注入的对象:
csharp
public class Student {
@Inject // 标记 代表此对象Student 是被注入的来源Student ---> MainActivity目标
public Student() {
}
}
首先处理第一个注解Provides,生成相应的Factory的逻辑,需要注意的是,我们应该手动生成对应的Factory,而不是利用Dagger2的逻辑:
typescript
// TODO 模拟 这个是编译期 APT 自动生成的 // 第一个注解
public enum Student_Factory implements Factory<Student> {
INSTANCE;
@Override
public Student get() {
return new Student();
}
public static Factory<Student> create() {
return INSTANCE;
}
}
然后处理第二个注解:Module。 新建一个StudentModule,以便用来模拟注入流程:
typescript
@Module
public class StudentModule {
@Provides
public Student providerStudent() {
return new Student();
}
}
Javac之后编译生成的类是这样的:
typescript
// TODO 模拟 这个是编译期 APT 自动生成的 // 第二个注解
public class StudentModule_ProviderStudentFactory implements Factory<Student> {
private StudentModule studentModule; // 包裹
public StudentModule_ProviderStudentFactory(StudentModule studentModule) {
this.studentModule = studentModule;
}
@Override
public Student get() {
return Preconditions.checkNotNull(
studentModule.providerStudent(), "你个货乱搞 无法studentModule.providerStudent()");
}
// 暴露给外界用的
public static Factory<Student> create(StudentModule module) {
return new StudentModule_ProviderStudentFactory(module);
}
}
开始处理第三个注解,Component:
java
@Component(modules = StudentModule.class)
public interface StudentComponent { // 快递员
// 写注入目标 MainActivity的this
void inject(MainActivity mainActivity);
}
经过Javac之后,会生成下边类似的代码:
typescript
// TODO 模拟 这个是编译期 APT 自动生成的 // 第三个注解
public class DaggerStudentComponent implements StudentComponent {
public DaggerStudentComponent(Builder builder) {
// 这里面没有代码生成,是靠我们的第四个注解
initialize(builder);
}
// 第四个注解生成的代码
private Provider<Student> providerDogProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;
private void initialize(final Builder builder) {
this.providerDogProvider = StudentModule_ProviderStudentFactory.create(builder.studentModule);
this.mainActivityMembersInjector = MainActivity_MembersInjector.create(providerDogProvider);
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
// 定义我们的包裹 是靠我们的第四个注解
StudentModule studentModule;
private Builder() {}
public StudentComponent build() {
if (studentModule == null) {
studentModule = new StudentModule();
}
return new DaggerStudentComponent(this);
}
/**
* @deprecated This module is declared, but an instance is not used in the component. This method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
*/
@Deprecated
public Builder studentModule(StudentModule dogModule) {
Preconditions.checkNotNull(dogModule);
return this;
}
}
// 对外提供Builder
public static StudentComponent create() {
return builder().build();
}
// 第四个注解生成的
// 往目标(MainActivity)中去注入
@Override // MainActivity this
public void inject(MainActivity mainActivity) {
// 启动 MainActivity_MembersInjector 完成依赖注入
mainActivityMembersInjector.injectMembers(mainActivity);
}
}
这里也解释了为什么我们写的Components必须是接口,因为源码中使用的implements.
最后我们来处理第四个注解:Inject
java
@Inject // 第四个注解, 这里不能写成private,因为生成的源码中,如果是同一个包,也可以写成 Student student;
public Student student;
**因为一旦写成private,这里就无法拿到对应的引用了。 **
最终,我们来测试一下最终的效果:
scala
// 第四步 注入目标
public class MainActivity extends AppCompatActivity {
@Inject // 第四个注解
public Student student;
@Inject // 第四个注解
public Student student2;
@Override
protected void onCreate(Bundle savedInstanceState) { // 方法进栈
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 第三个注解生成的类
DaggerStudentComponent.create().inject(this);
Toast.makeText(this, "student:" + student.hashCode(), Toast.LENGTH_SHORT).show();
Toast.makeText(this, "student:" + student2.hashCode(), Toast.LENGTH_SHORT).show();
}
}
Dagger2的流程解析
Dagger2的入口,通过一个inject标记一个对象。
java
@Inject // 第四个注解
public Student student;
然后会进入这里
kotlin
// 第三个注解生成的类
DaggerStudentComponent.create().inject(this);
之后会进入到,DaggerStudentComponent#inject:
typescript
@Override // MainActivity this
public void inject(MainActivity mainActivity) {
// 启动 MainActivity_MembersInjector 完成依赖注入
mainActivityMembersInjector.injectMembers(mainActivity);
}
然后会进入MainActivity_MembersInjector#injectMembers:
ini
@Override // 注入的方法
public void injectMembers(MainActivity instance) { // instance == MainActivity this
if (null == instance) {
throw new NullPointerException("inject target instance is null");
}
// MainActivity this student = new Student(); 依赖注入框架
instance.student = studentProvider.get(); // MainActivity this.student = new Student();
instance.student2 = studentProvider.get();
}
之后会到这里StudentModule_ProviderStudentFactory#get:
kotlin
@Override
public Student get() {
return Preconditions.checkNotNull(
studentModule.providerStudent(), "Cannot return null for studentModule.providerStudent()");
}
在这里通过调用studentModule.providerStudent(),最终生成了实例:
typescript
@Provides
public Student providerStudent() {
return new Student();
}
这就是Dagger2整个依赖入住的流程,MainActivity依赖Dagger2对一些对象进行注入。
测试源码链接:github.com/xingchaozha...