美文网首页
Spring 注解(二)注解工具类 AnnotationUtil

Spring 注解(二)注解工具类 AnnotationUtil

作者: 从零开始_1b00 | 来源:发表于2019-07-19 10:18 被阅读0次

    转载自:https://www.cnblogs.com/binarylei/p/10415585.html

    Spring 注解(二)注解工具类 AnnotationUtils 和 AnnotatedElementUtils

    Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html)

    Spring 注解系列文章:

    Spring 注解(一)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 的注解查找语义,只是增加了一级元注解的查找。

    publicstaticAgetAnnotation(Annotation annotation, Class<A> annotationType){// 1. 直接查找本地注解if(annotationType.isInstance(annotation)) {returnsynthesizeAnnotation((A) annotation);    }// 2. 元注解上查找,注意相对于 find* 而言,这里只查找一级元注解Class annotatedElement = annotation.annotationType();try{        A metaAnn = annotatedElement.getAnnotation(annotationType);return(metaAnn !=null? synthesizeAnnotation(metaAnn, annotatedElement) :null);    }catch(Throwable ex) {        handleIntrospectionFailure(annotatedElement, ex);returnnull;    }}

    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 动态代理解决别名问题

    staticAsynthesizeAnnotation(A annotation, @Nullable Object annotatedElement){// 1. SynthesizedAnnotation 为一个标记,表示已经动态代理过了//    hasPlainJavaAnnotationsOnly 如果是 java 中的注解不可能有注解别名,直接返回if(annotationinstanceofSynthesizedAnnotation || hasPlainJavaAnnotationsOnly(annotatedElement)) {returnannotation;    }// 2. 判断是否需要进行动态代理,即注解中存在别名,包括显示别名、隐式别名、传递的隐式别名Class annotationType = annotation.annotationType();if(!isSynthesizable(annotationType)) {returnannotation;    }// 3. AnnotationAttributeExtractor 用于从注解 annotation 中提取属性的值DefaultAnnotationAttributeExtractor attributeExtractor =newDefaultAnnotationAttributeExtractor(annotation, annotatedElement);// 4. SynthesizedAnnotationInvocationHandler 动态代理的类InvocationHandler handler =newSynthesizedAnnotationInvocationHandler(attributeExtractor);// 5. 接口中有 SynthesizedAnnotation,并返回动态代理的对象Class[] exposedInterfaces =newClass[] {annotationType, SynthesizedAnnotation.class};return(A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), exposedInterfaces, handler);}

    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

    相关文章

      网友评论

          本文标题:Spring 注解(二)注解工具类 AnnotationUtil

          本文链接:https://www.haomeiwen.com/subject/hiqjlctx.html