Spring 源码阅读 12:BeanFactory 预处理

简介: 本文通过阅读源码,分析了 Spring 容器初始化过程中,在 BeanFactory 初始化完成之后,对 BeanFactory 进行预处理的过程。

image.png

基于 Spring Framework v5.2.6.RELEASE

前情提要

在之前的 ApplicationContext 初始化 Spring 容器 一文中,提到 AbstractApplicationContext#refresh 方法是一个非常重要的方法,它包含了 Spring 容器初始化的整个流程。在最近的一系列文章中,我通过阅读源码的方式,分析了上下文信息初始化、BeanFactory 初始化的过程。

本文接着分析AbstractApplicationContext#refresh中的下一个步骤,也就是下面这句方法调用:

// Prepare the bean factory for use in this context.// 对 BeanFactory 进行初始配置(预处理)prepareBeanFactory(beanFactory);

beanFactory 的预处理

直接找到这个方法的源码:

protectedvoidprepareBeanFactory(ConfigurableListableBeanFactorybeanFactory) {
// 第一部分// Tell the internal bean factory to use the context's class loader etc.beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(newStandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(newResourceEditorRegistrar(this, getEnvironment()));
// 第二部分// Configure the bean factory with context callbacks.beanFactory.addBeanPostProcessor(newApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// 第三部分// BeanFactory interface not registered as resolvable type in a plain factory.// MessageSource registered (and found for autowiring) as a bean.beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// 第四部分// Register early post-processor for detecting inner beans as ApplicationListeners.beanFactory.addBeanPostProcessor(newApplicationListenerDetector(this));
// 第五部分// Detect a LoadTimeWeaver and prepare for weaving, if found.if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(newLoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.beanFactory.setTempClassLoader(newContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
   }
// 第六部分// Register default environment beans.if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
   }
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
   }
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
   }
}

我将以上的方法体代码分成了六个部分,并在注释中标注了出来,先简单介绍这几部分的功能:

  1. beanFactory添加了几个扩展属性来增强它的功能。
  2. 处理了一些感知接口相关的逻辑。
  3. 注册了指定接口的依赖实现类。
  4. 注册了应用监听探测的后处理器。
  5. 如果容器中注册了loadTimeWeaver,为其添加后处理器和临时的类加载器。(这部分与 AspectJ 有关,本文暂不讨论)
  6. beanFactory中注册与环境相关的 Bean。

下面对关键部分进行深入分析。

添加 SpEL 解析器

第一部分的第一行是将当前的类加载器赋值给beanFactory中相应的成员变量,比较简单,直接看下一行:

beanFactory.setBeanExpressionResolver(newStandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));

这行代码给beanFactory设置了一个表达式解析器,找到 StandardBeanExpressionResolver 的构造方法如下:

publicStandardBeanExpressionResolver(@NullableClassLoaderbeanClassLoader) {
this.expressionParser=newSpelExpressionParser(newSpelParserConfiguration(null, beanClassLoader));
}

可以看到这里创建了一个 SpEL 解析器。除此之外,在 StandardBeanExpressionResolver 中还可以发现一下两个常量的定义:

publicstaticfinalStringDEFAULT_EXPRESSION_PREFIX="#{";
publicstaticfinalStringDEFAULT_EXPRESSION_SUFFIX="}";

它们分别是表达式的前缀和后缀。

对于 SpEL 表达式语言,Spring 开发者们都非常熟悉,不管是在 XML 配置中,还是在注解的属性中,都会用到用#{}修饰的各种表达式。通过 SpEL,可以很好地将 Spring 配置和我们开发的应用的配置分离开。Spring 对 SpEL 的支持,正是这里添加的。

但是,这里只是把 SpEL 的解析器给到了beanFactory中,具体对表达式的解析,是在 Bean 初始化的过程中,我们之后遇到了这部分逻辑在进行具体分析。

另外,SpEL 的具体使用方法是非常易于学习的,想学习的话直接找文档即可。

添加属性编辑器注册组件

接下来是这一行代码:

