美文网首页
注解处理器

注解处理器

作者: gczxbb | 来源:发表于2019-03-27 16:38 被阅读0次

    一、注解

    在类,接口,变量,方法前面的声明,注解的作用是对这些元素进行注释说明,不会影响编译器,通过编译器读取注解内容,如在编译时实现格式检查操作,运行时读取注解跟踪代码。

    定义注解,类似定义接口,在interface前加上一个@符号,@interface。

    @Target(ElementType.METHOD)//作用范围是方法
    @Retention(RetentionPolicy.SOURCE)//源码级注解
    public @interface Override {
    }
    

    Java Sdk定义的一个注解Override,它是Java中常用注解,作用范围是方法,表示该方法继承父类,仅在源码中保留。当编译器编译该Java源文件时,检查到注解,通知编译器这是一个继承父类的方法,编译器会检查父类是否有该方法,如果没有会编译报错:方法不会覆盖或实现超类型的方法。
    @Target和@Retention是元注解,即作用于其他注解的注解。

    常用元注解

    元注解@Target,注解的使用范围,即注解可以在哪里使用。

    FIELD ,字段
    TYPE,类,接口
    METHOD,方法
    CONSTRUCTOR,构造方法
    PARAMETER,方法参数

    TYPE,表示注解声明在接口和类的前面,如果把一个@Target=TYPE的注解声明在某个方法或变量上,IDE会报错,其他类型也是一样。

    元注解@Retention,注解生命周期,即该保留到哪一步。

    SOURCE,仅在Java源码文件中保留,编译成class字节码后被抛弃。
    CLASS,在class字节码文件中保留,在字节码处理时有用,类加载时忽略。
    RUNTIME,在class字节码文件中保留,第一次加载时读取,运行时一直保留,反射获取。

    只有注解生命周期@Retention=RUNTIME时,在运行时才能获取定义的注解,通过getAnnotation()方法。

    元注解@Inherited,一个类声明了注解,当该注解有@Inherited时,该类的子类可以继承这个注解,即使子类没有声明该注解。

    元注解@Documented,是否保存Javadoc文档。


    二、自定义注解

    定义一个类注解AnnotationClass,作用范围是类,运行时。

    @Target(TYPE)//作用范围是类
    @Retention(RUNTIME)//运行时注解
    public @interface AnnotationClass {
        String authorName();
        String createTime();
    }
    

    定义一个变量注解AnnotationField,作用范围是变量,运行时。

    @Documented
    @Target({ FIELD, })//作用范围是变量
    @Retention(RUNTIME)//运行时注解
    public @interface AnnotationField {
        String descInfo() default "默认:我是Filed注解";
    }
    

    定义一个方法注解AnnotationMethod,作用范围是方法,运行时。

    @Target(METHOD)//作用范围是方法
    @Retention(RUNTIME)//运行时注解,
    public @interface AnnotationMethod {
        String name() default "";
    }
    

    在类中使用注解。

    //注解类
    @AnnotationClass(authorName = "chen", createTime = "2019年")
    public class MainActivity extends Activity {
        //注解变量
        @AnnotationField(descInfo = "成功信息")
        String mSuccessMsg;
    
        //注解方法
        @AnnotationMethod(name = "initView方法")
        private void initView(Context context) {
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
           
            //运行时注解打印,包括方法注解、类注解和变量注解
            //只有运行时注解才能获取到
            try {
                Method method = MainActivity.class.getDeclaredMethod("initView", Context.class);
                AnnotationMethod appAnnot = method.getAnnotation(AnnotationMethod.class);
                if (appAnnot != null) {
                    Log.d(TAG, "method:" + method.getName() + ",name:" + appAnnot.name());
                }
            } catch (NoSuchMethodException e) {
            }
    
            try {
                Field field = this.getClass().getDeclaredField("mSuccessMsg");
                AnnotationField appAnnot = field.getAnnotation(AnnotationField.class);
                if (appAnnot != null) {
                    Log.d(TAG, "field:" + field.getName() + ",mSuccessMsg:" + appAnnot.descInfo());
                }
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
    
            AnnotationClass annotationClass = MainActivity.class.getAnnotation(AnnotationClass.class);
            if (annotationClass != null) {
                 Log.d(TAG, "createTime:" + annotationClass.createTime() + ",author:" + annotationClass.authorName());
            } 
        }
    }
    

    注解类,作者和创建时间,Class类getAnnotation方法获取AnnotationClass注解对象,具体对象是动态代理对象$Proxy2。
    注解方法,反射,根据方法名获取Method,Method的getAnnotation()方法,获取AnnotationMethod,读取注解对象的name字段。
    注解变量,反射,根据变量名获取Field,Field的getAnnotation()方法,获取AnnotationField,读取注解对象的descInfo字段。
    Class类的getAnnotation方法,返回参数是泛型,继承Annotation接口的类。

    public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
        A annotation = getDeclaredAnnotation(annotationClass);
        if (annotation != null) {
            return annotation;
        }
        if (annotationClass.isDeclaredAnnotationPresent(Inherited.class)) {
            for (Class<?> sup = getSuperclass(); sup != null; sup = sup.getSuperclass()) {
                annotation = sup.getDeclaredAnnotation(annotationClass);
                if (annotation != null) {
                    return annotation;
                }
            }
        }
        return null;
    }
    

    调用native方法,最终获取注解,注解方法和注解变量,同样调用native方法,获取注解。

    public native Annotation[] getDeclaredAnnotations();
    

    @注解,实现Annotation接口。

    运行时注解动态代理对象

    运行时反射获取的AnnotationClass注解是动态代理对象$Proxy2,继承class java.lang.reflect.Proxy类,实现注解接口中的方法。
    Proxy2对象的createTime()方法时,调用Proxy的invoke方法,内部h的invoke方法,即InvocationHandler。

    运行时注解方法调用

    内部h是AnnotationFactory,注解的变量值在AnnotationMember中保存,h.invoke方法,将对应值返回。

    AnnotationFactory类实现InvocationHandler接口

    在Android系统源码中找到AnnotationFactory,它实现InvocationHandler接口。

    AnnotationFactory的invoke方法

    注解本质是继承Annotation的接口,getAnnotation()方法获取的具体实现类是继承Proxy的动态代理对象$Proxy2,实现注解接口。


    三、自定义注解处理器

    编译时注解,需要自己写注解处理器
    1,创建一个Java Library类型的library,注解和注解处理器的library可以分开。

    //使用注解
    implementation project(':annotions-lib')
    //注解的处理,替代以前的android-apt
    annotationProcessor project(':complier-lib')
    

    annotationProcessor是一中apt工具,内置的。在编译的时候会执行依赖的complier-lib库,生成.java文件,库本身不会将它打包到apk。
    2,注意,要在library中创建一个注解处理器类,继承AbstractProcessor,而不是在主App中创建。

    //注解处理器,编译时,编译器会自动查找所有继承AbstractProcessor的类,触发process方法
    @SupportedAnnotationTypes("xxx.xxx.xxx.AppAnnotClass")//注解类路径
    @AutoService(Processor.class)
    public class AnnotationProcessor extends AbstractProcessor {
    

    写完了注解处理器,还需要将它注册都javac中,用@AutoService注解。
    AutoService是google的,用于自动生成META-INF。在build目录下生成。

    屏幕快照 .png

    该文件下保存你写的自定义xxx.xxx.xxx.AnnotationProcessor,注解处理器的名称。
    javac会自动检查读取该文件下的注解处理器,注册到javac中,javac会选择它为注解处理器,才能在编译时执行里面的代码。

    APT(Annotation Processing Tool)即注解处理器,是一种处理注解的工具,它是javac的一个工具,它用来在编译时扫描和处理注解。注解处理器以Java代码(或者编译过的字节码)作为输入,找出其中的注解,然后可以根据注解生成额外的.java文件作为输出。简单来说就是在编译期,通过注解生成.java文件。

    APT的优点就是方便、简单,可以少些很多重复的代码,很多框架Dagger、ButterKnife都是注解框架,可以少些很多代码,只要写一些注解就可以了。


    四、调试方法

    1,在gradle.properties文件加入代码

    org.gradle.jvmargs= -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
    org.gradle.parallel=true
    

    2,在Edit Configurations中创建一个Remote。

    Edit Configurations Debug Configurations

    3,创建Apply后,在自定义AnnotationProcessor中打上断点,启动Debug调试。

    Debug

    4,最后,Rebuild Project,就会运行到断点位置调试。

    自定义注解处理器调试

    任重而道远

    相关文章

      网友评论

          本文标题:注解处理器

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