站点图标

浅谈组合注解 & 注解别名

2021-01-15折腾记录Java / 框架 / Spring
本文最后更新于 410 天前,文中所描述的信息可能已发生改变

前言

这篇文章很早就躺在了草稿里了,一直没有写 2333,最近在考试,因为都是一些相对简单的考试,同时又暂停的项目的开发,所以最近相对较闲,便打算把这个坑填一下。

什么是组合注解和注解别名?

如果你看过 Spring 的注解的源码,那么这两个概念一定不会陌生。

注解别名 指的注解的属性拥有别名的功能,让多个属性值表达同一个意思,如 Spring 的 @Bean 注解:


_7
public @interface Bean {
_7
@AliasFor("name")
_7
String[] value() default {};
_7
_7
@AliasFor("value")
_7
String[] name() default {};
_7
}

从代码中看到 value 属性和 name 属性是相同的,设置 valuename 任意一个值都代表了设置了 Bean 的名称。这就是注解别名。

组合注解 简单来说就是 Spring 自行实现的将多个注解组合到一个注解上的功能。如 Spring 里的 @RestController 注解:


_9
@Target(ElementType.TYPE)
_9
@Retention(RetentionPolicy.RUNTIME)
_9
@Documented
_9
@Controller
_9
@ResponseBody
_9
public @interface RestController {
_9
@AliasFor(annotation = Controller.class)
_9
String value() default "";
_9
}

从代码中看到,@RestController 注解上标记了 @Controller@ResponseBody 注解,这样 @RestController 就组合了 @Controller@ResponseBody 的功能。

除了 组合注解注解别名,Spring 还提供了类似于类继承的 注解继承 功能,比如 @RestControllervalue 属性上标记了 @AliasFor(annotation = Controller.class),此时若设置了 @RestContollervalue 属性,则代表设置了 @Contollervalue 属性。

Spring 是如何实现的?

首先我们先随便写一个 Demo:


_9
@Controller
_9
public class CityController {
_9
_9
@GetMapping("/city")
_9
public String index(Model model) {
_9
model.addAttribute("provinces", CityConst.getProvinces());
_9
return "city/index";
_9
}
_9
}


_13
@SpringBootTest
_13
@Slf4j
_13
class ApplicationTests {
_13
_13
@Test
_13
void contextLoads() throws NoSuchMethodException {
_13
final RequestMapping annotation = AnnotationUtils.getAnnotation(
_13
CityController.class.getMethod("index", Model.class),
_13
RequestMapping.class
_13
);
_13
annotation.path();
_13
}
_13
}

启动调试会话,一路步入就可以看到以下的代码段:


_12
final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnnotation<A> {
_12
_12
// ...
_12
_12
static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source, A annotation) {
_12
Assert.notNull(annotation, "Annotation must not be null");
_12
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(annotation.annotationType());
_12
return new TypeMappedAnnotation<>(mappings.get(0), null, source, annotation, ReflectionUtils::invokeMethod, 0);
_12
}
_12
_12
// ...
_12
}

可以看到,Spring 在创建 MergedAnnotation 前会先获取 AnnotationTypeMappings,该对象保存了 MergedAnnotation.from 传入的注解及其所有父注解(非 JDK 注解)的 AnnotationTypeMapping 信息,AnnotationTypeMapping 里保存了根注解(Spring 保存的方式是自下向上的,所以这个的根注解是 from 传入的注解),源注解(下一级注解,如 RequestMapping 的源注解是 GetMapping),别名索引数组,别名指向的方法等信息。然后获取当前传入注解的 AnnotationTypeMapping 组成 MergedAnnotation

不过有了 MergedAnnotation 那么如何把 MergedAnnotation 变成 Java 的注解呢?如果直接返回通过反射获取的注解,那么别名和子注解的值传上来的值就无法被更改,所以为了获得 Java 的注解,Spring 会重新创建该注解的注解代理类。

如果你看过 Java 注解的源码,Java 注解其实是通过 JDK 代理实现的,通过 JDK 代理从 AnnotationInvocationHandler 里的 memberValues Map 获取注解值。Spring 就是使用类似的方式通过 MergedAnnotation.synthesize 方法调用了 SynthesizedMergedAnnotationInvocationHandler.createProxy 动态创建了 Java 注解,而 SynthesizedMergedAnnotationInvocationHandler 包装了 MergedAnnotationAttributeMethods


