注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包,类,接口,字段,方法参数,局部变量等进行标记
分三类:
1.java自带的标准注解,如@Override(重写某个方法),@Deprecated(标明某个类或方法过时),@SuppressWarnings(标明要忽略的警告)
2.一类是元注解,元注解用于定义注解的注解,包括@Retention(标明注解被保留的阶段),@Target(标明注解使用的范围),@Inherited(标明注解可继承),@Documented(标明是否生成javadoc文档)
3.自定义注解,可以根据自己的需求定义注解
作用:
1.生成文档,通过代码里标识的元数据生成javadoc文档
2.编译检查,通过代码里标识的元数据让编译器在编译期间,进行检查验证
3.编译时动态处理,编译时通过代码里表示的元数据动态处理,例如动态生成代码
4.运行时动态处理,运行时通过代码里表示的元数据动态处理,例如使用反射注入实例
android 的自定义注解
要使用元注解来定义我们自定义的注解
元注解 | 说明 |
---|---|
@Target | 表明我们注解可以出现的地方。是一个ElementType枚举 |
@Retention | 这个注解的的存活时间 |
@Document | 表明注解可以被javadoc此类的工具文档化 |
@Inherited | 是否允许子类继承该注解,默认为false |
@Target ElementType类型 说明:
ElementType.TYPE 接口、类、枚举、注解
ElementType.FIELD 字段
ElementType.METHOD 方法
ElementType.PARAMETER 方法参数
ElementType.CONSTRUCTOR 构造函数
ElementType.LOCAL_VARIABLE 局部变量
ElementType.ANNOTATION_TYPE 注解
ElementType.PACKAGE 包
@Retention RetentionPolicy类型 说明:
RetentionPolicy.SOURCE 注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃
RetentionPolicy.CLASS 注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期
RetentionPolicy.RUNTIME 注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
声明注解:
通过@Retention元注解确定我们注解使用时机,如上
通过@Target确定我们注解是作用在什么上面的
自定义注解分类:
**运行时注解,**在代码运行的过程中通过反射机制找到我们自定义的注解,然后做相应的事情
**编译时注解,**在编译的过程者中用javac注解处理器来扫描到我们自定义的注解,处理注解生成我们需要的一些文件(通常是java文件)
编译时注解:
1.声明注解
2.解析注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindView {
int value();
}
编译时注解的解析需要我们自己实现一个注解解析处理器。注意处理器所作的工作,就是在代码编译过程中,找到指定我们的注解,然后加上自己特定的逻辑(通常生成java文件)
注意:要实现一个module必须是java-library(因为java library才能继承处理器AbstractProcessor)
goole为了方便我们注册注解处理器,提供一个注册处理器的库@AutoService(Processor.class)的注解来进行注册
添加依赖
dependencies {
implementation 'com.google.auto.service:auto-service:1.0-rc2'
}
@AutoService(Process.class)
public class BindViewProcessor extends AbstractProcessor {
private Elements elements;
private Messager messager;
private Filer filer;
private Types types;
/**
* 注解处理器的核心方法,在这里来处理注解,并生成Java辅助类
*
* @param set
* @param roundEnvironment
* @return
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Set<? extends Element> annotationElements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
for (Element element : annotationElements){
//获取字段名称
if (element instanceof TypeElement) {
//注解用于类
try {
TypeElement typeElement2 = (TypeElement) element;
System.out.println("typeElement2:" + typeElement2);
} catch (Exception e) {
e.printStackTrace();
}
} else {
//注解用于字段
}
}
return false;
}
/**
* 编译期间,init()会自动被注解处理工具调用,并传入ProcessingEnvironment参数,
* 通过该参数可以获取到很多有用的工具类(Elements,Filer,Messager等)
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
elements = processingEnv.getElementUtils();
messager = processingEnv.getMessager();
filer = processingEnv.getFiler();
types = processingEnv.getTypeUtils();
}
/**
* 返回processor可处理的注解
*
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> sets = new HashSet<>();
sets.add(BindView.class.getCanonicalName());
return sets;
}
/**
* 用于指定你的java版本,一般返回:SourceVersion.latestSupported()
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return super.getSupportedSourceVersion();
}
}
process()方法的核心是Element元素,Element代表程序的元素,在注解处理过程中,编译器会扫描所有的java源文件,并将源码中的每个部分都看作特定类型的Element
自定义处理器的过程除了要了解Element类和其它的子类,还需要4个帮助类:
注解处理器帮助类 | 说明 |
---|---|
Elements | 一个用来处理Element的工具类 |
Types | 一个用来处理TypeMirror的工具类 |
Filer | 用于创建文件(比如创建java文件) |
Messager | 用于输出,类似print函数 |
这四个帮助类,可以通过在init()函数中,通过ProcessingEnvironment获取到
编写完注解处理器在app模块导入
implementation project(":模块名称")
在process函数中,通过RoundEnvironment可以返回得到使用给定注解的元素
源码:
public interface RoundEnvironment {
// 返回使用给定类型注解的元素。
Set<? extends Element> getElementsAnnotatedWith(TypeElement var1);
// 返回使用给定类型注解的元素。
Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> var1);
}