什么是AliasFor
AliasFor
是Spring 4.2引入的注解,它提供了给注解中的属性定义别名的功能。所谓注解中的属性,指的是注解中定义的方法,因为它无参并且有返回值。
Java标准定义了几个术语来表示注解A
和被标注的元素E
之间的关系:
-
direct present
:注解A
是运行时可见的,并且A
直接标注在E
上,比如E
是一个类,A
标注在这个类上 -
indirect present
:满足direct present
语义,但是限定注解A
必须是repeatable
的 -
present
:满足direct present
语义,或注解A
是inheritable
的且E
是一个类,A
标注在E
的基类上 -
associated
:满足present
或indirect present
的语义
随着AliasFor
注解的引入,Spring在Java标准之上延伸出了一个新的概念——meta present
。介绍meta present
之前,简单说一下什么是meta annotation
。顾名思义,元注解(meta annotation
)就是注解的注解,它作用在别的注解之上,通过它可以合成新的注解,比如Component
就是一个元注解,通过它衍生出了Controller
。
-
meta present
:如果注解A
作为元注解作用在注解B
上,并且注解B
和元素E
之间满足present
语义,就说注解A meta present
于元素E
;注解A meta present
于注解B
是指注解A
和注解B
之间满足direct present
或meta present
语义。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AliasFor {
/**
* attribute的别名,用来简化编码
*/
@AliasFor("attribute")
String value() default "";
/**
* 要指向的属性名
*/
@AliasFor("value")
String attribute() default "";
/**
* 包含源属性定义的注解,Annotation.class表示当前注解
*/
Class<? extends Annotation> annotation() default Annotation.class;
}
基于AliasFor
的使用方式,分为显式别名(explicit aliases
)和隐式别名(implicit aliases
)两种情况:
- 显式别名也有两种情况:
- 在同一个注解中的不同属性上使用时(对应
AliasFor#annotation()
使用默认值),表示它们是可互换的(interchangeable
),比如AliasFor
注解本身的attribute
和value
属性 - 显式设置了
AliasFor#annotation()
属性(相当于指定元注解),那么当前注解中的属性就作为元注解中属性的别名,比如Controller#value()
是Component#value()
的别名
- 在同一个注解中的不同属性上使用时(对应
- 隐式别名:如果显式设置了
AliasFor#annotation()
属性,并且当前注解中有多个属性指向元注解中的某个属性(可以是直接指向或者通过传递指向),那么这几个属性互为隐式别名
/**
* 自定义注解MyComponent的value和beanName互为隐式别名,
* 因为它们都指向元注解Component的value属性
*/
@Component
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyComponent {
@AliasFor(annotation = Component.class)
String value() default "";
@AliasFor(attribute = "value", annotation = Component.class)
String beanName() default "";
}
为什么要引入AliasFor
Java语言中,注解是不能被继承的。这意味着,在组合注解时,我们无法覆盖元注解中的属性,灵活性很差。
/**
* 使用元注解Configuration和ImportResource创建了组合
* 注解ImportResourceConfiguration,但是我们无法自由
* 指定配置文件的位置。
*/
@Configuration
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@ImportResource("classpath:test.properties")
public @interface ImportResourceConfiguration {
}
引入AliasFor
以后,让原本不存在层次结构的注解体系也拥有了层次的概念。换个角度思考一下,AliasFor
的别名机制其实是一种属性重写,因为作为别名的属性是可以无缝替换源属性的,和继承中重写的概念很像不是吗?
/**
* 使用AliasFor改造以后,组合注解的value属性重写了
* 元注解ImportResource注解的value属性,通过设置
* 重写的属性就可以自由指定配置文件的位置了
*/
@Configuration
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@ImportResource
public @interface ImportResourceConfiguration {
/**
* 对value的设置会反应到ImportResource#value()
*/
@AliasFor(attribute = "value", annotation = ImportResource.class)
String[] value() default {};
}
AliasFor
还可以用来简化编码,当我们让注解的value
属性和长的、更具描述性的属性互为别名以后,就能够使用注解的简写形式了。AliasFor
注解本身就是这么做的,不指定AliasFor#annotation()
的情况下@AliasFor("someAttribute")
和@AliasFor(attribute = "someAttribute")
是等价的。
AliasFor的语义是如何实现的
天下没有免费的午餐,只定义AliasFor
注解并不会自动获得其语义,接下来让我们一起探究一下Spring 5.1.8
中是如何实现AliasFor
语义的。AliasFor
注解的JavaDoc上有提到,其语义生效的前提是相关注解必须通过AnnotationUtils
中的工具方法来加载,转到AnnotationUtils
中定义的第一个方法:
/**
* 在指定的注解上获取指定类型的某个注解:
* 1. 指定注解的类型就是指定类型,返回指定注解本身
* 2. 指定注解上标注了指定类型的注解,返回此元注解
* 注意,此方法只支持一级元注解,也就是元注解是直接标注在给定注解上的情况,
* 如果需要支持任意层级,使用find开头的系列方法。
*/
@Nullable
@SuppressWarnings("unchecked")
public static <A extends Annotation> A getAnnotation(Annotation annotation, Class<A> annotationType) {
// annotation就是要寻找的注解
if (annotationType.isInstance(annotation)) {
// 对annotation进行加强,使之可以处理标注了AliasFor的情况
return synthesizeAnnotation((A) annotation);
}
// annotation不是要寻找的注解
Class<? extends Annotation> annotatedElement = annotation.annotationType();
try {
// 查找它的直接元注解
A metaAnnotation = annotatedElement.getAnnotation(annotationType);
// 对注解进行增强
return metaAnnotation != null ? synthesizeAnnotation(metaAnnotation, annotatedElement) : null;
} catch (Throwable e) {
handleIntrospectionFailure(annotatedElement, e);
return null;
}
}
很明显,AliasFor
的黑魔法隐藏在synthesizeAnnotation(...)
方法中。
/**
* 对传入的annotation进行增强,原理是使用动态代理,
* 这么做的目的是为了使其满足AliasFor的语义
*
* @param annotation 要被包装的注解
* @param annotatedElement 标注了传入的annotation的元素,不为空说明是meta present
*/
@SuppressWarnings("unchecked")
static <A extends Annotation> A synthesizeAnnotation(A annotation, @Nullable Object annotatedElement) {
// 如果已经被代理过了
// 或者是java标准注解
if (annotation instanceof SynthesizedAnnotation ||
hasPlainJavaAnnotationsOnly(annotatedElement)) {
return annotation;
}
// 获取表示这个注解的接口
Class<? extends Annotation> annotationType = annotation.annotationType();
// 不能合成直接返回原注解
if (!isSynthesizable(annotationType)) {
return annotation;
}
// 创建动态代理来保证AliasFor语义
DefaultAnnotationAttributeExtractor extractor = new DefaultAnnotationAttributeExtractor(annotation, annotatedElement);
InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(extractor);
Class<?>[] interfaces = new Class<?>[]{annotationType, SynthesizedAnnotation.class};
return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), interfaces, handler);
}
可以看到,Spring是使用JDK动态代理来实现AliasFor
的语义的。SynthesizedAnnotation
是一个标记接口,它的作用是保证作为实参的annotation
不会被多次代理,相同的设计技巧在org.springframework.core.SeriablizableTypeWrapper
中也有体现。
/**
* 检测指定类型的注解是否是可合成的,也就是说是否可以使用动态代理
* 进行包装用以获得标准java语义外的功能,比如说AliasFor
*
* 一个注解是可合成的指的是:
* 1、此注解内使用了AliasFor注解
* 2、此注解使用到的注解(比如某个方法的返回类型)使用了AliasFor注解
*
* @param annotationType 待检测的注解类型
*/
@SuppressWarnings("unchecked")
private static boolean isSynthesizable(Class<? extends Annotation> annotationType) {
// 基本注解不能合成
if (hasPlainJavaAnnotationsOnly(annotationType)) {
return false;
}
// 读缓存
Boolean synthesizable = synthesizableCache.get(annotationType);
if (synthesizable != null) {
return synthesizable;
}
synthesizable = Boolean.FALSE;
// 遍历注解中定义的方法
for (Method attribute : getAttributeMethods(annotationType)) {
// 有使用AliasFor注解定义别名
if (!getAttributeAliasNames(attribute).isEmpty()) {
synthesizable = Boolean.TRUE;
break;
}
// 没有直接使用AliasFor注解
// 此时要看看是否有间接使用,比如注解的某个属性返回某个注解,这个返回的注解使用了AliasFor注解
Class<?> returnType = attribute.getReturnType();
if (Annotation[].class.isAssignableFrom(returnType)) {
Class<? extends Annotation> nestedAnnotationType = (Class<? extends Annotation>) returnType.getComponentType();
if (isSynthesizable(nestedAnnotationType)) {
synthesizable = Boolean.TRUE;
break;
}
} else if (Annotation.class.isAssignableFrom(returnType)) {
Class<? extends Annotation> nestedAnnotationType = (Class<? extends Annotation>) returnType;
if (isSynthesizable(nestedAnnotationType)) {
synthesizable = Boolean.TRUE;
break;
}
}
}
synthesizableCache.put(annotationType, synthesizable);
return synthesizable;
}
我们知道,Java中所有的注解都间接继承自java.lang.annotation.Annotation
,因此注解中除去自定义的属性外,还有继承自Annotation
接口的方法,getAttributeMethods(...)
方法的作用就是排除继承自Annotation
接口的方法。现在我们可以很容易地发现isSynthesizable(...)
方法的核心是getAttributeAliasNames(...)
。
static List<String> getAttributeAliasNames(Method attribute) {
AliasDescriptor descriptor = AliasDescriptor.from(attribute);
return descriptor != null ?
descriptor.getAttributeAliasNames() : Collections.emptyList();
}
很不幸,getAttributeAliasNames(...)
又代理给了AliasDescriptor
。AliasDescriptor
是一个静态内部类,它封装了注解属性上AliasFor
的相关信息,继续往下追踪。
/**
* 通过注解内的属性方法创建AliasDescriptor
* @param attribute 注解内的属性方法
*/
@Nullable
public static AliasDescriptor from(Method attribute) {
// 读缓存
AliasDescriptor descriptor = aliasDescriptorCache.get(attribute);
if (descriptor != null) {
return descriptor;
}
// 查看一下是否有AliasFor注解
// 没有的话就没必要创建了
AliasFor aliasFor = attribute.getAnnotation(AliasFor.class);
if (aliasFor == null) {
return null;
}
// 有的话进行创建并校验
descriptor = new AliasDescriptor(attribute, aliasFor);
descriptor.validate(); // 校验一下
aliasDescriptorCache.put(attribute, descriptor);
return descriptor;
}
工厂方法AliasDescriptor#from(...)
首先检测注解属性上是否标注有AliasFor
注解,没有的话自然没有继续分析的必要了。如果注解属性标注有AliasFor
,就会创建一个AliasDescriptor
并进行校验。
/**
* 创建AliasDescriptor实例
*/
@SuppressWarnings("unchecked")
private AliasDescriptor(Method sourceAttribute, AliasFor aliasFor) {
// sourceAttribute是注解中的属性方法,因此declaringClass就是注解的类型
Class<?> declaringClass = sourceAttribute.getDeclaringClass();
// 很容易通过sourceAttribute获取以下几个信息
this.sourceAttribute = sourceAttribute;
this.sourceAttributeName = sourceAttribute.getName();
this.sourceAnnotationType = (Class<? extends Annotation>) declaringClass;
// 如果没有指定AliasFor的annotation()属性
// 那么默认是同一注解内的相互指向,否则使用指定的
this.aliasedAnnotationType = (Annotation.class == aliasFor.annotation()) ?
this.sourceAnnotationType :
aliasFor.annotation();
// 获取别名
this.aliasedAttributeName = getAliasedAttributeName(aliasFor, sourceAttribute);
// 不能自己指向自己
// 在attributeName相同的情况下,{@link AliasFor#annotation()}必须指向某个元注解
if (this.aliasedAnnotationType == this.sourceAnnotationType &&
this.aliasedAttributeName.equals(this.sourceAttributeName)) {
String msg = String.format("@AliasFor declaration on attribute '%s' in annotation [%s] points to " +
"itself. Specify 'annotation' to point to a same-named attribute on a meta-annotation.",
sourceAttribute.getName(), declaringClass.getName());
throw new AnnotationConfigurationException(msg);
}
try {
// 获取别名属性方法,已经知道了名字和类型,可以通过反射直接获取
// 根据java标准,注解内的方法是没有参数的
this.aliasedAttribute = this.aliasedAnnotationType.getDeclaredMethod(this.aliasedAttributeName);
} catch (NoSuchMethodException ex) {
String msg = String.format(
"Attribute '%s' in annotation [%s] is declared as an @AliasFor nonexistent attribute '%s' in annotation [%s].",
this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName,
this.aliasedAnnotationType.getName());
throw new AnnotationConfigurationException(msg, ex);
}
// source和alias类型相同的话表示是同一注解内部的两个属性互相指向
// 否则的话说明指定了元注解
this.isAliasPair = this.sourceAnnotationType == this.aliasedAnnotationType;
}
/**
* 校验合法性
*/
private void validate() {
// 如果不是注解内部的互相指向,说明是指向了其它的注解属性
// 这种情况下要求其它注解要meta-present在当前注解上
if (!this.isAliasPair && isAnnotationMetaPresent(this.sourceAnnotationType, this.aliasedAnnotationType)) {
String msg = String.format("@AliasFor declaration on attribute '%s' in annotation [%s] declares " +
"an alias for attribute '%s' in meta-annotation [%s] which is not meta-present.",
this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName,
this.aliasedAnnotationType.getName());
throw new AnnotationConfigurationException(msg);
}
// 如果是注解内部的互相指向,那么相互指向的两个属性都需要标注AliasFor
// 比如 AliasFor注解的attribute()和value()
if (this.isAliasPair) {
AliasFor mirrorAliasFor = this.aliasedAttribute.getAnnotation(AliasFor.class);
if (mirrorAliasFor == null) {
String msg = String.format("Attribute '%s' in annotation [%s] must be declared as an @AliasFor [%s].",
this.aliasedAttributeName, this.sourceAnnotationType.getName(), this.sourceAttributeName);
throw new AnnotationConfigurationException(msg);
}
// 互相指向时,属性名称要能对应上(A指向B,B也要指向A)
String mirrorAliasedAttributeName = getAliasedAttributeName(mirrorAliasFor, this.aliasedAttribute);
if (!this.sourceAttributeName.equals(mirrorAliasedAttributeName)) {
String msg = String.format("Attribute '%s' in annotation [%s] must be declared as an @AliasFor [%s], not [%s].",
this.aliasedAttributeName, this.sourceAnnotationType.getName(), this.sourceAttributeName,
mirrorAliasedAttributeName);
throw new AnnotationConfigurationException(msg);
}
}
// 返回类型必须相同
// 1. 完全相同
// 2. 如果aliasedReturnType是数组的话,returnType必须和数组元素类型相同
// 关于第2点,这是因为注解在书写时,如果是数组类型,并且只有一个元素的话,是可以省略
// 花括号的
Class<?> returnType = this.sourceAttribute.getReturnType();
Class<?> aliasedReturnType = this.aliasedAttribute.getReturnType();
if (returnType != aliasedReturnType &&
(!aliasedReturnType.isArray() ||
returnType != aliasedReturnType.getComponentType())) {
String msg = String.format("Misconfigured aliases: attribute '%s' in annotation [%s] " +
"and attribute '%s' in annotation [%s] must declare the same return type.",
this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName,
this.aliasedAnnotationType.getName());
throw new AnnotationConfigurationException(msg);
}
// 如果是相互指向,继续校验默认值
// 如果不是相互指向,需要在获取了其它AliasDescriptor后校验
if (this.isAliasPair) {
validateDefaultValueConfiguration(this.aliasedAttribute);
}
}
private void validateDefaultValueConfiguration(Method aliasedAttribute) {
Object defaultValue = this.sourceAttribute.getDefaultValue();
Object aliasedDefaultValue = aliasedAttribute.getDefaultValue();
// 必须定义默认值
if (defaultValue == null || aliasedDefaultValue == null) {
String msg = String.format("Misconfigured aliases: attribute '%s' in annotation [%s] " +
"and attribute '%s' in annotation [%s] must declare default values.",
this.sourceAttributeName, this.sourceAnnotationType.getName(), aliasedAttribute.getName(),
aliasedAttribute.getDeclaringClass().getName());
throw new AnnotationConfigurationException(msg);
}
// 默认值必须相等
if (!ObjectUtils.nullSafeEquals(defaultValue, aliasedDefaultValue)) {
String msg = String.format("Misconfigured aliases: attribute '%s' in annotation [%s] " +
"and attribute '%s' in annotation [%s] must declare the same default value.",
this.sourceAttributeName, this.sourceAnnotationType.getName(), aliasedAttribute.getName(),
aliasedAttribute.getDeclaringClass().getName());
throw new AnnotationConfigurationException(msg);
}
}
创建AliasDescriptor
对象的逻辑是比较简单的,校验的逻辑略微复杂一点,对于显式别名的第一种情况,它要求:
-
AliasFor
的value
或attribute
属性要互相指向对方 - 互为别名的两个属性要有相同的返回类型
- 互为别名的两个属性要有默认值,并且默认值也要相同
对于显式别名的第二种情况:
-
AliasFor
的attribute
属性要指向meta annotation
内的某个属性 - 作为别名的属性要和它指向的
meta annotation
内的那个属性具有相同的返回类型 -
AliasFor
的annotation
属性要正确指向某个meta annotation
,并且这个meta annotation
要meta present
在注解上
对于隐式别名的情况:
-
AliasFor
的attribute
属性要指向meta annotation
内的某个属性,可以是直接指向也可以是传递指向 - 互为隐式别名的属性要有相同的返回类型
- 互为隐式别名的属性要有默认值,并且默认值也要相同
-
AliasFor
的annotation
属性要正确指向某个meta annotation
,并且这个meta annotation
要meta present
在注解上
创建并且校验通过以后,紧接着执行真正的getAttributeAliasNames(...)
逻辑。
/**
* 获取其它的互为别名的注解属性
* AliasDescriptor#getAttributeAliasNames()
*/
public List<String> getAttributeAliasNames() {
// 相互指向,不涉及元注解
if (this.isAliasPair) {
// aliasedAttributeName就是所有的别名了
// 因为同一注解内属性->别名是一对一的,一个属性不会有多个别名,否则通过不了校验
return Collections.singletonList(this.aliasedAttributeName);
}
// 不是互相指向,因此是有meta annotation的情况
// 遍历其它的AliasDescriptor
List<String> aliases = new ArrayList<>();
for (AliasDescriptor descriptor : getOtherDescriptors()) {
// this和descriptor互为别名
if (this.isAliasFor(descriptor)) {
// 绝大多数校验都在创建时执行了,除了默认值检验
this.validateAgainst(descriptor);
// 添加的是sourceAttributeName,也就是注解内的属性名
aliases.add(descriptor.sourceAttributeName);
}
}
return aliases;
}
不涉及meta annotation
的情况比较简单,有meta annotation
的时候就需要遍历其它AliasDescriptor
,逐个判断它们之间是否存在别名关系,这个判断逻辑就封装在isAliasFor(...)
中。
private boolean isAliasFor(AliasDescriptor otherDescriptor) {
// 核心算法是沿着alias链循环比较
for (AliasDescriptor lhs = this; lhs != null; lhs = lhs.getAttributeOverrideDescriptor()) {
for (AliasDescriptor rhs = otherDescriptor; rhs != null; rhs = rhs.getAttributeOverrideDescriptor()) {
if (lhs.aliasedAttribute.equals(rhs.aliasedAttribute)) {
return true;
}
}
}
return false;
}
isAliasFor(...)
会沿着alias
链逐层向上比较,这样就能处理有多层meta annotation
时出现的别名传递,比如:
@Service
public @interface @TransitiveService {
@AliasFor(annotation = Component.class, attribute = "value")
String value() default "";
@AliasFor(annotation = Service.class, attribute = "value")
String beanName() default "";
}
TransitiveService#value()
属性是Component#value()
属性的别名, TransitiveService#beanName()
属性是Service#value()
属性的别名,而Service#value()
属性也是Component#value()
属性的别名,因此TransitiveService
的value
和beanName
属性最终都指向了Component#value()
,那么它们也应该互为别名,isAliasFor(...)
的算法使得它可以正确处理这种情况。
回到AnnotationUtils#isSynthesizable(...)
方法,如果getAttributeAliasNames(...)
返回了结果,说明注解属性上标注了AliasFor
注解,也就有必要进行动态代理的包装了。继续回退,回到AnnotationUtils#synthesizeAnnotation(...)
方法,现在万事俱备,着手对传入的注解进行动态代理。
// AnnotationUtils#synthesizeAnnotation(...)片段
// 创建动态代理来保证AliasFor语义
DefaultAnnotationAttributeExtractor extractor = new DefaultAnnotationAttributeExtractor(annotation, annotatedElement);
InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(extractor);
Class<?>[] interfaces = new Class<?>[]{annotationType, SynthesizedAnnotation.class};
return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), interfaces, handler);
被代理的接口中包含SynthesizedAnnotation
,前面提过它是用来防止重复代理的,继续看SynthesizedAnnotationInvocationHandler
:
// SynthesizedAnnotationInvocationHandler#invoke(...)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// equals/hashCode/toString/annotationType
// 上述四个方法是定义在Annotation接口中的,而所有的
// 注解都隐式实现了Annotation接口,因此要特殊处理
if (ReflectionUtils.isEqualsMethod(method)) {
return annotationEquals(args[0]);
}
if (ReflectionUtils.isHashCodeMethod(method)) {
return annotationHashCode();
}
if (ReflectionUtils.isToStringMethod(method)) {
return annotationToString();
}
if (AnnotationUtils.isAnnotationTypeMethod(method)) {
return annotationType();
}
// 除掉Annotation接口中的方法后,剩下的就都是属性方法了
if (!AnnotationUtils.isAttributeMethod(method)) {
throw new AnnotationConfigurationException(String.format(
"Method [%s] is unsupported for synthesized annotation type [%s]", method, annotationType()));
}
return getAttributeValue(method);
}
// SynthesizedAnnotationInvocationHandler#getAttributeValue(...)
private Object getAttributeValue(Method attributeMethod) {
// 代理给attributeExtractor做真正的提取工作
String attributeName = attributeMethod.getName();
Object value = this.valueCache.get(attributeName);
if (value == null) {
value = this.attributeExtractor.getAttributeValue(attributeMethod);
if (value == null) {
String msg = String.format("%s returned null for attribute name [%s] from attribute source [%s]",
this.attributeExtractor.getClass().getName(), attributeName, this.attributeExtractor.getSource());
throw new IllegalStateException(msg);
}
// 如果返回的是另一个注解,尝试包装
// 很可能返回的这个注解也有使用AliasFor标注
if (value instanceof Annotation) {
value = AnnotationUtils.synthesizeAnnotation((Annotation) value, this.attributeExtractor.getAnnotatedElement());
} else if (value instanceof Annotation[]) {
value = AnnotationUtils.synthesizeAnnotationArray((Annotation[]) value, this.attributeExtractor.getAnnotatedElement());
}
this.valueCache.put(attributeName, value);
}
// Clone arrays so that users cannot alter the contents of values in our cache.
if (value.getClass().isArray()) {
value = cloneArray(value);
}
return value;
}
很遗憾SynthesizedAnnotationInvocationHandler
只是一个骨架,实际的逻辑封装在AnnotationAttributeExtractor
中。AnnotationAttributeExtractor
是一个策略接口,它封装了从底层源
中获取属性值的功能,底层源
可能是一个注解或者Map,顺藤摸瓜,来到DefaultAnnotationAttributeExtractor
,它的底层源
就是传入的注解:
@Override
public final Object getAttributeValue(Method attribute) {
String attributeName = attribute.getName();
Object attributeValue = getRawAttributeValue(attribute);
List<String> aliasNames = this.attributeAliasMap.get(attributeName);
if (aliasNames != null) {
Object defaultValue = AnnotationUtils.getDefaultValue(this.annotationType, attributeName);
for (String aliasName : aliasNames) {
Object aliasValue = getRawAttributeValue(aliasName);
// 存在多个别名的时候,只能设置其中一个的值
// 否则的话,就不知道选取哪个作为最终的值了
if (!ObjectUtils.nullSafeEquals(attributeValue, aliasValue) &&
!ObjectUtils.nullSafeEquals(attributeValue, defaultValue) &&
!ObjectUtils.nullSafeEquals(aliasValue, defaultValue)) {
String elementName = (this.annotatedElement != null ? this.annotatedElement.toString() : "unknown element");
throw new AnnotationConfigurationException(String.format(
"In annotation [%s] declared on %s and synthesized from [%s], attribute '%s' and its " +
"alias '%s' are present with values of [%s] and [%s], but only one is permitted.",
this.annotationType.getName(), elementName, this.source, attributeName, aliasName,
ObjectUtils.nullSafeToString(attributeValue), ObjectUtils.nullSafeToString(aliasValue)));
}
// 如果attributeValue没有设置的话
// 让它同步别名的值,这样对外就保持了一致性
if (ObjectUtils.nullSafeEquals(attributeValue, defaultValue)) {
attributeValue = aliasValue;
}
}
}
return attributeValue;
}
@Override
@Nullable
protected Object getRawAttributeValue(Method attributeMethod) {
// 通过方法调用获取值
ReflectionUtils.makeAccessible(attributeMethod);
return ReflectionUtils.invokeMethod(attributeMethod, getSource());
}
@Override
@Nullable
protected Object getRawAttributeValue(String attributeName) {
Method attributeMethod = ReflectionUtils.findMethod(getAnnotationType(), attributeName);
return (attributeMethod != null ? getRawAttributeValue(attributeMethod) : null);
}
至此,属性值的提取水落石出,对注解的动态代理也告一段落,AliasFor
的语义就在这不经意间被安排上了。
网友评论