spring注解解析流程_深入理解Kafka

spring注解解析流程_深入理解Kafka前言众所周知,spring从2.5版本以后开始支持使用注解代替繁琐的xml配置,到了springboot更是全面拥抱了注解式配置。平时在使用的时候,点开一些常见的等注解,会发现往往在一

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

前言

众所周知,spring 从 2.5 版本以后开始支持使用注解代替繁琐的 xml 配置,到了 springboot 更是全面拥抱了注解式配置。平时在使用的时候,点开一些常见的等注解,会发现往往在一个注解上总会出现一些其他的注解,比如 @Service

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // @Component
public @interface Service {
    @AliasFor(annotation = Component.class)
    String value() default "";
}

大部分情况下,我们可以将 @Service 注解等同于 @Component 注解使用,则是因为 spring 基于其 JDK 对元注解的机制进行了扩展。

在 java 中,元注解是指可以注解在其他注解上的注解,spring 中通过对这个机制进行了扩展,实现了一些原生 JDK 不支持的功能,比如允许在注解中让两个属性互为别名,或者将一个带有元注解的子注解直接作为元注解看待,或者在这个基础上,通过 @AliasFor 或者同名策略让子注解的值覆盖元注解的值。

本文将基于 spring 源码 5.2.x 分支,解析 spring 如何实现这套功能的。

这是系列的第三篇文章,将详细介绍 Spring 是如何在经过搜索与属性映射后,将处理后的注解合成为合并注解的。

相关文章:

一、合并注解

我们在前文了解用于搜索注解的合并注解聚合 MergedAnnotations与用于完成注解属性映射的 AnnotationTypeMappingsAnnotationTypeMapping,现在我们需要知道在 MergedAnnotations 这个容器中,AnnotationTypeMappingsAnnotationTypeMapping 是如何转为一个我们所需要的合并注解 MergedAnnotation 的。

与前文一样,我们以 AnnotatedElementUtils.findMergedAnnotations 方法作为入口:

public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element, Class<A> annotationType) {
    // 1、下述任意情况下直接获取元素上声明的注解:
    // a.查找的注解属于java、javax或者org.springframework.lang包
    // b.被处理的元素属于java包,或被java包中的对象声明,或者就是Ordered.class
    if (AnnotationFilter.PLAIN.matches(annotationType) ||
        AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) {
        return element.getDeclaredAnnotation(annotationType);
    }

    // 2、将元素上的全部注解合成MergedAnnotation
    return findAnnotations(element)
        // 3、从MergedAnnotation获取与该类型对应的MergedAnnotations
        .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared())
        // 4、根据MergedAnnotation通过动态代理生成一个注解实例
        .synthesize(MergedAnnotation::isPresent).orElse(null);
}

我们在上文顺着 MergedAnnotations.get 一路找到 TypeMappedAnnotations.MergedAnnotationFinderprocess 方法,在这里我们目睹了一个普通的注解的元注解被解析为 AnnotationTypeMappingsAnnotationTypeMapping 的过程:

private MergedAnnotation<A> process(
    Object type, int aggregateIndex, @Nullable Object source, Annotation annotation) {

    Annotation[] repeatedAnnotations = repeatableContainers.findRepeatedAnnotations(annotation);
    if (repeatedAnnotations != null) {
        return doWithAnnotations(type, aggregateIndex, source, repeatedAnnotations);
    }
    // 将该注解上的元注解解析为由多个AnnotationTypeMapping聚合的AnnotationTypeMappings
    AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(
        annotation.annotationType(), repeatableContainers, annotationFilter);
    for (int i = 0; i < mappings.size(); i++) {
        // 获取元注解对应的AnnotationTypeMapping
        AnnotationTypeMapping mapping = mappings.get(i);
        if (isMappingForType(mapping, annotationFilter, this.requiredType)) {
            // 使用AnnotationTypeMapping创建一个合并注解
            MergedAnnotation<A> candidate = TypeMappedAnnotation.createIfPossible(
                mapping, source, annotation, aggregateIndex, IntrospectionFailureLogger.INFO);
            if (candidate != null && (this.predicate == null || this.predicate.test(candidate))) {
                if (this.selector.isBestCandidate(candidate)) {
                    return candidate;
                }
                updateLastResult(candidate);
            }
        }
    }
    return null;
}

