美文网首页
APT注解处理

APT注解处理

作者: waiwaaa | 来源:发表于2020-03-31 17:53 被阅读0次

    什么是APT?

    APT(Annotation Processing Tool)注解处理工具,它是jdk提供的一套工具,通过这套工具我们可以在编译时,根据注解自动生成Java代码。

    注解

    注解可以理解为代码的标识,不会对运行有直接影响。

    1、内置注解

    Java内置几个常用注解,这部分标识源码,会被编译器识别,提示错误等。

    @Override 标记覆盖方法
    @Deprecated 标记为过时
    @SuppressWarnings 忽略警告

    2、元注解

    假设我们要自定义一个注解,这时候就需要用元注解去声明自定义注解,包括像注解作用域、生命周期等。

    @Documented 可以被javadoc文档化。
    @Target 注解用在哪
    CONSTRUCTOR 构造函数
    FIELD 域声明
    LOCAL_VARIABLE 局部变量
    METHOD 方法
    PACKAGE 包声明
    PARAMETER 参数
    TYPE 类、接口
    @Inherited
    允许子类继承父类注解
    @Retention
    SOURCE:编译时剔除
    CLASS:在CLASS文件保留标识,运行时剔除
    RUNTIME 运行时保留标识

    3、自定义注解

    使用元注解创建自定义注解

    //直到运行时还保留注解
    @Retention(RetentionPolicy.RUNTIME)
    //作用域 类
    @Target(ElementType.TYPE)
    public @interface BindV {
        int resId() default 0;
    }
    

    4、解析注解

    在代码中定义了标识,现在想拿到这些信息,可以通过反射、APT获取注解的值。

    1、反射

    通过反射解析注解(这里也不阐述什么是反射)

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface ReadAnnotation {
        String value() default "read annotation defalult";
    }
    
    //MainActivity.java 文件
    @ReadAnnotation("test")
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Class clz = MainActivity.class;
            ReadAnnotation read = (ReadAnnotation) clz.getAnnotation(ReadAnnotation.class);//反射拿到类的注解
            TextView textview=findViewById(R.id.txt);
            textview.setText(read.value());
        }
    }
    

    2、APT

    APT(Annotation Processing Tool)注解处理器,是在编译期间读取注解,生成Java文件。反射解析注解是损耗性能的,接下来通过APT来生成java源码,从而避免反射。

    1. 在之前的项目lib_annotation上创建新的注解AptTest

    @Retention(RetentionPolicy.SOURCE)
    @Target(ElementType.TYPE)
    public @interface AptTest {
        String value() default "atp default String";
    }
    
    

    2. 创建注解处理(java)项目 lib_compiler

    原理为通过获取注解参数,通过拼接字符串方式生成代码,然后写入文件。通过javapeat可以减少导包等操作。

    @SupportedAnnotationTypes({"com.yy.lib_annotation.AptTest"})//声明需要匹配的注解
    @SupportedSourceVersion(SourceVersion.RELEASE_8)//支持的Java版本
    public class AptProcessor extends AbstractProcessor {
        @Override
        public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
    
    
            Set<? extends Element> element = roundEnvironment.getElementsAnnotatedWith(AptTest.class);
            StringBuilder sb=new StringBuilder("all aptTest values is:");
            for (Element ele:element) {
                TypeElement typeelement = (TypeElement) ele;
                AptTest aptTest=typeelement.getAnnotation(AptTest.class);
                sb.append(aptTest.value());
                sb.append("|");
            }
    
            //新建一个方法
            MethodSpec getString = MethodSpec.methodBuilder("getString")
                    .addModifiers(Modifier.PUBLIC)
                    .returns(String.class)
                    .addCode("return \""+sb.toString()+"\";\n")
                    .build();
            //新建一个类
            TypeSpec testType = TypeSpec.classBuilder("APTTest").addModifiers(Modifier.PUBLIC).addMethod(getString).build();
    
            //生成这个类
            JavaFile javaFile = JavaFile.builder("com.yy.aptdemo", testType)
                    .build();
            try {
                javaFile.writeTo(processingEnv.getFiler());
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println(e);
            }
    
            return true;
        }
    }
    

    项目结构变为


    项目结构

    3. lib_compiler注册处理器

    gradle4可以直接用auto-service注解进行自动注册,现在新建的是gradle版本是5以上的,所以要手动注册,手动注册方法如下:
    创建一个
    META-INF/services/javax.annotation.processing.Processor文件,
    其内容是一系列的自定义注解处理器完整有效类名集合,以换行切割:

    com.yy.lib_compiler.AptProcessor
    

    4.引入运行项目
    在app项目中,
    在build.gradle文件中引入

    dependencies {
        implementation project(path: ':lib_annotation')
        annotationProcessor project(path: ':lib_compiler')
    }
    

    修改MainActivity

    @ReadAnnotation("test")
    @AptTest("this is apt from annotation")
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Class clz = MainActivity.class;
            ReadAnnotation read = (ReadAnnotation) clz.getAnnotation(ReadAnnotation.class);//拿到布局文件id
            TextView textview=findViewById(R.id.txt);
            textview.setText(read.value());
    
            //
            textview.setOnClickListener(view -> Toast.makeText(MainActivity.this,new APTTest().getString(),Toast.LENGTH_SHORT).show());
        }
    }
    

    运行,点击就可以得到AptTest的内容,同时我们可以在看到生成的类


    apt生成的类文件

    外记:有时我们生成的文件不同项目传入不同参数,可以在项目的build.gradle文件中配置能数

     compileOptions {
                sourceCompatibility JavaVersion.VERSION_1_8
                targetCompatibility JavaVersion.VERSION_1_8
                javaCompileOptions.annotationProcessorOptions.arguments=[prjKey:project.getName()]//传入参数
            }
    

    在处理器中类增加注解

    @SupportedOptions("prjKey")//指定可以接收从build.gradle中传过来的参数prjKey
    

    就可以得到传入的参数

    @Override
        public synchronized void init(ProcessingEnvironment processingEnvironment) {
            super.init(processingEnvironment);
            Map<String, String> opts = processingEnvironment.getOptions();
            prjKey=opts.get("prjKey");
        }
    

    本文示例代码已传至github

    相关文章

      网友评论

          本文标题:APT注解处理

          本文链接:https://www.haomeiwen.com/subject/ejywuhtx.html