beanFactory.addPropertyEditorRegistrar(newResourceEditorRegistrar(this, getEnvironment()));

从方法的名称看,这里给beanFactory添加了一个属性编辑器的注册器,具体它是做什么的,先到 ResourceEditorRegistrar 中一探究竟。

publicResourceEditorRegistrar(ResourceLoaderresourceLoader, PropertyResolverpropertyResolver) {
this.resourceLoader=resourceLoader;
this.propertyResolver=propertyResolver;
}

构造方法很简单,就是把传入的参数赋值给相应的成员变量。除此之外,还可以发现,ResourceEditorRegistrar 类实现了 PropertyEditorRegistrar,其中定义了唯一的方法 registerCustomEditors,可以推断,这个方法非常重要,下面看一下 ResourceEditorRegistrar 中这个方法的具体实现:

publicvoidregisterCustomEditors(PropertyEditorRegistryregistry) {
ResourceEditorbaseEditor=newResourceEditor(this.resourceLoader, this.propertyResolver);
doRegisterEditor(registry, Resource.class, baseEditor);
doRegisterEditor(registry, ContextResource.class, baseEditor);
doRegisterEditor(registry, InputStream.class, newInputStreamEditor(baseEditor));
doRegisterEditor(registry, InputSource.class, newInputSourceEditor(baseEditor));
doRegisterEditor(registry, File.class, newFileEditor(baseEditor));
doRegisterEditor(registry, Path.class, newPathEditor(baseEditor));
doRegisterEditor(registry, Reader.class, newReaderEditor(baseEditor));
doRegisterEditor(registry, URL.class, newURLEditor(baseEditor));
ClassLoaderclassLoader=this.resourceLoader.getClassLoader();
doRegisterEditor(registry, URI.class, newURIEditor(classLoader));
doRegisterEditor(registry, Class.class, newClassEditor(classLoader));
doRegisterEditor(registry, Class[].class, newClassArrayEditor(classLoader));
if (this.resourceLoaderinstanceofResourcePatternResolver) {
doRegisterEditor(registry, Resource[].class,
newResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
   }
}

这里向registry中注册了大量的各种类型的xxxEditor,它们都是 PropertyEditorSupport 的子类。

因为涉及到的类太多,这里就不大量贴代码了,通过查看这些 XXXEditor 类型的源码,发现它们继承 PropertyEditorSupport 后,都重写了getAsTextsetAsText两个方法,这两个方法的作用分别是:

  • getAsText可以将成员变量value的值以字符串方式获取到
  • setAsText可以通过一个给定的字符串,给value赋值

也就是,不管成员变量value的类型是什么,都可以通过一个字符串来获取或者修改它。实际上,value成员变量是 Object 类型。结合上面代码中大量的doRegisterEditor(registry, XX.class, new XXEditor(baseEditor));可以分析出这些 XXEditor中value成员变量的类型。

那么,它们的作用到底是什么呢?

虽然从这里的代码看不出来它们的具体作用,但是,可以在这里先简单介绍一下。所谓的属性编辑器,就是将 XML 配置文件中通过字符串方式配置的 Bean 属性,转换成其对应的类型。

比如说,在 XML 文件中配置了一个bean标签,这个 Bean 的类定义中,有一个属性是 InputStream 类型的,或者是 Class 类型的,我们需要在 XML 配置文件中给这个属性配置一个值,但是又没有办法在 XML 中配置一个 InputStream 或者 Class 类型的对象,只能配置一个字符串内容。这个时候,属性解析器就帮我们把这个字符串,转换成对应的 InputStream 或者 Class 类型的对象。

这些属性编辑器同样是在 Bean 初始化的时候用到的。

添加感知接口的后处理器

下面看prepareBeanFactory方法的第二部分代码:

beanFactory.addBeanPostProcessor(newApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

如果你看过我之前的源码分析,第一行之后的所有代码,以及它的作用在之前的文章就介绍过,这里就不再过多介绍了,具体可以参考:Spring 源码阅读 05:忽略感知接口对应成员变量的自动装配

这里重点看第一行代码,Spring 在这里给beanFactory添加了一个 BeanPostProcessor,它的类型是 ApplicationContextAwareProcessor。

先看 BeanPostProcessor,它也是 Spring 框架中最重要的接口之一,定义如下:

publicinterfaceBeanPostProcessor {
@NullabledefaultObjectpostProcessBeforeInitialization(Objectbean, StringbeanName) throwsBeansException {
returnbean;
   }
@NullabledefaultObjectpostProcessAfterInitialization(Objectbean, StringbeanName) throwsBeansException {
returnbean;
   }
}

这里有两个方法,根据方法名可知,它们的作用是在 Bean 初始化之前和之后执行一些处理逻辑,默认实现为空。这也是 Spring 留给开发者的一个重要的扩展点。

关于 BeanPostProcessor,后面会详细分析,这里先简单介绍这些。

然后看 ApplicationContextAwareProcessor,它是 BeanPostProcessor 的实现类,所以是用来给 Bean 初始化添加处理逻辑,并且从它的名字可以看出,跟我们之前提到的感知接口有关。具体看源码:

@Override@NullablepublicObjectpostProcessBeforeInitialization(Objectbean, StringbeanName) throwsBeansException {
if (!(beaninstanceofEnvironmentAware||beaninstanceofEmbeddedValueResolverAware||beaninstanceofResourceLoaderAware||beaninstanceofApplicationEventPublisherAware||beaninstanceofMessageSourceAware||beaninstanceofApplicationContextAware)){
returnbean;
   }
AccessControlContextacc=null;
if (System.getSecurityManager() !=null) {
acc=this.applicationContext.getBeanFactory().getAccessControlContext();
   }
if (acc!=null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
returnnull;
      }, acc);
   }
else {
invokeAwareInterfaces(bean);
   }
returnbean;
}

