Dagger2进阶学习(1)

@Override

public Set getSupportedOptions() { // 4

return Sets.union(

ProcessingEnvironmentCompilerOptions.supportedOptions(),

bindingGraphPlugins.allSupportedOptions())

.immutableCopy();

}

@Override

protected Iterable<? extends ProcessingStep> initSteps() { // 5

}

}

注释1: @AutoService的注解可以用来自动生成配置文件注册注解处理器。

注释2:ComponentProcessor继承自 BasicAnnotationProcessor,而不是和 EventBus、ButterKnife一样继承自AbstractProcessor

注释3:返回支持的JDK版本

注释4:返回支持的编译参数,这里指的是 Component…

注释5:初始化处理步骤。

BasicAnnotationProcessor是属于Google的Auto项目,来看看它的部分源码:

// auto/common/src/main/java/com/google/auto/common/BasicAnnotationProcessor.java

public abstract class BasicAnnotationProcessor extends AbstractProcessor {

@Override

public final synchronized void init(ProcessingEnvironment processingEnv) { // 1

super.init(processingEnv);

this.elements = processingEnv.getElementUtils();

this.messager = processingEnv.getMessager();

this.steps = ImmutableList.copyOf(initSteps()); // 2

}

// 3

private ImmutableSet<? extends Class<? extends Annotation>> getSupportedAnnotationClasses() {

checkState(steps != null);

ImmutableSet.Builder<Class<? extends Annotation>> builder = ImmutableSet.builder();

for (ProcessingStep step : steps) { //4

builder.addAll(step.annotations());

}

return builder.build();

}

@Override

public final ImmutableSet getSupportedAnnotationTypes() { // 5

ImmutableSet.Builder builder = ImmutableSet.builder();

for (Class<? extends Annotation> annotationClass : getSupportedAnnotationClasses()) {

builder.add(annotationClass.getCanonicalName());

}

return builder.build();

}

@Override

public final boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

process(validElements(roundEnv)); // 6

postRound(roundEnv); // 7

}

}

注释1: init方法用来初始化处理器

注释2:调用了之前出现在 ComponentProcessorinitStep(),