其他部分在上文已经详细的分析了,因此此处我们仅需关注 TypeMappedAnnotation.createIfPossible 这一个方法:

static <A extends Annotation> TypeMappedAnnotation<A> createIfPossible(
    AnnotationTypeMapping mapping, @Nullable Object source, Annotation annotation,
    int aggregateIndex, IntrospectionFailureLogger logger) {

    return createIfPossible(mapping, source, annotation,
                            ReflectionUtils::invokeMethod, aggregateIndex, logger);
}

该方法是 AnnotationTypeMapping 转为 MergedAnnotation 的关键。

1、合并注解的创建

TypeMappedAnnotationMergedAnnotation 一个通用实现,在大部分情况下,我们所说的合并注解其实指的就是这个类。

通过它的构造方法我们得以了解其创建过程:

private TypeMappedAnnotation(AnnotationTypeMapping mapping, @Nullable ClassLoader classLoader,
                             @Nullable Object source, @Nullable Object rootAttributes, ValueExtractor valueExtractor,
                             int aggregateIndex, @Nullable int[] resolvedRootMirrors) {

    // 当前合并注解对应的AnnotationTypeMapping
    this.mapping = mapping;
    // 类加载器
    this.classLoader = classLoader;
    // 当前注解的数据源
    this.source = source;
    // AnnotationTypeMapping对应的root注解
    this.rootAttributes = rootAttributes;
    // 通过属性方法对象获得属性值的方法,一般是ReflectionUtils::invokeMethod
    this.valueExtractor = valueExtractor;
    // 该注解对应的聚合索引
    this.aggregateIndex = aggregateIndex;
    // 是否使用映射过的属性值
    this.useMergedValues = true;
    // 属性过滤器
    this.attributeFilter = null;
    // 通过MirrorSets解析得到的确认属性
    this.resolvedRootMirrors = (resolvedRootMirrors != null ? resolvedRootMirrors :
                                mapping.getRoot().getMirrorSets().resolve(source, rootAttributes, this.valueExtractor));
    this.resolvedMirrors = (getDistance() == 0 ? this.resolvedRootMirrors :
                            mapping.getMirrorSets().resolve(source, this, this::getValueForMirrorResolution));
}

可以看得出,TypeMappedAnnotation 基本可以认为是 AnnotationTypeMapping 的包装类,它以一个 AnnotationTypeMapping 实例作为数据源,从而提供一些关于映射后的属性的相关功能。

它本身并没有很多特殊的逻辑,我们仅需要关注通过它合成注解的代理对象,以及后续调用代理对象时,是如何从映射过的属性获取值的。

2、合并注解的合成

回到 AnnotatedElementUtils.findMergedAnnotations,我们可以看到,在通过 MergedAnnotations 获得了一个 MergedAnnotation 对象——实际上是 TypeMappedAnnotation 对象——之后,又调用了 MergedAnnotation.synthesize 方法,将 MergedAnnotation 转成了一个调用方指定类型的注解对象。

该方法先调用了 AbstractMergedAnnotationsynthesize 方法:

public Optional<A> synthesize(Predicate<? super MergedAnnotation<A>> condition)
    throws NoSuchElementException {

    return (condition.test(this) ? Optional.of(synthesize()) : Optional.empty());
}

随后再调用了实现类 TypeMappedAnnotationsynthesize 方法:

