Spring源码分析(十九)容器的功能扩展概览
摘要: 本文结合《Spring源码深度解析》来分析Spring 5.0.6版本的源代码。若有描述错误之处,欢迎指正。
经过前面几章的分析,相信大家已经对 Spring 中的容器功能有了简单的了解,在前面的章节中我们一直以 BeanFactory 接口以及它的默认实现类 XmlBeanFactory 为例进行分析,但是, Spring还提供了另一个接口 ApplicationContext,用于扩展 BeanFactory 现有的功能。
ApplicationContext 和 BeanFactory两者都是用于加载 Bean 的,但是相比之下,ApplicationContext 提供了更多的扩展功能,简单一点说: ApplicationContext 包含 BeanFactory 的所有功能。通常建议比 BeanFactory 优先,除非在一些限制的场合,比如字节长度对内存有很大的影响时 ( Applet )。绝大多数“典型的”企业应用 和系统, ApplicationContext 就是你需要使用的。
那么究竟 ApplicationContext 比BeanFactory 多出了哪些功能呢?还需要我们进一步的探索。首先我们来看看使用两个不同的类去加载配置文件在写法上的不同。
- 使用 BeanFactory 方式加载 XML。
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring/spring-test.xml"));
- 使用 ApplicationContext 方式加载 XML。
ApplicationContext context = new ClassPathXmlApplicationContext("spring/lookup-test.xml");
同样,我们还是以ClassPathXmlApplicationContext作为切入点,开始对整体功能进行分析。
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
} public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException { super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
设置路径是必不可少的步骤,ClassPathXmlApplicationContext可以将配置文件路径以数组的方式传入,ClassPathXmlApplicationContext可以对数组进行解析并进行加载。而对于解析及功能实现都在refresh()中实现。
一、设置配置路径
ClassPathXmlApplicationContext支持多个配置文件以数组的方式同时传入:
/**
* Set the config locations for this application context.
* <p>If not set, the implementation may use a default as appropriate.
*/
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] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
此函数主要用于解析给定的路径数据,当然,如果数组中包含特殊符号,如${var},那么在resolvePath中会搜索匹配的系统变量并替换。
二、扩展功能
设置了路径之后,便可以根据路径做配置文件的解析以及各种功能的实现了。可以说 refresh 函数中包含了几乎 ApnlicationContext 中提供的全部功能,而且此函数中逻辑非常清晰明了,使我们很容易分析对应的层次及逻辑。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
// 准备刷新的上下文环境
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
// 初始化BeanFactory,并进行XML文件读取
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
// 对BeanFactory进行各种功能填充
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses.
// 子类覆盖方法做额外的处理
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
// 激活各种BeanFactory处理器
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
// 注册拦截Bean创建Bean处理器,这里只是注册,真正的调用是在getBean的时候
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
// 为上下文初始化Message源,即不同语言的消息体,国际化处理
initMessageSource(); // Initialize event multicaster for this context.
// 初始化应用消息广播器,并放入applicationEventMulticaster bean中
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
// 留给子类来初始化其他的Bean
onRefresh(); // Check for listener beans and register them.
// 在所有注册的bean中查找Listener bean,注册到消息广播器中
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
// 初始化剩下的单实例(非惰性的)
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
// 完成刷新过程,通知生命周期处理器 lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} // Destroy already created singletons to avoid dangling resources.
destroyBeans(); // Reset 'active' flag.
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
下面概括一下ClassPathXmlApplicationContext初始化的步骤,并从中解释一下它为我们提供的功能。
(1)初始化前的准备工作,例如对系统属性或者环境变最进行准备及验证。
在某种情况下项目的使用需要读取某些系统变量,而这个变量的设置很可能会影响着系统的正确性,那么ClassPathXmlApplicationContext为我们提供的这个准备函数就显得非常必要,它可以在Spring 启动的时候提前对必须的变量进行存在性验证。
( 2 )初始化BeanFactory,并进行XML文件读取。
之前有提到 ClassPathXmlApplicationContext包含着 BeanFactory所提供的一切特征,那么在这一步骤中将会复用BeanFactory 中的配置文件读取解析及其他功能,这一步之后, ClassPathXmlApplicationContext实际上就已经包含了 BeanFactory所提供的功能,也就是可以进行 Bean 的提取等基础操作了。
(3)对 BeanFactory 进行各种功能填充。
@Qualifier与@Autowired 应该是大家非常熟悉的注解,那么这两个注解正是在这一步骤中增加的支持。
(4)子类覆盖方法做额外的处理。
Spring 之所以强大,为世人所推崇,除了它功能上为大家提供了便利外,还有一方面是它的完美架构,开放式的架构让使用它的程序员很容易根据业务需要扩展已经存在的功能。这种开放式的设计在 Spring 中随处可见,例如在本例中就提供了一个空的函数实现 postProcessBeanFactory 来方便程序员在业务上做进一步扩展。
(5)激活各种BeanFactory处理器。
(6)注册拦截 bean 创建的 bean 处理器,这甩只是注册,真正的调用是在 getBean 时候。
(7)为上下文初始化 Message 源,即对不同语言的消息体进行国际化处理。
(8)初始化应用消息广播器,并放入applicationEventMulticaster bean 中。
(9)留给子类来初始化其他的 bean 。
(10)在所有注册的bean中查找Listener bean,注册到消息广播器中。
(11)初始化剩下的单实例(非惰性的)。
(12)完成刷新过程,通知生命周期处理器 lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人。
Spring源码分析(十九)容器的功能扩展概览的更多相关文章
- 【spring源码分析】IOC容器初始化(十)
前言:前文[spring源码分析]IOC容器初始化(九)中分析了AbstractAutowireCapableBeanFactory#createBeanInstance方法中通过工厂方法创建bean ...
- 【spring源码分析】IOC容器初始化(总结)
前言:在经过前面十二篇文章的分析,对bean的加载流程大致梳理清楚了.因为内容过多,因此需要进行一个小总结. 经过前面十二篇文章的漫长分析,终于将xml配置文件中的bean,转换成我们实际所需要的真正 ...
- 【spring源码分析】IOC容器初始化(二)
前言:在[spring源码分析]IOC容器初始化(一)文末中已经提出loadBeanDefinitions(DefaultListableBeanFactory)的重要性,本文将以此为切入点继续分析. ...
- 【spring源码分析】IOC容器初始化(三)
前言:在[spring源码分析]IOC容器初始化(二)中已经得到了XML配置文件的Document实例,下面分析bean的注册过程. XmlBeanDefinitionReader#registerB ...
- 【spring源码分析】IOC容器初始化(四)
前言:在[spring源码分析]IOC容器初始化(三)中已经分析了BeanDefinition注册之前的一些准备工作,下面将进入BeanDefinition注册的核心流程. //DefaultBean ...
- 【spring源码分析】IOC容器初始化(七)
前言:在[spring源码分析]IOC容器初始化(六)中分析了从单例缓存中加载bean对象,由于篇幅原因其核心函数 FactoryBeanRegistrySupport#getObjectFromFa ...
- 【spring源码分析】IOC容器初始化——查漏补缺(一)
前言:在[spring源码分析]IOC容器初始化(十一)中提到了初始化bean的三个步骤: 激活Aware方法. 后置处理器应用(before/after). 激活自定义的init方法. 这里我们就来 ...
- Spring源码分析专题 —— IOC容器启动过程(上篇)
声明 1.建议先阅读<Spring源码分析专题 -- 阅读指引> 2.强烈建议阅读过程中要参照调用过程图,每篇都有其对应的调用过程图 3.写文不易,转载请标明出处 前言 关于 IOC 容器 ...
- 【spring源码分析】IOC容器初始化——查漏补缺(五)
前言:我们知道在Spring中经常使用配置文件的形式对进行属性的赋值,那配置文件的值是怎么赋值到属性上的呢,本文将对其进行分析. 首先了解一个类:PropertySourcesPlaceholderC ...
- 【spring源码分析】IOC容器初始化——查漏补缺(二)
前言:在[spring源码分析]IOC容器初始化(八)中多次提到了前置处理与后置处理,本篇文章针对此问题进行分析.Spring对前置处理或后置处理主要通过BeanPostProcessor进行实现. ...
随机推荐
- BZOJ1856: [Scoi2010]字符串(组合数)
题意 题目链接 Sol \(30 \%\)dp: \(f[i][j]\)表示放了\(i\)个\(1\)和\(j\)个\(0\)的不合法方案 f[0][0] = 1; cin >> N &g ...
- JavaScript standard 代码规范的全文
这是 JavaScript standard 代码规范的全文. 掌握本规范的最好方法是安装并在自己的代码中使用它. 细则 使用两个空格进行缩进. eslint: indent function hel ...
- Checkpoint not complete
Checkpoint not complete Current log# 2 seq# 876 mem# 0: +DATA/tykfdb/onlinelog/group_2.258.983586883 ...
- javaSE——简单的文件浏览器
import java.io.File; public class Demo02 { public static void main(String[]args){ File file = new Fi ...
- 在RecyclerView列表滚动的时候显示或者隐藏Toolbar
先看一下效果: 本文将讲解如何实现类似于Google+应用中,当列表滚动的时候,ToolBar(以及悬浮操作按钮)的显示与隐藏(向下滚动隐藏,向上滚动显示),这种效果在Material Design ...
- Angularjs 表格插件的使用
对于相关的table组件可以使用:UI Grid (ng-grid),ng-table,smart table,Angular-Datatables,tablelite,kendo-ui中的grid. ...
- spring boot(16)-mail发邮件
上一篇讲了如何处理异常,并且异常最终会写入日志.但是日志是写在服务器上的,我们无法及时知道.如果能够将异常发送到邮箱,我们可以在第一时间发现这个异常.当然,除此以外,还可以用来给用户发验证码以及各种离 ...
- numpy深入理解剖析
http://www.scipy-lectures.org/advanced/advanced_numpy/index.html
- Java 两个日期间的天数计算
在Java中计算两个日期间的天数,大致有2种方法:一是使用原生JDK进行计算,在JDK8中提供了更为直接和完善的方法:二是使用第三方库. 1.使用原生的JDK private static long ...
- REST Framework 的用户认证组件
用户认证流程: 我们要知道这个流程是怎么走的? 认证之后做的什么? 怎么认证?这三个条件 认证流程:就是使用BaseAuthentication这个模块来做认证,判断你登陆成功传递过来的随机字符串是否 ...