spring - IOC

IOC体系结构

BeanFactory


ClassPathXmlApplicationContext 使用代码执行xml文件的类,是一个入口方法
通过这个文件向上找,会发现最终会继承ListableBeanFactory、HierarchicalBeanFactory等等一些实现了BeanFactory的抽象类

所以通过这个入口,我们发现BeanFactory是spring一切的基础,最顶级的抽象类

  • ListableBeanFactory:可列表化的bean工厂
  • HierarchicalBeanFactory:有层级关系的bean工厂(有父子继承关系的)
  • AutowireCapableBeanFactory:可自动注入的Bean工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public interface BeanFactory {
//对 FactoryBean 的转义定义,因为如果使用 bean 的名字检索 FactoryBean 得到的对象是工厂生成的对象,
//如果需要得到工厂本身,需要转义
String FACTORY_BEAN_PREFIX = "&";

//根据 bean 的名字,获取在 IOC 容器中得到 bean 实例
Object getBean(String name) throws BeansException;

//根据 bean 的名字和 Class 类型来得到 bean 实例,增加了类型安全验证机制。
Object getBean(String name, Class requiredType) throws BeansException;

//提供对 bean 的检索,看看是否在 IOC 容器有这个名字的 bean
boolean containsBean(String name);

//根据 bean 名字得到 bean 实例,并同时判断这个 bean 是不是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

//得到 bean 实例的 Class 类型
Class getType(String name) throws NoSuchBeanDefinitionException;

//得到 bean 的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name);
}

在 BeanFactory 里只对 IOC 容器的基本行为作了定义,根本不关心你的 bean 是如何定义怎样加载的。
正如我们只关心工厂里得到什么的产品对象,至于工厂是怎么生产这些对象的,这个基本的接口不关心。

要知道工厂是如何产生对象的,我们需要看具体的 IOC 容器实现,Spring 提供了许多 IOC 容器的
实现。比如 XmlBeanFactory,ClasspathXmlApplicationContext 等。其中 XmlBeanFactory 就是针对最
基本的 IOC 容器的实现,这个 IOC 容器可以读取 XML 文件定义的 BeanDefinition(XML 文件中对 bean
的描述),如果说 XmlBeanFactory 是容器中的低配屌丝,ApplicationContext 应该算容器中的高帅富

从 ApplicationContext 接口的实现,我们看出其特点:

1
2
3
4
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

}

1.支持信息源,可以实现国际化。(实现 MessageSource 接口)
2.访问资源。(实现 ResourcePatternResolver 接口)
3.支持应用事件。(实现 ApplicationEventPublisher 接口)

BeanDefinition

这个主要是存储了bean定义的一些配置信息(其实就是讲xml中配置的信息,保存到BeanDefinition)

SpringIOC 容器管理了我们定义的各种 Bean 对象及其相互的关系,Bean 对象在 Spring 实现中是以
BeanDefinition 来描述的,其继承体系如下:

Bean 的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵
活性,以应对可能的变化。Bean 的解析主要就是对 Spring 配置文件的解析。这个解析过程主要通
过下图中的类完成:

IOC容器的初始化

IOC 容器的初始化包括 BeanDefinition 的 Resource 定位、载入和注册这三个基本的过程

  • 定位:就是找到定义的xml配置文件在哪里
  • 加载:通过xml解析工具解析xml文件
  • 注册:将配置信息交给beanFactory,对xml中的bean进行创建生产
    以ApplicationContext 为例,ApplicationContext 系列容器也许是我们最熟悉的,因为 web 项目中
    使用的 XmlWebApplicationContext 就属于这个继承体系,还有 ClasspathXmlApplicationContext 等,
    其继承体系如下图所示:

    ApplicationContext 允许上下文嵌套,通过保持父上下文可以维持一个上下文体系。对于 bean 的查找
    可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的 Spring
    应用提供了一个共享的 bean 定义环境。

使用FileSystemXmlApplicationContext和使用ClassPathXmlApplicationContext的区别在于:

  • FileSystemXmlApplicationContext在指定的文件系统路径下查找xml文件和本项目下的classPath
    ApplicationContext context = new FileSystemXmlApplicationContext(“C:/bean.xml, classPath: bean.xml”);
  • ClassPathXmlApplicationContext是在所有的类路径(包含JAR文件) 下查找xml文件(classPath*)
    ApplicationContext context = new ClassPathXmlApplicationContext(“bean.xml”);

XmlBeanFactory

低配BeanFactory的整个流程
先看下XmlBeanFactory的源码:

1
2
3
4
5
6
7
8
9
10
11
public class XmlBeanFactory extends DefaultListableBeanFactory{
private final XmlBeanDefinitionReader reader;
public XmlBeanFactory(Resource resource)throws BeansException{
this(resource, null);
}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException{
super(parentBeanFactory);
this.reader = new XmlBeanDefinitionReader(this);
this.reader.loadBeanDefinitions(resource);
}
}

还原一下调用过程

1
2
3
4
5
6
7
8
9
10
11
12
//根据 Xml 配置文件创建 Resource 资源对象,该对象中包含了 BeanDefinition 的信息
ClassPathResource resource =new ClassPathResource("application-context.xml");

//创建 DefaultListableBeanFactory
DefaultListableBeanFactory factory =new DefaultListableBeanFactory();

//创建 XmlBeanDefinitionReader 读取器,用于载入 BeanDefinition。之所以需要 BeanFactory 作为参数,是因为会将读取的信息配置回调给 factory
XmlBeanDefinitionReader reader =new XmlBeanDefinitionReader(factory);

//XmlBeanDefinitionReader 执行载入 BeanDefinition 的方法,最后会完成 Bean 的载入和注册。完成后 Bean 就成功
的放置到 IOC 容器当中,以后我们就可以从中取得 Bean 来使用
reader.loadBeanDefinitions(resource);

然后我们看下loadBeanDefinitions方法

其实就是对xml文件的解析,并把解析出来的bean 定义配置回传给factory,让其进行new对象

FileSystemXmlApplicationContext

最主要的构造方法,其余构造方法都是调用这个

1
2
3
4
5
6
7
8
9
/**
* Create a new FileSystemXmlApplicationContext, loading the definitions
* from the given XML files and automatically refreshing the context.
* @param configLocations array of file paths
* @throws BeansException if context creation failed
*/
public FileSystemXmlApplicationContext(String... configLocations) throws BeansException {
this(configLocations, true, null);
}

实际调用

