美文网首页
注解处理器

注解处理器

作者: 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