public A synthesize() {
    if (!isPresent()) {
        throw new NoSuchElementException("Unable to synthesize missing annotation");
    }
    // 如果已经合成过就直接返回缓存的实例
    A synthesized = this.synthesizedAnnotation;
    if (synthesized == null) {
        // 合成注解
        synthesized = createSynthesized();
        this.synthesizedAnnotation = synthesized;
    }
    return synthesized;
}

继续点开 createSynthesized

protected A createSynthesized() {
    // 满足下述条件时,直接返回根注解:
    // 1.若当前注解的属性与根注解相同
    // 2.且该合成注解还没有被合成过
    // 3.该注解存在映射后的属性
    if (getType().isInstance(this.rootAttributes) && !isSynthesizable()) {
        return (A) this.rootAttributes;
    }
    // 使用动态代理创建一个代理注解
    return SynthesizedMergedAnnotationInvocationHandler.createProxy(this, getType());
}

private boolean isSynthesizable() {
    // Already synthesized?
    if (this.rootAttributes instanceof SynthesizedAnnotation) {
        return false;
    }
    return this.mapping.isSynthesizable();
}

SynthesizedMergedAnnotationInvocationHandler 是一个用于 JDK 动态代理的 InvocationHandler,我们不需要完全站看,仅需看看它的构造函数与 InvocationHandler.invoke 就能明白它的运作机制了:

final class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation> implements InvocationHandler {

    // 合并注解实例
    private final MergedAnnotation<?> annotation;

    // 代理注解的类型
    private final Class<A> type;

    // 代理注解的注解方法
    private final AttributeMethods attributes;

    // 注解属性的值缓存
    private final Map<String, Object> valueCache = new ConcurrentHashMap<>(8);

    // hashcode
    @Nullable
    private volatile Integer hashCode;

    // toString的值
    @Nullable
    private volatile String string;


    private SynthesizedMergedAnnotationInvocationHandler(MergedAnnotation<A> annotation, Class<A> type) {
        Assert.notNull(annotation, "MergedAnnotation must not be null");
        Assert.notNull(type, "Type must not be null");
        Assert.isTrue(type.isAnnotation(), "Type must be an annotation");
        this.annotation = annotation;
        this.type = type;
        this.attributes = AttributeMethods.forAnnotationType(type);
    }
}

至此,合并注解的合成机制已经很明确了:

  • 先通过 AnnotationTypeMapping 创建 TypeMappedAnnotation
  • 通过 TypeMappedAnnotation.synthesize 创建一个对应的类型的注解对象;
  • 如果该 TypeMappedAnnotation 无需合成,则直接返回对应的原始注解对象,否则返回基于该合并注解生成的JDK动态代理对象;

二、合并注解的取值

承接上文,当我们使用 MergedAnnotation.synthesize 方法时,我们可能会得到两种对象:

  • 第一种就是原始的注解对象,这个没什么好说的;
  • 第二种就是通过 SynthesizedMergedAnnotationInvocationHandler 生成的注解代理对象;

而通过注解代理对象取值时,这些方法会被代理到 SynthesizedMergedAnnotationInvocationHandler 中存放的 MergedAnnotation 对象上,从而让这个代理对象通过原始注解的属性,获得与原始注解不一样的属性值。

1、方法代理

当我们调用代理对象的属性值时,它会在 SynthesizedMergedAnnotationInvocationHandler 中,通过 invoke 代理到对应的方法上:

@Override
public Object invoke(Object proxy, Method method, Object[] args) {
    // 代理 equals 方法
    if (ReflectionUtils.isEqualsMethod(method)) {
        return annotationEquals(args[0]);
    }
    // 代理 hashCode 方法
    if (ReflectionUtils.isHashCodeMethod(method)) {
        return annotationHashCode();
    }
    // 代理 toString 方法
    if (ReflectionUtils.isToStringMethod(method)) {
        return annotationToString();
    }
    // 代理 isAnnotationTypeMethod 方法
    if (isAnnotationTypeMethod(method)) {
        return this.type;
    }
    // 代理获取注解属性的方法
    if (this.attributes.indexOf(method.getName()) != -1) {
        return getAttributeValue(method);
    }
    throw new AnnotationConfigurationException(String.format(
        "Method [%s] is unsupported for synthesized annotation type [%s]", method, this.type));
}

