编译时注解(BufferKnife等)与运行时注解(otto),注解处理器APT

Android路由开源库,阿里开源的ARouter路由。
 路由与注解。

MainDex 优化记- https://juejin.im/post/5c5bee986fb9a049bc4d1b58
此插件只作用于打包过程,编码过程无感知、无影响,删除注解类 -https://github.com/jokermonn/thinAnnotation

--  1.运行期注解(RunTime)利用反射去获取信息还是比较损耗性能的;
  2.编译期(Compile time)注解,以及处理编译期注解的手段APT和Javapoet,@Retention(RetentionPolicy.CLASS)。

  因为运行时注解涉及到反射,所以运行时注解的效率多少会受到影响,现在很多的开源项目使用的是编译时注解。编译时注解(如@Override@Deprecated)和运行时注解(如@Autowired)都是通过反射API可以得到,所以他们到底有什么区别呢?
  个人理解:编译时注解并不是通过反射API来获得注解类的,都没有运行呢何来的反射?我们在编译程序时可以通过“-processor”选项在编译时指定一个Annotation处理器,该处理器实现Processor接口,通过该接口的方法来检查获取类中的注解类,你可以看一下Processor的process方法,对注解的处理就可以在这个方法中实现,而注解类就可以从该方法传入的属性中获取。举一个编译时注解的用处,就是在编译时通过注解生成xml文件,好像hibernate就是这么做的 。
  java提供了一个javax.lang.model包,来描述编译时的信息。与之相对的是我们更为熟悉的java.lang.reflect包,用以描述运行时的信息。两个包在某种程度上有很大的相似性。 这个javax.lang.model包里,有两个重要的类TypeMirror和Element。它们共同保存了一个代码片段的信息,可以通过asElement()和asType()相互转化。

APT注解学习小案例,比较系统性学习注解并且应用实践- https://github.com/yangchong211/YCApt

 Java元注解,元注解是指注解的注解,包括@Retention @Target @Document @Inherited四种。

-- 元注解@Retention 按生命周期来划分可分为3类:(注解的“存活时间”),即注解保留策略
1、RetentionPolicy.SOURCE:注解只保留在Java源文件中,当Java文件编译成class文件的时候,注解被遗弃擦除,类似于@Override只是给程序员看的;
2、RetentionPolicy.CLASS:注解被保留到class文件中,但jvm加载class文件时候被遗弃,这是默认的生命周期,注解在编译的时候会存在,在运行的时候就会擦除,(编译时注解);
3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,用反射来实现,(运行时注解);
   这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。明确生命周期长度 SOURCE < CLASS < RUNTIME

> 编译时注解,apt+javaPoet ,(EventBus3.0,ButterKnife)
  其中apt+javaPoet目前也是应用比较广泛,在一些大的开源库,如EventBus3.0+,页面路由 ARout、Dagger、Retrofit等均有使用的身影,注解不仅仅是通过反射一种方式来使用,也可以使用APT在编译期处理:
 android-apt注解ioc-apt-sample- https://github.com/hymanAndroid/ioc-apt-sample
 通过注解来生成java源文件javapoet- https://github.com/square/javapoet

-- Annotation processing 是javac中用于编译时扫描和解析Java注解的工具.
 ButterKnife APT涉及3个核心技术:BufferKnife千万不要说成用注解+反射哦~,
  1.编译期注解;2.APT(注解处理器);3.javaPoet(自动生成代码)

