组件化之AutoService使用与源码解析

您所在的位置:网站首页 组件扫描注解教程视频 组件化之AutoService使用与源码解析

组件化之AutoService使用与源码解析

2024-01-18 18:57| 来源: 网络整理| 查看: 265

*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

在JDK 1.5之后,java提供了对注解的支持,这些注解与普通代码一样,在运行期间发挥作用。在JDK 1.6中实现了JSR-269规范,提供了一组插入式注解处理器的标准API在编译期间对注解进行处理,可以看作是一组编译器的插件,可以读取/修改/添加抽象语法树中的任意元素。

在Android模块开发之APT技术介绍了自定义注解处理器的一些知识,自定义注解处理器注册才能被Java虚拟机调用,在上面的博客第四小节中用的方法是手动注册,这比较违反程序员懒的特点,在里面也提到了自动注册的方法,就是AutoService,今天这篇博客就是来扒一扒谷歌提供的这个开源库。

先通过一个栗子看下AutoService怎么用的。

1.使用

定义一个简单的接口:

public interface Display { String display(); }

有两个Module A和B分别实现了这个接口,然后在app Module中调用这两个实现类, 比较低级的办法就是在app Module中直接依赖这两个模块,然后就可以调用实现类了。这有两个坏处,一个是app Module直接强依赖A和B两个Module,另外如果开发中拿不到依赖的模块呢,有可能模块是第三方的,这个时候强依赖这种方式就行不通了。

看下AutoService是怎么实现的,先看下包结构,interfaces只简单包含上面的Display接口,modulea和moduleb实现这个接口,app统一加载所有这个接口的实现类。

包结构.png

看下modulea和moduleb实现,方法实现里面简单返回一个字符串,主要是上面的@AutoService(Display.class)注解,注解值是接口的名称,也就是implements实现的类接口名称。

// modulea import com.google.auto.service.AutoService; @AutoService(Display.class) public class ADisplay implements Display{ @Override public String display() { return "A Display"; } } // moduleb @AutoService(Display.class) public class BDisplay implements Display { @Override public String display() { return "B Display"; } }

再看下app Module里面的怎么调用上面的ADispaly和BDisplay,加载原理就是通过ServiceLoader去加载,可以得到接口Display的所有实现类,在我们这个栗子中就是上面的ADisplay和BDisplay两个实现者。DisplayFactory通过getDisplay可以拿到所有的实现类。

import com.example.juexingzhe.interfaces.Display; import java.util.Iterator; import java.util.ServiceLoader; public class DisplayFactory { private static DisplayFactory mDisplayFactory; private Iterator mIterator; private DisplayFactory() { ServiceLoader loader = ServiceLoader.load(Display.class); mIterator = loader.iterator(); } public static DisplayFactory getSingleton() { if (null == mDisplayFactory) { synchronized (DisplayFactory.class) { if (null == mDisplayFactory) { mDisplayFactory = new DisplayFactory(); } } } return mDisplayFactory; } public Display getDisplay() { return mIterator.next(); } public boolean hasNextDisplay() { return mIterator.hasNext(); } }

使用就是这么几个步骤,比较简单,下面看下AutoService实现原理。

2.实现原理

首先先简单介绍下Javac的编译过程,大致可以分为3个过程:

解析与填充符号表 插入式注解处理器的注解处理过程 分析与字节码生成过程

看下一个图片,图片来源深入理解Java虚拟机,首先会进行词法和语法分析,词法分析将源代码的字符流转变为Token集合,关键字/变量名/字面量/运算符读可以成为Token,词法分析过程由com.sun.tools.javac.parserScanner类实现;

语法分析是根据Token序列构造抽象语法树的过程,抽象语法树AST是一种用来描述程序代码语法结构的树形表示,语法树的每一个节点读代表着程序代码中的一个语法结构,例如包/类型/修饰符/运算符/接口/返回值/代码注释等,在javac的源码中,语法分析是由com.sun.tools.javac.parser.Parser类实现,这个阶段产出的抽象语法树由com.sun.tools.javac.tree.JCTree类表示。经过上面两个步骤编译器就基本不会再对源码文件进行操作了,后续的操作读建立在抽象语法树上。

完成了语法和词法分析后就是填充符号表的过程。符号表是由一组符号地址和符号信息构成的表格。填充符号表的过程由com.sun.tools.javac.comp.Enter类实现。

如前面介绍的,如果注解处理器在处理注解期间对语法树进行了修改,编译器将回到解析与填充符号表的过程重新处理,直到所有插入式注解处理器都没有再对语法树进行修改为止,每一次循环称为一个Round,如下图中的环。

javac.jpeg

上面简单回顾了下编译注解的一些东西,接下来看下AutoService这个注解的实现,使用它有三个限定条件;

不能是内部类和匿名类,必须要有确定的名称 必须要有公共的,可调用的无参构造函数 使用这个注解的类必须要实现value参数定义的接口 @Documented @Target(TYPE) public @interface AutoService { /** Returns the interface implemented by this service provider. */ Class value(); }

有注解,必须要有对应的注解处理器,AutoServiceProcessor继承AbstractProcessor,一般我们会实现其中的3个方法, 在getSupportedAnnotationTypes中返回了支持的注解类型AutoService.class;getSupportedSourceVersion ,用来指定支持的java版本,一般来说我们都是支持到最新版本,因此直接返回 SourceVersion.latestSupported()即可;主要还是process方法。

public class AutoServiceProcessor extends AbstractProcessor { @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public Set getSupportedAnnotationTypes() { return ImmutableSet.of(AutoService.class.getName()); } @Override public boolean process(Set


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3