1
2
3
4
5
6
7
8
9
10
11
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh,
ApplicationContext parent) throws BeansException {
// 为了动态的确定用什么加载器去加载配置文件
super(parent);
// 告诉读取器reader 配置文件在哪里:定位配置文件,为了加载配置文件
setConfigLocations(configLocations);
// 刷新
if (refresh) {
refresh();
}
}

因为spring的context是可以自定义的,所以如果不设置,则使用默认的资源加载器,如果配置则使用用户自定义的ApplicationContext

设置资源加载器和资源定位
通过分析 FileSystemXmlApplicationContext 的源代码可以知道,在创建
FileSystemXmlApplicationContext 容器时,构造方法做以下两项重要工作:
1.调用父类容器的构造方法(super(parent)方法)为容器设置好 Bean 资源加载器。
2.再调用父类 AbstractRefreshableConfigApplicationContext 的
setConfigLocations(configLocations)方法设置 Bean 定义资源文件的定位路径。
通过追踪 FileSystemXmlApplicationContext 的继承体系,发现其父类的父类
AbstractApplicationContext 中初始化 IOC 容器所做的主要源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext, DisposableBean {
//静态初始化块,在整个容器创建过程中只执行一次
static {
//为了避免应用程序在 Weblogic8.1 关闭时出现类加载异常加载问题,加载 IoC 容器关闭事件(ContextClosedEvent)类
ContextClosedEvent.class.getName();
}
public AbstractApplicationContext() {
// 解析资源文件,动态的匹配
this.resourcePatternResolver = getResourcePatternResolver();
}
//FileSystemXmlApplicationContext 调用父类构造方法调用的就是该方法
public AbstractApplicationContext(ApplicationContext parent) {
this();
setParent(parent);
}
//获取一个 Spring Source 的加载器用于读入 Spring Bean 定义资源文件
protected ResourcePatternResolver getResourcePatternResolver() {
//AbstractApplicationContext 继承 DefaultResourceLoader,因此也是一个资源加载器
//Spring 资源加载器,其 getResource(String location)方法用于载入资源
return new PathMatchingResourcePatternResolver(this);
}
……
}

AbstractApplicationContext构造方法中调用PathMatchingResourcePatternResolver的构造方法创建
Spring 资源加载器:

1
2
3
4
5
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
//设置 Spring 的资源加载器
this.resourceLoader = resourceLoader;
}

在设置容器的资源加载器之后,接下来 FileSystemXmlApplicationContet 执行 setConfigLocations 方
法通过调用其父类 AbstractRefreshableConfigApplicationContext 的方法进行对 Bean 定义资源文件
的定位,该方法的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//处理单个资源文件路径为一个字符串的情况
public void setConfigLocation(String location) {
//String CONFIG_LOCATION_DELIMITERS = ",; /t/n";
//即多个资源文件路径之间用” ,; /t/n”分隔,解析成数组形式
setConfigLocations(StringUtils.tokenizeToStringArray(location, CONFIG_LOCATION_DELIMITERS));
}

//解析 Bean 定义资源文件的路径,处理多个资源文件字符串数组
public void setConfigLocations(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++) {
// resolvePath 将字符串解析为路径的方法
this.configLocations[i] = resolvePath(locations[i]).trim();
}
} else {
this.configLocations = null;
}
}

通过这两个方法的源码我们可以看出,我们既可以使用一个字符串来配置多个 Spring Bean 定义资源
文件,也可以使用字符串数组,即下面两种方式都是可以的:
1.ClasspathResource res = new ClasspathResource(“a.xml,b.xml,……”);
多个资源文件路径之间可以是用” ,; /t/n”等分隔。
2.ClasspathResource res = new ClasspathResource(newString[]{“a.xml”,”b.xml”,……});
Spring IOC 容器在初始化时将配置的 Bean 定义资源文件定位为 Spring 封装的 Resource。
3.AbstractApplicationContext 的 refresh 函数载入 Bean 定义过程:
Spring IOC 容器对 Bean 定义资源的载入是从 refresh()函数开始的,refresh()是一个模板方法,
refresh()方法的作用是:在创建 IOC 容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,
以保证在 refresh 之后使用的是新建立起来的 IOC 容器。refresh 的作用类似于对 IOC 容器的重启,在
新建立好的容器中对容器进行初始化,对 Bean 定义资源进行载入

refresh()

1
2
3
4
5
6
7
8
9
10
11
public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh,
ApplicationContext parent) throws BeansException {
// 为了动态的确定用什么加载器去加载配置文件
super(parent);
// 告诉读取器reader 配置文件在哪里:定位配置文件,为了加载配置文件
setConfigLocations(configLocations);
// 刷新
if (refresh) {
refresh();
}
}

FileSystemXmlApplicationContext 通过调用其父类 AbstractApplicationContext 的 refresh()函数启
动整个 IoC 容器对 Bean 定义的载入过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//调用容器准备刷新的方法,获取容器的当时时间,同时给容器设置同步标识
prepareRefresh();
//告诉子类启动 refreshBeanFactory()方法,Bean 定义资源文件的载入从子类的 refreshBeanFactory()方法启动
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//为 BeanFactory 配置容器特性,例如类加载器、事件处理器等
prepareBeanFactory(beanFactory);
try {
//以下三个方法其实都是加载一些所谓的监听器(ApplicationAware/SessionAware等等)

//为容器的某些子类指定特殊的 BeanPost 事件处理器
//比如监听spring是否启动,ApplicationAware中的setApplicationContext方法就可以进行一些容器初始化事件操作
postProcessBeanFactory(beanFactory);
//调用所有注册的 BeanFactoryPostProcessor 的 Bean
invokeBeanFactoryPostProcessors(beanFactory);
//为 BeanFactory 注册 BeanPost 事件处理器.
//BeanPostProcessor 是 Bean 后置处理器,用于监听容器触发的事件
registerBeanPostProcessors(beanFactory);

//初始化信息源,和国际化相关.
initMessageSource();

//初始化容器事件传播器.
initApplicationEventMulticaster();
//调用子类的某些特殊 Bean 初始化方法
onRefresh();
//为事件传播器注册事件监听器.
registerListeners();
//初始化所有剩余的单例 Bean.
finishBeanFactoryInitialization(beanFactory);
//初始化容器的生命周期事件处理器,并发布容器的生命周期事件
finishRefresh();
} catch (BeansException ex) {
//销毁以创建的单态 Bean
destroyBeans();
//取消 refresh 操作,重置容器的同步标识.
cancelRefresh(ex);
throw ex;
}
}
}

refresh()方法主要为 IOC 容器 Bean 的生命周期管理提供条件,Spring IOC 容器载入 Bean 定义资源文
件从其子类容器的 refreshBeanFactory()方法启动,所以整个 refresh()中
“ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();”这句以后代码的
都是注册容器的信息源和生命周期事件,载入过程就是从这句代码启动。

