注解处理器,注解在java中很常见,那注解是如何生效的呢?提供了怎样的作用?
- @Override 最常见的一个注解,该注解声明一个方法被重写。来看看这个注解的源码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
-
这个注解同样被两个注解声明,@Target 声明注解作用的Java结构。这里声明该注解用于方法。
-
@Retention 声明注解的生命周期。有三个常量值:SOURCE(作用于源码)CLASS(作用于源码字节码)
RUNTIME(作用于源码字节码运行时)。
-
这说明注解三种声明周期,只存在源码中(SOURCE),即编译器使用该注解生成字节码后,该注解就不存在编译后的字节码文件里了。作用到字节码,即在字节码依然有注解(CLASS)。作用运行时(RUNTIME)即在虚拟机执行时该注解依然有作用。
-
注解处理器原理:java编译源代码过程
-
-
1 将源代码解析成语法树。
-
2 注解处理器处理
-
3.生成字节码
-
注解处理器生成新的源文件就会重复 1、2 ,直到不再生成新的源文件,再执行第三步生成字节码。
-
自定义一个注解该如何做?
-
继承AbstractProcessor抽象类,该类实现了Processor 接口,Processor接口定义了注解应该实现的方法。四个重要的方法如下:
// 注解处理器支持的java版本,与编译器一致 public Set<String> getSupportedOptions() // 返回注解支持的类型,字符串 public Set<String> getSupportedAnnotationTypes() // 注解处理器初始化方法,注解处理器通过反射API生成,所以每个注解处理器都需要一个无参构造方法。 void init(ProcessingEnvironment var1) // 处理注解逻辑的方法 boolean process(Set<? extends TypeElement> var1, RoundEnvironment var2);
- 自定义一个注解,这是一个检查属性是否有set方法的注解,作用于属性。生命周期是源码阶段。
@Target(value = ElementType.FIELD) @Retention(value=RetentionPolicy.SOURCE) public @interface CheckSetter { }
- 注解实现:基于jdk1.8,主要实现process接口
package com.lucene.demo.processor;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic;
import java.util.Set;
/**
* @author linghongkang
* @description:
* @create: 2018-12-13 15:40
**/
@SupportedAnnotationTypes(value = "com.lucene.demo.processor.CheckSetter")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class CheckSetterProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// todo field 获取注解的属性集合
for (TypeElement annotation : annotations) {
if (!"com.lucene.demo.processor.CheckSetter".equals(annotation.getQualifiedName().toString())) {
continue;
}
Set<TypeElement> rootElements =ElementFilter.typesIn(roundEnv.getRootElements());
for (TypeElement element : rootElements) {
Set<VariableElement> typeElements = ElementFilter.fieldsIn(roundEnv.getElementsAnnotatedWith(CheckSetter.class));
for (VariableElement field : typeElements) {
System.out.println("字段名:"+field.getSimpleName().toString());
if (!containsSetter(element,field.getSimpleName().toString())){
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
String.format("setter not found '%s.%s'.",element.getSimpleName(),field.getSimpleName()));
}
}
}
}
return true;
}
private static boolean containsSetter(TypeElement typeElement,String name){
String setter="set"+name.substring(0,1).toUpperCase()+name.substring(1).toLowerCase();
// 获取类或接口的方法集合
System.out.println("typeElement"+typeElement.getEnclosedElements());
for (ExecutableElement executableElement:ElementFilter.methodsIn(typeElement.getEnclosedElements())) {
// 判断该方法是否是静态方法 判断该方法是否等于setter 该方法参数不能为空
System.out.println(executableElement.getSimpleName().toString());
if(!executableElement.getModifiers().contains(Modifier.STATIC)
&&executableElement.getSimpleName().toString().equals(setter)
&&!executableElement.getParameters().isEmpty()){
return true;
}
}
return false;
}
}
- 效果:测试类
import java.lang.reflect.Method; import com.lucene.demo.processor.CheckSetter; public class Test { @CheckSetter private String field; public static void target(int i) { new Exception("#" + i).printStackTrace(); } public static void main(String[] args) throws Exception { Class<?> klass = Class.forName("Test"); Method method = klass.getMethod("target", int.class); method.invoke(null, 0); } }
- 效果:打成jar包进行测试。
- 到此完成了一个自定义注解器的开发。
- 源码位于:https://github.com/hongkangling/processor
- 参考 注解处理器:郑雨笛