Spring 源码阅读 41:AutowiredAnnotationBeanPostProcessor 分析(2)

简介: 本文介绍了自动装配注解后处理器中的 postProcessMergedBeanDefinition 方法解析 BeanDefinition 中添加了注入注解的属性和方法。

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 40:ConfigurationClassPostProcessor 分析(1)

概述

上一篇介绍了 AutowiredAnnotationBeanPostProcessor 后处理器中的determineCandidateConstructors。这个方法为 Spring 通过反射创建 Bean 实例是提供了候选的构造方法。

本文开始分析 AutowiredAnnotationBeanPostProcessor 中另一个比较重要的处理方法postProcessMergedBeanDefinition,它被调用的时机是在 Spring 通过反射创建 Bean 实例对象之后、属性装配之前。它的作用,是将类中标记了相关注解的注入点解析出来。

postProcessMergedBeanDefinition方法分析

进入postProcessMergedBeanDefinition方法的源码。

// org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition@OverridepublicvoidpostProcessMergedBeanDefinition(RootBeanDefinitionbeanDefinition, Class<?>beanType, StringbeanName) {
InjectionMetadatametadata=findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}

非常简洁,只有两行代码,分别调用了两个方法,我们逐个分析。

findAutowiringMetadata

先进入findAutowiringMetadata方法。

// org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadataprivateInjectionMetadatafindAutowiringMetadata(StringbeanName, Class<?>clazz, @NullablePropertyValuespvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.StringcacheKey= (StringUtils.hasLength(beanName) ?beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.InjectionMetadatametadata=this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata=this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata!=null) {
metadata.clear(pvs);
            }
metadata=buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
         }
      }
   }
returnmetadata;
}

从方法的名称中可以看出,它用来从类中找到自动注入元信息,最后返回一个 InjectionMetadata 类型的结果。

首先,会从缓存injectionMetadataCache中,查找注入元信息,如果获取到的注入元信息不需要被刷新,则直接返回。如果需要刷新,则通过buildAutowiringMetadata方法,构建元信息,并放入缓存中,再在方法末尾返回。

在这里,如果缓存中获取到的注入元信息为空,也属于需要刷新的情况。

接下来我们看buildAutowiringMetadata方法是如何构建这些元信息的。

image.png

这个方法比较长,我们逐步来分析。

if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
returnInjectionMetadata.EMPTY;
}

首先会判断当前的类型是不是 Java 内部的类型,如果是的话,则直接返回空的信息。

List<InjectionMetadata.InjectedElement>elements=newArrayList<>();
Class<?>targetClass=clazz;
do {
// 循环语句块}
while (targetClass!=null&&targetClass!=Object.class);

接着声明了一个 InjectionMetadata.InjectedElement 类型的空列表,以及一个表示类型的targetClass变量,初始值为方法参数传入的clazz。然后,在targetClass不为空且不是 Object 类型的情况下,执行do-while循环中的逻辑。

下面我们看do-while语句块中的内容。

finalList<InjectionMetadata.InjectedElement>currElements=newArrayList<>();
ReflectionUtils.doWithLocalFields(targetClass, field-> {
MergedAnnotation<?>ann=findAutowiredAnnotation(field);
if (ann!=null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static fields: "+field);
         }
return;
      }
booleanrequired=determineRequiredStatus(ann);
currElements.add(newAutowiredFieldElement(field, required));
   }
});

在循环语句块中,首先会遍历所有的被标记了@AutoWired@Value@Inject注解的字属性字段封装为 AutowiredFieldElement 对象,并添加到事先声明好的currElements集合中。需要注意的是,这里会跳过静态字段,因此静态字段是无法通过这几个注解进行注入的。

ReflectionUtils.doWithLocalMethods(targetClass, method-> {
MethodbridgedMethod=BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
   }
MergedAnnotation<?>ann=findAutowiredAnnotation(bridgedMethod);
if (ann!=null&&method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation is not supported on static methods: "+method);
         }
return;
      }
if (method.getParameterCount() ==0) {
if (logger.isInfoEnabled()) {
logger.info("Autowired annotation should only be used on methods with parameters: "+method);
         }
      }
booleanrequired=determineRequiredStatus(ann);
PropertyDescriptorpd=BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(newAutowiredMethodElement(method, required, pd));
   }
});

接着,再将添加了这些注解的非晶态方法,封装成 AutowiredMethodElement 对象,添加到currElements集合中。

elements.addAll(0, currElements);
targetClass=targetClass.getSuperclass();