refresh()方法的作用是:在创建 IOC 容器前,如果已经有容器存在,则需要把已有的容器销毁和关
闭,以保证在 refresh 之后使用的是新建立起来的 IOC 容器。refresh 的作用类似于对 IOC 容器的重启,
在新建立好的容器中对容器进行初始化,对 Bean 定义资源进行载入

AbstractApplicationContext的obtainFreshBeanFactory()方法调用子类容器的refreshBeanFactory()
方法,启动容器载入 Bean 定义资源文件的过程,代码如下:

1
2
3
4
5
6
7
8
9
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//这里使用了委派设计模式,父类定义了抽象的 refreshBeanFactory()方法,具体实现调用子类容器的refreshBeanFactory()方法
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}

AbstractApplicationContext 子类的 refreshBeanFactory()方法:
AbstractApplicationContext 类中只抽象定义了 refreshBeanFactory()方法,容器真正调用的是
其子类AbstractRefreshableApplicationContext实现的refreshBeanFactory()方法,方法的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
protected final void refreshBeanFactory() throws BeansException {
//如果已经有容器,销毁容器中的 bean,关闭容器
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建 IOC 容器
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
//对 IOC 容器进行定制化,如设置启动参数,开启注解的自动装配等
customizeBeanFactory(beanFactory);
//调用载入 Bean 定义的方法,主要这里又使用了一个委派模式,在当前类中只定义了抽象的 loadBeanDefinitions方法,具体的实现调用子类容器
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}

在这个方法中,先判断 BeanFactory 是否存在,如果存在则先销毁 beans 并关闭 beanFactory,接着创
建 DefaultListableBeanFactory,并调用 loadBeanDefinitions(beanFactory)装载 bean 定义。

AbstractRefreshableApplicationContext 子类的 loadBeanDefinitions 方法:
AbstractRefreshableApplicationContext 中只定义了抽象的 loadBeanDefinitions 方法,容器真正调
用的是其子类 AbstractXmlApplicationContext 对该方法的实现,AbstractXmlApplicationContext 的
主要源码如下:
loadBeanDefinitions 方法同样是抽象方法,是由其子类实现的,也即在
AbstractXmlApplicationContext 中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {

……

//实现父类抽象的载入 Bean 定义方法
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException,
IOException {
//创建 XmlBeanDefinitionReader,即创建 Bean 读取器,并通过回调设置到容器中去,容 器使用该读取器读取 Bean 定义资源
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
//为 Bean 读取器设置 Spring 资源加载器,AbstractXmlApplicationContext 的祖先父类 AbstractApplicationContext 继承 DefaultResourceLoader,因此,容器本身也是一个资源加载器
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
//为 Bean 读取器设置 SAX xml 解析器
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
//当 Bean 读取器读取 Bean 定义的 Xml 资源文件时,启用 Xml 的校验机制
initBeanDefinitionReader(beanDefinitionReader);
//Bean 读取器真正实现加载的方法
loadBeanDefinitions(beanDefinitionReader);
}

//Xml Bean 读取器加载 Bean 定义资源
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException,IOException {
//获取 Bean 定义资源的定位
Resource[] configResources = getConfigResources();
if (configResources != null) {
//Xml Bean 读取器调用其父类 AbstractBeanDefinitionReader 读取定位的 Bean 定义资源
reader.loadBeanDefinitions(configResources);
}
//如果子类中获取的 Bean 定义资源定位为空,则获取 FileSystemXmlApplicationContext 构造方法中setConfigLocations 方法设置的资源
String[] configLocations = getConfigLocations();
if (configLocations != null) {
//Xml Bean 读取器调用其父类 AbstractBeanDefinitionReader 读取定位的 Bean 定义资源
reader.loadBeanDefinitions(configLocations);
}
}

//这里又使用了一个委托模式,调用子类的获取 Bean 定义资源定位的方法
//该方法在 ClassPathXmlApplicationContext 中进行实现,对于我们
//举例分析源码的 FileSystemXmlApplicationContext 没有使用该方法
protected Resource[] getConfigResources() {
return null;
}
……
}

Xml Bean 读取器(XmlBeanDefinitionReader)调用其父类 AbstractBeanDefinitionReader
的 reader.loadBeanDefinitions 方法读取 Bean 定义资源。

由于我们使用 FileSystemXmlApplicationContext 作为例子分析,因此 getConfigResources 的返回值
为 null,因此程序执行 reader.loadBeanDefinitions(configLocations)分支。

AbstractBeanDefinitionReader 读取 Bean 定义资源:
AbstractBeanDefinitionReader 的 loadBeanDefinitions 方法源码如下
可以到 org.springframework.beans.factory.support 看一下 BeanDefinitionReader 的结构

在其抽象父类 AbstractBeanDefinitionReader 中定义了载入过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//重载方法,调用下面的 loadBeanDefinitions(String, Set<Resource>);方法
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
return loadBeanDefinitions(location, null);
}
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
//获取在 IoC 容器初始化过程中设置的资源加载器
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader
available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
try {
//将指定位置的 Bean 定义资源文件解析为 Spring IOC 容器封装的资源
//加载多个指定位置的 Bean 定义资源文件
// ResourcePatternResolver就是多个文件解析器,就是下面说到的多个文件可以使用,;等符号进行分割
Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
//委派调用其子类 XmlBeanDefinitionReader 的方法,实现加载功能
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" +
location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", ex);
}
} else {
//将指定位置的 Bean 定义资源文件解析为 Spring IOC 容器封装的资源
//加载单个指定位置的 Bean 定义资源文件
// AbstractXmlApplicationContext line 89 设置了默认的资源加载器
Resource resource = resourceLoader.getResource(location);
//委派调用其子类 XmlBeanDefinitionReader 的方法,实现加载功能
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location +
"]");
}
return loadCount;
}
}
//重载方法,调用 loadBeanDefinitions(String);
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
Assert.notNull(locations, "Location array must not be null");
int counter = 0;
for (String location : locations) {
counter += loadBeanDefinitions(location);
}
return counter;
}

loadBeanDefinitions(Resource…resources)方法和上面分析的 3 个方法类似,同样也是调用
XmlBeanDefinitionReader 的 loadBeanDefinitions 方法。

从对 AbstractBeanDefinitionReader 的 loadBeanDefinitions 方法源码分析可以看出该方法做了以下
两件事:

  • 调用资源加载器的获取资源方法 resourceLoader.getResource(location),获取到要加载的资源。
  • 真正执行加载功能是其子类 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法。

