Dagger2 是 Google 开发的依赖注入框架,基于 Java Annotation Processing 和 Apt 技术,提供了高效的编译时依赖注入功能。以下是 Dagger2 的源码解析。
Dagger2 核心概念
-
Component:
- 依赖注入的入口,负责将依赖注入到目标类中。
- 通过
@Component注解生成代码。
-
Module:
- 提供依赖对象的工厂类。
- 使用
@Module和@Provides注解标记。
-
Scope:
- 控制依赖对象的生命周期。
- 使用自定义
@Scope注解(如@Singleton)。
-
Injection Target:
- 目标类,通过
@Inject标记字段或构造函数,声明需要注入的依赖。
- 目标类,通过
-
Generated Code:
- 编译期间生成的 Java 代码(如
DaggerXxxComponent)。
- 编译期间生成的 Java 代码(如
工作原理
编译期代码生成
Dagger2 通过注解处理器(@Component, @Module, @Inject 等)在编译期间生成目标代码,避免了运行时反射,提升了性能。
-
注解解析:
@Inject:标记需要注入的字段或构造函数。@Component:解析依赖注入的入口。@Module和@Provides:定义如何创建依赖对象。
-
代码生成:
- 根据注解生成依赖注入的实现类。
源码解析
1. 注解解析过程
Dagger2 的注解处理器主要实现了 javax.annotation.processing.Processor 接口。
Processor 初始化
java
复制代码
@AutoService(Processor.class)
public final class ComponentProcessor extends BasicAnnotationProcessor {
@Override
protected Iterable<? extends ProcessingStep> initSteps() {
return Arrays.asList(new ComponentProcessingStep(), new ModuleProcessingStep());
}
}
-
@AutoService:注册Processor。 -
ComponentProcessingStep和ModuleProcessingStep:- 解析
@Component和@Module注解。
- 解析
解析注解
ComponentProcessingStep 负责解析 @Component。
java
复制代码
@Override
public Set<? extends Element> process(SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation) {
for (Element element : elementsByAnnotation.get(Component.class)) {
ComponentDescriptor descriptor = ComponentDescriptor.create(element);
generateComponentImplementation(descriptor);
}
return ImmutableSet.of();
}
-
ComponentDescriptor:- 将
@Component的信息抽象为描述符。
- 将
-
generateComponentImplementation:- 根据描述符生成
DaggerXxxComponent类。
- 根据描述符生成
2. 生成 Component 实现
生成代码示例
java
复制代码
public final class DaggerAppComponent implements AppComponent {
private final AppModule appModule;
private DaggerAppComponent(Builder builder) {
this.appModule = builder.appModule;
}
public static Builder builder() {
return new Builder();
}
public static AppComponent create() {
return new Builder().build();
}
@Override
public void inject(MainActivity activity) {
injectMainActivity(activity);
}
private MainActivity injectMainActivity(MainActivity instance) {
instance.someDependency = new SomeDependency();
return instance;
}
public static final class Builder {
private AppModule appModule;
public Builder appModule(AppModule appModule) {
this.appModule = appModule;
return this;
}
public AppComponent build() {
return new DaggerAppComponent(this);
}
}
}
-
DaggerXxxComponent:- 负责注入依赖对象。
- 调用
@Provides方法创建依赖。
3. @Module 和 @Provides
@Module 和 @Provides 定义如何提供依赖对象。
示例
java
复制代码
@Module
public class AppModule {
@Provides
SomeDependency provideSomeDependency() {
return new SomeDependency();
}
}
代码生成
java
复制代码
@Override
public void generateComponentImplementation(ComponentDescriptor descriptor) {
ClassName componentName = ClassName.get(descriptor.componentElement());
MethodSpec providesMethod = MethodSpec.methodBuilder("provideSomeDependency")
.returns(SomeDependency.class)
.addStatement("return new SomeDependency()")
.build();
TypeSpec generatedClass = TypeSpec.classBuilder("Dagger" + componentName.simpleName())
.addMethod(providesMethod)
.build();
JavaFile.builder(componentName.packageName(), generatedClass)
.build()
.writeTo(processingEnv.getFiler());
}
4. @Inject
@Inject 标记的字段或构造函数由 Dagger 生成的代码自动注入。
示例
java
复制代码
public class MainActivity {
@Inject SomeDependency someDependency;
@Override
protected void onCreate(Bundle savedInstanceState) {
DaggerAppComponent.create().inject(this);
}
}
代码生成
java
复制代码
@Override
public void inject(MainActivity activity) {
activity.someDependency = new SomeDependency();
}
inject()方法负责将依赖注入到目标类。
5. Scope 生命周期
示例
java
复制代码
@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
void inject(MainActivity activity);
}
代码生成
- 使用
@Singleton标记的对象在DaggerXxxComponent中缓存,保证全局唯一。
java
复制代码
private final SomeDependency someDependency = new SomeDependency();
@Override
public void inject(MainActivity activity) {
activity.someDependency = someDependency;
}
Dagger2 优化与特性
-
编译时检查:
- 在编译期发现依赖注入问题,避免运行时错误。
-
无反射:
- 通过代码生成避免运行时反射,提高性能。
-
Scope 管理:
- 精确控制对象生命周期。
-
多模块支持:
- 支持复杂项目中的模块化依赖注入。
Dagger2 优缺点
优点
- 编译时依赖检查,性能优异。
- 支持多种依赖模式(构造函数注入、字段注入等)。
- 自动生成代码,减少手动工作。
缺点
- 学习曲线较高。
- 编译期生成代码可能增加构建时间。
- 对于小型项目可能显得过于复杂。
总结
Dagger2 是一款强大且高效的依赖注入框架,其核心基于注解处理器和编译期代码生成。通过对 @Component、@Module 和 @Inject 的源码解析,可以清晰了解其依赖解析和注入过程。它适用于大型项目,尤其是需要严格依赖管理和性能要求的场景。