Skip to content

实例讲解 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() {}

Released under the MIT License.