看到第 8、16 行,结合上面的 ResourceLoader 与 ApplicationContext 的继承系图,可以知道此时调用
的是 DefaultResourceLoader 中的 getSource()方法定位 Resource,因为
FileSystemXmlApplicationContext 本身就是 DefaultResourceLoader 的实现类,所以此时又回到了
FileSystemXmlApplicationContext 中来。

资源加载器获取要读入的资源:
XmlBeanDefinitionReader 通过调用其父类 DefaultResourceLoader 的 getResource 方法获取要加载的
资源,其源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//获取 Resource 的具体实现方法
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
//如果是类路径的方式,那需要使用 ClassPathResource 来得到 bean 文件的资源对象
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()),getClassLoader());
}
try {
// 如果是 URL 方式,使用 UrlResource 作为 bean 文件的资源对象
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
}
//如果既不是 classpath 标识,又不是 URL 标识的 Resource 定位,则调用
//容器本身的 getResourceByPath 方法获取 Resource
return getResourceByPath(location);
}

FileSystemXmlApplicationContext 容器提供了 getResourceByPath 方法的实现,就是为了处理既不是
classpath 标识,又不是 URL 标识的 Resource 定位这种情况。

1
2
3
4
5
6
7
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
//这里使用文件系统资源对象来定义 bean 文件
return new FileSystemResource(path);
}

这样代码就回到了 FileSystemXmlApplicationContext 中来,他提供了 FileSystemResource 来完
成从文件系统得到配置文件的资源定义。

这样,就可以从文件系统路径上对 IOC 配置文件进行加载 - 当然我们可以按照这个逻辑从任何地方
加载,在 Spring 中我们看到它提供 的各种资源抽象,比如
ClassPathResource, URLResource,FileSystemResource 等来供我们使用。上面我们看到的是定位
Resource 的一个过程,而这只是加载过程的一部分.

XmlBeanDefinitionReader 加载 Bean 定义资源

Bean 定义的 Resource 得到了

继续回到 XmlBeanDefinitionReader 的 loadBeanDefinitions(Resource …)方法看到代表 bean 文
件的资源定义以后的载入过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//XmlBeanDefinitionReader 加载资源的入口方法
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//将读入的 XML 资源进行特殊编码处理
return loadBeanDefinitions(new EncodedResource(resource));
}

//这里是载入 XML 形式 Bean 定义资源文件方法
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
.......
// 主要去除配置文件的循环依赖
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
//将资源文件转为 InputStream 的 IO 流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
//从 InputStream 中得到 XML 的解析源
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//这里是具体的读取过程
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
//关闭从 Resource 中得到的 IO 流
inputStream.close();
}
}
.........
}

//从特定 XML 文件中实际载入 Bean 定义资源的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
//将 XML 文件转换为 DOM 对象,解析过程由 documentLoader 实现
Document doc = this.documentLoader.loadDocument(
inputSource, this.entityResolver, this.errorHandler, validationMode,
this.namespaceAware);
//这里是启动对 Bean 定义解析的详细过程,该解析过程会用到 Spring 的 Bean 配置规则
return registerBeanDefinitions(doc, resource);
}
.......
}

通过源码分析,载入 Bean 定义资源文件的最后一步是将 Bean 定义资源转换为 Document 对象,该过程
由 documentLoader 实现

DocumentLoader 将 Bean 定义资源转换为 Document 对象:
DocumentLoader 将 Bean 定义资源转换成 Document 对象的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//使用标准的 JAXP 将载入的 Bean 定义资源转换成 document 对象
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
//创建文件解析器工厂
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode,
namespaceAware);
if (logger.isDebugEnabled()) {
logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
}
//创建文档解析器
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
//解析 Spring 的 Bean 定义资源
return builder.parse(inputSource);
}

protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware) throws ParserConfigurationException {
//创建文档解析工厂
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(namespaceAware);
//设置解析 XML 的校验
if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
factory.setValidating(true);
if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
factory.setNamespaceAware(true);
try {
factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
} catch (IllegalArgumentException ex) {
ParserConfigurationException pcex = new ParserConfigurationException(
"Unable to validate using XSD: Your JAXP provider [" + factory +
"] does not support XML Schema. Are you running on Java 1.with Apache Crimson?
" +
"Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
pcex.initCause(ex);
throw pcex;
}
}
}
return factory;
}

该解析过程调用 JavaEE 标准的 JAXP 标准进行处理。
至此 Spring IOC 容器根据定位的 Bean 定义资源文件,将其加载读入并转换成为 Document 对象过程完成。
接下来我们要继续分析 Spring IOC 容器将载入的 Bean 定义资源文件转换为 Document 对象之后,是如
何将其解析为 Spring IOC 管理的 Bean 对象并将其注册到容器中的。

XmlBeanDefinitionReader 解析载入的 Bean 定义资源文件

XmlBeanDefinitionReader类中的doLoadBeanDefinitions方法是从特定XML 文件中实际载入Bean 定
义资源的方法,该方法在载入 Bean 定义资源之后将其转换为 Document 对象,接下来调用
registerBeanDefinitions 启动 Spring IOC 容器对 Bean 定义的解析过程,registerBeanDefinitions
方法源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//按照 Spring 的 Bean 语义要求将 Bean 定义资源解析并转换为容器内部数据结构
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//得到 BeanDefinitionDocumentReader 来对 xml 格式的 BeanDefinition 解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
//获得容器中注册的 Bean 数量
int countBefore = getRegistry().getBeanDefinitionCount();
//解析过程入口,这里使用了委派模式,BeanDefinitionDocumentReader 只是个接口,
//具体的解析实现过程有实现类 DefaultBeanDefinitionDocumentReader 完成
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//统计解析的 Bean 数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
//创建 BeanDefinitionDocumentReader 对象,解析 Document 对象
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));
}

Bean 定义资源的载入解析分为以下两个过程:

  • 首先,通过调用 XML 解析器将 Bean 定义资源文件转换得到 Document 对象,但是这些 Document 对象并没有按照 Spring 的 Bean 规则进行解析。这一步是载入的过程
  • 在完成通用的 XML 解析之后,按照 Spring 的 Bean 规则对 Document 对象进行解析。

按照 Spring 的 Bean 规则对 Document 对象解析的过程是在接口 BeanDefinitionDocumentReader 的实现
类 DefaultBeanDefinitionDocumentReader 中实现的。

DefaultBeanDefinitionDocumentReader 对 Bean 定义的 Document 对象解析

