Spring 源码学习~12容器的拓展功能简介
Posted 戴泽supp
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring 源码学习~12容器的拓展功能简介相关的知识,希望对你有一定的参考价值。
容器的拓展功能简介
一、简介
ApplicationContext 和 BeanFactory 两者都是用于加载 bean 的,相比之下,ApplicationContext 除了包含 BeanFactory 的所有功能之外,还提供了更多的扩展功能。
那么 ApplicationContext 比 BeanFactory 多出了哪些功能呢?
写法上的不同:
BeanFactory
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring/app-context.xml"));
TestBean testBean = (TestBean) beanFactory.getBean("testBean");
System.out.println(testBean);
ApplicationContext
ApplicationContext bf = new ClassPathXmlApplicationContext("spring/app-context.xml");
TestBean testBean1 = (TestBean)bf.getBean("testBean");
System.out.println(testBean1);
下面我们以 ClassPathXmlApplicationContext 为切入点,来对整体功能进行分析。
public ClassPathXmlApplicationContext(String configLocation) throws BeansException
this(new String[]configLocation, true, (ApplicationContext)null);
public ClassPathXmlApplicationContext(String... configLocations) throws BeansException
this(configLocations, true, (ApplicationContext)null);
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException
super(parent);
this.setConfigLocations(configLocations);
if (refresh)
this.refresh();
可以看到首先是设置路径,然后解析和功能的实现都在 refresh() 中实现。
二、设置配置路径
ClassPathXmlApplicationContext 支持多个配置文件以数组的形式同时传入:
public void setConfigLocations(@Nullable String... locations)
if (locations != null)
Assert.noNullElements(locations, "Config locations must not be null");
this.configLocations = new String[locations.length];
for(int i = 0; i < locations.length; ++i)
//解析给定路径
this.configLocations[i] = this.resolvePath(locations[i]).trim();
else
this.configLocations = null;
如果数组中包含特殊符号,如$var,那么在 resolvePath 中会搜寻匹配的系统变量进行替换。
三、核心方法 refresh 简介
设置路径之后,便可以根据路径对配置文件进行解析,以及各种功能的实现了,我们来分析 refresh() 函数。
public void refresh() throws BeansException, IllegalStateException
synchronized(this.startupShutdownMonitor)
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
//准备刷新的上下文环境
this.prepareRefresh();
//初始化 beanFactory,并进行 XML 文件读取
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
//对 beanFactory 进行各种功能填充
this.prepareBeanFactory(beanFactory);
try
//子类覆盖方法做额外处理
this.postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
//激活各种 BeanFactory 处理器
this.invokeBeanFactoryPostProcessors(beanFactory);
//注册拦截 Bean 创建时的后置处理器,这里只是注册,真正的调用是在 getBean 的时候
this.registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
//为上下文初始化 Message 源,即不同语言的消息体,国际化处理
this.initMessageSource();
//初始化应用消息广播器,并放入 “ApplicationEventMulticaster” bean 中
this.initApplicationEventMulticaster();
//留给子类来初始化其他的 bean
this.onRefresh();
//在所有注册的 bean 中查找 Listener bean,注册到消息广播器中
this.registerListeners();
//初始化剩下的单实例(非惰性的)
this.finishBeanFactoryInitialization(beanFactory);
//完成刷新过程,通知声明周期处理器 lifecycleProcessor 刷新过程,同时发出
//ContextRefreshEvent 通知别人
this.finishRefresh();
catch (BeansException var10)
if (this.logger.isWarnEnabled())
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
//出异常,销毁所有 bean
this.destroyBeans();
//取消刷新
this.cancelRefresh(var10);
throw var10;
finally
//重置缓存
this.resetCommonCaches();
contextRefresh.end();
下面概括一下 ClassPathXmlApplicationContext 初始化的步骤:
- 1、初始化前的准备工作。如:对系统属性或环境变量进行准备及验证
- 2、初始化 BeanFactory,并进行 XML 文件读取。
- 3、对 BeanFactory 进行各种功能填充。
- @Qualifier 和 @Autowired 正是在这一步骤中增加的支持。
- 4、子类覆盖方法做额外处理。
- Spring 中随处可见到一些可拓展的空函数,如 postProcessBeanFactory 函数可以方便程序员在业务上做进一步的拓展。
- 5、激活各种 BeanFactory 处理器。
- 6、注册拦截 bean 创建的 bean 处理器,这里只是注册,真正的调用是在 getBean 的时候
- 7、为上下文初始化 Message 源,即不同语言的消息体,国际化处理
- 8、初始化应用消息广播器,并放入 “ApplicationEventMulticaster” bean 中
- 9、留给子类来初始化其他的 bean
- 10、在所有注册的 bean 中查找 Listener bean,注册到消息广播器中
- 11、初始化剩下的单实例(非惰性的)
- 12、完成刷新过程,通知声明周期处理器 lifecycleProcessor 刷新过程,同时发出 ContextRefreshEvent 通知别人
接下来具体分析下每个步骤的具体功能。
四、环境准备
prepareRefresh 函数主要是做些准备工作,例如对系统属性及环境变量的初始化及验证。
protected void prepareRefresh()
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (this.logger.isDebugEnabled())
if (this.logger.isTraceEnabled())
this.logger.trace("Refreshing " + this);
else
this.logger.debug("Refreshing " + this.getDisplayName());
//留给子类覆盖
this.initPropertySources();
//验证需要的属性文件是否已经放入到环境中
this.getEnvironment().validateRequiredProperties();
if (this.earlyApplicationListeners == null)
this.earlyApplicationListeners = new LinkedHashSet(this.applicationListeners);
else
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
this.earlyApplicationEvents = new LinkedHashSet();
咋一看这个步骤好像没什么作用,因为 initPropertySources() 是空函数,this.getEnvironment().validateRequiredProperties() 也因为没有验证的属性而没有做任何处理。但是如果这个函数用好了,作用还是很大的,我们需要来探讨下怎么使用它?首先我们研究下这个函数里面各个步骤的作用。
- 1、initPropertySources 符合开放式结构设计,给用户最大扩展 Spring 的能力。用户可以根据自身的需要重写 initPropertySources 方法,并在方法中进行个性化的属性处理及设置。
- 2、validateRequiredProperties 是对属性进行验证。
我们举例说明 validateRequiredProperties 的验证功能。
**场景描述:**工程运行过程中用到的某个设置(例如 VAR)是从系统环境变量中取得的,而如果用户没有在系统环境变量中配置这个参数,那么工程不会工作。这样的场景下,我们可以自定义类:
package com.luo.spring.guides.helloworld.applicationcontext.validate.required;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author : archer
* @date : Created in 2022/11/29 16:03
* @description :
*/
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext
public MyClassPathXmlApplicationContext(String... configLocations)
super(configLocations);
@Override
protected void initPropertySources()
//添加验证要求
getEnvironment().setRequiredProperties("VAR");
测试
package com.luo.spring.guides.helloworld.applicationcontext.validate.required;
import com.luo.spring.guides.helloworld.common.TestBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author : archer
* @date : Created in 2022/11/29 16:07
* @description :
*/
public class Main
public static void main(String[] args)
ApplicationContext bf = new MyClassPathXmlApplicationContext("spring/app-context.xml");
TestBean testBean1 = (TestBean)bf.getBean("testBean");
System.out.println(testBean1);
//输出 Exception in thread "main" org.springframework.core.env.MissingRequiredPropertiesException: The following properties were declared as required but could not be resolved: [VAR]
五、加载 BeanFactory
obtainFreshBeanFactory 是实现 BeanFactory 的地方,经过此函数后,ApplicationContext 就包含了 BeanFactory 的全部功能了。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory()
//初始化 BeanFactory ,并进行 XML 文件读取,并将得到的 BeanFactory 记录到当前实体的属性中
this.refreshBeanFactory();
// 返回 BeanFactory
return this.getBeanFactory();
protected final void refreshBeanFactory() throws BeansException
if (this.hasBeanFactory())
this.destroyBeans();
this.closeBeanFactory();
try
//创建 DefaultListableBeanFactory
DefaultListableBeanFactory beanFactory = this.createBeanFactory();
//为了序列化指定 id,如果需要的话,让 BeanFactory 从 id 反序列化到 BeanFactory 对象
beanFactory.setSerializationId(this.getId());
//定制 BeanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象、循环依赖
this.customizeBeanFactory(beanFactory);
//初始化 DocumentReader,并进行 XML 文件读取及解析
this.loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
catch (IOException var2)
throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var2);
我们详细分析上面每个步骤。
- 1、创建 DefaultListableBeanFactory。
- 之前使用的 XmlBeanFactory 继承自 DefaultListableBeanFactory,并提供了 XmlBeanDefinitionReader 类型的 reader 属性,所以 DefaultListableBeanFactory 是基础,这里就首先实例化。
- 2、指定序列化 ID
- 3、定制 BeanFactory。
- 4、加载 BeanDefinition。
- 5、使用全局变量记录 BeanFactory 类实例。
1、定制 BeanFactory
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory)
//如果属性 allowBeanDefinitionOverriding 不为 null,设置给 beanFactory 对象相应属性,
//此属性含义:是否允许覆盖同名称的不同定义的对象
if (this.allowBeanDefinitionOverriding != null)
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
//如果属性 allowCircularReferences 不为 null,设置给 beanFactory 对象相应属性,
//此属性含义:是否允许 bean 之间存在循环依赖
if (this.allowCircularReferences != null)
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
想要实现相应的设置,可以自己在实现的子类中使用方法覆盖:
package com.luo.spring.guides.helloworld.applicationcontext.validate.required;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author : archer
* @date : Created in 2022/11/29 16:03
* @description :
*/
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext
public MyClassPathXmlApplicationContext(String... configLocations)
super(configLocations);
@Override
protected void initPropertySources()
//添加验证要求
getEnvironment().setRequiredProperties("VAR");
@Override
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory)
super.setAllowBeanDefinitionOverriding(false);
super.setAllowCircularReferences(false);
super.customizeBeanFactory(beanFactory);
2、加载 BeanDefinition
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
//为指定 BeanFactory 创建 XmlBeanDefinitionReader
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
//对 beanDefinitionReader 进行环境变量的设置
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
//对 BeanDefinitionReader 进行设置,可以被子类覆盖
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
在初始化 DefaultListableBeanFactory 和 XmlBeanDefinitionReader 后,就可以进行配置文件的读取了。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException
Resource[] configResources = getConfigResources();
if (configResources != null)
reader.loadBeanDefinitions(configResources);
String[] configLocations = getConfigLocations();
if (configLocations != null)
reader.loadBeanDefinitions(configLocations);
到 XmlBeanDefinitionReader#loadBeanDefinitions 这一步,就到之前我们分析 XmlBeanFactory 解析配置文件的步骤了,这里不再赘述。
六、功能扩展
进入 prepareBeanFactory 前,Spring 已经完成了对配置的解析,而 ApplicationContext 在功能上的扩展也由此展开。
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory)
// Tell the internal bean factory to use the context's class loader etc.
//设置 beanFactory 的 classLoader 为当前 context 的classLoader
beanFactory.setBeanClassLoader(getClassLoader());
if (!shouldIgnoreSpel)
//设置 beanFactory 的表达式语言处理器,Spring 3 增加了表达式语言的支持
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
//为 beanFactory 增加一个默认的 PropertyEditor,这个主要是对 bean 的属性等设置管理的一个工具
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
//增加 BeanPostProcessor
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(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.ignoreDependencyInterface(ApplicationStartupAware.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.
//将用于检测内部 bean 的早期后处理器注册为 ApplicationListeners。
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
//检测 LoadTimeWeaver 和 增加对 AspectJ 的支持
if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME))
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFac以上是关于Spring 源码学习~12容器的拓展功能简介的主要内容,如果未能解决你的问题,请参考以下文章
spring源码-BeanFactoryPostProcessor-3.2
Spring MVC 初始化源码—ContextLoaderListener监听器与根上下文容器的初始化