_17
final class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation> implements InvocationHandler {
_17
_17
private final MergedAnnotation<?> annotation;
_17
private final AttributeMethods attributes;
_17
_17
// ...
_17
_17
static <A extends Annotation> A createProxy(MergedAnnotation<A> annotation, Class<A> type) {
_17
ClassLoader classLoader = type.getClassLoader();
_17
InvocationHandler handler = new SynthesizedMergedAnnotationInvocationHandler<>(annotation, type);
_17
Class<?>[] interfaces = isVisible(classLoader, SynthesizedAnnotation.class) ?
_17
new Class<?>[] {type, SynthesizedAnnotation.class} : new Class<?>[] {type};
_17
return (A) Proxy.newProxyInstance(classLoader, interfaces, handler);
_17
}
_17
_17
// ...
_17
}

到此 Spring 处理组合注解的原理的关键部分就差不多讲完了,至于获取值部分就不说明了,只是简单的取到属性对应的方法,然后利用反射获取值。

实现

既然知道了原理,那么就可以自行实现一个组合注解 & 注解别名了。

方式

在进行编码前我们要先确定我们组合注解的实现方式。Spring 的实现方式较为复杂,所以我们不采用 Spring 的实现方法,而是直接对 Java 注解里的 memberValues Map 动手,通过修改这个 Map 的值,我们就可以修改注解的属性值。不过需要注意,Java 注解是单例的,所以我们不能直接修改从反射获取的注解里的 memberValues,而是要克隆一份,另外使用 JDK 动态代理创建注解对象。

代码

首先我们先准备 @AliasFor 别名注解:


_9
@Target({ ElementType.METHOD })
_9
@Retention(RetentionPolicy.RUNTIME)
_9
public @interface AliasFor {
_9
String value() default "";
_9
_9
Class<? extends Annotation> annotation() default Annotation.class;
_9
_9
String attribute() default "";
_9
}

为了方便实现重复注解我添加了一个 @RepeatItem 注解:


_5
@Target({ ElementType.ANNOTATION_TYPE })
_5
@Retention(RetentionPolicy.RUNTIME)
_5
public @interface RepeatItem {
_5
Class<? extends Annotation> value();
_5
}

然后就是注解工具类的一些基础方法:


_75
public class AnnotationUtils {
_75
@SuppressWarnings("unchecked")
_75
private static Map<String, Object> getMemberValues(
_75
final Annotation annotation
_75
) {
_75
try {
_75
final InvocationHandler invocationHandler = Proxy.getInvocationHandler(
_75
annotation
_75
);
_75
final Field field = invocationHandler
_75
.getClass()
_75
.getDeclaredField("memberValues");
_75
field.setAccessible(true);
_75
return (Map<String, Object>) field.get(invocationHandler);
_75
} catch (final Exception e) {
_75
throw new RuntimeException("Get annotation member values failed");
_75
}
_75
}
_75
_75
public static boolean isDefaultValue(
_75
final Method method,
_75
final Map<String, Object> memberValues
_75
) {
_75
return isDefaultValue(method, memberValues.get(method.getName()));
_75
}
_75
_75
public static boolean isDefaultValue(
_75
final Method method,
_75
final Object value
_75
) {
_75
final Object defaultValue = method.getDefaultValue();
_75
if (method.getReturnType().isArray()) {
_75
return Arrays.equals((Object[]) defaultValue, (Object[]) value);
_75
} else {
_75
return defaultValue.equals(value);
_75
}
_75
}
_75
_75
public static boolean isJdkAnnotation(
_75
final Class<? extends Annotation> type
_75
) {
_75
return (
_75
type == Documented.class ||
_75
type == Retention.class ||
_75
type == Inherited.class ||
_75
type == Native.class ||
_75
type == Repeatable.class ||
_75
type == Target.class
_75
);
_75
}
_75
_75
public static Class<? extends Annotation> getRepeatItem(
_75
final Class<? extends Annotation> annotationType
_75
) {
_75
final RepeatItem repeatItem = annotationType.getAnnotation(
_75
RepeatItem.class
_75
);
_75
if (repeatItem == null) {
_75
return null;
_75
}
_75
return repeatItem.value();
_75
}
_75
_75
public static Class<? extends Annotation> getRepeatable(
_75
final Class<? extends Annotation> annotationType
_75
) {
_75
final Repeatable repeatable = annotationType.getAnnotation(
_75
Repeatable.class
_75
);
_75
if (repeatable == null) {
_75
return null;
_75
}
_75
return repeatable.value();
_75
}
_75
}

