注解处理器

19 篇文章 0 订阅

注解处理器,注解在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
  • 参考 注解处理器:郑雨笛
 
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值