BeanDefinitionDocumentReader 接口通过 registerBeanDefinitions 方法调用其实现类
DefaultBeanDefinitionDocumentReader 对 Document 对象进行解析,解析的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
//根据 Spring DTD 对 Bean 的定义规则解析 Bean 定义 Document 对象
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
//获得 XML 描述符
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
//获得 Document 的根元素
Element root = doc.getDocumentElement();
//具体的解析过程由 BeanDefinitionParserDelegate 实现,
//BeanDefinitionParserDelegate 中定义了 Spring Bean 定义 XML 文件的各种元素
BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);
//在解析 Bean 定义之前,进行自定义的解析,增强解析过程的可扩展性
preProcessXml(root);
//从 Document 的根元素开始进行 Bean 定义的 Document 对象
parseBeanDefinitions(root, delegate);
//在解析 Bean 定义之后,进行自定义的解析,增加解析过程的可扩展性
postProcessXml(root);
}

//创建 BeanDefinitionParserDelegate,用于完成真正的解析过程
protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root) {
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
//BeanDefinitionParserDelegate 初始化 Document 根元素
delegate.initDefaults(root);
return delegate;
}

//使用 Spring 的 Bean 规则从 Document 的根元素开始进行 Bean 定义的 Document 对象
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//Bean 定义的 Document 对象使用了 Spring 默认的 XML 命名空间
if (delegate.isDefaultNamespace(root)) {
//获取 Bean 定义的 Document 对象根元素的所有子节点
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//获得 Document 节点是 XML 元素节点
if (node instanceof Element) {
Element ele = (Element) node;
//Bean 定义的 Document 的元素节点使用的是 Spring 默认的 XML 命名空间
if (delegate.isDefaultNamespace(ele)) {
//使用 Spring 的 Bean 规则解析元素节点
parseDefaultElement(ele, delegate);
} else {
//没有使用 Spring 默认的 XML 命名空间,则使用用户自定义的解析规则解析元素节点
delegate.parseCustomElement(ele);
}
}
}
} else {
//Document 的根节点没有使用 Spring 默认的命名空间,则使用用户自定义的解析规则解析 Document 根节点
delegate.parseCustomElement(root);
}
}

//使用 Spring 的 Bean 规则解析 Document 元素节点
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//如果元素节点是<Import>导入元素,进行导入解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//如果元素节点是<Alias>别名元素,进行别名解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//元素节点既不是导入元素,也不是别名元素,即普通的<Bean>元素,
//按照 Spring 的 Bean 规则解析元素
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
}

//解析<Import>导入元素,从给定的导入路径加载 Bean 定义资源到 Spring IoC 容器中
protected void importBeanDefinitionResource(Element ele) {
//获取给定的导入元素的 location 属性
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
//如果导入元素的 location 属性值为空,则没有导入任何资源,直接返回
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
//使用系统变量值解析 location 属性值
location = SystemPropertyUtils.resolvePlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<Resource>(4);
//标识给定的导入元素的 location 是否是绝对路径
boolean absoluteLocation = false;
try {
absoluteLocation = ResourcePatternUtils.isUrl(location) ||
ResourceUtils.toURI(location).isAbsolute();
} catch (URISyntaxException ex) {
//给定的导入元素的 location 不是绝对路径
}
//给定的导入元素的 location 是绝对路径
if (absoluteLocation) {
try {
//使用资源读入器加载给定路径的 Bean 定义资源
int importCount = getReaderContext().getReader().loadBeanDefinitions(location,
actualResources);
if (logger.isDebugEnabled()) {
logger.debug("Imported " + importCount + " bean definitions from URL location [" +
location + "]");
}
} catch (BeanDefinitionStoreException ex) {
getReaderContext().error(
"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
}
} else {
//给定的导入元素的 location 是相对路径
try {
int importCount;
//将给定导入元素的 location 封装为相对路径资源
Resource relativeResource = getReaderContext().getResource().createRelative(location);
//封装的相对路径资源存在
if (relativeResource.exists()) {
//使用资源读入器加载 Bean 定义资源
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
actualResources.add(relativeResource);
}
//封装的相对路径资源不存在
else {
//获取 Spring IOC 容器资源读入器的基本路径
String baseLocation = getReaderContext().getResource().getURL().toString();
//根据 Spring IoC 容器资源读入器的基本路径加载给定导入路径的资源
importCount = getReaderContext().getReader().loadBeanDefinitions(
StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
if (logger.isDebugEnabled()) {
logger.debug("Imported " + importCount + " bean definitions from relative location ["
+ location + "]");
}
} catch (IOException ex) {
getReaderContext().error("Failed to resolve current resource location", ele, ex);
} catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to import bean definitions from relative location [" +
location + "]",
ele, ex);
}
}
Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
//在解析完<Import>元素之后,发送容器导入其他资源处理完成事件
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
}

//解析<Alias>别名元素,为 Bean 向 Spring IoC 容器注册别名
protected void processAliasRegistration(Element ele) {
//获取<Alias>别名元素中 name 的属性值
String name = ele.getAttribute(NAME_ATTRIBUTE);
//获取<Alias>别名元素中 alias 的属性值
String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
boolean valid = true;
//<alias>别名元素的 name 属性值为空
if (!StringUtils.hasText(name)) {
getReaderContext().error("Name must not be empty", ele);
valid = false;
}
//<alias>别名元素的 alias 属性值为空
if (!StringUtils.hasText(alias)) {
getReaderContext().error("Alias must not be empty", ele);
valid = false;
}
if (valid) {
try {
//向容器的资源读入器注册别名
getReaderContext().getRegistry().registerAlias(name, alias);
} catch (Exception ex) {
getReaderContext().error("Failed to register alias '" + alias +
"' for bean with name '" + name + "'", ele, ex);
}
//在解析完<Alias>元素之后,发送容器别名处理完成事件
getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
}
}

//解析 Bean 定义资源 Document 对象的普通元素
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// BeanDefinitionHolder 是对 BeanDefinition 的封装,即 Bean 定义的封装类
//对 Document 对象中<Bean>元素的解析由 BeanDefinitionParserDelegate 实现 BeanDefinitionHolder
bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//向 Spring IoC 容器注册解析得到的 Bean 定义,这是 Bean 定义向 IOC 容器注册的入口
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,
getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
//在完成向 Spring IoC 容器注册解析得到的 Bean 定义之后,发送注册事件
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

通过上述 Spring IOC 容器对载入的 Bean 定义 Document 解析可以看出,我们使用 Spring 时,在 Spring
配置文件中可以使用<Import>元素来导入 IOC 容器所需要的其他资源,Spring IoC 容器在解析时会首
先将指定导入的资源加载进容器中。使用<Ailas>别名时,Spring IOC 容器首先将别名元素所定义的别
名注册到容器中。

对于既不是<Import>元素,又不是<Alias>元素的元素,即 Spring 配置文件中普通的<Bean>元素的解析
由 BeanDefinitionParserDelegate 类的 parseBeanDefinitionElement 方法来实现。

BeanDefinitionParserDelegate 解析 Bean 定义资源文件中的<Bean>元素

Bean 定义资源文件中的<Import><Alias>元素解析在 DefaultBeanDefinitionDocumentReader 中已经
完成,对 Bean 定义资源文件中使用最多的<Bean>元素交由 BeanDefinitionParserDelegate 来解析,其解析实现的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//解析<Bean>元素的入口
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
//解析 Bean 定义资源文件中的<Bean>元素,这个方法中主要处理<Bean>元素的 id,name和别名属性
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition
containingBean) {
//获取<Bean>元素中的 id 属性值
String id = ele.getAttribute(ID_ATTRIBUTE);
//获取<Bean>元素中的 name 属性值
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
//获取<Bean>元素中的 alias 属性值
List<String> aliases = new ArrayList<String>();
//将<Bean>元素中的所有 name 属性值存放到别名中
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
//如果<Bean>元素中没有配置 id 属性时,将别名中的第一个值赋值给 beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
//检查<Bean>元素所配置的 id 或者 name 的唯一性,containingBean 标识<Bean>
//元素中是否包含子<Bean>元素
if (containingBean == null) {
//检查<Bean>元素所配置的 id、name 或者别名是否重复
checkNameUniqueness(beanName, aliases, ele);
}
//详细对<Bean>元素中配置的 Bean 定义进行解析的地方
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName,
containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
//如果<Bean>元素中没有配置 id、别名或者 name,且没有包含子元素
//<Bean>元素,为解析的 Bean 生成一个唯一 beanName 并注册
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
//如果<Bean>元素中没有配置 id、别名或者 name,且包含了子元素
//<Bean>元素,为解析的 Bean 使用别名向 IOC 容器注册
beanName = this.readerContext.generateBeanName(beanDefinition);
//为解析的 Bean 使用别名注册时,为了向后兼容
//Spring1.2/2.0,给别名添加类名后缀
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() >
beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
//当解析出错时,返回 null
return null;
}
//详细对<Bean>元素中配置的 Bean 定义其他属性进行解析,由于上面的方法中已经对
//Bean 的 id、name 和别名等属性进行了处理,该方法中主要处理除这三个以外的其他属性数据
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
//记录解析的<Bean>
this.parseState.push(new BeanEntry(beanName));
//这里只读取<Bean>元素中配置的 class 名字,然后载入到 BeanDefinition 中去
//只是记录配置的 class 名字,不做实例化,对象的实例化在依赖注入时完成
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
//如果<Bean>元素中配置了 parent 属性,则获取 parent 属性的值
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
//根据<Bean>元素配置的 class 名称和 parent 属性值创建 BeanDefinition
//为载入 Bean 定义信息做准备
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//对当前的<Bean>元素中配置的一些属性进行解析和设置,如配置的单态(singleton)属性等
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//为<Bean>元素解析的 Bean 设置 description 信息
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//对<Bean>元素的 meta(元信息)属性解析
parseMetaElements(ele, bd);
//对<Bean>元素的 lookup-method 属性解析
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//对<Bean>元素的 replaced-method 属性解析
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析<Bean>元素的构造方法设置
parseConstructorArgElements(ele, bd);
//解析<Bean>元素的<property>设置
parsePropertyElements(ele, bd);
//解析<Bean>元素的 qualifier 属性
parseQualifierElements(ele, bd);
//为当前解析的 Bean 设置所需的资源和依赖对象
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
}
catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
}
catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
}
finally {
this.parseState.pop();
}
//解析<Bean>元素出错时,返回 null
return null;
}