为了创建注解代理类,我们还需要一个 InvocationHandler 用于代理属性方法,我懒得写了就直接把 JDK 里的 AnnotationInvocationHandler 拷贝了出来(JDK 里的是私有的不方便使用),由于代码太长了,这里就不贴了,可以到 Github 上查看。

然后就是相应的代理工具方法:


_13
public class AnnotationUtils {
_13
@SuppressWarnings("unchecked")
_13
public static <A extends Annotation> A annotationForMap(
_13
final Class<A> annotationType,
_13
final Map<String, Object> memberValues
_13
) {
_13
return (A) Proxy.newProxyInstance(
_13
annotationType.getClassLoader(),
_13
new Class[] { annotationType },
_13
new AnnotationInvocationHandler(annotationType, memberValues)
_13
);
_13
}
_13
}

然后就是最关键的合并注解值的方法了:


_56
public class AnnotationUtils {
_56
private static Map<String, Object> mergeAnnotationValue(
_56
final Annotation annotation,
_56
final Map<Class<? extends Annotation>, Map<String, Object>> overwriteMap
_56
) {
_56
final Class<? extends Annotation> annotationType = annotation.annotationType();
_56
final Map<String, Object> overwrite = overwriteMap.get(annotationType);
_56
// 原始 memberValues,不可修改,因为这是单例的
_56
final Map<String, Object> memberValues = getMemberValues(annotation);
_56
// 实际复制并操作后的 memberValues
_56
final Map<String, Object> values = new HashMap<>(memberValues.size());
_56
for (Entry<String, Object> entry : memberValues.entrySet()) {
_56
final String attributeName = entry.getKey();
_56
Object attributeValue = entry.getValue();
_56
final Method method = ReflectUtil.getMethod(
_56
annotationType,
_56
attributeName
_56
);
_56
final AliasFor aliasFor = method.getAnnotation(AliasFor.class);
_56
if (overwrite != null && overwrite.containsKey(attributeName)) {
_56
// 如果从子元素设置了重写的值,那么就设置该值
_56
attributeValue = overwrite.get(attributeName);
_56
} else if (
_56
aliasFor != null &&
_56
!aliasFor.value().isEmpty() &&
_56
isDefaultValue(method, memberValues)
_56
) {
_56
// 如果为默认值,同时设置了 AliasFor.value 那么就使用别名的值(即使是默认值也一样)
_56
final String alias = aliasFor.value();
_56
if (overwrite != null && overwrite.containsKey(alias)) {
_56
attributeValue = overwrite.get(alias);
_56
} else {
_56
attributeValue = memberValues.get(alias);
_56
}
_56
}
_56
// 否则把自身 memberValues 值设置到新 Map 中
_56
values.put(attributeName, attributeValue);
_56
// 如果设置了 AliasFor.annotation 那么就设置父注解的重写值
_56
if (aliasFor != null && aliasFor.annotation() != Annotation.class) {
_56
final Class<? extends Annotation> parentType = aliasFor.annotation();
_56
final Map<String, Object> parentOverwrite = overwriteMap.getOrDefault(
_56
parentType,
_56
new HashMap<>()
_56
);
_56
parentOverwrite.put(
_56
aliasFor.attribute().isEmpty()
_56
? attributeName
_56
: aliasFor.attribute(),
_56
attributeValue
_56
);
_56
overwriteMap.put(parentType, parentOverwrite);
_56
}
_56
}
_56
return values;
_56
}
_56
}

梳理下流程会更方便阅读:

首先我们要明确一点,注解的处理顺序是先子注解,然后父注解。

  1. 获取原始 memberValues 的 Map
  2. 创建克隆的空 memberValues
  3. 循环遍历原始 memberValues,取出每一项的值
  4. 通过属性名称从注解 Class 查找 Method,然后取得 @AliasFor 的注解
  5. 如果子注解有传上来覆盖的值,那么就使用这个值(overwriteMap 里存放)
  6. 否则看下有没有设置 @AliasFor 注解及 value 值(别名)如果设置了,则判断是否是默认值(Method 可以获取方法默认值,也就是属性默认值),如果不是默认值,那么这个值就是被设置过的,此时就不应该覆盖它。
  7. 如果设置了 @AliasFor 注解同时不是默认值,那么就获取别名的值(注意这个别名的值也有可能是子注解传上来的,所以需要判断下 overwriteMap 里有没有设置)
  8. 最后将获得的最终的值存入克隆的 memberValues
  9. 然后处理要传到父注解的值,如果设置了 @AliasForannotation 值,那么就将这个值设置到对应的 overwriteMap 里,这样父注解在处理的时候就可以获取到这个值了

