Spring 注解(二)注解工具類 AnnotationUtils 和 AnnotatedElementUtils
Spring 系列目錄(https://www.cnblogs.com/binarylei/p/10198698.html)
Spring 注解系列文章:
首先回顧一下 AnnotationUtils 和 AnnotatedElementUtils 這兩個注解工具類的用法:
@Test
@GetMapping(value = "/GetMapping", consumes = MediaType.APPLICATION_JSON_VALUE)
public void test() throws NoSuchMethodException {
Method method = ReflectUtils.findDeclaredMethod(
AliasForTest.class, "test", null);
// AnnotationUtils 不支持注解屬性覆蓋
RequestMapping requestMappingAnn1 = AnnotationUtils.getAnnotation(method, RequestMapping.class);
Assert.assertEquals(new String[]{}, requestMappingAnn1.value());
Assert.assertEquals(new String[]{}, requestMappingAnn1.consumes());
// AnnotatedElementUtils 支持注解屬性覆蓋
RequestMapping requestMappingAnn2 = AnnotatedElementUtils.getMergedAnnotation(method, RequestMapping.class);
Assert.assertEquals(new String[]{"/GetMapping"}, requestMappingAnn2.value());
Assert.assertEquals(new String[]{MediaType.APPLICATION_JSON_VALUE}, requestMappingAnn2.consumes());
}
一、AnnotationUtils 源碼分析
AnnotationUtils 解決注解別名,包括顯式別名、隱式別名、傳遞的隱式別名,還可以查的指定注解的屬性信息。
AnnotationUtils 底層使用動態代理的方式處理注解別名的問題。
1.1 get* 系列注解查找
get 遵循 JDK 的注解查找語義,只是增加了一級元注解的查找。
public static <A extends Annotation> A getAnnotation(Annotation annotation, Class<A> annotationType) {
// 1. 直接查找本地注解
if (annotationType.isInstance(annotation)) {
return synthesizeAnnotation((A) annotation);
}
// 2. 元注解上查找,注意相對於 find* 而言,這里只查找一級元注解
Class<? extends Annotation> annotatedElement = annotation.annotationType();
try {
A metaAnn = annotatedElement.getAnnotation(annotationType);
return (metaAnn != null ? synthesizeAnnotation(metaAnn, annotatedElement) : null);
}
catch (Throwable ex) {
handleIntrospectionFailure(annotatedElement, ex);
return null;
}
}
1.2 find* 系列注解查找
遵循 JDK 的注解查找語義,只是增加了多級元注解的查找。
// visited 表示已經查找的元素,Spring 的遞歸很多都用到了這個參數
private static <A extends Annotation> A findAnnotation(
AnnotatedElement annotatedElement, Class<A> annotationType, Set<Annotation> visited) {
try {
// 1. 本地注解查找
A annotation = annotatedElement.getDeclaredAnnotation(annotationType);
if (annotation != null) {
return annotation;
}
// 2. 元注解上查找
for (Annotation declaredAnn : getDeclaredAnnotations(annotatedElement)) {
Class<? extends Annotation> declaredType = declaredAnn.annotationType();
if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) {
// 3. 元注解上遞歸查找
annotation = findAnnotation((AnnotatedElement) declaredType, annotationType, visited);
if (annotation != null) {
return annotation;
}
}
}
}
catch (Throwable ex) {
handleIntrospectionFailure(annotatedElement, ex);
}
return null;
}
1.3 synthesizeAnnotation 動態代理解決別名問題
static <A extends Annotation> A synthesizeAnnotation(A annotation, @Nullable Object annotatedElement) {
// 1. SynthesizedAnnotation 為一個標記,表示已經動態代理過了
// hasPlainJavaAnnotationsOnly 如果是 java 中的注解不可能有注解別名,直接返回
if (annotation instanceof SynthesizedAnnotation || hasPlainJavaAnnotationsOnly(annotatedElement)) {
return annotation;
}
// 2. 判斷是否需要進行動態代理,即注解中存在別名,包括顯示別名、隱式別名、傳遞的隱式別名
Class<? extends Annotation> annotationType = annotation.annotationType();
if (!isSynthesizable(annotationType)) {
return annotation;
}
// 3. AnnotationAttributeExtractor 用於從注解 annotation 中提取屬性的值
DefaultAnnotationAttributeExtractor attributeExtractor =
new DefaultAnnotationAttributeExtractor(annotation, annotatedElement);
// 4. SynthesizedAnnotationInvocationHandler 動態代理的類
InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor);
// 5. 接口中有 SynthesizedAnnotation,並返回動態代理的對象
Class<?>[] exposedInterfaces = new Class<?>[] {annotationType, SynthesizedAnnotation.class};
return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), exposedInterfaces, handler);
}
1.4 SynthesizedAnnotationInvocationHandler
下面主要看一下動態代理的 invoke 實現是怎么實現的。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (ReflectionUtils.isEqualsMethod(method)) {
return annotationEquals(args[0]);
}
if (ReflectionUtils.isHashCodeMethod(method)) {
return annotationHashCode();
}
if (ReflectionUtils.isToStringMethod(method)) {
return annotationToString();
}
// 注解的 annotationType 返回注解的 Class 類型
if (AnnotationUtils.isAnnotationTypeMethod(method)) {
return annotationType();
}
if (!AnnotationUtils.isAttributeMethod(method)) {
throw new AnnotationConfigurationException(String.format(
"Method [%s] is unsupported for synthesized annotation type [%s]", method, annotationType()));
}
// 真正獲取注解的屬性值
return getAttributeValue(method);
}
getAttributeValue 的核心其實就一句話 this.attributeExtractor.getAttributeValue(attributeMethod);
委托給了對應的 AnnotationAttributeExtractor 處理。
1.4.1 AnnotationAttributeExtractor
AbstractAliasAwareAnnotationAttributeExtractor(
Class<? extends Annotation> annotationType, @Nullable Object annotatedElement, S source) {
Assert.notNull(annotationType, "annotationType must not be null");
Assert.notNull(source, "source must not be null");
this.annotationType = annotationType;
this.annotatedElement = annotatedElement;
this.source = source;
this.attributeAliasMap = AnnotationUtils.getAttributeAliasMap(annotationType);
}
在構造方法中有一個很重要的方法 AnnotationUtils.getAttributeAliasMap(annotationType) 用於獲取其別名。
public final Object getAttributeValue(Method attributeMethod) {
String attributeName = attributeMethod.getName();
// attributeValue 表示屬性的真實值
Object attributeValue = getRawAttributeValue(attributeMethod);
// 獲取所有的別名
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)) {
throw new AnnotationConfigurationException();
}
if (ObjectUtils.nullSafeEquals(attributeValue, defaultValue)) {
attributeValue = aliasValue;
}
}
}
return attributeValue;
}
1.5 AliasDescriptor
(1) getAttributeAliasMap
在 AbstractAliasAwareAnnotationAttributeExtractor 的構造器中有一個很重要的方法 getAttributeAliasMap 獲取注解中所有屬性的別名。
static Map<String, List<String>> getAttributeAliasMap(@Nullable Class<? extends Annotation> annotationType) {
map = new LinkedHashMap<>();
for (Method attribute : getAttributeMethods(annotationType)) {
List<String> aliasNames = getAttributeAliasNames(attribute);
if (!aliasNames.isEmpty()) {
map.put(attribute.getName(), aliasNames);
}
}
return map;
}
static List<String> getAttributeAliasNames(Method attribute) {
AliasDescriptor descriptor = AliasDescriptor.from(attribute);
return (descriptor != null ? descriptor.getAttributeAliasNames() : Collections.emptyList());
}
可以別名獲取的所有的工作都是委托給了 AliasDescriptor 完成,這一小節我們就主要看一下這個類。
(2) AliasDescriptor 構造及校驗
public static AliasDescriptor from(Method attribute) {
AliasFor aliasFor = attribute.getAnnotation(AliasFor.class);
if (aliasFor == null) {
return null;
}
descriptor = new AliasDescriptor(attribute, aliasFor);
descriptor.validate();
return descriptor;
}
構建一個 AliasDescriptor 分為兩步:一是獲取注解信息(構造器),二是校驗別名是否成立(validate)。@AliasFor 有以下的規約:
- 規約1:顯示別名可以不用配置 annotation 屬性
- 規約2:隱式別名默認和原注解屬性名稱一致,getAliasedAttributeName 中體現
- 規約3:隱式別名 @AliasFor 配置的注解必須出現在元注解中,可以是多級元注解
- 規約4:顯示別名必須成對配置
- 規約5:別名必須配置默認值,且默認值一致。注意別名可以為數組類型,而原屬性為數組的元素類型
private AliasDescriptor(Method sourceAttribute, AliasFor aliasFor) {
Class<?> declaringClass = sourceAttribute.getDeclaringClass();
// 1. 注解原字段的信息
this.sourceAttribute = sourceAttribute;
this.sourceAnnotationType = (Class<? extends Annotation>) declaringClass;
this.sourceAttributeName = sourceAttribute.getName();
// 2. @AliasFor 注解的信息
// 規約1:顯示的別名可以不用配置 annotation 屬性
// 規約2:隱式別名默認和原注解屬性名稱一致,getAliasedAttributeName 中體現
this.aliasedAnnotationType = (Annotation.class == aliasFor.annotation() ?
this.sourceAnnotationType : aliasFor.annotation());
this.aliasedAttributeName = getAliasedAttributeName(aliasFor, sourceAttribute);
if (this.aliasedAnnotationType == this.sourceAnnotationType &&
this.aliasedAttributeName.equals(this.sourceAttributeName)) {
throw new AnnotationConfigurationException(...);
}
try {
// @AliasFor 配置的別名不存在直接拋出異常
this.aliasedAttribute = this.aliasedAnnotationType.getDeclaredMethod(this.aliasedAttributeName);
} catch (NoSuchMethodException ex) {
throw new AnnotationConfigurationException(..., ex);
}
// 3. isAliasPair=true 表示就同一個注解內的顯示別名
this.isAliasPair = (this.sourceAnnotationType == this.aliasedAnnotationType);
}
(3) getAttributeAliasNames 獲取別名
public List<String> getAttributeAliasNames() {
// 1. 顯示別名,直接返回
if (this.isAliasPair) {
return Collections.singletonList(this.aliasedAttributeName);
}
// 2. 隱式別名,包括可傳遞的隱式別名
List<String> aliases = new ArrayList<>();
// 2.1 遍歷注解中的其它屬性,一一判斷是否互為別名
// getOtherDescriptors 獲取其它的所有屬性
// isAliasFor 判斷兩個屬性是否互為別名,會遞歸向上查找
for (AliasDescriptor otherDescriptor : getOtherDescriptors()) {
if (this.isAliasFor(otherDescriptor)) {
this.validateAgainst(otherDescriptor);
aliases.add(otherDescriptor.sourceAttributeName);
}
}
return aliases;
}
(4) getAttributeOverrideName 獲取當前屬性在元注解中對應的別名
public String getAttributeOverrideName(Class<? extends Annotation> metaAnnotationType) {
// 遞歸向上查找別名,如果 sourceAnnotationType==metaAnnotationType 則查找到了
for (AliasDescriptor desc = this; desc != null; desc = desc.getAttributeOverrideDescriptor()) {
if (desc.isOverrideFor(metaAnnotationType)) {
return desc.aliasedAttributeName;
}
}
return null;
}
二、AnnotatedElementUtils 源碼分析
2.1 Processor 對匹配的注解進行后置處理
Processor 對匹配的注解進行后置處理,可以通過 process 方法的返回值來控制查找的流程:返回 null 時繼續查找,非 null 時直接返回。有一種情況例外就是 aggregates=true,這種情況要查找所有的注解,所以會繼續查找。
(1) 接口
private interface Processor<T> {
// 兩個作用:一是根據返回值是否為 null 控制查詢的流程;二是對查詢的注解進行處理,主要是用於獲取該注解的屬性值
T process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth);
// 只有 MergedAnnotationAttributesProcessor 有效,用於處理元注解屬性覆蓋
// annotation 為當前注解,result 為元注解屬性信息,annotation 會覆蓋元注解中的屬性信息
void postProcess(@Nullable AnnotatedElement annotatedElement, Annotation annotation, T result);
// 查詢所有元注解時有效,不管是否匹配都要執行 process 方法
boolean alwaysProcesses();
// MergedAnnotationAttributesProcessor 查找所有的注解有效
boolean aggregates();
List<T> getAggregatedResults();
}
有兩個方法要特別關注:
-
process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth)
有兩個作用:一是根據返回值來控制查找的流程;二是 MergedAnnotationAttributesProcessor 的 process 方法返回查找到的注解信息 AnnotationAttributes -
postProcess(@Nullable AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes)
只有 MergedAnnotationAttributesProcessor 有效,用來處理元注解屬性覆蓋。其中 annotation 表示當前的注解,attributes 表示元注解的屬性信息,執行時會用 annotation 覆蓋 attributes。
(2) 類圖
Processor 的有幾個實現:SimpleAnnotationProcessor 相當於一個簡單的適配器;AlwaysTrueBooleanAnnotationProcessor 的 process 方法永遠返回 TRUE;MergedAnnotationAttributesProcessor 用於處理元注解屬性覆蓋。
常用的方法對應的 Processor 返回值如下:
getMetaAnnotationTypes
獲取指定注解上的所有元注解,所以 process 方法返回 null 且 alwaysProcesses=truehasMetaAnnotationTypes
判斷指定的注解上是否有元注解,所以 process 方法返回 metaDepth > 0 ? Boolean.TRUE : CONTINUE,即當 metaDepth>0 表示有元注解就停止查詢isAnnotated
是否存在指定的注解,所以只配匹配到 process 方法就返回 TRUE,使用 AlwaysTrueBooleanAnnotationProcessorgetMergedAnnotationAttributes
元注解會進行屬性覆蓋,使用 MergedAnnotationAttributesProcessorgetAllMergedAnnotations
查找所有的注解,使用 MergedAnnotationAttributesProcessor 且 aggregates=true
(3) MergedAnnotationAttributesProcessor
// AnnotationUtils#retrieveAnnotationAttributes 方法獲取當前注解的屬性
public AnnotationAttributes process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
return AnnotationUtils.retrieveAnnotationAttributes(annotatedElement, annotation,
this.classValuesAsString, this.nestedAnnotationsAsMap);
}
// annotation 為當前注解,result 為元注解屬性信息,這個元注解的屬性信息是 process 方法提取的
// annotation 會覆蓋元注解中的屬性信息
public void postProcess(@Nullable AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes) {
annotation = AnnotationUtils.synthesizeAnnotation(annotation, element);
Class<? extends Annotation> targetAnnotationType = attributes.annotationType();
// 1. 已經解析過的屬性,避免循環查找
Set<String> valuesAlreadyReplaced = new HashSet<>();
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) {
String attributeName = attributeMethod.getName();
// 2. 查找 attributeMethod 屬性到底覆蓋了 targetAnnotationType 元注解的那個屬性
String attributeOverrideName = AnnotationUtils.getAttributeOverrideName(attributeMethod, targetAnnotationType);
// 3 顯示進行屬性覆蓋,通過 @AliasFor 注解
if (attributeOverrideName != null) {
if (valuesAlreadyReplaced.contains(attributeOverrideName)) {
continue;
}
List<String> targetAttributeNames = new ArrayList<>();
targetAttributeNames.add(attributeOverrideName);
valuesAlreadyReplaced.add(attributeOverrideName);
// 確保所有的別名都要進行屬性覆蓋 (SPR-14069)
List<String> aliases = AnnotationUtils.getAttributeAliasMap(targetAnnotationType).get(attributeOverrideName);
if (aliases != null) {
for (String alias : aliases) {
if (!valuesAlreadyReplaced.contains(alias)) {
targetAttributeNames.add(alias);
valuesAlreadyReplaced.add(alias);
}
}
}
// 將 targetAttributeNames 的屬性值設置為 attributeName 的值
overrideAttributes(element, annotation, attributes, attributeName, targetAttributeNames);
}
// 3.2 隱式的進行屬性覆蓋,只要字段與元注解的屬性字段一下致(規約)
else if (!AnnotationUtils.VALUE.equals(attributeName) && attributes.containsKey(attributeName)) {
overrideAttribute(element, annotation, attributes, attributeName, attributeName);
}
}
}
2.2 searchWithGetSemantics
searchWithGetSemantics 有 7 個參數:
- element 注解標注的 AnnotatedElement
- annotationTypes、annotationName、containerType 分別表示要查找的注解類型、注解名稱、以及可重復注解的容器對象
- processor 后置的處理器,process 返回 null 繼續查找,否則停止查找。aggregates=true 時例外,因為此時查找全部的注解。
- visited 已經查找的元素,避免重復查找。
- metaDepth 注解深度,普通注解為 0
// 用於查找 element 上的 annotationTypes、annotationName、containerType 類型注解
// 返回后置處理器對查找后的注解 process 后的值
private static <T> T searchWithGetSemantics(AnnotatedElement element,
Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
@Nullable Class<? extends Annotation> containerType, Processor<T> processor,
Set<AnnotatedElement> visited, int metaDepth) {
if (visited.add(element)) {
try {
// 1. 本地注解查找 Start searching within locally declared annotations
List<Annotation> declaredAnnotations = Arrays.asList(AnnotationUtils.getDeclaredAnnotations(element));
T result = searchWithGetSemanticsInAnnotations(element, declaredAnnotations,
annotationTypes, annotationName, containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
// 2. @Inherited 類型查找
if (element instanceof Class) { // otherwise getAnnotations doesn't return anything new
Class<?> superclass = ((Class<?>) element).getSuperclass();
if (superclass != null && superclass != Object.class) {
List<Annotation> inheritedAnnotations = new LinkedList<>();
for (Annotation annotation : element.getAnnotations()) {
if (!declaredAnnotations.contains(annotation)) {
inheritedAnnotations.add(annotation);
}
}
// Continue searching within inherited annotations
result = searchWithGetSemanticsInAnnotations(element, inheritedAnnotations,
annotationTypes, annotationName, containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
}
}
}
catch (Throwable ex) {
AnnotationUtils.handleIntrospectionFailure(element, ex);
}
}
return null;
}
searchWithGetSemanticsInAnnotations 真正用於在指定的注解集合 annotations 中查找指定的注解。
private static <T> T searchWithGetSemanticsInAnnotations(@Nullable AnnotatedElement element,
List<Annotation> annotations, Set<Class<? extends Annotation>> annotationTypes,
@Nullable String annotationName, @Nullable Class<? extends Annotation> containerType,
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) {
// 1. 直接匹配 Search in annotations
for (Annotation annotation : annotations) {
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
// 1.1 注解類型或注解名相同
if (annotationTypes.contains(currentAnnotationType) ||
currentAnnotationType.getName().equals(annotationName) ||
processor.alwaysProcesses()) {
T result = processor.process(element, annotation, metaDepth);
if (result != null) {
if (processor.aggregates() && metaDepth == 0) {
processor.getAggregatedResults().add(result);
}
else {
return result;
}
}
}
// 1.2 可重復注解,注意可重復注解不可能是組合注解 Repeatable annotations in container?
else if (currentAnnotationType == containerType) {
for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) {
T result = processor.process(element, contained, metaDepth);
if (result != null) {
processor.getAggregatedResults().add(result);
}
}
}
}
}
// 2. 遞歸查找元注解 Recursively search in meta-annotations
for (Annotation annotation : annotations) {
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
if (!AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) {
T result = searchWithGetSemantics(currentAnnotationType, annotationTypes,
annotationName, containerType, processor, visited, metaDepth + 1);
if (result != null) {
// MergedAnnotationAttributesProcessor 用於元注解屬性覆蓋
// annotation 表示當前的注解,attributes 表示元注解的屬性信息,annotation 會覆蓋 attributes。
processor.postProcess(element, annotation, result);
if (processor.aggregates() && metaDepth == 0) {
processor.getAggregatedResults().add(result);
} else {
return result;
}
}
}
}
return null;
}
參考:
- 《spring注解工具類AnnotatedElementUtils和AnnotationUtils》:https://blog.csdn.net/qq_22845447/article/details/83210559
每天用心記錄一點點。內容也許不重要,但習慣很重要!