摘要: 本文结合《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源码分析(十九)容器的功能扩展概览的更多相关文章

  1. 【spring源码分析】IOC容器初始化(十)

    前言:前文[spring源码分析]IOC容器初始化(九)中分析了AbstractAutowireCapableBeanFactory#createBeanInstance方法中通过工厂方法创建bean ...

  2. 【spring源码分析】IOC容器初始化(总结)

    前言:在经过前面十二篇文章的分析,对bean的加载流程大致梳理清楚了.因为内容过多,因此需要进行一个小总结. 经过前面十二篇文章的漫长分析,终于将xml配置文件中的bean,转换成我们实际所需要的真正 ...

  3. 【spring源码分析】IOC容器初始化(二)

    前言:在[spring源码分析]IOC容器初始化(一)文末中已经提出loadBeanDefinitions(DefaultListableBeanFactory)的重要性,本文将以此为切入点继续分析. ...

  4. 【spring源码分析】IOC容器初始化(三)

    前言:在[spring源码分析]IOC容器初始化(二)中已经得到了XML配置文件的Document实例,下面分析bean的注册过程. XmlBeanDefinitionReader#registerB ...

  5. 【spring源码分析】IOC容器初始化(四)

    前言:在[spring源码分析]IOC容器初始化(三)中已经分析了BeanDefinition注册之前的一些准备工作,下面将进入BeanDefinition注册的核心流程. //DefaultBean ...

  6. 【spring源码分析】IOC容器初始化(七)

    前言:在[spring源码分析]IOC容器初始化(六)中分析了从单例缓存中加载bean对象,由于篇幅原因其核心函数 FactoryBeanRegistrySupport#getObjectFromFa ...

  7. 【spring源码分析】IOC容器初始化——查漏补缺(一)

    前言:在[spring源码分析]IOC容器初始化(十一)中提到了初始化bean的三个步骤: 激活Aware方法. 后置处理器应用(before/after). 激活自定义的init方法. 这里我们就来 ...

  8. Spring源码分析专题 —— IOC容器启动过程(上篇)

    声明 1.建议先阅读<Spring源码分析专题 -- 阅读指引> 2.强烈建议阅读过程中要参照调用过程图,每篇都有其对应的调用过程图 3.写文不易,转载请标明出处 前言 关于 IOC 容器 ...

  9. 【spring源码分析】IOC容器初始化——查漏补缺(五)

    前言:我们知道在Spring中经常使用配置文件的形式对进行属性的赋值,那配置文件的值是怎么赋值到属性上的呢,本文将对其进行分析. 首先了解一个类:PropertySourcesPlaceholderC ...

  10. 【spring源码分析】IOC容器初始化——查漏补缺(二)

    前言:在[spring源码分析]IOC容器初始化(八)中多次提到了前置处理与后置处理,本篇文章针对此问题进行分析.Spring对前置处理或后置处理主要通过BeanPostProcessor进行实现. ...

随机推荐

  1. Review——JS的异步与同步

    一.概念 同步(synchronous):指在js的主线程上,所有任务被依次执行: 异步(asynchronous):指任务不进入主线程,进入任务队列(task):当“任务队列”通知主线程,异步任务才 ...

  2. 【代码笔记】iOS-JASidePanelsDemo(侧滑)

    一,效果图. 二,工程图. 三,代码. AppDelegate.h #import <UIKit/UIKit.h> @class JASidePanelController; @inter ...

  3. JavaScript之DOM操作(二)

    这一篇写写常用并且通用的操作节点的一些方法. 1.节点方法 1>appendChild() 向childNodes列表的末尾添加一个节点,语法 var aChild = element.appe ...

  4. Spring Boot—19Session

    pom.xm <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...

  5. Intel超低功耗CPU的一些信息

    2015年底: Intel Braswell是专门针对超低功耗移动和桌面平台的一个家族,现有赛扬N3000/N3050/N3150.奔腾N3700四款型号,其中N300的热设计功耗只有区区4W,其他三 ...

  6. ArcGIS for JavaScript 关于路径开发的一些记录(一)

    今年毕业,进入公司的第一个任务就是单独负责一个项目的地图模块,用ArcGIS API for JavaScript来开发web地图.花了大概一个礼拜的时间学会了安装和搭建ArcGIS Server和A ...

  7. 润乾在jetty应用服务器下的JNDI配置一

     一. 此处绑定的数据源是以 DBCP 为实现.首先必须将数据库驱动(这里用了MYSQL数据库)和DBCP所需要的 Jar 包复制到 Jetty 根目录的 lib 目录下.DBCP主要需要以下3个 ...

  8. h5调用微信分享

    https://blog.csdn.net/qq_39562787/article/details/79217386

  9. C#关于微信昵称中存在的表情图标乱码解决

    //在获取微信用户信息时加密保存到数据库 System.Web.HttpUtility.UrlEncode("需要加密的字段") //前端在展示是解码 <script typ ...

  10. NSOperation的使用细节 [2]

    NSOperation的使用细节 [2] 这一节我们来写自定义nonconcurrent的operation,自定义nonconcurrent的operation很简单,重写main方法,之后处理好c ...