有了处理注解值的方法,那么就可以写遍历注解的方法了:


_44
public class AnnotationUtils {
_44
private static void walkAnnotation(
_44
final AnnotatedElement element,
_44
Map<Class<? extends Annotation>, List<Map<String, Object>>> annotationMap,
_44
Map<Class<? extends Annotation>, Map<String, Object>> overwriteMap
_44
) {
_44
for (Annotation annotation : element.getAnnotations()) {
_44
final Class<? extends Annotation> annotationType = annotation.annotationType();
_44
if (isJdkAnnotation(annotationType)) {
_44
continue;
_44
}
_44
// 处理当前注解
_44
final List<Map<String, Object>> annotations = annotationMap.getOrDefault(
_44
annotationType,
_44
new ArrayList<>()
_44
);
_44
for (Annotation item : element.getAnnotationsByType(
_44
annotationType
_44
)) {
_44
annotations.add(mergeAnnotationValue(item, overwriteMap));
_44
}
_44
annotationMap.put(annotationType, annotations);
_44
// 处理重复注解
_44
final Class<? extends Annotation> repeatItem = getRepeatItem(
_44
annotationType
_44
);
_44
if (repeatItem != null) {
_44
final List<Map<String, Object>> itemList = annotationMap.getOrDefault(
_44
repeatItem,
_44
new ArrayList<>()
_44
);
_44
for (final Annotation item : (Annotation[]) ReflectUtil.invoke(
_44
annotation,
_44
"value"
_44
)) {
_44
itemList.add(mergeAnnotationValue(item, overwriteMap));
_44
}
_44
annotationMap.put(repeatItem, itemList);
_44
}
_44
// 处理父注解
_44
walkAnnotation(annotationType, annotationMap, overwriteMap);
_44
}
_44
}
_44
}

照样写个流程吧:

  1. 因为传入的是可被标注的元素,所以就取出这个元素所标注的所有注解,遍历
  2. 如果是 JDK 注解就直接跳过,因为这些注解对我们的程序无用,而且会导致无限递归
  3. 接着就是使用 AnnotatedElementgetAnnotationsByType 方法获取标注在元素上的所有指定注解类型的值。之所以要这么做是因为 Java 支持重复注解,通过 getAnnotations 的方法只能取得一个同类型的注解
  4. 取得注解后就调用 mergeAnnotationValue 方法合并注解值,同时把要传到父注解的值存入 overwriteMap
  5. 除了 Java 标准的重复注解,我们还常用带 s 的注解包裹来做到重复注解,如 @MapperScans@MapperScan 此时就需要特殊处理,这里使用的方法是在带 s 的注解里添加一个 @RepeatItem 注解,然后该注解存储单个注解的类型,这样就能把带 s 注解里的单个注解存到它对应类型的注解里。
  6. 处理完当前注解后就接着处理父注解(递归处理)

遍历完所有的注解后,就取得了可标注元素的所有注解处理过的 memberValues,此时就可以将 memberValues 转成注解了。


_45
public class AnnotationUtils {
_45
private static Map<Class<? extends Annotation>, List<Annotation>> annotationForMergeAnnotation(
_45
Map<Class<? extends Annotation>, List<Map<String, Object>>> annotationMap
_45
) {
_45
return annotationMap
_45
.entrySet()
_45
.stream()
_45
.collect(
_45
Collectors.toMap(
_45
Entry::getKey,
_45
e ->
_45
e
_45
.getValue()
_45
.stream()
_45
.map(
_45
i ->
_45
AnnotationUtils.annotationForMap(
_45
e.getKey(),
_45
i
_45
)
_45
)
_45
.collect(Collectors.toList()),
_45
(u, v) -> {
_45
throw new IllegalStateException(
_45
String.format("Duplicate key %s", u)
_45
);
_45
},
_45
LinkedHashMap::new
_45
)
_45
);
_45
}
_45
_45
public static Map<Class<? extends Annotation>, List<Annotation>> mergeAnnotation(
_45
final AnnotatedElement element
_45
) {
_45
return MERGED_ANNOTATION_CACHE.computeIfAbsent(
_45
element,
_45
k -> {
_45
final Map<Class<? extends Annotation>, List<Map<String, Object>>> annotationMap = new LinkedHashMap<>();
_45
walkAnnotation(k, annotationMap, new HashMap<>());
_45
return annotationForMergeAnnotation(annotationMap);
_45
}
_45
);
_45
}
_45
}