只要使用过 Spring,对 Spring 配置文件比较熟悉的人,通过对上述源码的分析,就会明白我们在 Spring
配置文件中<Bean>元素的中配置的属性就是通过该方法解析和设置到 Bean 中去的。

注意:在解析<Bean>元素过程中没有创建和实例化 Bean 对象,只是创建了 Bean 对象的定义类BeanDefinition,将<Bean>元素中的配置信息设置到 BeanDefinition 中作为记录,当依赖注入时才使用这些记录信息创建和实例化具体的 Bean 对象。

上面方法中一些对一些配置如元信息(meta)、qualifier 等的解析,我们在 Spring 中配置时使用的也不多,我们在使用 Spring 的<Bean>元素时,配置最多的是<property>属性,因此我们下面继续分析源码,了解 Bean 的属性在解析时是如何设置的。

BeanDefinitionParserDelegate 解析<property>元素

BeanDefinitionParserDelegate 在解析<Bean>调用 parsePropertyElements 方法解析<Bean>元素中的
<property>属性子元素,解析源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//解析<Bean>元素中的<property>子元素
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
//获取<Bean>元素中所有的子元素
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//如果子元素是<property>子元素,则调用解析<property>子元素方法解析
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);
}
}
}
//解析<property>元素
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//获取<property>元素的名字
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
//如果一个 Bean 中已经有同名的 property 存在,则不进行解析,直接返回。
//即如果在同一个 Bean 中配置同名的 property,则只有第一个起作用
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
//解析获取 property 的值
Object val = parsePropertyValue(ele, bd, propertyName);
//根据 property 的名字和值创建 property 实例
PropertyValue pv = new PropertyValue(propertyName, val);
//解析<property>元素中的属性
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
//解析获取 property 值
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
String elementName = (propertyName != null) ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element";
//获取<property>的所有子元素,只能是其中一种类型:ref,value,list 等
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
//子元素不是 description 和 meta 属性
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
//当前<property>元素包含有子元素
else {
subElement = (Element) node;
}
}
}
//判断 property 的属性值是 ref 还是 value,不允许既是 ref 又是 value
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR
sub-element", ele);
}
//如果属性是 ref,创建一个 ref 的数据对象 RuntimeBeanReference
//这个对象封装了 ref 信息
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
//一个指向运行时所依赖对象的引用
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
//设置这个 ref 的数据对象是被当前的 property 对象所引用
ref.setSource(extractSource(ele));
return ref;
}
//如果属性是 value,创建一个 value 的数据对象 TypedStringValue
//这个对象封装了 value 信息
else if (hasValueAttribute) {
//一个持有 String 类型值的对象
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
//设置这个 value 数据对象是被当前的 property 对象所引用
valueHolder.setSource(extractSource(ele));
return valueHolder;
}
//如果当前<property>元素还有子元素
else if (subElement != null) {
//解析<property>的子元素
return parsePropertySubElement(subElement, bd);
}
else {
//propery 属性中既不是 ref,也不是 value 属性,解析出错返回 null
error(elementName + " must specify a ref or value", ele);
return null;
}
}