2、注解属性的获取

这里我们代理对象是如何获取注解属性值的:

private Object getAttributeValue(Method method) {
    // 缓存属性值
    Object value = this.valueCache.computeIfAbsent(method.getName(), attributeName -> {
        // 获取代理方法的返回值类型
        Class<?> type = ClassUtils.resolvePrimitiveIfNecessary(method.getReturnType());
        // 从 MergedAnnotation 获取对应属性值
        return this.annotation.getValue(attributeName, type).orElseThrow(
            () -> new NoSuchElementException("No value found for attribute named '" + attributeName +
                                             "' in merged annotation " + this.annotation.getType().getName()));
    });

    // Clone non-empty arrays so that users cannot alter the contents of values in our cache.
    if (value.getClass().isArray() && Array.getLength(value) > 0) {
        value = cloneArray(value);
    }

    return value;
}

这里的 MergedAnnotation.getValue 最终在经过多次跳转后,调到 TypeMappedAnnotation.getAttributeValue 上:

protected <T> T getAttributeValue(String attributeName, Class<T> type) {
    // 获取方法在AttributeMethods中的下标
    int attributeIndex = getAttributeIndex(attributeName, false);
    // 若方法存在,则通过下标调用
    return (attributeIndex != -1 ? getValue(attributeIndex, type) : null);
}

private <T> T getValue(int attributeIndex, Class<T> type) {
    // 获取属性对应的方法
    Method attribute = this.mapping.getAttributes().get(attributeIndex);
    // 获取属性值,若有必要则进行属性映射
    Object value = getValue(attributeIndex, true, false);
    if (value == null) {
        // 如果没有值则尝试获得默认值
        value = attribute.getDefaultValue();
    }
    // 进行类型适配
    return adapt(attribute, value, type);
}

而这边的 getValue 方法就是真正要获取属性值的地方。

private Object getValue(int attributeIndex, boolean useConventionMapping, boolean forMirrorResolution) {
    AnnotationTypeMapping mapping = this.mapping;
    // 是否要获取合并后的属性
    if (this.useMergedValues) {
        // 如果有必要,属性是否会被来自根注解的属性覆盖
        int mappedIndex = this.mapping.getAliasMapping(attributeIndex);
        // 如果不会被来自根注解的属性覆盖,并且允许使用子注解属性覆盖该属性
        if (mappedIndex == -1 && useConventionMapping) {
            mappedIndex = this.mapping.getConventionMapping(attributeIndex);
        }
        // 如果上述两情况只要有任意一点符合,则令attributeIndex变为根/子注解中覆盖属性的下标
        if (mappedIndex != -1) {
            mapping = mapping.getRoot();
            attributeIndex = mappedIndex;
        }
    }
    // 若有必要,且当前属性下标对应的属性在注解内还存在别名属性,则通过MirrorSets获得唯一确定的属性的下标
    if (!forMirrorResolution) {
        attributeIndex =
            (mapping.getDistance() != 0 ? this.resolvedMirrors : this.resolvedRootMirrors)[attributeIndex];
    }
    if (attributeIndex == -1) {
        return null;
    }
    // 如果自己就是根节点,则从自己身上获取
    if (mapping.getDistance() == 0) {
        Method attribute = mapping.getAttributes().get(attributeIndex);
        Object result = this.valueExtractor.extract(attribute, this.rootAttributes);
        return (result != null ? result : attribute.getDefaultValue());
    }
    // 如果自己不是根节点,则从元注解上获取
    return getValueFromMetaAnnotation(attributeIndex, forMirrorResolution);
}