在循环体的最后,将currElements中的内容都添加到elements集合中,然后将targetClass指向其父类。

这里结合循环体的while条件,可以知道,这个循环体要执行的工作是,以方法参数传入的类型开始遍历它的父类,一直到 Object 类之前,将这些类中声明的所有带注入相关注解的非静态的属性和方法都封装成 InjectionMetadata.InjectedElement 对象(属性对应 AutowiredFieldElement,方法对应 AutowiredMethodElement),然后统一放到elements集合中。

returnInjectionMetadata.forElements(elements, clazz);

方法的最后,将elements集合和类型信息封装成 InjectionMetadata 返回。

checkConfigMembers

回到postProcessMergedBeanDefinition方法,在得到 InjectionMetadata 类型的注解信息metadata后,会调用它的checkConfigMembers方法。

// org.springframework.beans.factory.annotation.InjectionMetadata#checkConfigMemberspublicvoidcheckConfigMembers(RootBeanDefinitionbeanDefinition) {
Set<InjectedElement>checkedElements=newLinkedHashSet<>(this.injectedElements.size());
for (InjectedElementelement : this.injectedElements) {
Membermember=element.getMember();
if (!beanDefinition.isExternallyManagedConfigMember(member)) {
beanDefinition.registerExternallyManagedConfigMember(member);
checkedElements.add(element);
if (logger.isTraceEnabled()) {
logger.trace("Registered injected element on class ["+this.targetClass.getName() +"]: "+element);
         }
      }
   }
this.checkedElements=checkedElements;
}

这个方法中,会对injectedElements成员变量集合进行遍历,这里 InjectionMetadata 的injectedElements的成员变量,其实就是上一部中创建 InjectionMetadata 对象时,传入的elements参数,也就是解析出的带注入注解的属性和方法封装后的对象集合。

循环中会判断当前遍历到的element,在 BeanDefinition 中是不是外部管理的配置成员,这里的成员指的就是属性或者方法,如果不是的话,则将其注册为外部管理的配置成员,也就是添加到 BeanDefinition 的externallyManagedConfigMembers集合中,然后再将element对象添加到事先声明好的 InjectedElement 集合checkedElements中。

方法的最后,将checkedElements集合赋值给 InjectionMetadata 的checkedElements成员变量。

总结

本文介绍了 AutowiredAnnotationBeanPostProcessor 后处理器中的postProcessMergedBeanDefinition方法,它的作用是在当前处理的类型中解析出需要注入的属性和方法。

下一篇讲分析 AutowiredAnnotationBeanPostProcessor 最后一个比较重要的处理方法postProcessProperties


目录
相关文章
|
2天前
|
Java 应用服务中间件 Nacos
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
37 0
|
2天前
|
监控 数据可视化 安全
一套成熟的Spring Cloud智慧工地平台源码,自主版权,开箱即用
这是一套基于Spring Cloud的智慧工地管理平台源码,具备自主版权,易于使用。平台运用现代技术如物联网、大数据等改进工地管理,服务包括建设各方,提供人员、车辆、视频监控等七大维度的管理。特色在于可视化管理、智能报警、移动办公和分布计算存储。功能涵盖劳务实名制管理、智能考勤、视频监控AI识别、危大工程监控、环境监测、材料管理和进度管理等,实现工地安全、高效的智慧化管理。
|
2天前
|
监控 Java 应用服务中间件
Spring Boot 源码面试知识点
【5月更文挑战第12天】Spring Boot 是一个强大且广泛使用的框架,旨在简化 Spring 应用程序的开发过程。深入了解 Spring Boot 的源码,有助于开发者更好地使用和定制这个框架。以下是一些关键的知识点:
20 6
|
2天前
|
Java 应用服务中间件 测试技术
深入探索Spring Boot Web应用源码及实战应用
【5月更文挑战第11天】本文将详细解析Spring Boot Web应用的源码架构,并通过一个实际案例,展示如何构建一个基于Spring Boot的Web应用。本文旨在帮助读者更好地理解Spring Boot的内部工作机制,以及如何利用这些机制优化自己的Web应用开发。
29 3
|
2天前
|
Java 关系型数据库 MySQL
【Java Spring开源项目】新蜂(NeeBee)商城项目运行、分析、总结
【Java Spring开源项目】新蜂(NeeBee)商城项目运行、分析、总结
13 4
|
2天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
16 1
|
2天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
103 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
2天前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。
|
2天前
|
存储 缓存 Java
【spring】06 循环依赖的分析与解决
【spring】06 循环依赖的分析与解决
9 1
|
2天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)