通过对上述源码的分析,我们可以了解在 Spring 配置文件中,<Bean>元素中<property>元素的相关配
置是如何处理的:

  • ref 被封装为指向依赖对象一个引用
  • value 配置都会封装成一个字符串类型的对象
  • ref 和 value 都通过“解析的数据类型属性值.setSource(extractSource(ele));”方法将属性值/引用与所引用的属性关联起来
    在方法的最后对于<property>元素的子元素通过 parsePropertySubElement 方法解析,我们继续分析
    该方法的源码,了解其解析过程。

解析<property>元素的子元素

在 BeanDefinitionParserDelegate 类中的 parsePropertySubElement 方法对<property>中的子元素解析,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//解析<property>元素中 ref,value 或者集合等子元素
public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
//如果<property>没有使用 Spring 默认的命名空间,则使用用户自定义的规则解析
//内嵌元素
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
}
//如果子元素是 bean,则使用解析<Bean>元素的方法解析
else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
}
//如果子元素是 ref,ref 中只能有以下 3 个属性:bean、local、parent
else if (nodeNameEquals(ele, REF_ELEMENT)) {
//获取<property>元素中的 bean 属性值,引用其他解析的 Bean 的名称
//可以不再同一个 Spring 配置文件中,具体请参考 Spring 对 ref 的配置规则
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
//获取<property>元素中的 local 属性值,引用同一个 Xml 文件中配置
//的 Bean 的 id,local 和 ref 不同,local 只能引用同一个配置文件中的 Bean
refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
if (!StringUtils.hasLength(refName)) {
//获取<property>元素中 parent 属性值,引用父级容器中的 Bean
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean', 'local' or 'parent' is required for <ref> element", ele);
return null;
}
}
}
//没有配置 ref 的目标属性值
if (!StringUtils.hasText(refName)) {
error("<ref> element contains empty target attribute", ele);
return null;
}
//创建 ref 类型数据,指向被引用的对象
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
//设置引用类型值是被当前子元素所引用
ref.setSource(extractSource(ele));
return ref;
}
//如果子元素是<idref>,使用解析 ref 元素的方法解析
else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
}
//如果子元素是<value>,使用解析 value 元素的方法解析
else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
}
//如果子元素是 null,为<property>设置一个封装 null 值的字符串数据
else if (nodeNameEquals(ele, NULL_ELEMENT)) {
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
}
//如果子元素是<array>,使用解析 array 集合子元素的方法解析
else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
}
//如果子元素是<list>,使用解析 list 集合子元素的方法解析
else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
}
//如果子元素是<set>,使用解析 set 集合子元素的方法解析
else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
}
//如果子元素是<map>,使用解析 map 集合子元素的方法解析
else if (nodeNameEquals(ele, MAP_ELEMENT)) {
return parseMapElement(ele, bd);
}
//如果子元素是<props>,使用解析 props 集合子元素的方法解析
else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
return parsePropsElement(ele);
}
//既不是 ref,又不是 value,也不是集合,则子元素配置错误,返回 null
else {
error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
return null;
}
}

通过上述源码分析,我们明白了在 Spring 配置文件中,对<property>元素中配置的 Array、List、Set、
Map、Prop 等各种集合子元素的都通过上述方法解析,生成对应的数据对象,比如 ManagedList、
ManagedArray、ManagedSet 等,这些 Managed 类是 Spring 对象 BeanDefiniton 的数据封装,对集合数据类型的具体解析有各自的解析方法实现,解析方法的命名非常规范,一目了然,我们对集合元素的解析方法进行源码分析,了解其实现过程。

解析<list>子元素

在 BeanDefinitionParserDelegate 类中的 parseListElement 方法就是具体实现解析<property>元素中的<list>集合子元素,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//解析<list>集合子元素
public List parseListElement(Element collectionEle, BeanDefinition bd) {
//获取<list>元素中的 value-type 属性,即获取集合元素的数据类型
String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
//获取<list>集合元素中的所有子节点
NodeList nl = collectionEle.getChildNodes();
//Spring 中将 List 封装为 ManagedList
ManagedList<Object> target = new ManagedList<Object>(nl.getLength());
target.setSource(extractSource(collectionEle));
//设置集合目标数据类型
target.setElementTypeName(defaultElementType);
target.setMergeEnabled(parseMergeAttribute(collectionEle));
//具体的<list>元素解析
parseCollectionElements(nl, target, bd, defaultElementType);
return target;
}
//具体解析<list>集合元素,<array>、<list>和<set>都使用该方法解析
protected void parseCollectionElements(
NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String
defaultElementType) {
//遍历集合所有节点
for (int i = 0; i < elementNodes.getLength(); i++) {
Node node = elementNodes.item(i);
//节点不是 description 节点
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
//将解析的元素加入集合中,递归调用下一个子元素
target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
}
}
}

经过对 Spring Bean 定义资源文件转换的 Document 对象中的元素层层解析,Spring IOC 现在已经将
XML 形式定义的 Bean 定义资源文件转换为 Spring IOC 所识别的数据结构——BeanDefinition,它是
Bean 定义资源文件中配置的 POJO 对象在 Spring IOC 容器中的映射,我们可以通过
AbstractBeanDefinition 为入口,看到了 IOC 容器进行索引、查询和操作。

通过 Spring IOC 容器对 Bean 定义资源的解析后,IOC 容器大致完成了管理 Bean 对象的准备工作,即
初始化过程,但是最为重要的依赖注入还没有发生,现在在 IOC 容器中 BeanDefinition 存储的只是一
些静态信息,接下来需要向容器注册 Bean 定义信息才能全部完成 IoC 容器的初始化过程

解析过后的 BeanDefinition 在 IOC 容器中的注册

让我们继续跟踪程序的执行顺序,接下来会到我们第3步中分析DefaultBeanDefinitionDocumentReader
对 Bean 定义转换的 Document 对象解析的流程中,在其 parseDefaultElement 方法中完成对 Document
对象的解析后得到封装 BeanDefinition 的 BeanDefinitionHold 对象,然后调用
BeanDefinitionReaderUtils 的 registerBeanDefinition 方法向 IOC 容器注册解析的 Bean,BeanDefinitionReaderUtils 的注册的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//将解析的 BeanDefinitionHold 注册到容器中
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
//获取解析的 BeanDefinition 的名称
String beanName = definitionHolder.getBeanName();
//向 IOC 容器注册 BeanDefinition
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//如果解析的 BeanDefinition 有别名,向容器为其注册别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
registry.registerAlias(beanName, aliase);
}
}
}