@Nullable
private Object getValueFromMetaAnnotation(int attributeIndex, boolean forMirrorResolution) {
    Object value = null;
    if (this.useMergedValues || forMirrorResolution) {
        value = this.mapping.getMappedAnnotationValue(attributeIndex, forMirrorResolution);
    }
    if (value == null) {
        Method attribute = this.mapping.getAttributes().get(attributeIndex);
        value = ReflectionUtils.invokeMethod(attribute, this.mapping.getAnnotation());
    }
    return value;
}

Object getMappedAnnotationValue(int attributeIndex, boolean metaAnnotationsOnly) {
    int mappedIndex = this.annotationValueMappings[attributeIndex];
    if (mappedIndex == -1) {
        return null;
    }
    AnnotationTypeMapping source = this.annotationValueSource[attributeIndex];
    if (source == this && metaAnnotationsOnly) {
        return null;
    }
    return ReflectionUtils.invokeMethod(source.attributes.get(mappedIndex), source.annotation);
}

这一步有点复杂,主要是根据不同的情况,通过 AnnotationTypeMapping 中的几个属性映射数组,包括 aliasMappingsconventionMappingsannotationValueMappingsannotationValueSource 来确定最终用于取值的 AnnotationTypeMapping 对象与调用的方法在 AttributeMethods 中的下标:

  • 如果要合并属性值,则:
    1. 若该属性被 root 中的同名属性覆盖,即 aliasMappings 数组对应下标不为 -1,则记录该 root 属性下标;
    2. 若该属性不被 root 中同名属性覆盖,则确定是否被子注解中的同名属性覆盖,即 conventionMappings 数组对应下标不为 -1,则记录该覆盖属性下标;
  • 若允许别名,则在上述基础上,继续进行处理:
    1. 若当前注解已经是根注解,则从 resolvedRootMirrors 上,获得该属性在同一注解下关联的别名属性中,唯一确定的有效属性的下标;
    2. 若当前注解不是根注解,则从 resolvedRootMirrors 上,获得该属性在同一注解下关联的别名属性中,唯一确定的有效属性的下标;
  • 若当前注解是根注解,则使用从根注解对象 rootAttributes 上根据属性下标获取对应方法,然后通过反射调用获取属性值;
  • 若当前注解不是根注解,则:
    1. 若不支持属性覆盖以及别名,则直接和获取该注解属性下标对应方法,并通过反射调用获取属性值;
    2. 若支持属性覆盖以及别名,则通过属性下标从 annotationValueSource 找到对应的注解对象,再通过 annotationValueMappings 找到要在该注解中调用的属性下标,然后在通过属性下标找到对应的属性方法后,通过反射调用获取属性值;

至此,获取属性值的方法流程也走完了。

总结

在这一章,我们了解了当通过 MergedAnnotations 获得注解并解析得到 AnnotationTypeMapping 后,AnnotationTypeMapping 是如何再转为我们所需的 MergedAnnotation ,以及在此之后,MergedAnnotation 又是如何生成我们最终所需要的代理注解的。

简而言之,当解析注解的元注解获得所需的 AnnotationTypeMapping 后,MergedAnnotation 会判断 AnnotationTypeMapping 是否发生过属性映射,如果没有则返回该映射对象对应的原始注解,否则就通过 SynthesizedMergedAnnotationInvocationHandler 生成一个对应类型的 JDK 动态代理对象。

当我们通过代理对象去调用注解的方法,获取注解的属性的时候,SynthesizedMergedAnnotationInvocationHandler 会把方法代理到对应的内部方法中,而获取属性时,还会通过 MergedAnnotation.getValue ,最终绕到 AnnotationTypeMapping 中获取被映射后的属性值。

题外话

至此,关于 Spring 注解机制的三篇文章已经全部写完了,在了解了它是实现原理的同时,笔者也深刻的认识到了其功能的强大,尤其是 MergeAnnotation 真切的弥补了 java 注解不支持继承所带来的的一些痛点,这点笔者在自己的项目尝试了全面拥抱 spring 的增强注解后深有感触。

