一 注解
Java 注解(Annotation)又称 Java 标注,是 JDK1.5 引入的一种注释机制。是元数据的一种形式,提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响
二 元注解
在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为 meta-annotation(元注解)。声明的注解允许作用于哪些节点使用@Target声明;保留级别由@Retention 声明。其中保留级别如下。
- RetentionPolicy.SOURCE 标记的注解仅保留在源级别中,并被编译器忽略
- RetentionPolicy.CLASS 标记的注解在编译时由编译器保留,但 Java 虚拟机(JVM)会忽略。
- RetentionPolicy.RUNTIME 标记的注解由 JVM 保留,因此运行时环境可以使用它
SOURCE < CLASS < RUNTIME,即CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS。
三 注解的应用场景
根据注解的保留级别不同,对注解的使用自然存在不同场景。由注解的三个不同保留级别可知,注解作用于:
源码、字节码与运行时
APT注解处理器
APT全称为:”Anotation Processor Tools”,意为注解处理器。顾名思义,其用于处理注解。编写好的Java源文 件,需要经过 javac 的编译,翻译为虚拟机能够加载解析的字节码Class文件。注解处理器是 javac 自带的一个工 具,用来在编译时期扫描处理注解信息。你可以为某些注解注册自己的注解处理器。 注册的注解处理器由 javac 调起,并将注解信息传递给注解处理器进行处理。
注解处理器是对注解应用最为广泛的场景。在Glide、EventBus3、Butterknifer、Tinker、ARouter等等常用 框架中都有注解处理器的身影。但是你可能会发现,这些框架中对注解的定义并不是 SOURCE 级别,更多的 是 CLASS 级别,别忘了:CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS
APT的使用详解
- 新建一个Module,里面新建一个注解
注意这里是 java-library
package com.base.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface Route {
/**
*路由的路径,标识一个路由节点
*/
String path();
/**
* 将路由节点进行分组,可以实现按组动态加载
*/
String group() default "";
}
复制代码
- 新建一个Module,创建一个注解处理器
package com.base.compiler.processor;
import com.base.annotation.Route;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
//1.这里是APT注解处理器与定义的注解相关联
@SupportedAnnotationTypes({"com.base.basejavapro.annotation.Route","com.base.annotation.Route"})
public class RouteProcessor extends AbstractProcessor {
private Filer mFiler;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mFiler = processingEnv.getFiler();//2.初始化一个项目编译文件路径
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv) {
Messager messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE,"=============RouteProcessor==================:"+set.size()); //编译期日志打印
Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);// 3.获取所有添加注解的元素集合
for (Element element : routeElements) {// 4.遍历 添加注解的元素
Route route = element.getAnnotation(Route.class);
String path = route.path();
String group = route.group();
String newClassName = path + "$$Hx";
//5.通过字符拼接方法
StringBuilder builder = new StringBuilder()
.append("package com.hongx.processor.auto;\n\n")
.append("public class ")
.append(newClassName)
.append(" {\n\n") // open class
.append("\tpublic String getMessage() {\n") // open method
.append("\t\treturn "");
builder.append(path).append(group).append(" !\n");
builder.append("";\n") // end return
.append("\t}\n") // close method
.append("}\n"); // close class
try {
JavaFileObject source = mFiler.createSourceFile("com.hongx.processor.auto."+newClassName);
Writer writer = source.openWriter();
writer.write(builder.toString());
writer.flush();
writer.close();
} catch (IOException e) {
}
if (!set.isEmpty()) {
createRoute(mFiler,newClassName,path);
}
}
return false;
}
//6.通过JavaPoet
public static void createRoute(Filer filer,String className,String path) {
//使用 MethodSpec 主方法main生成
MethodSpec main = MethodSpec.methodBuilder("getMessage") //主方法的名称
.addModifiers(Modifier.PUBLIC)
.returns(String.class)
.addStatement("return $S",path)
.build();
// 使用 TypeSpec 生成 HelloWorld 类
TypeSpec typeSpec = TypeSpec.classBuilder(className) //主类的名称
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.addJavadoc("注释注解代码块")
.build();
try {
JavaFile javaFile = JavaFile.builder("com.javapoet.processor.auto", typeSpec)
.build();
/**
* 代码写入控制台
*/
javaFile.writeTo(System.out);
/**
* 代码写入文件 E:\FastEc\latte_compiler\src\main\java
*/
javaFile.writeTo(filer);
} catch (Exception e) {
e.printStackTrace();
}
}
}
复制代码
3.手动创建配置文件 javax.annotation.processing.Processor
在main目录下创建resources/META-INF/services/javax.annotation.processing.Processor 文件
其内容就是自定义注解处理器的全类名
4.运行结果
通过编译,在编译目录产生了目标文件 1、2,以及
执行步骤
执行结果