此时就有了标注在可标注元素上所有的注解了,有了这些就可以封装成组合注解了:

首先是 MergedAnnotation 的接口:


_237
public interface MergedAnnotation {
_237
String VALUE = "value";
_237
Logger log = LoggerFactory.getLogger(MergedAnnotation.class);
_237
_237
/**
_237
* 获取所有注解
_237
*
_237
* @return 注解 Map
_237
*/
_237
Map<Class<? extends Annotation>, List<Annotation>> annotations();
_237
_237
/**
_237
* 获取注解
_237
*
_237
* @param annotationType 注解类型
_237
* @param <A> 注解类型
_237
* @return 注解
_237
*/
_237
default <A extends Annotation> A getAnnotation(
_237
final Class<A> annotationType
_237
) {
_237
return this.getAnnotation(annotationType, 0);
_237
}
_237
_237
/**
_237
* 获取注解
_237
*
_237
* @param annotationType 注解类型
_237
* @param index 索引
_237
* @param <A> 注解类型
_237
* @return 注解
_237
*/
_237
@SuppressWarnings("unchecked")
_237
default <A extends Annotation> A getAnnotation(
_237
final Class<A> annotationType,
_237
final int index
_237
) {
_237
final List<Annotation> annotations =
_237
this.annotations().get(annotationType);
_237
if (
_237
annotations == null ||
_237
annotations.isEmpty() ||
_237
index < 0 ||
_237
index >= annotations.size()
_237
) {
_237
return null;
_237
}
_237
if (annotations.size() > 1) {
_237
log.warn(
_237
"Annotation [{}] is multi, but only get one",
_237
annotationType.getName()
_237
);
_237
}
_237
return (A) annotations.get(index);
_237
}
_237
_237
/**
_237
* 获取指定类型的注解列表
_237
*
_237
* @param annotationType 注解类型
_237
* @param <A> 注解类型
_237
* @return 注解列表
_237
*/
_237
@SuppressWarnings("unchecked")
_237
default <A extends Annotation> List<A> getAnnotations(
_237
Class<A> annotationType
_237
) {
_237
return (List<A>) this.annotations()
_237
.getOrDefault(annotationType, Collections.emptyList());
_237
}
_237
_237
/**
_237
* 是否存在注解
_237
*
_237
* @param annotationType 注解类型
_237
* @return 是否存在
_237
*/
_237
default boolean hasAnnotation(
_237
final Class<? extends Annotation> annotationType
_237
) {
_237
return this.annotations().containsKey(annotationType);
_237
}
_237
_237
/**
_237
* 是否不存在注解
_237
*
_237
* @param annotationType 注解类型
_237
* @return 是否不存在
_237
*/
_237
default boolean notAnnotation(Class<? extends Annotation> annotationType) {
_237
return !this.hasAnnotation(annotationType);
_237
}
_237
_237
/**
_237
* 是否包含多个相同类型的注解
_237
*
_237
* @param annotationType 注解类型
_237
* @return 是否包含
_237
*/
_237
default boolean hasMultiAnnotation(
_237
final Class<? extends Annotation> annotationType
_237
) {
_237
return (
_237
this.annotations().containsKey(annotationType) &&
_237
this.annotations().get(annotationType).size() != 1
_237
);
_237
}
_237
_237
/**
_237
* 添加注解
_237
*
_237
* @param annotation 注解
_237
*/
_237
default void addAnnotation(Annotation annotation) {
_237
throw new UnsupportedOperationException(
_237
"Unsupported add annotation to merge annotation"
_237
);
_237
}
_237
_237
/**
_237
* 删除注解
_237
*
_237
* @param annotationType 注解类型
_237
*/
_237
default void removeAnnotation(Class<? extends Annotation> annotationType) {
_237
throw new UnsupportedOperationException(
_237
"Unsupported remove annotation to merge annotation"
_237
);
_237
}
_237
_237
/**
_237
* 删除注解
_237
*
_237
* @param annotationType 注解类型
_237
* @param index 索引
_237
*/
_237
default void removeAnnotation(
_237
Class<? extends Annotation> annotationType,
_237
int index
_237
) {
_237
throw new UnsupportedOperationException(
_237
"Unsupported add annotation to merge annotation"
_237
);
_237
}
_237
_237
/**
_237
* 获取注解值
_237
*
_237
* @param annotationType 注解类型
_237
* @return 注解值
_237
*/
_237
default Object get(Class<? extends Annotation> annotationType) {
_237
return this.get(annotationType, VALUE);
_237
}
_237
_237
/**
_237
* 获取注解值
_237
*
_237
* @param annotationType 注解类型
_237
* @param name 方法名称
_237
* @return 注解值
_237
*/
_237
default Object get(
_237
Class<? extends Annotation> annotationType,
_237
String name
_237
) {
_237
return this.get(annotationType, name, Object.class);
_237
}
_237
_237
/**
_237
* 获取注解值
_237
*
_237
* @param annotationType 注解类型
_237
* @return 注解值
_237
*/
_237
default <T> T get(
_237
Class<? extends Annotation> annotationType,
_237
Class<T> returnType
_237
) {
_237
return this.get(annotationType, VALUE, returnType);
_237
}
_237
_237
/**
_237
* 获取注解值
_237
*
_237
* @param annotationType 注解类型
_237
* @param name 方法名称
_237
* @param returnType 返回类型
_237
* @param <T> 注解值类型
_237
* @return 注解值
_237
*/
_237
default <T> T get(
_237
Class<? extends Annotation> annotationType,
_237
String name,
_237
Class<T> returnType
_237
) {
_237
return this.get(annotationType, name, returnType, 0);
_237
}
_237
_237
/**
_237
* 获取注解值
_237
*
_237
* @param annotationType 注解类型
_237
* @param name 方法名称
_237
* @param returnType 返回类型
_237
* @param index 索引
_237
* @param <T> 注解值类型
_237
* @return 注解值
_237
*/
_237
default <T> T get(
_237
Class<? extends Annotation> annotationType,
_237
String name,
_237
Class<T> returnType,
_237
int index
_237
) {
_237
final Annotation annotation = this.getAnnotation(annotationType, index);
_237
final Method method = ReflectUtil.getMethodByName(annotationType, name);
_237
if (annotation == null || method == null) {
_237
return null;
_237
}
_237
return Convert.convert(
_237
returnType,
_237
ReflectUtil.invoke(annotation, method)
_237
);
_237
}
_237
_237
static MergedAnnotation from(AnnotatedElement element) {
_237
return new MergedAnnotationImpl(element);
_237
}
_237
_237
static boolean has(
_237
AnnotatedElement element,
_237
Class<? extends Annotation> annotationType
_237
) {
_237
return from(element).getAnnotation(annotationType) != null;
_237
}
_237
}

