Spring源码阅读-ApplicationContext体系结构分析
- 继承层次图概览
- ConfigurableApplicationContext分析
- AbstractApplicationContext
- GenericApplicationContext
- GenericXmlApplicationContext
- StaticApplicationContext
- ResourceAdapterApplicationContext
- GenericGroovyApplicationContext
- AnnotationConfigApplicationContext
- AbstractRefreshableApplicationContext
- AbstractRefreshableConfigApplicationContext
- AbstractXmlApplicationContext
- FileSystemXmlApplicationContext
- ClassPathXmlApplicationContext
- WebApplicationContext
- 本文思维导图
上篇已经对IoC容器的设计进行了分析(Spring源码阅读-IoC容器解析),本篇将对ApplicationContext
经典的继承层次图进行详细的分析,在心中形成一个大致的印象,以便后面一步步调试源码的时候,不会太眼花缭乱。让我们一步步的前进吧...
继承层次图概览
使用IDEA的继承层次工具生成如下的图(选中ApplicationContext --> Ctrl+H):
(温馨提示:双击可查看高清大图)
从上图能很清楚的看出,ApplicationContext
的子接口分为两个部分:
ConfigurableApplicationContext
:大部分的应用上下文都实现了该接口WebApplicationContext
:在web应用程序中使用
ConfigurableApplicationContext分析
从上面的类的继承层次图能看到,ConfigurableApplicationContext
是比较上层的一个接口,该接口也是比较重要的一个接口,几乎所有的应用上下文都实现了该接口。该接口在ApplicationContext
的基础上提供了配置应用上下文的能力,此外提供了生命周期的控制能力。先看一下该接口的继承关系图(为了更加简洁,去掉了ApplicationContext
继承的接口):
(温馨提示:双击可查看高清大图)
Closeable
接口用于关闭应用上下文,释放所有的资源和锁,这也包括摧毁所有缓存的单例的bean,常见的try-with-resources用法如下,执行完try体中的代码后会自动的调用close
方法:
try (ConfigurableApplicationContext cac = ...) {
// 编写代码
...
}
Lifecycle
定义了启动/停止生命周期的控制的一些方法,其中的方法如下:
void start(); // 启动组件
void stop(); // 停止组件
boolean isRunning(); // 组件是否正在运行
接下来看一下ConfigurableApplicationContext
中的方法:
void setId(String id); // 设置应用上下文唯一的id
void setParent(ApplicationContext parent); // 设置应用程序上下文的父级
void setEnvironment(ConfigurableEnvironment environment); // 设置应用上下文的环境
ConfigurableEnvironment getEnvironment(); // 获取应用上下文的环境
// 添加一个新的BeanFactoryPostProcessor
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
// 添加应用程序监听器
void addApplicationListener(ApplicationListener<?> listener);
// 添加协议解析器,可能会覆盖默认的规则
void addProtocolResolver(ProtocolResolver resolver);
// 加载或者刷新配置
void refresh() throws BeansException, IllegalStateException;
// 向JVM runtime注册一个关闭钩子,JVM关闭时关闭这个上下文
void registerShutdownHook();
// 应用程序上问下是否是激活状态
boolean isActive();
// 获取应用上下文内部的bean factory
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
上面的这些方法基本上是提供了对某些特性的实现进行支撑的方法。
看了这么多方法,下面看一下ApplicationContext
的抽象的实现。
AbstractApplicationContext
AbstractApplicationContext
是ApplicationContext
接口的抽象实现,这个抽象类仅仅是实现了公共的上下文特性。这个抽象类使用了模板方法设计模式,需要具体的实现类去实现这些抽象的方法。对相关接口的实现如下:
ApplicationContext
接口的实现ConfigurableApplicationContext
接口的实现BeanFactory
接口的实现ListableBeanFactory
接口的实现HierarchicalBeanFactory
接口的实现MessageSource
接口的实现ResourcePatternResolver
的实现Lifecycle
接口的实现
本文不会详细的讲解这个类中的具体的实现细节,后面会有更加的详细的介绍。下面看下里面的抽象方法:
// 刷新BeanFactory,用于执行实际的配置加载,该方法在其他的初始化工作之前被refresh()方法调用
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
// 关闭BeanFactory,用于释放内部使用的BeanFactory·
protected abstract void closeBeanFactory();
// 获取内部使用的BeanFactory
public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
那么对需要实现的方法经过抽象后,只剩下少量的需要子类去实现的方法。
GenericApplicationContext
GenericApplicationContext
继承自AbstractApplicationContext
,是为通用目的设计的,它能加载各种配置文件,例如xml,properties等等。它的内部持有一个DefaultListableBeanFactory
的实例,实现了BeanDefinitionRegistry
接口,以便允许向其应用任何bean的定义的读取器。为了能够注册bean的定义,refresh()
只允许调用一次。常见的使用如下:
GenericApplicationContext ctx = new GenericApplicationContext();
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
xmlReader.loadBeanDefinitions(new ClassPathResource("applicationContext.xml"));
PropertiesBeanDefinitionReader propReader = new PropertiesBeanDefinitionReader(ctx);
propReader.loadBeanDefinitions(new ClassPathResource("otherBeans.properties"));
ctx.refresh();
MyBean myBean = (MyBean) ctx.getBean("myBean");
..
这个类的实现没有太多需要注意的地方,需要注意的有两点:
- 内部使用的
DefaultListableBeanFactory
的实例,提供了一些方法来配置该实例,例如是否允许bean定义的覆盖、是否允许bean之间的循环应用等等。 - 该类实现了
BeanDefinitionRegistry
,bean的定义注册。以便能通过BeanDefinitionReader
读取bean的配置,并注册。BeanDefinitionRegistry
接口的实现是直接使用内部的DefaultListableBeanFactory
的实例。
GenericXmlApplicationContext
GenericXmlApplicationContext
继承自GenericApplicationContext
,内置了对XML的支持。它非常的方便和灵活,是ClassPathXmlApplicationContext
和FileSystemXmlApplicationContext
的一种替代品。可以发现,它的内部有一个XmlBeanDefinitionReader
的实例,专门用于处理XML的配置。
StaticApplicationContext
StaticApplicationContext
继承自GenericApplicationContext
,主要用于编程式的注入bean和消息,而不是从外部的配置源读取bean的定义。主要是在测试时非常有用。通过阅读源代码可以看到,它的内部有一个StaticMessageSource
的实例,使用addMessage
方法添加消息。每次在编程式的注入bean时,都会创建一个GenericBeanDefinition
的实例。
ResourceAdapterApplicationContext
ResourceAdapterApplicationContext
继承自GenericApplicationContext
,是为JCA(J2EE Connector Architecture)的ResourceAdapter设计的,主要用于传递BootstrapContext
的实例给实现了BootstrapContextAware
接口且由spring管理的bean。覆盖了postProcessBeanFactory
方法来实现此功能。
GenericGroovyApplicationContext
GenericGroovyApplicationContext
继承自GenericApplicationContext
,实现了GroovyObject
接口以便能够使用点的语法(.xx)取代getBean
方法来获取bean。它主要用于Groovy bean的定义,与GenericXmlApplicationContext
一样,它也能解析XML格式定义的bean。内部使用GroovyBeanDefinitionReader
来完成groovy脚本和XML的解析。
AnnotationConfigApplicationContext
AnnotationConfigApplicationContext
继承自GenericApplicationContext
,提供了注解配置(例如:Configuration、Component、inject等)和类路径扫描(scan方法)的支持,可以使用register(Class<?>... annotatedClasses)
来注册一个一个的进行注册。实现了AnnotationConfigRegistry接口,来完成对注册配置的支持,只有两个方法:register和scan。内部使用AnnotatedBeanDefinitionReader
来完成注解配置的解析,使用ClassPathBeanDefinitionScanner
来完成类路径下的bean定义的扫描。
AbstractRefreshableApplicationContext
AbstractRefreshableApplicationContext
继承自AbstractApplicationContext
,支持多次进行刷新(多次调用refresh
方法),每次刷新时在内部创建一个新的bean工厂的实例。子类仅仅需要实现loadBeanDefinitions
方法,该方法在每次刷新时都会调用。
AbstractRefreshableConfigApplicationContext
AbstractRefreshableConfigApplicationContext
继承自AbstractRefreshableApplicationContext
,添加了对指定的配置文件路径的公共的处理,可以把他看作基于XML的应用上下文的基类。实现了如下的两个接口:
BeanNameAware
用于设置上下文的bean的名称,只有一个方法:void setBeanName(String name)
InitializingBean
用于上下文一切就绪后,如果还未刷新,那么就执行刷新操作,只有一个方法:void afterPropertiesSet()
AbstractXmlApplicationContext
AbstractXmlApplicationContext
继承自AbstractRefreshableConfigApplicationContext
,用于描绘包含能被XmlBeanDefinitionReader
所理解的bean定义的XML文档。子类只需要实现getConfigResources
和getConfigLocations
来提供配置文件资源。
FileSystemXmlApplicationContext
FileSystemXmlApplicationContext
继承自AbstractXmlApplicationContext
,用于解析文件系统中XML配置文件。文件的路径可以是具体的文件路径,例如:xxx/application.xml,也可以是ant风格的配置,例如:xxx/*-context.xml。
看下面的通过文件路径来获取资源的代码:
@Override
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
文件路径前面的/
会被去掉,无论是否路径前面是否加上/
,文件路径都会解析成相对路径,即基于JVM的当前工作路径。获取到的资源对象是FileSystemResource
,用于处理文件系统相关的资源。
ClassPathXmlApplicationContext
ClassPathXmlApplicationContext
继承自AbstractXmlApplicationContext
,和FileSystemXmlApplicationContext
类似,只不过ClassPathXmlApplicationContext
是用于处理类路径下的XML配置文件。文件的路径可以是具体的文件路径,例如:xxx/application.xml,也可以是ant风格的配置,例如:xxx/*-context.xml。
WebApplicationContext
该接口提供了在web应用中的配置,接口提供了一个ServletContext getServletContext()
用来获取ServletContext
对象。该接口会和ServletContext的一个属性进行绑定,这个属性就是ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
。定义了三个作用域的名称:SCOPE_REQUEST
,SCOPE_SESSION
,SCOPE_APPLICATION
。在工厂中的bean的名称:SERVLET_CONTEXT_BEAN_NAME
。ServletContext初始化参数名称:CONTEXT_PARAMETERS_BEAN_NAME
。在工厂中ServletContext属性值环境bean的名称:CONTEXT_ATTRIBUTES_BEAN_NAME
。
ConfigurableWebApplicationContext
ConfigurableWebApplicationContext
继承自WebApplicationContext
和ConfigurableApplicationContext
,提供了web应用上下文的可配置的能力。相关接口定义如下:
// 设置web应用上下文的ServletContext
void setServletContext(@Nullable ServletContext servletContext);
// 设置web应用上下文的ServletConfig
void setServletConfig(@Nullable ServletConfig servletConfig);
// 获取web应用上下文的ServletConfig
ServletConfig getServletConfig();
// 设置web应用上下文的命名空间
void setNamespace(@Nullable String namespace);
// 获取web应用上下文的命名空间
String getNamespace();
// 以初始化参数的形式设置web应用上下文的配置文件位置
void setConfigLocation(String configLocation);
// 设置web应用上下文的配置文件的位置
void setConfigLocations(String... configLocations);
// 获取web应用上下文的配置文件位置
String[] getConfigLocations();
上面的接口主要都是一些设置或者获取的方法,在web应用上下文中需要用到的一些东西。
GenericWebApplicationContext
GenericWebApplicationContext
继承自GenericApplicationContext
,实现了ConfigurableWebApplicationContext
和ThemeSource
接口。该类设计的目的不是在web.xml中进行声明式的安装,而是编程式的安装,例如使用WebApplicationInitializers
来构建内嵌的上下文。该接口在ConfigurableWebApplicationContext
的内容都是一个伪实现,调用其中的大多数方法都会抛出异常。你也许注意到了,他实现了ThemeSource
接口,那么他有什么用呢?字面意思是主题源,它设计的目的主要是用于消息的国际化。
StaticWebApplicationContext
StaticWebApplicationContext
继承自StaticApplicationContext
实现了ConfigurableWebApplicationContext
和ThemeSource
接口。该接口主要是用在测试的环境,不用于产品环境。
AbstractRefreshableWebApplicationContext
GenericWebApplicationContext
继承自AbstractRefreshableConfigApplicationContext
,实现了ConfigurableWebApplicationContext
和ThemeSource
接口,主要是用于web环境下。在web程序启动的时候,提供一个configLocations
属性,通过ConfigurableWebApplicationContext
接口来进行填充。子类化这个接口是很简单的,所有你所需要做的事情就是实现loadBeanDefinitions
方法,来实现你自己的bean定义的加载逻辑。
XmlWebApplicationContext
XmlWebApplicationContext
继承自AbstractRefreshableWebApplicationContext
,接受能被XmlBeanDefinitionReader
所理解的XML文档配置。对于根上下文,默认的配置文件路径是/WEB-INF/applicationContext.xml
,对于命名空间为test-servlet的上下文,默认的配置文件路径是/WEB-INF/test-servlet.xml
(就像servlet-name为test的DispatcherServlet实例)。
默认的配置文件路径处理的代码如下:
protected String[] getDefaultConfigLocations() {
if (getNamespace() != null) {
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
}
else {
return new String[] {DEFAULT_CONFIG_LOCATION};
}
}
和其他的上下文一样,bean定义的加载也是在void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
方法中,使用的是XmlBeanDefinitionReader
。
GroovyWebApplicationContext
GroovyWebApplicationContext
继承自AbstractRefreshableWebApplicationContext
,实现了GroovyObject
接口,接受能被GroovyBeanDefinitionReader
所理解的groovy bean定义脚本和XML文档配置。对于web环境,基本上是和GenericGroovyApplicationContext
是等价的。对于根上下文,默认的配置文件路径是/WEB-INF/applicationContext.groovy
,对于命名空间为test-servlet的上下文,默认的配置文件路径是/WEB-INF/test-servlet.xml
(就像servlet-name为test的DispatcherServlet实例)。
默认的配置文件路径处理的代码如下:
protected String[] getDefaultConfigLocations() {
if (getNamespace() != null) {
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
}
else {
return new String[] {DEFAULT_CONFIG_LOCATION};
}
}
和其他的上下文一样,bean定义的加载也是在void loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
方法中,使用的是GroovyBeanDefinitionReader
。
AnnotationConfigWebApplicationContext
AnnotationConfigWebApplicationContext
继承自AbstractRefreshableWebApplicationContext
,
接受注解的类作为输入(特殊的@Configuration注解类,一般的@Component注解类,与JSR-330兼容的javax.inject注解)。允许一个一个的注入,同样也能使用类路径扫描。对于web环境,基本上是和AnnotationConfigApplicationContext
等价的。使用AnnotatedBeanDefinitionReader
来对注解的bean进行处理,使用ClassPathBeanDefinitionScanner
来对类路径下的bean进行扫描。
部分代码如下:
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
AnnotatedBeanDefinitionReader reader = getAnnotatedBeanDefinitionReader(beanFactory);
ClassPathBeanDefinitionScanner scanner = getClassPathBeanDefinitionScanner(beanFactory);
...
if (!this.annotatedClasses.isEmpty()) {
....
reader.register(ClassUtils.toClassArray(this.annotatedClasses));
}
if (!this.basePackages.isEmpty()) {
....
scanner.scan(StringUtils.toStringArray(this.basePackages));
}
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
try {
Class<?> clazz = ClassUtils.forName(configLocation, getClassLoader());
if (logger.isTraceEnabled()) {
logger.trace("Registering [" + configLocation + "]");
}
reader.register(clazz);
}
catch (ClassNotFoundException ex) {
....
}
}
}
}
}
本文思维导图
Spring源码阅读-ApplicationContext体系结构分析的更多相关文章
- Spring源码阅读-BeanFactory体系结构分析
BeanFactory是Spring中非常重要的一个类,搞懂了它,你就知道了bean的初始化和摧毁过程,对于深入理解IOC有很大的帮助. BeanFactory体系结构 首先看一下使用IDEA生成的继 ...
- Spring源码分析——BeanFactory体系之抽象类、类分析(一)
上一篇介绍了BeanFactory体系的所有接口——Spring源码分析——BeanFactory体系之接口详细分析,本篇就接着介绍BeanFactory体系的抽象类和接口. 一.BeanFactor ...
- 初始化IoC容器(Spring源码阅读)
初始化IoC容器(Spring源码阅读) 我们到底能走多远系列(31) 扯淡: 有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的.未婚的你们,你们是怎么分配工资的? ...
- Spring源码阅读 之 配置的读取,解析
在上文中我们已经知道了Spring如何从我们给定的位置加载到配置文件,并将文件包装成一个Resource对象.这篇文章我们将要探讨的就是,如何从这个Resouce对象中加载到我们的容器?加载到容器后又 ...
- 搭建 Spring 源码阅读环境
前言 有一个Spring源码阅读环境是学习Spring的基础.笔者借鉴了网上很多搭建环境的方法,也尝试了很多,接下来总结两种个人认为比较简便实用的方法.读者可根据自己的需要自行选择. 方法一:搭建基础 ...
- Spring源码分析——BeanFactory体系之抽象类、类分析(二)
上一篇分析了BeanFactory体系的2个类,SimpleAliasRegistry和DefaultSingletonBeanRegistry——Spring源码分析——BeanFactory体系之 ...
- Bean实例化(Spring源码阅读)-我们到底能走多远系列(33)
我们到底能走多远系列(33) 扯淡: 各位: 命运就算颠沛流离 命运就算曲折离奇 命运就算恐吓着你做人没趣味 别流泪 心酸 更不应舍弃 ... 主题: Spring源码阅读还在继 ...
- Sping学习笔记(一)----Spring源码阅读环境的搭建
idea搭建spring源码阅读环境 安装gradle Github下载Spring源码 新建学习spring源码的项目 idea搭建spring源码阅读环境 安装gradle 在官网中下载gradl ...
- Spring源码阅读笔记02:IOC基本概念
上篇文章中我们介绍了准备Spring源码阅读环境的两种姿势,接下来,我们就要开始探寻这个著名框架背后的原理.Spring提供的最基本最底层的功能是bean容器,这其实是对IoC思想的应用,在学习Spr ...
随机推荐
- delphi备份恢复剪切板(使用了GlobalLock API函数和CopyMemory)
看了季世平老兄的C++代码后翻译过来的 unit clipbak; interface uses SysUtils, Classes, Clipbrd, Windows, Contnrs; type ...
- 采用WebService客户端调用WSDL/SOAP网络报错的解决办法
WebService接口是网络传输控制的重要途径,在Windows系统下运行客户端时,平时一直能正确运行,但某天可能突然会发生调用wsdl soap邮件标头无法识别等莫名其妙的错误提示,出现这种情况一 ...
- Qt4可以使用trUtf8函数,其内容可以是中文,也可以是\F硬编码
显示在textBrowser->setText 中文乱码 转成QObject::trUtf8即可. ui->textBrowser->setText((QObject::trUtf8 ...
- SQL Server 2017 SELECT…INTO 创建的新表指定到文件组
原文:SQL Server 2017 SELECT-INTO 创建的新表指定到文件组 SELECT-INTO 在 SQL Server 中也是常见的一个功能,过去用此方法创建的新表只能存储到默认的文件 ...
- 问题记录,Release模式和Debug运行效果不一样,Release必须加延时
这个程序大体是这样一个逻辑,通过win32程序与设备交互,主线程先向设备发送命令要求 循环验证 然后一个线程专门负责接收设备返回信息 两边通过全局变量的变化来交流,主线程通过接收线程收到的信息设置界面 ...
- 获取UWP配置文件中的版本信息
原文:获取UWP配置文件中的版本信息 在一般的软件中,我们都会显示当前软件的版本信息.以前作者都是在发版的时候修改一下UWP的配置文件中的版本信息和软件中的版本信息.但是每次这样很麻烦,有时间忘记修改 ...
- tf.nn.softmax & tf.nn.reduce_sum & tf.nn.softmax_cross_entropy_with_logits
tf.nn.softmax softmax是神经网络的最后一层将实数空间映射到概率空间的常用方法,公式如下: \[ softmax(x)_i=\frac{exp(x_i)}{\sum_jexp(x_j ...
- VC 调用 MinGW 生成的dll good
首先,如果dll 中导出了C++的类,那么就不要折腾了.不同的编译器编译出来的C++代码是不保证通用的.如果dll中只是一些C 函数,那么是可以互相调用的. MinGW 生成dll时即使生成了 .a ...
- LLVM和GCC的区别(LLVM提供了模块化的编译模块,非常有利于重用,以前的编译器都没有做到这一点)
最近在Mac OS X Mountain Lion下用Xcode进行开发,发现在编译选项里有如下所示的这两种编译器:一个是Apple LLVM compiler 4.2,另外一个是LLVM GCC 4 ...
- UISearchController 的大坑
UISearchBar+UISearchDisplayController这个组合的稳定性经过几次iOS版本迭代肯定不言而喻,但苹果爸爸就是任性的在iOS8.0中宣布弃用UISearchDi ...