出于巩固知识,也出于为想要在非 Spring 环境下享受这个功能的同学考虑,作者决定搞一套类似的开源项目,目前也已经有了一些成果:

  • 笔者尝试为常用的开源工具类库 hutool 提了一个 PR ,也有幸被作者大佬采纳了,有需要的同学可以在 Hutool-5.8.5 及以上版本使用该功能;
  • 由于考虑有些项目并没有引入 Hutool,以及跟作者沟通后觉得这个特性并不适合搬到即将发布的 Hutool-6.x ,因此笔者单独将作为一个注解工具类库开源了,有兴趣的同学可以了解一下 powerful-annotation
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/170765.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 使用matplotlib给女朋友画一个爱心吧,这份满满的爱意,一定要记得收下

    使用matplotlib给女朋友画一个爱心吧,这份满满的爱意,一定要记得收下

  • 空间相关分析(三) 局部莫兰指数的理解与计算「建议收藏」

    空间相关分析(三) 局部莫兰指数的理解与计算「建议收藏」        在上篇中,我们详细地阐述了全局莫兰指数(GlobalMoran’I)的含义以及具体的软件实操方法。今天,就来进一步地说明局部莫兰指数(LocalMoran’I)的含义与计算。        首先说明一下进行局部相关分析的必要性:在全局相关分析中,如果全局莫兰指数显著,我们即可认为在该区域上存在空间相关性。但是,我们还是不知道

  • 深度解析xxl-rpc之RPC原理

    深度解析xxl-rpc之RPC原理一.什么是RPC?RPC(remoteprocesscall),中文是远程过程调用的意思。怎么理解这个远程过程调用呢?可以这样理解,可以与本地的过程调用对比下,本地过程调用,也就是调用函数或者是调用方法,比如说,在单体架构中,我们要根据用户的id获取订单信息,我们就需要找到订单service,调用getOrderInfoById(Stringid)这个方法,这个调用动作这就是本地过程调…

    2022年10月31日
  • 数字图像处理均值滤波matlab函数_均值滤波怎么计算

    数字图像处理均值滤波matlab函数_均值滤波怎么计算图像的平滑、锐化都是利用掩模操作来完成的。通过掩模操作实现一种邻域运算,待处理像素点的结果由邻域的图像像素以及相应的与邻域有相同维数的子图像得到。这些子图像被称为滤波器、掩模、核、模板或窗口;掩模运算的数学含义是卷积(或互相关)运算;掩模子图像中的值是系数值,而不是灰度值;……

    2022年10月26日
  • Android版MT4使用方法,安卓版手机MT4使用手册「建议收藏」

    Android版MT4使用方法,安卓版手机MT4使用手册「建议收藏」一、安卓Android系统MT4下载安装1、下载安装:用户在安卓市场(应用市场)里直接输入MT4、“外汇交易软件”、“外汇交易系统”等进行搜索,选择左边图片中的软件后下载安装。(请认准下方标志)(MT4官方标志)2、登录:在成功安装后,会自动转跳到右图中的页面,客户根据自己的实际情况,选择登录模拟还是真实账户进行登录。3、选择服务器:客户需要在搜索框里输入“onef”系统会自动搜索出欧…

  • 五万字总结,深度学习基础。「建议收藏」

    五万字总结,深度学习基础。「建议收藏」文章目录1基本概念1.1神经网络组成?1.2神经网络有哪些常用模型结构?1.3如何选择深度学习开发平台?1.4为什么深层神经网络难以训练?1.5深度学习和机器学习的异同?2网络操作与计算2.1前向传播与反向传播?2.2如何计算神经网络的输出?2.3如何计算卷积神经网络输出值?2.4如何计算Pooling层输出值输出值?2.5实例理解反向传播2.6神经网络更“深”有什么意义?3超参数3.1什么是超参数?3.2如何寻找超参数的最优值?3.3超参数搜索一般过程?4激活函数4

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号