当调用 BeanDefinitionReaderUtils 向 IOC 容器注册解析的 BeanDefinition 时,真正完成注册功能的
是 DefaultListableBeanFactory。

DefaultListableBeanFactory 向 IOC 容器注册解析后的 BeanDefinition

DefaultListableBeanFactory 向 IOC 容器注册解析后的 BeanDefinition:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//存储注册信息的 BeanDefinition
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
//向 IoC 容器注册解析的 BeanDefiniton
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
//校验解析的 BeanDefiniton
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(),
beanName,
"Validation of bean definition failed", ex);
}
}
//注册的过程中需要线程同步,以保证数据的一致性
synchronized (this.beanDefinitionMap) {
Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);
//检查是否有同名的 BeanDefinition 已经在 IOC 容器中注册,如果已经注册,
//并且不允许覆盖已注册的 Bean,则抛出注册失败异常
if (oldBeanDefinition != null) {
if (!this.allowBeanDefinitionOverriding) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(),
beanName,
"Cannot register bean definition [" + beanDefinition + "] for bean '" +
beanName +
"': There is already [" + oldBeanDefinition + "] bound.");
}
else {
//如果允许覆盖,则同名的 Bean,后注册的覆盖先注册的
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
}
//IOC 容器中没有已经注册同名的 Bean,按正常注册流程注册
else {
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
}
this.beanDefinitionMap.put(beanName, beanDefinition);
//重置所有已经注册过的 BeanDefinition 的缓存
resetBeanDefinition(beanName);
}
}

至此,Bean 定义资源文件中配置的 Bean 被解析过后,已经注册到 IOC 容器中,被容器管理起来,真正
完成了 IOC 容器初始化所做的全部工作。现在 IOC 容器中已经建立了整个 Bean 的配置信息,这些
BeanDefinition 信息已经可以使用,并且可以被检索,IOC 容器的作用就是对这些注册的 Bean 定义信
息进行处理和维护。这些的注册的 Bean 定义信息是 IoC 容器控制反转的基础,正是有了这些注册的数
据,容器才可以进行依赖注入。

总结

现在通过上面的代码,总结一下 IOC 容器初始化的基本步骤:

  • 初始化的入口在容器实现中的 refresh()调用来完成
  • 对 bean 定义载入 IOC 容器使用的方法是 loadBeanDefinition,

其中的大致过程如下:
通过 ResourceLoader 来完成资源文件位置的定位,DefaultResourceLoader 是默
认的实现,同时上下文本身就给出了 ResourceLoader 的实现,可以从类路径,文件系统,URL 等方式来
定为资源位置。如果是 XmlBeanFactory 作为 IOC 容器,那么需要为它指定 bean 定义的资源,也就是说
bean 定义文件时通过抽象成 Resource 来被 IOC 容器处理的,容器通过 BeanDefinitionReader 来完成
定义信息的解析和 Bean 信息的注册,往往使用的是 XmlBeanDefinitionReader 来解析 bean 的 xml 定义
文件-实际的处理过程是委托给 BeanDefinitionParserDelegate来完成的,从而得到bean的定义信息,
这些信息在 Spring 中使用 BeanDefinition 对象来表示-这个名字可以让我们想到
loadBeanDefinition,RegisterBeanDefinition 这些相关方法-他们都是为处理BeanDefinitin 服务的,
容器解析得到 BeanDefinitionIoC 以后,需要把它在 IOC 容器中注册,这由 IOC 实
现 BeanDefinitionRegistry 接口来实现。注册过程就是在 IOC 容器内部维护的一个 HashMap 来保存得
到的 BeanDefinition 的过程。这个 HashMap 是 IOC 容器持有 bean 信息的场所,以后对 bean 的操作都
是围绕这个 HashMap 来实现的.

然后我们就可以通过 BeanFactory 和 ApplicationContext 来享受到 SpringIOC 的服务了,在使用 IOC
容器的时候,我们注意到除了少量粘合代码,绝大多数以正确 IOC 风格编写的应用程序代码完全不用关
心如何到达工厂,因为容器将把这些对象与容器管理的其他对象钩在一起。基本的策略是把工厂放到已
知的地方,最好是放在对预期使用的上下文有意义的地方,以及代码将实际需要访问工厂的地方。Spring
本身提供了对声明式载入 web 应用程序用法的应用程序上下文,并将其存储在 ServletContext 中的框架
实现。

在使用 SpringIOC 容器的时候我们还需要区别两个概念

BeanFactory 和 FactoryBean

  • BeanFactory 功能性工厂,专门用来生产bean的,如果是生产车的就叫做CarFactory,是 IOC 容器的编程抽象,比如ApplicationContext,XmlBeanFactory 等,这些都是 IOC 容器的具体表现,需要使用什么样的容器由客户决定,但 Spring 为我们提供了丰富的选择。
  • FactoryBean 是由spring工厂生产出来的bean, 只是一个可以在 IOC 而容器中被管理的一个bean,是对各种处理过程和资源使用的抽象,FactoryBean 在需要时产生另一个对象,而不返回FactoryBean 本身,我们可以把它看成是一个抽象工厂,对它的调用返回的是工厂生产的产品。所有的FactoryBean 都实现特殊的 org.springframework.beans.factory.FactoryBean 接口,当使用容器中FactoryBean 的时候,该容器不会返回 FactoryBean 本身,而是返回其生成的对象。Spring 包括了大部分的通用资源和服务访问抽象的 FactoryBean 的实现,其中包括:对 JNDI 查询的处理,对代理对象的处理,对事务性代理的处理,对 RMI 代理的处理等,这些我们都可以看成是具体的工厂,看成是 Spring 为我们建立好的工厂。也就是说 Spring 通过使用抽象工厂模式为我们准备了一系列工厂来生产一些特定的对象,免除我们手工重复的工作,我们要使用时只需要在 IOC 容器里配置好就能很方便的使用了

springboot

SpringApplication
AnnotationConfigServletWebServerApplicationContext
ServletWebServerApplicationContext
AbstractApplicationContext 在这个类中定义了扫描注解的执行器
ClassPathBeanDefinitionScanner
ClassPathScanningCandidateComponentProvider
AbstractTypeHierarchyTraversingFilter
AnnotationTypeFilter

Jeff-Eric wechat