实例讲解 Java 注解生命周期
Retention
用于控制注解的生命周期
RetentionPolicy.SOURCE
声明该类型的生命周期,则该注解仅作用在编译期之前,常用于 APT 处理获取注解信息,生成文档或代码。例如:路由框架 TheRouter 通过 Route 注解获取被注解类的信息。
kotlin
// 代码来源 -> apt/src/main/java/com/therouter/router/Route.kt
/**
* 声明注解
*/
@Retention(RetentionPolicy.SOURCE)
annotation class Route()
kotlin
//代码来源 -> app/src/main/java/com/therouter/app/navigator/NavigatorTestActivity.java
/**
* 使用注解
*/
@Route(path = HomePathIndex.DEMO_NAVIGATOR)
public class NavigatorTestActivity extends AppCompatActivity {}
kotlin
// 代码来源 -> apt/src/main/java/com/therouter/apt/TheRouterAnnotationProcessor.kt
/**
* 注解处理器获取注解信息
* ps: 对原代码进行了裁剪
*/
class TheRouterAnnotationProcessor : AbstractProcessor() {
private fun parseRoute(roundEnv: RoundEnvironment): List<RouteItem> {
val list: MutableList<RouteItem> = ArrayList()
val set = roundEnv.getElementsAnnotatedWith(Route::class.java)
val arraySet = roundEnv.getElementsAnnotatedWith(Routes::class.java)
//...
if (set != null && set.isNotEmpty()) {
for (element in set) {
val annotation = element.getAnnotation(Route::class.java)
val routeItem = RouteItem()
routeItem.className = element.toString()
routeItem.path = annotation.path
//....
list.add(routeItem)
}
}
}
}
RetentionPolicy.CLASS
声明该类型的生命周期,则该注解仅作用在运行时之前,常用于 APT 处理获取注解信息,生成文档或代码,或编译时根据注解修改字节码。例如:路由框架 ARouter 通过 Router 注解获取被注解类的信息;热修复框架 Robust 通过注解插入热修复代码。
groovy
// 代码来源 -> auto-patch-plugin/src/main/groovy/com/meituan/robust/autopatch/ReadAnnotation.groovy
/**
* 通过Transform + ASM + 注解 插入热修复代码
* ps: 对原代码进行了裁剪
*/
class AutoPatchTransform extends Transform implements Plugin<Project> {
@Override
void transform(Context context,
Collection<TransformInput> inputs,
Collection<TransformInput> referencedInputs,
TransformOutputProvider outputProvider,
boolean isIncremental)
throws IOException, TransformException, InterruptedException {
//....
def box = ReflectUtils.toCtClasses(inputs, Config.classPool)
autoPatch(box)
//...
}
def autoPatch(List<CtClass> box) {
ReadAnnotation.readAnnotation(box, logger);
//...
}
}
class ReadAnnotation {
public static void readAnnotation(List<CtClass> box, Logger log) {
synchronized (AutoPatchTransform.class) {
if (Constants.ModifyAnnotationClass == null) {
// Constants.MODIFY_ANNOTATION = com.meituan.robust.patch.annotaion.Modify
Constants.ModifyAnnotationClass = box.get(0).getClassPool().get(Constants.MODIFY_ANNOTATION).toClass();
}
if (Constants.AddAnnotationClass == null) {
Constants.AddAnnotationClass = box.get(0).getClassPool().get(Constants.ADD_ANNOTATION).toClass();
}
//...
}
}
}
RetentionPolicy.RUNTIME
声明该类型的生命周期,则该注解仅作用在运行时之前,常用于运行时反射获取注解信息。例如:网络请求框架 Retrofit 通过 Get/Post 注解,通过动态代理获取注解信息并执行请求。
java
// 代码来源 -> retrofit/src/main/java/retrofit2/http/GET.java
/**
* 声明注解
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {
String value() default "";
}
java
// 代码来源 -> samples/src/main/java/com/example/retrofit/SimpleService.java
/**
* 使用注解
*/
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(@Path("owner") String owner, @Path("repo") String repo);
}
java
// 代码来源 -> retrofit/src/main/java/retrofit2/Retrofit.java
/**
* 动态代理(运行时)获取注解信息
* ps: 对原代码进行了裁剪
*/
public final class Retrofit {
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
for (Annotation annotation : method.getAnnotations()) {
if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
}
}
}
});
}
拓展
常见元注解:
- Repeatable:重复标注
- Retention:控制注解生命周期
- RetentionPolicy.SOURCE:仅在源文件阶段有效
- RetentionPolicy.CLASS:仅在源文件、字节码阶段有效
- RetentionPolicy.RUNTIME:在源文件、字节码、运行时阶段有效
- Target:控制注解作用范围
- ElementType.TYPE:类、接口(包括注解类型接口)、枚举
- ElementType.FIELD: 类字段(包括枚举常量)
- ElementType.METHOD:方法
- ElementType.PARAMETER:方法参数
- ElementType.CONSTRUCTOR:构造函数
- ElementType.LOCAL_VARIABLE:局部变量
- ElementType.ANNOTATION_TYPE:注解类型
- ElementType.PACKAGE:包
- ElementType.TYPE_PARAMETER:泛型参数
Repeatable
重复标注,用于实现多注解作用于一个类型中。
该注解主要应用场景为,可以在一个类型上添加多个注解。例如:路由框架 TheRouter 支持多个 path 指向一个 Activity 就是通过该注解实现。
kotlin
// 代码来源 -> apt/src/main/java/com/therouter/router/Route.kt
/**
* https://youtrack.jetbrains.com/issue/KT-12794
* Kotlin 的 @Repeatable 不能注解到 Java class,得要到1.6才能支持,但是 Java 的却能注解到 Kotlin 代码,所以这个类只能写 Java 的
* Created by ZhangTao on 17/8/11.
*/
@Retention(RetentionPolicy.SOURCE)
@java.lang.annotation.Repeatable(value = Routes::class)
@Repeatable
annotation class Route(
/**
* 路由path,不限格式,建议是一个url,允许多个path对应同一个Activity
*/
val path: String = "",
//....
)
kotlin
// 代码来源 -> apt/src/main/java/com/therouter/router/Routes.kt
@Retention(RetentionPolicy.SOURCE)
annotation class Routes(vararg val value: Route)
kotlin
// 代码来源 -> app/src/main/java/com/therouter/app/navigator/KotlinTestActivity.kt
/**
* 多 path Route注解使用
*/
@Route(path = HomePathIndex.KOTLIN)
@Route(path = HomePathIndex.KOTLIN2)
class KotlinTestActivity : AppCompatActivity() {}