作者:IT魔幻师
博客:www.huyingzi.top
转载请注明出处:https://www.jianshu.com/p/2d6fdbb6ead2
一、介绍
- APT(Annotation Processing Tool)是一种注解处理工具,它对源文件代码进行检测,找出其中的注解,并对此进行额外的处理,如检查代码书写规范,生成额外的源文件等(具体内容由注解处理器的实现所决定),现在很多流行的第三方库,如Dagger2、ButterKnife等,都是采用APT技术实现的。
二、AutoService与Javapoet
-
AutoService依赖存放于http://mvnrepository.com/artifact/com.google.auto.service/auto-service
AutoService会自动在META-INF文件夹下生成Processor配置信息文件,该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,
就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。
基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定,方便快捷。使用时添加如下配置:implementation 'com.google.auto.service:auto-service:1.0-rc2'
-
Javapoet来源与JakeWharton大神https://github.com/square/javapoet他是一个用于生成.java源文件的API库,其采用build模式进行创建,并自动帮助生成.java文件,使用时添加如下配置:
implementation 'com.squareup:javapoet:1.11.1'
简单用法,在com.hubin.helloworld包下创建一个Helloworld类,类中创建一个main函数:
//创建一个main函数
MethodSpec main = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC) //public static
.returns(void.class) //返回值 voild
.addParameter(String[].class, "args") //添加参数 String[] arrgs
.addStatement("$T.out.println($S)", System.class, "Hello, hubin!") //添加内容: System.out.println("Hello, JavaPoet!");
.build();
//创建一个类 HelloWorld
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main) //将main函数添加到类中
.build();
//将helloWorld类添加到 包com.hubin.helloworld中
JavaFile javaFile = JavaFile.builder("com.hubin.helloworld", helloWorld)
.build();
try {
javaFile.writeTo(filer); //执行写入
} catch (IOException e) {
e.printStackTrace();
}
三、注解
java中元注解有四个: @Retention @Target @Document @Inherited;
-
@Retention:注解的保留位置
@Retention(RetentionPolicy.SOURCE) //注解仅存在于源码中,在class字节码文件中不包含 @Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得, @Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
-
@Target:注解的作用目标
@Target(ElementType.TYPE) //接口、类、枚举、注解 @Target(ElementType.FIELD) //字段、枚举的常量 @Target(ElementType.METHOD) //方法 @Target(ElementType.PARAMETER) //方法参数 @Target(ElementType.CONSTRUCTOR) //构造函数 @Target(ElementType.LOCAL_VARIABLE)//局部变量 @Target(ElementType.ANNOTATION_TYPE)//注解 @Target(ElementType.PACKAGE) ///包
@Document:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解
四、创建一个注解(以阿里路由框架为例)
-
创建一个javaLibrary模块 名为libRouter
-
创建一个注解如下
@Target(ElementType.TYPE) //该注解的作用范围接口、类、枚举 @Retention(RetentionPolicy.CLASS) //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得, public @interface Route { /** * 定义:路由的路径,标识一个路由节点 * @return */ String path(); /** * 将路由节点进行分组,可以实现按组动态加载 * @return */ String group() default ""; }
五、用抽象处理器 AbstractProcessor处理注解
-
再创建一个java依赖模块,引入谷歌的AutoService和Javapoet框架,以及上一步中创建的注解模块
apply plugin: 'java-library' dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.google.auto.service:auto-service:1.0-rc2' //引入谷歌的AutoService implementation 'com.squareup:javapoet:1.11.1' //引入Javapoet框架 implementation project(':libRouter') //依赖刚刚创建的注解模块 } //指定为java1.7 sourceCompatibility = "1.7" targetCompatibility = "1.7"
-
继承AbstractProcessor构建注解处理器
@AutoService(Processor.class)//当前注解处理器能够处理的注解 代替 getSupportedAnnotationTypes函数 @SupportedSourceVersion(SourceVersion.RELEASE_7)//java版本 代替 getSupportedAnnotationTypes 函数 public class HubinProvessor extends AbstractProcessor { private Messager messager; Filer filer; @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); filer = processingEnvironment.getFiler(); messager = processingEnvironment.getMessager(); messager.printMessage(Diagnostic.Kind.NOTE, "HubinProvessor----init!"); } @Override public Set<String> getSupportedAnnotationTypes() { //支持的注解也可以用@SupportedAnnotationTypes({"com.hubin.librouter.Route"})代替 Set<String> annotations = new LinkedHashSet<>(); annotations.add(Route.class.getCanonicalName()); return annotations; } /** * 注解处理 * @param set * @param roundEnvironment * @return */ @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { messager.printMessage(Diagnostic.Kind.NOTE,"HubinProvessor----process,start process"); for (TypeElement typeElement : set) { messager.printMessage(Diagnostic.Kind.NOTE,"HubinProvessor----process"); //创建一个main函数 MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) //public static .returns(void.class) //返回值 voild .addParameter(String[].class, "args") //添加参数 String[] arrgs .addStatement("$T.out.println($S)", System.class, "Hello, hubin!") //添加内容: System.out.println("Hello, JavaPoet!"); .build(); //创建一个类 HelloWorld TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(main) //将main函数添加到类中 .build(); //将helloWorld类添加到 包com.hubin.helloworld中 JavaFile javaFile = JavaFile.builder("com.hubin.helloworld", helloWorld) .build(); try { javaFile.writeTo(filer); //执行写入 } catch (IOException e) { e.printStackTrace(); } } return false; } }
第一行@AutoService(Processor.class)就是Google开发的一个注解处理器,用来生成META-INF/services/javax.annotation.processing.Processor文件的。
init(ProcessingEnvironment processingEnvironment): 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的init()方法,它会被注解处理工具调用,并输入ProcessingEnviroment参数。ProcessingEnviroment提供很多有用的工具类Elements,Types和Filer。
process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment): 这相当于每个处理器的主函数main()。你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。输入参数RoundEnviroment,可以让你查询出包含特定注解的被注解元素。后面我们将看到详细的内容。
getSupportedAnnotationTypes(): 这里你必须指定,这个注解处理器是注册给哪个注解的。它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。换句话说,你在这里定义你的注解处理器注册到哪些注解上。这个函数可以用注解@SupportedAnnotationTypes({"com.hubin.librouter.Route"})代替
六、使用
-
在app模块的build.gradle文件中加入自定义的两个依赖,引入自己定义的注解和注解处理器的依赖
annotationProcessor project(':libProcessor')//引入注解处理器模块 implementation project(':libRouter')//引入自己定义的注解
-
在需要使用的地方用自己定义的注解给其标记
- 执行一次编译就会自动生成处理器中根据自己的规则创建的类的.class文件
public final class HelloWorld {
public HelloWorld() {
}
public static void main(String[] args) {
System.out.println("Hello, hubin!");
}
}
- 使用注解生成的类在控制台打印 "Hello,hubin!"
- 查看控制台确实输出了该函数