java retention注解_Java元注解 - 生命周期 @Retention

Java1.5推出元注解后,时下最活跃的开源社区Spring便开始大力推崇,原本大家熟悉的、一目了然的各种xml配置,突然消失了,各种注解纷至沓来,配置可读性受到严重威胁。另一方面,AOP的各种炫酷特性,让开发者情不自禁地使用各种自定义注解。在自定义元注解@Annotation的时候,有两个特性是必须要定义清楚的,一个是Target(注解目标),另一个就是Retention(注解生命周期,也叫声明周期),今天我们先认识下周期。

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

public @interface CallerServiceValidator {

String value() default "";

Class> validatorClass() default DEFAULT.class;

final class DEFAULT {}

}

元注解生命周期@Retention

元注解的生命周期有三种,枚举定义在RetentionPolicy中,分别是SOURCE、CLASS和RUNTIME。

自定义元注解时,绝大多数开发者(除非你是下面两种场景的使用者)都是使用RUNTIME,这个很好理解,我们期望在程序运行时,能够获取到这些注解,并干点有意思的事儿,而只有RetentionPolicy.RUNTIME,能确保自定义的注解在运行时依然可见。举个例子,在spring项目启动后,获取所有或者部分可用接口的定义并列出来:

try {

String basePath = "";

RequestMapping baseRequestMapping = AnnotationUtils.findAnnotation(bean.getClass(), RequestMapping.class);

if (baseRequestMapping != null) {

basePath = StringUtils.join(baseRequestMapping.path());

}

Method[] methods = ReflectionUtils.getAllDeclaredMethods(AopUtils.getTargetClass(bean));

if (methods != null) {

beanMetas = new CopyOnWriteArrayList<>();

for (Method method : methods) {

try {

RequestMapping methodRequestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);

ApiIgnore apiIgnore = AnnotationUtils.findAnnotation(method, ApiIgnore.class);

if (methodRequestMapping != null && (apiIgnore == null || !apiIgnore.value())) {

PlatformApiMeta platformApiMeta = new PlatformApiMeta(AopUtils.getTargetClass(bean).getName(),

method.getName(), basePath + StringUtils.join(methodRequestMapping.path()));

RequestMethod[] httpMethods = methodRequestMapping.method();

if (httpMethods != null && httpMethods.length > 0) {

String[] httpMethodArr = new String[httpMethods.length];

for (int i = 0; i < httpMethods.length; i++) {

RequestMethod httpMethod = httpMethods[i];

httpMethodArr[i] = httpMethod.name();

}

platformApiMeta.setHttpMethods(httpMethodArr);

}

platformApiMeta.setModuleName(moduleName);

ApiOperation apiOperation = AnnotationUtils.findAnnotation(method, ApiOperation.class);

if (apiOperation != null) {

platformApiMeta.setDesc(apiOperation.value());

}

collectMethodParamsReturn(platformApiMeta, method, bean);

beanMetas.add(platformApiMeta);

}

} catch (Exception e) {

logger.error(ExceptionUtils.getStackTrace(e));

}

}

}

} catch (Exception e) {

logger.error(ExceptionUtils.getStackTrace(e));

}

RetentionPolicy.SOURCE一般的开发者很少用到,举几个Java自带的使用场景。

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.SOURCE)

public @interface Override {

}

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})

@Retention(RetentionPolicy.SOURCE)

public @interface SuppressWarnings {

看到了吗,这些注解只是在编译的时候用到,一旦编译完成后,运行时没有任何意义,所以他们被称作源码级别注解。有过代码自动生成经验的开发者,譬如lombok开发者,都知道它是通过注解在编译时自动生成一部分代码,让源码看起来更简洁,字节码却很强大。当然,这种方式有它自身的缺陷,譬如不一致性,问题排解时的困扰,以及依赖问题,在此不展开讨论。

同样的原因,RetentionPolicy.CLASS虽然作为注解的默认周期定义,也不是普通开发者自定义注解的首选,CLASS类型比起SOURCE和RUNTIME要更难理解些,因为通常开发者对编译前和运行时理解没有障碍,但是编译之后的字节码保留了元注解,又不能在运行时用到,这期间到底有什么用? 我们看个Spring-boot的例子。

Springboot的配置元数据注解处理器(ConfigurationMetadataAnnotationProcessor),是专门用来处理配置注解的,通过MetaCollector收集的,然后用MetaStore存在META-INF/spring-configuration-metadata.json。我们先不讨论Spring为何要这么做,有什么好处,感兴趣的可以自己去读源码,此刻我们关注的是RetentionPolic.CLASS,和这个实现有什么关系。ConfigurationMetadataAnnotationProcessor实现了Processor接口,而Processor接口,则是专门用来处理注解的,故名注解处理器。注解处理器,是javac的一个工具,它用来在编译时扫描和处理注解(Annotation)。注解处理器是运行它自己的虚拟机JVM中,javac启动一个完整Java虚拟机来运行注解处理器。这对你意味着什么?你可以使用任何你在其他java应用中使用的的东西。更多关于注解处理器的知识,感兴趣的可以参照这篇翻译文章(Java注解处理器),有干货。

最后小节下:

SOURCE是源码级别的注解,仅在编译时使用;

CLASS是字节码级别的注解,大多也是在编译时使用;

RUNTIME才是我们的亲朋好友,运行时可见的注解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值