Java中的APT注解

java中常用的一些框架都使用到了注解,比如EventBus、Dragger2、Butterknife等,从这些流行框架可以看出,注解在Android中使用还是很广泛的,常见的注解有运行时注解和编译期注解,运行时注解是通过反射在运行时拿到注解信息进行处理,编译期注解是在编译阶段根据注解生成相应的中间类来处理注解,因为运行时注解需要在运行时执行反射代码和注解处理逻辑,所以它的运行时性能没有编译期注解高,本文学习的注解方式就是APT编译时注解。

1、创建一个名称为annotation的module用来单独存放自定义的注解类,然后在该模块下可以新建一个自定义的注解类(这里以HuiAnnotation为例)

/**
 * Created by znh on 2020/7/29
 * <p>
 * 自定义注解
 */
@Target(ElementType.TYPE)//作用在类上
@Retention(RetentionPolicy.CLASS)//编译期注解
public @interface HuiAnnotation {

}

2、创建一个名称为processor的module用来存放注解处理器,这个模块中需要处理上一步中自定义的注解,所以需要将annotation模块引入进来,自定义的注解处理器需要继承至谷歌注解处理器框架中的AbstractProcessor类,所以需要引入注解处理器框架,为了更好的生成java模板代码,还可以引入javapoet代码模板生成框架(这个不是必须的,使用它可以更方便的生成java代码),以上3个依赖包在gradle中的依赖配置如下:

//添加注解处理器依赖
implementation 'com.google.auto.service:auto-service:1.0-rc6'
//gradle5.0及以上需要
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'

//添加自定义的注解类依赖
implementation project(':annotation')

//用来生成java模板代码的框架
implementation 'com.squareup:javapoet:1.11.1'

然后在该模块下新建一个注解处理器类(这里以HuiProcessor为例),这个自定义的注解处理器类中有几个比较重要的方法需要重写:

  • @AutoService(Processor.class):要为注解处理器添加上这个注解才可以生效
  • init(ProcessingEnvironment processingEnvironment):做一些初始化的操作
  • getSupportedAnnotationTypes():支持的注解有哪些,将自己定义的注解添加进去
  • getSupportedSourceVersion() :支持的jdk版本号
  • process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment):注解处理的核心方法,注解处理逻辑就是在这个方法中写的

注解处理器HuiProcessor的代码如下:

/**
 * Created by znh on 2020/7/29
 * <p>
 * 自定义注解处理器
 */
@AutoService(Processor.class)
public class HuiProcessor extends AbstractProcessor {

    private Elements mElementUtils;
    private Messager mMessager;

    /**
     * 做一些初始化操作
     *
     * @param processingEnvironment
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        mElementUtils = processingEnvironment.getElementUtils();
        mMessager = processingEnvironment.getMessager();
    }

    /**
     * 支持的注解有哪些
     *
     * @return
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotationTypes = new LinkedHashSet<>();
        annotationTypes.add(HuiAnnotation.class.getCanonicalName());
        return annotationTypes;
    }

    /**
     * 支持的jdk版本
     *
     * @return
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }


    /**
     * 注解核心处理方法
     *
     * @param set
     * @param roundEnvironment
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {

        //没有需要处理的注解
        if (set.isEmpty()) return false;

        //获取项目中使用了HuiAnnotation注解的元素集合
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(HuiAnnotation.class);

        //遍历elements
        for (Element element : elements) {
            //获取元素的包名
            String packageName = mElementUtils.getPackageOf(element).getQualifiedName().toString();
            //获取元素的类名
            String className = element.getSimpleName().toString();
            //自定义需要生成的类文件名称
            String customClassName = className + "Helper";

            //打印日志
            mMessager.printMessage(Diagnostic.Kind.NOTE, "packageName:" + packageName);
            mMessager.printMessage(Diagnostic.Kind.NOTE, "className:" + className);
            mMessager.printMessage(Diagnostic.Kind.NOTE, "customClassName:" + customClassName);

            //创建一个方法参数
            ParameterSpec msgParam = ParameterSpec
                    .builder(String.class, "msg") //定义参数类型和参数名称
                    .build();

            //生成方法
            MethodSpec print = MethodSpec.methodBuilder("print")
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    .returns(String.class)
                    .addStatement("$T.out.println($S)", System.class, "Hello," + customClassName)//定义打印语句
                    .addParameter(msgParam)
                    .addStatement("return $N", msgParam)
                    .build();

            //生成类
            TypeSpec customClassType = TypeSpec.classBuilder(customClassName)
                    .addModifiers(Modifier.PUBLIC)
                    .addMethod(print)
                    .build();

            //生成java文件
            JavaFile javaFile = JavaFile.builder(packageName, customClassType).build();
            try {
                javaFile.writeTo(processingEnv.getFiler());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }
}

3、这个注解处理器的作用是生成一个类名为自定义的customClassName中间辅助类,在这个类中生成一个能打印信息的方法。注解和注解处理器定义完成之后就可以在项目中使用了,在app的module中使用时需要将注解和注解处理器依赖进去,gradle配置如下:

implementation project(':annotation')
annotationProcessor project(':processor')

然后在需要的类上添加注解并编译项目,会生成一个中间辅助类:
在这里插入图片描述
然后可以调用生成的中间辅助类的方法去完成自己的业务逻辑了,使用代码如下:

package com.znh.aptdemo;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import com.znh.annotation.HuiAnnotation;

@HuiAnnotation //为MainActivity添加注解
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        test();
    }

    public void test() {
    	//调用中间类的方法进行测试
        String msg = MainActivityHelper.print("测试一下...");
        System.out.println(msg);
    }
}

打印结果如下:
13077-13077/com.znh.aptdemo I/System.out: Hello,MainActivityHelper
13077-13077/com.znh.aptdemo I/System.out: 测试一下...

Demo地址:https://github.com/huihuigithub/blog_demo_projects (java_apt_demo项目)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值