注解
注解技术在BufferKnife、Dagger、Retrofit、EventBus等开源框架中大量使用,
@Retention
这个表示注解的保留方式,具体有一下三种类型:
-
RetentionPolicy.SOURCE(源码级注解): 只保留在源码中,不保留在class中,同时也不加载到虚拟机中。在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。
源码级注解主要有两个作用:
(1).作为源代码的补充,给开发者看的,比如@Override注解
(2).给注解处理器(APT)用的,用于生成源代码。 -
RetentionPolicy.CLASS(字节码注解): 保留在源码中,同时也保留到class中,但是不加载到虚拟机中。在类加载的时候丢弃。在字节码文件的处理中有用,比如字节码插桩。注解,默认使用这种方式。
字节码级注解主要的作用:
用于字节码修改,字节码插桩。比如,使用aspectj、ASM等工具进行字节码修改。 -
RetentionPolicy.RUNTIME(运行时注解): 保留到源码中,同时也保留到class中,最后加载到虚拟机中。始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
运行时注解主要的作用:
在运行时使用反射进行使用,参与某些业务逻辑。
总结:
RetentionPolicy.SOURCE:在源码中有效,被编译器丢弃。
RetentionPolicy.CLASS:在class文件中有效,类加载的时候被丢弃,即被虚拟机丢弃。
RetentionPolicy.RUNTIME:运行期也保留该注解,始终不会被丢弃。
运行时注解与编译时注解的区别
运行时注解一般在运行时用反射机制去处理注解,也就是RetentionPolicy.RUNTIME类型的注解,处理阶段是在运行时。
编译时注解一般在编译时用注解处理器(APT)去处理注解,也就是RetentionPolicy.SOURCE类型的注解,处理阶段是在Java代码编译时。
编译时注解是在编译时用注解处理器去处理注解,生成额外的java类;而运行时注解是在运行时用反射机制去处理注解。
源码时注入、编译时注入、字节码注入、运行时注入的区别
源码时注入一般是用于ide的插件,比如Android Studio自动生成constructor/getter/setter相关代码(没有用到注解)
编译时注入一般是在编译时使用注解处理器处理相应注解(使用的注解的类型是RetentionPolicy.SOURCE
)
字节码注入一般是在class文件被类加载器加载前使用字节码注入框架进行修改字节码,比如ASM等。(使用的注解的类型是RetentionPolicy.CLASS
)
运行时注入一般是在运行时使用反射进行处理相应注解(使用的注解的类型是RetentionPolicy.RUNTIME
)
生命周期长度:SOURCE < CLASS < RUNTIME,所以某个注解的类型如果设置为SOURCE类型可以正常使用,那么设置为后面的CLASS或者RUNTIME必然也可以正常使用。
一般在运行时需要用到的注解那么只能设置为RUNTIME;如果需要在字节码中做一些处理工作,那么可以设置为CLASS 、RUNTIME;如果需要在编译时进行一些处理工作,那么可以设置为SOURCE、CLASS 、RUNTIME均可,比如Butterknife的@BindView;如果是做一些代码方面的检查性的工作,那么可以设置为SOURCE、CLASS 、RUNTIME均可(但是一般设置为SOURCE即可),比如@Override 和 @SuppressWarnings。