它实现了 BeanPostProcessor 的postProcessBeforeInitialization方法,也就是给 Bean 初始化之前添加了处理逻辑:判断当前处理的 Bean 是不是实现了 EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware 这些接口。如果没有,直接返回,如果有,则进行之后的处理。

后面的处理方法中,主要关注invokeAwareInterfaces方法:

privatevoidinvokeAwareInterfaces(Objectbean) {
if (beaninstanceofEnvironmentAware) {
      ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
   }
if (beaninstanceofEmbeddedValueResolverAware) {
      ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
   }
if (beaninstanceofResourceLoaderAware) {
      ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
   }
if (beaninstanceofApplicationEventPublisherAware) {
      ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
   }
if (beaninstanceofMessageSourceAware) {
      ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
   }
if (beaninstanceofApplicationContextAware) {
      ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
   }
}

其实就是针对实现了这些感知接口的 Bean,调用它们的 setter 方法,给感知接口对应的属性赋值。

再次提醒,这里只是把处理逻辑添加到了beanFactory,并没有真正被执行,只有在 Bean 初始化的时候才会执行。

注册指定接口的依赖实现类

我们要分析的第三部分代码如下:

beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);

这里给几个类型或接口,注册了固定的对象,保证了在 Spring 的容器中,这几个指定类型的 Bean,都是这里设定的对象。这里主要是为了安全考虑,如果开发者定义了一个类,实现了 BeanFactory 并注册到了 Spring 的容器中,当其他的类获取 BeanFactory 类型的 Bean 的时候,仍然能从容器中取出了正确的 Bean。

其他

第四部分,添加了类行为 ApplicationListenerDetector 的 BeanPostProcessor,可以将实现了 ApplicationListener 的 Bean 添加到监听器列表中。

第五部分,是一些 AspectJ 相关的逻辑,这部分不在本文的讨论范畴。

第六部分,向beanFactory中注册了环境相关的 Bean。注意这里是注册了一个单例的 Bean,不是 BeanDefinition。

后续

这一篇分析了beanFactory预处理的一些关键步骤,之后会到 BeanFactoryPostProcessor 相关的内容。(注意区分 BeanFactoryPostProcessor 和 BeanPostProcessor)

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