然后就是实现类:


_13
public class MergedAnnotationImpl implements MergedAnnotation {
_13
_13
private final Map<Class<? extends Annotation>, List<Annotation>> annotations;
_13
_13
public MergedAnnotationImpl(final AnnotatedElement element) {
_13
this.annotations = AnnotationUtils.mergeAnnotation(element);
_13
}
_13
_13
@Override
_13
public Map<Class<? extends Annotation>, List<Annotation>> annotations() {
_13
return this.annotations;
_13
}
_13
}

到这里我们自己的组合注解就实现完成了,让我们来使用下吧:


_5
@Target(ElementType.TYPE)
_5
@Retention(RetentionPolicy.RUNTIME)
_5
@Parent(name = "123")
_5
public @interface Children1 {
_5
}


_13
@Children1
_13
class MergedAnnotationTest {
_13
_13
@Test
_13
void from() {
_13
final MergedAnnotation annotation = MergedAnnotation.from(
_13
MergedAnnotationTest.class
_13
);
_13
final Parent parent = annotation.getAnnotation(Parent.class);
_13
assertEquals("123", parent.name());
_13
assertEquals("123", parent.value());
_13
}
_13
}

结语

组合注解简单化注解配置,用很少的注解来标注特定含义的多个元注解,同时提供了很好的扩展性,可以根据实际需要灵活的自定义注解。经过组合注解的重构后,我们就不再需要写很多注解处理器,避免了重复,同时也不易出错。

浅谈组合注解 & 注解别名

https://blog.ixk.me/post/talking-about-merged-annotation
  • 许可协议

    BY-NC-SA

  • 发布于

    2021-01-15

  • 本文作者

    Otstar Lin

转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!

浅谈并发:三大特性[青空之蓝-2020]-迷茫