注释3:返回处理器支持解析的注解集合(返回的是Set<Class>

注释4:遍历添加

注释5:返回支持的注解类型(返回的是 Set<String>

注释6:内部遍历所有的ProcessingStep,然后执行每个ProcessingStep的 process方法

注释7:解析完注解后的处理,子类可实现该方法完成Java文件的创建。

BasicAnnotationProcessor的作用是为了规范注解解析的过程,上述的代码其实并没有太多实质上的做的事情,就跟我们所定义的 BaseActivitiy、BaseFragment一样。

不过这里需要注意的是:

这个类定义了 ProcessingStep,它是BasicAnnotationProcessor体系里解析注解的最小逻辑单元,我们来看下它的代码:

// BasicAnnotationProcessor.java

public interface ProcessingStep {

// 处理注解的集合

Set<? extends Class<? extends Annotation>> annotations();

//处理注解对应的元素,返回没有进行处理的元素的集合

Set<? extends Element> process(

SetMultimap<Class<? extends Annotation>, Element> elementsByAnnotation);

}

来看下之前注释6的process():

// BasicAnnotationProcessor.java

private void process(ImmutableSetMultimap<Class<? extends Annotation>, Element> validElements) {

for (ProcessingStep step : steps) { // 1

ImmutableSetMultimap<Class<? extends Annotation>, Element> stepElements =

new ImmutableSetMultimap.Builder<Class<? extends Annotation>, Element>()

.putAll(indexByAnnotation(elementsDeferredBySteps.get(step), step.annotations()))

.putAll(filterKeys(validElements, Predicates.in(step.annotations())))

.build(); // 2

if (stepElements.isEmpty()) {

elementsDeferredBySteps.removeAll(step);

} else {

Set<? extends Element> rejectedElements = step.process(stepElements); // 3

elementsDeferredBySteps.replaceValues(

step, transform(rejectedElements, ElementName::forAnnotatedElement));

}

}

}

注释1:遍历所有的 ProcessingStep

注释2:获取一个step需要处理的 Element的集合,主要通过 setp.annotation() 过滤掉不是该step需要处理的注解。

比如说一个注解,他需要处理的Element集合为{方法/ 属性 / 类…}

注释3:如果集合不为空,就调用step的 process,即子类实现的process方法。

BasicAnnotationprocessor所做的事情可以总结成:

  1. 定义最小逻辑处理单元 ProcessingStep

  2. 让子类实现 initSteps(),这个方法里面,让子类自己去分出几个ProcessingStep

  3. 遍历每个 ProcessingStep,调用他们的 process

分成每个逻辑单元分布实现,这样做的好处就是:对于一个processor要处理众多的注解时,显然写在一个process方法里面未免太过混杂, BasicAnnotationprocessor对每个注解的处理进行了管理,清晰且简单。

那我们来看看 ComponentProcessor是如何在 initStep()里面创建 ProcessingSteps的:

// ComponentProcessor.java

protected Iterable<? extends ProcessingStep> initSteps() {

ProcessorComponent.factory().create(processingEnv, testingPlugins).inject(this);

statisticsCollector.processingStarted();

bindingGraphPlugins.initializePlugins();

return Iterables.transform(

processingSteps, // 1

step -> new DaggerStatisticsCollectingProcessingStep(step, statisticsCollector));

}

注释1:传入了 processingSteps,它就是ComponentProcessor定义的ProcessingStep集合,但是发现它没在这个方法中创建。在查看它是怎么来的时候,发现它也是通过了注解的方法实现创建…(套娃了),这里不讲过程,直接看看它的创建:

// ComponentProcessor.java

@Module

interface ProcessingStepsModule {

@Provides

static ImmutableList processingSteps(

MapKeyProcessingStep mapKeyProcessingStep,

InjectProcessingStep injectProcessingStep,

MonitoringModuleProcessingStep monitoringModuleProcessingStep,

MultibindingAnnotationsProcessingStep multibindingAnnotationsProcessingStep,

BindsInstanceProcessingStep bindsInstanceProcessingStep,

ModuleProcessingStep moduleProcessingStep,

ComponentProcessingStep componentProcessingStep,

ComponentHjarProcessingStep componentHjarProcessingStep,

BindingMethodProcessingStep bindingMethodProcessingStep,

CompilerOptions compilerOptions) {

return ImmutableList.of(

mapKeyProcessingStep,

injectProcessingStep,

monitoringModuleProcessingStep,

multibindingAnnotationsProcessingStep,

bindsInstanceProcessingStep,

moduleProcessingStep,

compilerOptions.headerCompilation()

? componentHjarProcessingStep
componentProcessingStep,

bindingMethodProcessingStep);

}

}

在 ImmutatbleList.of 中 创建了很多个 xxxPrcessingStep,他们都间接的继承了 ProcessingStep,代表着每一种注解,可以看出来ComponentProcessor要处理的注解有 @MapKey、@Inject、@Mutibinding、@Module、@Component等等。

所以真正实现process方法,都是在这些 xxxProcessingStep.process()中,这里以 @Inject为例子,看看他是怎么实现对 @Inject注解的处理。

1.2 从InjectProcessingStep到Inject文件的生成


InjectProcessingStep实现了继承了 TypeCheckingProcessingStep

// InjectProcessingStep.java

final class InjectProcessingStep extends TypeCheckingProcessingStep {

private final ElementVisitor<Void, Void> visitor;

@Inject

InjectProcessingStep(InjectBindingRegistry injectBindingRegistry) {

super(e -> e);

this.visitor =

new ElementKindVisitor8<Void, Void>() { // 构造一个访问者,对不同的Element做出反应

@Override

public Void visitExecutableAsConstructor(

ExecutableElement constructorElement, Void aVoid) {

injectBindingRegistry.tryRegisterConstructor(constructorElement);

return null;

}

@Override

public Void visitVariableAsField(VariableElement fieldElement, Void aVoid) {

injectBindingRegistry.tryRegisterMembersInjectedType(

MoreElements.asType(fieldElement.getEnclosingElement()));

return null;

}

@Override

public Void visitExecutableAsMethod(ExecutableElement methodElement, Void aVoid) {

injectBindingRegistry.tryRegisterMembersInjectedType(

MoreElements.asType(methodElement.getEnclosingElement()));

return null;

}

};

}

@Override

public Set<Class<? extends Annotation>> annotations() {

return ImmutableSet.of(Inject.class);

}

@Override

protected void process(

Element injectElement, ImmutableSet<Class<? extends Annotation>> annotations) {

injectElement.accept(visitor, null);

}

}

可以看到 Inject注解器也实现了给自己注解。我们先不用搞懂它是怎么给自己实现注解处理的。

我们先看看 被@Inject修饰的构造方法里面做了什么:创建了一个 ElementKindVisitor8对象并赋值给了 ElementVisitor类型的 vistor。然后又在 process()中调用了 Element.accept(visitor)

这个环节的操作,使用了面向对象编程的设计模式中 “访问者模式”。当Android中提到访问者模式的例子时,Dagger2中这块地方,就是被拿出来讲解的最经典的例子= = 。所以要看懂这块代码的结构,先要搞清楚什么是访问者模式,对此,我还专门学习写了一篇出来,请参考:浅学设计模式之访问者模式(9/23)

从命名上就很显然的看出:

  • Element是元素类,它定义了 accept(ElementVistor)方法,来接收访问者

  • ElementVistor是访问者的接口类,它代表的是一种状态,当给出一个状态时,它的子类要根据Element的种类,来做出对应的处理。

而上文中,提到的 ElementKinVistor8就是ElementVistor的一个子类(它是ElementVistor基于JDK8的实现),它根据在 process(Element)的不同的Element做出不同的反应:

  • 当传入的Element是构造函数时,调用 visitExecutableAsConstructor(),内部调用 InjectBindingRegistry.tryRegisterConstructor()实现

  • 当传入的Element是成员变量时,调用visitVariableAsField(),内部调用 InjectBindingRegistry.tryRegisterMembersInjectedType()实现

  • 当传入的Element是方法时,调用 visitExecutableAsMethod(),内部调用 InjectBindingRegistry.tryRegisterMembersInjectedType()实现

这说明了什么:@Inject注解可以修饰:构造函数、成员变量、方法。对于不同的元素,用不同的处理。

这就体现了访问者模式。

在源码中我们可以看到具体的实现是由 InjectBindingRegistry类实现,它是一个抽象类,真正的实现是在 InjectBindingRegistryImpl中, 上面三个方法最终都会调用 tryRegisterMembersInjectedType(),我们来看看它的实现:

// dagger/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java

private Optional tryRegisterMembersInjectedType(

TypeElement typeElement,

Optional resolvedType,

boolean warnIfNotAlreadyGenerated) {

DeclaredType type = MoreTypes.asDeclared(typeElement.asType()); // 1

Key key = keyFactory.forInjectConstructorWithResolvedType(type); // 2

MembersInjectionBinding cachedBinding = membersInjectionBindings.getBinding(key); // 3

if (cachedBinding != null) {

return Optional.of(cachedBinding);

}

ValidationReport report =

injectValidator.validateMembersInjectionType(typeElement); // 4

report.printMessagesTo(messager); // 5

if (report.isClean()) {

MembersInjectionBinding binding = bindingFactory.membersInjectionBinding(type, resolvedType); // 6

registerBinding(binding, warnIfNotAlreadyGenerated); // 7

for (Optional supertype = types.nonObjectSuperclass(type);

supertype.isPresent();

supertype = types.nonObjectSuperclass(supertype.get())) {

getOrFindMembersInjectionBinding(keyFactory.forMembersInjectedType(supertype.get()));

}

return Optional.of(binding);

}

return Optional.empty();

}

private void registerBinding(ProvisionBinding binding, boolean warnIfNotAlreadyGenerated) {

provisionBindings.tryRegisterBinding(binding, warnIfNotAlreadyGenerated); // 8

if (binding.unresolved().isPresent()) {

provisionBindings.tryToGenerateBinding(binding.unresolved().get(), warnIfNotAlreadyGenerated);

}

}

注释1:将TypeElement转换成 DeclaredType

注释2:通过注释1获取的type,通过Key工厂获得一个Key

注释3:通过Key去内存中看看之前有没有缓存过该注解内容,如果缓存过则取缓存的的内容

注释4、5:在 messager上打印TypeElement信息

注释6、7:通过工厂模式创建出 注解信息 MembersInjectonBinding,它是 ProvisionBinding抽象类的子类,它的作用是描述一个被注解修饰的信息。然后调用 registerBinding()

注释8:将 注释7的 ProvisionBinding注册到 provisionBindings中,它的类型是 BindingsCollection<ProvisionBinding>,即专门用来收集注解信息。

到这里,通过 InjectProcessingStep.process()方法,@Inject修饰的所有信息,全都被注册到了 provisionBindings中。

我们都知道注解处理器无非就做两件事情:①收集注解信息 ②将这些信息写成Java文件

怎么实现第二步呢?在 BasicAnnotationProcessor中,在调用完 process()遍历执行完每个ProcessingStep后,还会执行postRound(),它会让子类来实现。

在 @Inject中,其Java文件的产生是在 ComponentProcessor中:

// ComponentProcessor.java

protected void postRound(RoundEnvironment roundEnv) {

statisticsCollector.roundFinished();

if (roundEnv.processingOver()) {

statisticsCollector.processingStopped();

} else {

try {

injectBindingRegistry.generateSourcesForRequiredBindings(

factoryGenerator, membersInjectorGenerator); // 1

} catch (SourceFileGenerationException e) {

e.printMessageTo(processingEnv.getMessager());

}

}

clearableCaches.forEach(ClearableCache::clearCache);

}

}

注释1:调用 injectBindingRegistry.generateSourcesForRequiredBindings实际上就是调用:InjectBindingRegistryImpl.generateSourcesForRequiredBindings()

// dagger/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java

@Override

public void generateSourcesForRequiredBindings(

SourceFileGenerator factoryGenerator,

SourceFileGenerator membersInjectorGenerator)

throws SourceFileGenerationException {

provisionBindings.generateBindings(factoryGenerator); // 1

membersInjectionBindings.generateBindings(membersInjectorGenerator); // 2

}

注释1:生成 XXX_Factory 类的Java文件

注释2:生成 XXXX_MemberInjector 类的Java

由于其细节都是文件生成,到这里就不再讲解下去了。

1.3 源码总结


虽然上面我讲解了只有关于 @Inject的注解处理,但是我相信触类旁通,Dagger2对其他的注解肯定也是做了类似的处理。

这里来总结一下 1.1、1.2节中所讲的知识。

(1)关于Dagger2 注解处理架构:

由于Dagger所要处理的注解很多, Inject、Module、Scope、Qualifier、Name、Component…为每个注解写一个Processor未免太枯燥,且难于管理,并且他们之间又存在着一些共性。

所以Dagger把这些共性抽了出来,并编写了 BasicAnnotationProcessor继承自AbstractProcessor。

并且实现了内部类ProcessingStep对应着每一个注解,它负责收集自己那部分注解修饰的信息。比如处理 @InjectInjectProcessingStep等。

BasicAnnotationPrcocessor.process() 会去遍历所有的 ProcessingStep.process()收集信息。

BasicAnnotationPrcocessor.postRound() 还会去根据需要产生Java文件(当然有些注解产生文件的入口不在这里)

(2)关于 XXXProcessingStep

间接的继承了 ProcessingStep,具备收集、注册信息的功能。

例如在 InjectProcessingStep中,它会把收集到的 Element,通过访问者模式,将Element信息注册到集合BindingsCollection中。

最后写文件的时候,就是根据这个集合的内容编写的。

2. dagger.android

===================================================================================

Dagger针对Android平台提供了专门的库来简化注入过程。需要我们添加额外的依赖库。

在Android中Dagger的注入是分层级的,有对应用层级全局属性或服务(OkHttp、Retrofit、Database)的注入,有对Activity中的变量注入,也有对Fragment中的变量注入,对应Component的层级如下图所示:

在这里插入图片描述

下面举几个例子,看下使用 dagger.android 在上面层级中的使用。

2.1 MainActivity中一般对象的注入


(1)创建 MainActivitySubComponent:

@Subcomponent

public interface MainActivitySubComponent extends AndroidInjector {

@Subcomponent.Builder

写在最后

对程序员来说,很多技术的学习都是“防御性”的。也就是说,我们是在为未来学习。我们学习新技术的目的,或是为了在新项目中应用,或仅仅是为了将来的面试。但不管怎样,一定不能“止步不前”,不能荒废掉。

![
[]


文章以下内容会给出阿里与美团的面试题(答案+解析)、面试题库、Java核心知识点梳理等

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

ySubComponent extends AndroidInjector {

@Subcomponent.Builder

写在最后

对程序员来说,很多技术的学习都是“防御性”的。也就是说,我们是在为未来学习。我们学习新技术的目的,或是为了在新项目中应用,或仅仅是为了将来的面试。但不管怎样,一定不能“止步不前”,不能荒废掉。

[外链图片转存中…(img-opdmaKn0-1714484009162)]

[外链图片转存中…(img-K9WYo8nL-1714484009163)]
[]

[外链图片转存中…(img-2MlfXsZq-1714484009163)]
[外链图片转存中…(img-v0wdo16O-1714484009164)]

文章以下内容会给出阿里与美团的面试题(答案+解析)、面试题库、Java核心知识点梳理等

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值