转载自:https://www.cnblogs.com/binarylei/p/10415585.html
Spring 注解(二)注解工具类 AnnotationUtils 和 AnnotatedElementUtils
Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)
Spring 注解系列文章:
Spring 注解(二)注解工具类 AnnotationUtils 和 AnnotatedElementUtils
首先回顾一下 AnnotationUtils 和 AnnotatedElementUtils 这两个注解工具类的用法:
@Test@GetMapping(value ="/GetMapping", consumes = MediaType.APPLICATION_JSON_VALUE)publicvoidtest()throwsNoSuchMethodException{ Method method = ReflectUtils.findDeclaredMethod( AliasForTest.class,"test",null);// AnnotationUtils 不支持注解属性覆盖RequestMapping requestMappingAnn1 = AnnotationUtils.getAnnotation(method, RequestMapping.class); Assert.assertEquals(newString[]{}, requestMappingAnn1.value()); Assert.assertEquals(newString[]{}, requestMappingAnn1.consumes());// AnnotatedElementUtils 支持注解属性覆盖RequestMapping requestMappingAnn2 = AnnotatedElementUtils.getMergedAnnotation(method, RequestMapping.class); Assert.assertEquals(newString[]{"/GetMapping"}, requestMappingAnn2.value()); Assert.assertEquals(newString[]{MediaType.APPLICATION_JSON_VALUE}, requestMappingAnn2.consumes());}
一、AnnotationUtils 源码分析
AnnotationUtils 解决注解别名,包括显式别名、隐式别名、传递的隐式别名,还可以查的指定注解的属性信息。
AnnotationUtils 底层使用动态代理的方式处理注解别名的问题。
1.1 get* 系列注解查找
get 遵循 JDK 的注解查找语义,只是增加了一级元注解的查找。
1.2 find* 系列注解查找
遵循 JDK 的注解查找语义,只是增加了多级元注解的查找。
// visited 表示已经查找的元素,Spring 的递归很多都用到了这个参数privatestaticAfindAnnotation(
AnnotatedElement annotatedElement, Class<A> annotationType, Set<Annotation> visited){try{// 1. 本地注解查找A annotation = annotatedElement.getDeclaredAnnotation(annotationType);if(annotation !=null) {returnannotation; }// 2. 元注解上查找for(Annotation declaredAnn : getDeclaredAnnotations(annotatedElement)) { Class declaredType = declaredAnn.annotationType();if(!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) {// 3. 元注解上递归查找annotation = findAnnotation((AnnotatedElement) declaredType, annotationType, visited);if(annotation !=null) {returnannotation; } } } }catch(Throwable ex) { handleIntrospectionFailure(annotatedElement, ex); }returnnull;}
1.3 synthesizeAnnotation 动态代理解决别名问题
1.4 SynthesizedAnnotationInvocationHandler
下面主要看一下动态代理的 invoke 实现是怎么实现的。
publicObjectinvoke(Object proxy, Method method, Object[] args)throwsThrowable{if(ReflectionUtils.isEqualsMethod(method)) {returnannotationEquals(args[0]); }if(ReflectionUtils.isHashCodeMethod(method)) {returnannotationHashCode(); }if(ReflectionUtils.isToStringMethod(method)) {returnannotationToString(); }// 注解的 annotationType 返回注解的 Class 类型if(AnnotationUtils.isAnnotationTypeMethod(method)) {returnannotationType(); }if(!AnnotationUtils.isAttributeMethod(method)) {thrownewAnnotationConfigurationException(String.format("Method [%s] is unsupported for synthesized annotation type [%s]", method, annotationType())); }// 真正获取注解的属性值returngetAttributeValue(method);}
getAttributeValue 的核心其实就一句话 this.attributeExtractor.getAttributeValue(attributeMethod); 委托给了对应的 AnnotationAttributeExtractor 处理。
1.4.1 AnnotationAttributeExtractor
AbstractAliasAwareAnnotationAttributeExtractor( Class annotationType,@NullableObject 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) 用于获取其别名。
publicfinalObjectgetAttributeValue(Method attributeMethod){ String attributeName = attributeMethod.getName();// attributeValue 表示属性的真实值Object attributeValue = getRawAttributeValue(attributeMethod);// 获取所有的别名List 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)) {thrownewAnnotationConfigurationException(); }if(ObjectUtils.nullSafeEquals(attributeValue, defaultValue)) { attributeValue = aliasValue; } } }returnattributeValue;}
1.5 AliasDescriptor
(1) getAttributeAliasMap
在 AbstractAliasAwareAnnotationAttributeExtractor 的构造器中有一个很重要的方法 getAttributeAliasMap 获取注解中所有属性的别名。
staticMap> getAttributeAliasMap(@NullableClass annotationType) { map =newLinkedHashMap<>();for(Method attribute : getAttributeMethods(annotationType)) { List aliasNames = getAttributeAliasNames(attribute);if(!aliasNames.isEmpty()) { map.put(attribute.getName(), aliasNames); } }returnmap;}staticListgetAttributeAliasNames(Method attribute){ AliasDescriptor descriptor = AliasDescriptor.from(attribute);return(descriptor !=null? descriptor.getAttributeAliasNames() : Collections.emptyList());}
可以别名获取的所有的工作都是委托给了 AliasDescriptor 完成,这一小节我们就主要看一下这个类。
(2) AliasDescriptor 构造及校验
publicstaticAliasDescriptorfrom(Method attribute){ AliasFor aliasFor = attribute.getAnnotation(AliasFor.class);if(aliasFor ==null) {returnnull; } descriptor =newAliasDescriptor(attribute, aliasFor); descriptor.validate();returndescriptor;}
构建一个 AliasDescriptor 分为两步:一是获取注解信息(构造器),二是校验别名是否成立(validate)。@AliasFor 有以下的规约:
规约1:显示别名可以不用配置 annotation 属性
规约2:隐式别名默认和原注解属性名称一致,getAliasedAttributeName 中体现
规约3:隐式别名 @AliasFor 配置的注解必须出现在元注解中,可以是多级元注解
规约4:显示别名必须成对配置
规约5:别名必须配置默认值,且默认值一致。注意别名可以为数组类型,而原属性为数组的元素类型
privateAliasDescriptor(Method sourceAttribute, AliasFor aliasFor){ Class declaringClass = sourceAttribute.getDeclaringClass();// 1. 注解原字段的信息this.sourceAttribute = sourceAttribute;this.sourceAnnotationType = (Class) 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)) {thrownewAnnotationConfigurationException(...); }try{// @AliasFor 配置的别名不存在直接抛出异常this.aliasedAttribute =this.aliasedAnnotationType.getDeclaredMethod(this.aliasedAttributeName); }catch(NoSuchMethodException ex) {thrownewAnnotationConfigurationException(..., ex); }// 3. isAliasPair=true 表示就同一个注解内的显示别名this.isAliasPair = (this.sourceAnnotationType ==this.aliasedAnnotationType);}
(3) getAttributeAliasNames 获取别名
publicListgetAttributeAliasNames(){// 1. 显示别名,直接返回if(this.isAliasPair) {returnCollections.singletonList(this.aliasedAttributeName); }// 2. 隐式别名,包括可传递的隐式别名List aliases =newArrayList<>();// 2.1 遍历注解中的其它属性,一一判断是否互为别名// getOtherDescriptors 获取其它的所有属性// isAliasFor 判断两个属性是否互为别名,会递归向上查找for(AliasDescriptor otherDescriptor : getOtherDescriptors()) {if(this.isAliasFor(otherDescriptor)) {this.validateAgainst(otherDescriptor); aliases.add(otherDescriptor.sourceAttributeName); } }returnaliases;}
(4) getAttributeOverrideName 获取当前属性在元注解中对应的别名
publicStringgetAttributeOverrideName(Class<? extends Annotation> metaAnnotationType){// 递归向上查找别名,如果 sourceAnnotationType==metaAnnotationType 则查找到了for(AliasDescriptor desc =this; desc !=null; desc = desc.getAttributeOverrideDescriptor()) {if(desc.isOverrideFor(metaAnnotationType)) {returndesc.aliasedAttributeName; } }returnnull;}
二、AnnotatedElementUtils 源码分析
2.1 Processor 对匹配的注解进行后置处理
Processor 对匹配的注解进行后置处理,可以通过 process 方法的返回值来控制查找的流程:返回 null 时继续查找,非 null 时直接返回。有一种情况例外就是 aggregates=true,这种情况要查找所有的注解,所以会继续查找。
(1) 接口
privateinterfaceProcessor{// 两个作用:一是根据返回值是否为 null 控制查询的流程;二是对查询的注解进行处理,主要是用于获取该注解的属性值Tprocess(@Nullable AnnotatedElement annotatedElement, Annotation annotation,intmetaDepth);// 只有 MergedAnnotationAttributesProcessor 有效,用于处理元注解属性覆盖// annotation 为当前注解,result 为元注解属性信息,annotation 会覆盖元注解中的属性信息voidpostProcess(@Nullable AnnotatedElement annotatedElement, Annotation annotation, T result);// 查询所有元注解时有效,不管是否匹配都要执行 process 方法booleanalwaysProcesses();// MergedAnnotationAttributesProcessor 查找所有的注解有效booleanaggregates();ListgetAggregatedResults();}
有两个方法要特别关注:
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=true
hasMetaAnnotationTypes 判断指定的注解上是否有元注解,所以 process 方法返回 metaDepth > 0 ? Boolean.TRUE : CONTINUE,即当 metaDepth>0 表示有元注解就停止查询
isAnnotated 是否存在指定的注解,所以只配匹配到 process 方法就返回 TRUE,使用 AlwaysTrueBooleanAnnotationProcessor
getMergedAnnotationAttributes 元注解会进行属性覆盖,使用 MergedAnnotationAttributesProcessor
getAllMergedAnnotations 查找所有的注解,使用 MergedAnnotationAttributesProcessor 且 aggregates=true
(3) MergedAnnotationAttributesProcessor
// AnnotationUtils#retrieveAnnotationAttributes 方法获取当前注解的属性publicAnnotationAttributesprocess(@Nullable AnnotatedElement annotatedElement, Annotation annotation,intmetaDepth){returnAnnotationUtils.retrieveAnnotationAttributes(annotatedElement, annotation,this.classValuesAsString,this.nestedAnnotationsAsMap);}// annotation 为当前注解,result 为元注解属性信息,这个元注解的属性信息是 process 方法提取的// annotation 会覆盖元注解中的属性信息publicvoidpostProcess(@Nullable AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes){ annotation = AnnotationUtils.synthesizeAnnotation(annotation, element); Class targetAnnotationType = attributes.annotationType();// 1. 已经解析过的属性,避免循环查找Set valuesAlreadyReplaced =newHashSet<>();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 targetAttributeNames =newArrayList<>(); targetAttributeNames.add(attributeOverrideName); valuesAlreadyReplaced.add(attributeOverrideName);// 确保所有的别名都要进行属性覆盖 (SPR-14069)List 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 隐式的进行属性覆盖,只要字段与元注解的属性字段一下致(规约)elseif(!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 后的值privatestaticTsearchWithGetSemantics(AnnotatedElement element, Set> annotationTypes, @Nullable String annotationName, @Nullable Class containerType, Processor processor, Set visited,intmetaDepth){if(visited.add(element)) {try{// 1. 本地注解查找 Start searching within locally declared annotationsList declaredAnnotations = Arrays.asList(AnnotationUtils.getDeclaredAnnotations(element)); T result = searchWithGetSemanticsInAnnotations(element, declaredAnnotations, annotationTypes, annotationName, containerType, processor, visited, metaDepth);if(result !=null) {returnresult; }// 2. @Inherited 类型查找if(elementinstanceofClass) {// otherwise getAnnotations doesn't return anything newClass superclass = ((Class) element).getSuperclass();if(superclass !=null&& superclass != Object.class) { List inheritedAnnotations =newLinkedList<>();for(Annotation annotation : element.getAnnotations()) {if(!declaredAnnotations.contains(annotation)) { inheritedAnnotations.add(annotation); } }// Continue searching within inherited annotationsresult = searchWithGetSemanticsInAnnotations(element, inheritedAnnotations, annotationTypes, annotationName, containerType, processor, visited, metaDepth);if(result !=null) {returnresult; } } } }catch(Throwable ex) { AnnotationUtils.handleIntrospectionFailure(element, ex); } }returnnull;}
searchWithGetSemanticsInAnnotations 真正用于在指定的注解集合 annotations 中查找指定的注解。
privatestaticTsearchWithGetSemanticsInAnnotations(@Nullable AnnotatedElement element, List annotations, Set> annotationTypes, @Nullable String annotationName, @Nullable Class containerType, Processor processor, Set visited,intmetaDepth){// 1. 直接匹配 Search in annotationsfor(Annotation annotation : annotations) { Class 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{returnresult; } } }// 1.2 可重复注解,注意可重复注解不可能是组合注解 Repeatable annotations in container?elseif(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-annotationsfor(Annotation annotation : annotations) { Class 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{returnresult; } } } }returnnull;}
参考:
《spring注解工具类AnnotatedElementUtils和AnnotationUtils》:https://blog.csdn.net/qq_22845447/article/details/83210559
网友评论