Android 如何编写基于编译时注解的项目- https://blog.csdn.net/lmj623565791/article/details/51931859
Android注解使用之通过annotationProcessor注解生成代码实现自己的ButterKnife框架- https://www.cnblogs.com/whoislcj/p/6168641.html
编译期注解之APT- https://blog.csdn.net/xsf50717/article/details/54318874
  
  处理编译器注解的第一个手段就是APT(Annotation Processor Tool),即注解处理器。APT是一种处理注解的工具,确切的说它是javac的一个工具,它用来在编译时扫描和处理注解,一个注解的注解处理器,以java代码(或者编译过的字节码)作为输入,生成.java文件作为输出,核心是交给自己定义的处理器去处理。
  注解处理器,即 annotation processor tool,简称apt,apt是在使用ButterKnife的时候。我一看,嘿,编译时注解、自动生成样板代码findViewById()...,这么厉害,研究一下。这的确和运行时通过反射处理注解,是完全不同的路子,能达到一些意想不到的效果。
   Annotation注解在Android的开发中的使用越来越普遍,例如EventBus、ButterKnife、Dagger2等,之前使用注解的时候需要利用反射机制势必影响到运行效率及性能,直到后来android-apt的出现通过注解根据反射机制动态编译生成代码的方式来解决在运行时不再使用发射机制,不过随着android-apt的退出不再维护,我们今天利用Android studio的官方插件annotationProcessor来实现一下自己的ButterKnife UI注解框架。
  java官方可能是出于注解性能考虑,很快在java_1.6推出了”注解处理器”这个东西。注解处理器,即 annotation processor tool,简称apt。它使得我们能在“编译时”处理注解,既保留了注解的功能,又不会在“运行时”造成性能损耗。

  任何的注解处理器的编写方式基本都遵循着收集信息、生成代理类的步骤。使用编译时注解的成员变量一般都不允许private修饰符修饰(有的允许,但是需要提供getter,setter访问方法)。

-- BufferKnife使用的注解是编译时的吗? 是的。
从 ButterKnife 到“编译时注解”实战- https://blog.csdn.net/a153614131/article/details/53248571
一开始,注解是为编译时检查服务的,不会影响程序运行,反而增强了程序的可读性。常见的有:
 @Override: 检查是否正确重写
 @Deprecated: 表示该函数已弃用,会划一条横线
 @NonNull: 检查参数非空。空指针可以在编译时发现,而不必等到运行时抛出NullPointerException

> 运行时注解;otto运行时注解。运行时注解靠反射获取注解。
自定义注解之运行时注解(RetentionPolicy.RUNTIME)- https://blog.csdn.net/github_35180164/article/details/52118286
 
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到  
@Target({ElementType.FIELD,ElementType.METHOD})//定义注解的作用目标**作用范围字段、枚举的常量/方法  
@Documented//说明该注解将被包含在javadoc中  。

> 不同的注解处理器
custom annotation processor- https://github.com/yuweiguocn/CustomAnnotation
注解框架—AndroidAnnotations - https://github.com/androidannotations/androidannotations

@Override
public boolean process(Set annotations, RoundEnvironment env) {
        Map classMap = new HashMap<>();

        // 得到所有注解@Interface的Element集合
        Set elementSet = env.getElementsAnnotatedWith(Interface.class);

        for (Element e : elementSet) {
            if (e.getKind() != ElementKind.METHOD) {
                error(e, "错误的注解类型,只有函数能够被该 @%s 注解处理", Interface.class.getSimpleName());
                return true;
            }

            ExecutableElement element = (ExecutableElement) e;
            AnnotatedMethod annotatedMethod = new AnnotatedMethod(element);

            String classname = annotatedMethod.getSimpleClassName();
            AnnotatedClass annotatedClass = classMap.get(classname);
            if (annotatedClass == null) {
                PackageElement pkg = elementUtils.getPackageOf(element);
                annotatedClass = new AnnotatedClass(pkg.getQualifiedName().toString(), element.getAnnotation(Interface.class).value());
                annotatedClass.addMethod(annotatedMethod);
                classMap.put(classname, annotatedClass);
            } else
                annotatedClass.addMethod(annotatedMethod);

        }
        // 代码生成
        for (AnnotatedClass annotatedClass : classMap.values()) {
            annotatedClass.generateCode(elementUtils, filer);
        }
        return false;
    }

public class CustomProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set annotations, RoundEnvironment roundEnvironment) {
        return false;
    }
    @Override
    public Set getSupportedAnnotationTypes() {
        Set annotataions = new LinkedHashSet();
        annotataions.add(CustomAnnotation.class.getCanonicalName());
        return annotataions;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}

-- 每个自定义的处理器都要继承虚处理器AbstractProcessor,实现其关键的几个方法:
public class MyProcessor extends AbstractProcessor {
    @Override
    public synchronized void init(ProcessingEnvironment env){ }
    @Override
    public boolean process(Set annoations, RoundEnvironment env) { }

    @Override
    public Set getSupportedAnnotationTypes() { }

    @Override
    public SourceVersion getSupportedSourceVersion() { }
}

你可能感兴趣的:(Java,base,热点(hot)技术)