1. Spring源码解析之IOC(上篇)
- 1. Spring 概述
- 2. 依赖注入和控制反转解析
- 2.1 Spring IOC 体系结构
- 2.2 IOC 容器初始化
- 2.2.2 ApplicationContext
- 2.2.2.1 设置资源加载器和资源定位
- 2.2.2.2 refresh 函数载入 Bean 定义
- 2.2.2.3 loadBeanDefinitions 方法
- 2.2.2.4 读取 Bean 定义资源
- 2.2.2.5 资源加载器获取要读入的资源
- 2.2.2.6 XmlBeanDefinitionReader 加载定义资源
- 2.2.2.7 DocumentLoader 将定义资源转换为 Document 对象
- 2.2.2.8 XmlBeanDefinitionReader 解析载入定义资源文件
- 2.2.2.9 DefaultBeanDefinitionDocumentReader 对 Bean 定义的 Document 对象解析
- 2.2.2.10 BeanDefinitionParserDelegate 解析 Bean 定义资源文件中的元素
- 2.2.2.11 解析过后的 BeanDefinition 在 IOC 容器中的注册
- 2.2.2.12 DefaultListableBeanFactory 向 IOC 容器注册解析后的 BeanDefinition
1. Spring 概述
Spring 是一个开源的轻量级 Java SE(Java 标准版本)/Java EE(Java 企业版本)开发应用框架,其目的是用于
简化企业级应用程序开发 。
Spring 的特点:
- 轻量级 零配置编程、API使用简单
- 面向Bean 只需要编写非常普通的Bean
- 松耦合 充分利用AOP思想
- 万能胶 与主流框架无缝集成
- 设计模式 将Java中经典的设计模式运用的淋漓尽致
Spring 简化Java开发,主要采用了4个关键策略:
- 基于POJO的轻量级和最小 侵入性 编程,独自开发,合并运行
- 通过 依赖注入 和 面向接口 松耦合,Java是面向接口编程
- 基于 切面 和 惯性 进行 声明式编程,声明式编程相对命令式编程而言,不需要告诉程序一步步怎么做,而是告诉程序在哪儿做什么,实现解耦
- 通过切面和 模板 减少样板式代码,数据处理逻辑一样,数据处理内容不一样
主要是通过:面向 Bean、依赖注入以及面向切面这三种方式来达成的
- 面向Bean,Spring是面向Bean编程(BOP), IOC容器通过配置文件或注解的方式管理对象之间的依赖关系。其中用到了 权力反转(DI),又称为依赖注入,它的概念是:不创建对象,但是描述它们创建的 方式。
- 依赖注入,最高级抽象是BeanFactory接口,允许通过名称创建和检索对象,也可以管理对象之间的关系。它支持两个对象模型:一个是单例(共享实例,无状态服务对象),一个是原型(每个用户都需要自己创建对象)。bean 工厂的概念是 Spring 作为 IOC 容器的基础。IOC 则将处理事情的责任从应用程序代码转移到
框架。注入方式有:setter、构造方法、强制赋值(注解),同时保证了实例化的先后顺序。 - 面向切面,即 AOP,是一种编程思想,它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化。AOP 的核心构造是方面(切面),它将那些影响多个类的行为封装到可重用的模块中,实现松散耦合。Authentication 权限认证、Logging 日志、Transctions Manager 事务、Lazy Loading 懒加载、Context Process 上下文处理、Error Handler 错误跟踪(异常捕获机制)、Cache 缓存等。
@AutoWrite
private InterfaceA a;//自动把实现类注入进来
@Resource("aaa")
private A a;//IOC容器中类的id为aaa对象自动注入进来,可以区分父子类
@AutoWrite
private A a;//根据类型自动注入
1.1 架构设计
以下是 Spring 4 的系统架构图。
- Core Container(核心容器):由spring-beans(包含BeanFactory接口)、spring-core(包含控制反转和依赖注入 )、 spring-context(扩展了BeanFactory,添加了*Bean生命周期控制、框架事件体系、资源加载透明化等功能,此外提供了许多企业级支持,ApplicationContext是该模块的核心接口,是BeanFactory的超类,它在容器实例化后会自动对所有的单例Bean进行实例化与依赖关系的装配,使之处于待用状态)、spring-expression(统一表达式语言(unified EL)的扩展模块,可以查询、管理运行中的对象*,同时也方便的可以调用对象方法、操作数组、集合等)组成
- Data Access/Integration(数据访问/集成):由spring-jdbc、spring-tx(Spring JDBC 事务控制实现模块)、spring-orm(对象关系映射框架支持模块,主要集成 Hibernate, Java Persistence API (JPA) 和 Java
Data Objects (JDO) 用于资源管理、数据访问对象(DAO)的实现和事务策略)、spring-jms(能够发送和接受信息)、spring-oxm(主要提供一个抽象层以支撑 OXM ,它是 Object-to-XML-Mapping 的缩写,是一
个 O/M-mapper,将 java 对象映射成 XML 数据,或者将 XML 数据映射成 java 对象,例如:JAXB, Castor,
XMLBeans, JiBX 和 XStream )组成。 - Web:由spring-web(Spring 提供了最基础 Web 支持,主要建立于核心容器之上,通过 Servlet 或者Listeners 来初始化 IoC 容器,也包含一些与 Web 相关的支持)、spring-webmvc(是一个的 Web-Servlet 模块)、spring-websocket(与 Web 前端的全双工通讯的协议)、spring-webmvc-portlet(Portlets 在 Web 门户上管理和显示的可插拔的用户界面组件。Portlet 产生可以聚合到门户页面中的标记语言代码的片段,如 HTML,XML 等)组成。
- AOP 和设备支持:spring-aop、spring-aspects(集成自 AspectJ 框架,主要是为 Spring Aop 提供多种 Aop 实现方法)、spring-instrumentation(是基于 JAVA SE 中的
java.lang.instrument
进行设计的,应该算是Aop 的一个支援模块,主要作用是在 JVM 启用时,生成一个代理类,程序员通过代理类在运行时修改类的字节,从而改变一个类的功能,实现 Aop 的功能)组成 - Messaging(报文发送):spring-messaging模块
- Test:spring-test模块
1.2 各个模块依赖关系
- IOC 的实现包 spring-beans 和 AOP 的实现包 spring-aop 是整个框架的基础
- spring-core 则是整个框架的核心
- spring-context 提供上下文环境,为各个模块提供粘合作用
源码解析从 spring-core 入手,其次是 spring-beans 和spring-aop,随后是 spring-context,再其次是 spring-tx 和 spring-orm,最后是 spring-web 和其他部分。
2. 依赖注入和控制反转解析
先来看下两个基本概念:
- IOC(Inversion of Control)控制反转:所谓控制反转,就是把原先我们代码里面需要实现的对象创建、依
赖的代码,反转给容器来帮忙实现。 - DI(Dependency Injection)依赖注入:指对象不是从容器中查找它依赖的类,而是在容器实例化对象的时候主动将它依赖的类注入给它。
2.1 Spring IOC 体系结构
Spring开发过程中我们了解到,Spring初始化时,会加载配置文件 ApplicationContext.xml,下面说下加载该文件的四种方式:
-
XmlBeanFactory:引用资源
Resource resource = new ClassPathResource("appcontext.xml"); BeanFactory factory = new XmlBeanFactory(resource);
-
ClassPathXmlApplicationContext:编译路径
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); ApplicationContext ac = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml","dao.xml"}); ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:/*.xml"); ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:appcontext.xml"); ApplicationContext ac = new ClassPathXmlApplicationContext("conf/appcontext.xml"); ApplicationContext ac = new ClassPathXmlApplicationContext("file:G:/Test/src/appcontext.xml"); RegisterDAO registerDAO = (RegisterDAO)ac.getBean("RegisterDAO");
-
FileSystemXmlApplicationContext:文件系统路径
ApplicationContext ac = new FileSystemXmlApplicationContext("src/appcontext.xml"); ApplicationContext ac = new FileSystemXmlApplicationContext("classpath:appcontext.xml"); ApplicationContext ac = new FileSystemXmlApplicationContext("file:G:/Test/src/appcontext.xml"); ApplicationContext ac = new FileSystemXmlApplicationContext("G:/Test/src/appcontext.xml");
-
XmlWebApplicationContext:专为Web工程定制
ServletContext sc = request.getSession().getServletContext(); ApplicationContext ac = WebApplicationContextUtils.getWebApplicationContext(sc);
依据 XmlBeanFactory 为例,简要说明一下调用关系。最终我们发现实际上的接口是BeanFactory。依赖关系如下图所示:
2.1.1 BeanFactory
Spring是面向Bean的编程,所以解析Spring以围绕Bean展开,首先说下Bean的作用域,有以下几种:
-
singleton(IOC容器每次都返回同一个实例)
-
prototype(IOC容器每次产生一个新的实例)
-
request(每次HTTP请求都会创建一个新的Bean,适用于WebApplicationContext环境)
-
session(同一个Session共享一个Bean实例。不同Session使用不同的实例)
-
global-session(所有的Session共享一个Bean实例)
BeanFactory 作为最顶层的一个接口类,它定义了 IOC 容器的基本功能规范,有三个子类:
- ListableBeanFactory(描述可列表的,实现Bean的list集合操作功能)
- HierachicalBeanFactory(Bean 是有继承关系,描述父子关系)
- AutowireCapableBeanFactory(描述自动装配规则)
另外,ConfigurableBeanFactory 在 HierachicalBeanFactory 的基础上实现了BeanFactory全部配置管理功能,ConfigurableListableBeanFactory是三个接口的一个综合接口。默认实现类是 DefaultListableBeanFactory ,具体如下图所示:
最基本的 IOC 容器接口 BeanFactory都定义了哪些规范呢?
public interface BeanFactory {
//对 FactoryBean 的转义定义
//因为如果使用 bean 的名字检索 FactoryBean 得到的对象是工厂生成的对象。
String FACTORY_BEAN_PREFIX = "&";
//根据 bean 的名字,获取在 IOC 容器中得到 bean 实例
Object getBean(String name) throws BeansException;
//增加了类型安全验证机制
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
//提供对 bean 的检索,看看是否在 IOC 容器有这个名字的 bean
boolean containsBean(String name);
//根据 bean 名字得到 bean 实例,并同时判断这个 bean 是不是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
//得到 bean 实例的 Class 类型
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
//得到 bean 的别名,如果根据别名检索,那么其原名也会被检索出来
String[] getAliases(String name);
}
具体的实现有 XmlBeanFactory,ClasspathXmlApplicationContext 等。其中 XmlBeanFactory 就是针对最
基本的 IOC 容器的实现,这个 IOC 容器可以读取 XML 文件定义的 BeanDefinition(XML 文件中对 bean
的描述)。ClasspathXmlApplicationContext 调用的是 ApplicationContext 接口,它除了能够提供 IOC 容器的基本功能外,还为用户提供了以下的附加服务,比如:
- 支持信息源,可以实现国际化(实现 MessageSource 接口)
- 访问资源(实现 ResourcePatternResolver 接口)
- 支持应用事件(实现 ApplicationEventPublisher 接口)
2.1.2 BeanDefinition
SpringIOC 容器管理了我们定义的各种 Bean 对象及其相互的关系,Bean 对象在 Spring 实现中是以
BeanDefinition 来描述的,其继承体系如下:
2.1.3 XmlBeanDefinitionReader
Bean 的解析主要就是对 Spring 配置文件的解析。这个解析过程主要通过下图中的类完成:
- BeanDefinitionReader:资源文件读取并转换为BeanDefinition的各个功能
- EnvironmentCapable:定义获取Environment方法
- AbstractBeanDefinitionReader:对上面两个接口的实现
- BeanDefinitionDocumentReader:定义读取 Document并注册BeanDefinition功能
- BeanDefinitionParserDelegate:定义解析Element的各种方法
2.2 IOC 容器初始化
IOC 容器的初始化包括 BeanDefinition 的 Resource **定位、载入和注册**这三个基本的过程。以
ApplicationContext 为例讲解,XmlWebApplicationContext 、ClasspathXmlApplicationContext就属于这个继承体系。
ApplicationContext 允许上下文嵌套,通过保持父上下文可以维持一个上下文体系。对于 bean 的查找
可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的 Spring
应用提供了一个共享的 bean 定义环境。下面解析一下四种加载该文件方式的过程:
2.2.1 XmlBeanFactory
通过 XmlBeanFactory 的源码将调用全过程还原,定位、载入、注册三个过程:
//根据 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);
2.2.2 ApplicationContext
此处依据 FileSystemXmlApplicationContext为例进行解析,构造函数如下:
public FileSystemXmlApplicationContext(
String[] configLocations,
boolean refresh,
@Nullable ApplicationContext parent) throws BeansException {
//设置 Bean 资源加载器
super(parent);
//设置 Bean 定义资源文件的定位路径
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
2.2.2.1 设置资源加载器和资源定位
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext {
static {
//为了避免应用程序在 Weblogic8.1 关闭时出现类加载异常加载问题,
//加载 IoC 容器关闭事件(ContextClosedEvent)类
ContextClosedEvent.class.getName();
}
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
setParent(parent);
}
//获取一个 Spring Resource 的加载器用于读入 Spring Bean 定义资源文件
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
}
//单个处理
public void setConfigLocation(String location) {
setConfigLocations(
StringUtils.tokenizeToStringArray(
location,
CONFIG_LOCATION_DELIMITERS));
}
//多个处理
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++) {
// resolvePath 为同一个类中将字符串解析为路径的方法
this.configLocations[i] = resolvePath(locations[i]).trim();
}
}
else {
this.configLocations = null;
}
}
通过这两个方法的源码可以看出,既可以使用一个字符串来配置多个 Spring Bean 定义资源文件,也可以使用字符串数组,即下面两种方式都是可以的:
ClasspathResource res = new ClasspathResource(“a.xml,b.xml,……”);
ClasspathResource res = new ClasspathResource(newString[]{“a.xml”,”b.xml”,……});
解析路径时,用到三个主要的类:
-
PropertyResolver:属性解析器,提供属性访问功能
-
ConfigurablePropertyResolver:可配置的属性解析器,主要提供属性类型转换器
-
Environment:当前运行环境,提供访问和判断配置文件(profiles)的功能和运行环境属性(PropertyResolver解析运行属性)。
-
ConfigurableEnvironment:提供设置激活的profile和默认的profile的功能
-
AbstractEnvironment:默认属性和存储容器的定义
-
StandardEnvironment:除了ConfigurableEnvironment通用的属性解析和profile相关的操作外,还提供了系统属性(system properties) 和系统环境变量( system environment variables) 两个属性
-
PropertyPlaceholderHelper:占位符解析器
protected String parseStringValue(
String value,
PlaceholderResolver placeholderResolver,
Set<String> visitedPlaceholders) {
StringBuilder result = new StringBuilder(value);
//获取路径中占位符前缀的索引
int startIndex = value.indexOf(this.placeholderPrefix);
while (startIndex != -1) {
int endIndex = findPlaceholderEndIndex(result, startIndex);
if (endIndex != -1) {
//截取前缀占位符和后缀占位符之间的字符串placeholder
String placeholder =
result.substring(startIndex + this.placeholderPrefix.length(),
endIndex);
String originalPlaceholder = placeholder;
//递归调用,继续解析placeholder
placeholder = parseStringValue(placeholder,
placeholderResolver, visitedPlaceholders);
//获取placeholder的值
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
if (propVal == null && this.valueSeparator != null) {
int separatorIndex = placeholder.indexOf(this.valueSeparator);
if (separatorIndex != -1) {
String actualPlaceholder =
placeholder.substring(0, separatorIndex);
String defaultValue =
placeholder.substring(separatorIndex +
this.valueSeparator.length());
propVal = placeholderResolver
.resolvePlaceholder(actualPlaceholder);
if (propVal == null) {
propVal = defaultValue;
}
}
}
if (propVal != null) {
//对替换完成的value进行解析,防止properties的value值里也有占位符
propVal = parseStringValue(propVal,
placeholderResolver, visitedPlaceholders);
result.replace(startIndex,
endIndex + this.placeholderSuffix.length(), propVal);
//重新定位开始索引
startIndex = result.indexOf(this.placeholderPrefix,
startIndex + propVal.length());
}
...
}
visitedPlaceholders.remove(originalPlaceholder);
}
else {
startIndex = -1;
}
}
return result.toString();
}
protected String resolvePlaceholder(String placeholder,
Properties props,
int systemPropertiesMode) {
String propVal = null;
if (systemPropertiesMode == SYSTEM_PROPERTIES_MODE_OVERRIDE) {
propVal = resolveSystemProperty(placeholder);
}
if (propVal == null) {
propVal = resolvePlaceholder(placeholder, props);
}
if (propVal == null && systemPropertiesMode == SYSTEM_PROPERTIES_MODE_FALLBACK) {
propVal = resolveSystemProperty(placeholder);
}
return propVal;
}
2.2.2.2 refresh 函数载入 Bean 定义
refresh()是一个模板方法,作用是:在创建 IOC 容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在 refresh 之后使用的是新建立起来的 IOC 容器。refresh 的作用类似于对 IOC 容器的重启,在
新建立好的容器中对容器进行初始化,对 Bean 定义资源进行载入。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//准备刷新上下文环境
//获取容器的当时时间,同时给容器设置同步标识
prepareRefresh();
//告诉子类启动 refreshBeanFactory()方法,
//初始化BeanFactory,并进行xml文件读取
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//为 BeanFactory 配置容器特性,例如类加载器、事件处理器等
prepareBeanFactory(beanFactory);
try {
//为容器的某些子类指定特殊的 BeanPost 事件处理器
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;
} finally {
resetCommonCaches();
}
}
}
AbstractApplicationContext的obtainFreshBeanFactory()方法调用子类容器的refreshBeanFactory()
方法,启动容器载入 Bean 定义资源文件的过程,代码如下:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//这里使用了委派设计模式,父类定义了抽象的 refreshBeanFactory()方法,
//具体实现调用子类容器的 refreshBeanFactory()方法
refreshBeanFactory();
return getBeanFactory();
}
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {//如果已经有容器,销毁容器中的 bean,关闭容器
destroyBeans();
closeBeanFactory();
}
try {
//创建 IOC 容器
//创建BeanFactory,同时关联父亲BeanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
//对 IOC 容器进行定制化,
//包括是否允许覆盖同名称的不同定义的对象、是否允许循环依赖、
customizeBeanFactory(beanFactory);
//初始化DocumentReader,并进行XML文件读取和解析
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
}
2.2.2.3 loadBeanDefinitions 方法
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);
}
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);
}
}
FileSystemXmlApplicationContext 作为例子分析,因此 getConfigResources 的返回值为 null,因此程序执行 reader.loadBeanDefinitions(configLocations)分支。
2.2.2.4 读取 Bean 定义资源
public int loadBeanDefinitions(String location,
@Nullable Set<Resource> actualResources)
throws BeanDefinitionStoreException {
//获取在 IoC 容器初始化过程中设置的资源加载器
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader instanceof ResourcePatternResolver) {
//将指定位置的 Bean 定义资源文件解析为 Spring IOC 容器封装的资源
//加载多个指定位置的 Bean 定义资源文件
try {
Resource[] resources =
((ResourcePatternResolver) resourceLoader).getResources(location);
//委派调用其子类 XmlBeanDefinitionReader 的方法,实现加载功能
int count = loadBeanDefinitions(resources);
if (actualResources != null) {
Collections.addAll(actualResources, resources);
}
return count;
}
}
else {
//将指定位置的 Bean 定义资源文件解析为 Spring IOC 容器封装的资源
//加载单个指定位置的 Bean 定义资源文件
Resource resource = resourceLoader.getResource(location);
//委派调用其子类 XmlBeanDefinitionReader 的方法,实现加载功能
int count = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
return count;
}
}
从对 AbstractBeanDefinitionReader 的 loadBeanDefinitions 方法源码分析可以看出该方法做了以下
两件事:
- 调用资源加载器的获取资源方法 resourceLoader.getResource(location),获取到要加载的资源
- 真正执行加载功能是其子类 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法。
2.2.2.5 资源加载器获取要读入的资源
public Resource getResource(String location) {
for (ProtocolResolver protocolResolver : this.protocolResolvers) {
Resource resource = protocolResolver.resolve(location, this);
if (resource != null) {
return resource;
}
}
//如果是类路径的方式,那需要使用 ClassPathResource 来得到 bean 文件的资源对象
if (location.startsWith("/")) {
return getResourceByPath(location);
}
else if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(
location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// 如果是 URL 方式,使用 UrlResource 作为 bean 文件的资源对象
URL url = new URL(location);
return (ResourceUtils.isFileURL(url) ?
new FileUrlResource(url) : new UrlResource(url));
}
catch (MalformedURLException ex) {
//如果既不是 classpath 标识,又不是 URL 标识的 Resource 定位,
//则调用容器本身的 getResourceByPath 方法获取 Resource
return getResourceByPath(location);
}
}
}
FileSystemXmlApplicationContext 容器提供了 getResourceByPath 方法的实现,就是为了处理既不是
classpath 标识,又不是 URL 标识的 Resource 定位这种情况。
protected Resource getResourceByPath(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemResource(path);
}
代码就回到了 FileSystemXmlApplicationContext 中来,它提供了 FileSystemResource 来完成从文件系统得到配置文件的资源定义,这样就可以从文件系统路径上对 IOC 配置文件进行加载。
当然我们可以按照这个逻辑从任何地方加载,在 Spring 中我们看到它提供的各种资源抽象,比如ClassPathResource, URLResource,FileSystemResource 等来供我们使用。
2.2.2.6 XmlBeanDefinitionReader 加载定义资源
重新回到 XmlBeanDefinitionReader 的 loadBeanDefinitions(Resource …)方法看到代表 bean 文
件的资源定义以后的载入过程。
public int loadBeanDefinitions(Resource resource)
throws BeanDefinitionStoreException {
//将读入的 XML 资源进行特殊编码处理
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource)
throws BeanDefinitionStoreException {
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());
}
}
}
//从特定 XML 文件中实际载入 Bean 定义资源的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//将 XML 文件转换为 DOM 对象,解析过程由 documentLoader 实现
Document doc = doLoadDocument(inputSource, resource);
//这里是启动对 Bean 定义解析的详细过程,该解析过程会用到 Spring 的 Bean 配置规则
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
}
载入 Bean 定义资源文件的最后一步是将 Bean 定义资源转换为 Document 对象,该过程由 documentLoader 实现。
2.2.2.7 DocumentLoader 将定义资源转换为 Document 对象
public Document loadDocument(InputSource inputSource,
EntityResolver entityResolver,
ErrorHandler errorHandler,
int validationMode,
boolean namespaceAware) throws Exception {
//创建文件解析器工厂
DocumentBuilderFactory factory =
createDocumentBuilderFactory(validationMode, namespaceAware);
//创建文档解析器
DocumentBuilder builder =
createDocumentBuilder(factory, entityResolver, errorHandler);
//解析 Spring 的 Bean 定义资源
return builder.parse(inputSource);
}
2.2.2.8 XmlBeanDefinitionReader 解析载入定义资源文件
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;
}
2.2.2.9 DefaultBeanDefinitionDocumentReader 对 Bean 定义的 Document 对象解析
//根据 Spring DTD 对 Bean 的定义规则解析 Bean 定义 Document 对象
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
//获得 XML 描述符
this.readerContext = readerContext;
//获得 Document 的根元素
doRegisterBeanDefinitions(doc.getDocumentElement());
}
protected void doRegisterBeanDefinitions(Element root) {
//具体的解析过程由 BeanDefinitionParserDelegate 实现,
//BeanDefinitionParserDelegate 中定义了 Spring Bean 定义 XML 文件的各种元素
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
//在解析 Bean 定义之前,进行自定义的解析,增强解析过程的可扩展性
preProcessXml(root);
//从 Document 的根元素开始进行 Bean 定义的 Document 对象
parseBeanDefinitions(root, this.delegate);
//在解析 Bean 定义之后,进行自定义的解析,增加解析过程的可扩展性
postProcessXml(root);
this.delegate = parent;
}
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);
}
}
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);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(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());
}
//在完成向 Spring IoC 容器注册解析得到的 Bean 定义之后,发送注册事件
getReaderContext().fireComponentRegistered(new
BeanComponentDefinition(bdHolder));
}
}
2.2.2.10 BeanDefinitionParserDelegate 解析 Bean 定义资源文件中的元素
Bean 定义资源文件中的和元素解析在 DefaultBeanDefinitionDocumentReader 中已经完成,对 Bean 定义资源文件中使用最多的元素交由 BeanDefinitionParserDelegate 来解析,其解析实现的源码如下:
//解析 Bean 定义资源文件中的<Bean>元素
//这个方法中主要处理<Bean>元素的 id,name和别名属性
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele,
@Nullable BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<>();
//将<Bean>元素中的所有 name 属性值存放到别名中
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr,
MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
//如果<Bean>元素中没有配置 id 属性时,将别名中的第一个值赋值给 beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
}
//检查<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);
}
}
}
}
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,
@Nullable 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();
}
String parent = null;
//如果<Bean>元素中配置了 parent 属性,则获取 parent 属性的值
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
//根据<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;
}
//解析<Bean>元素出错时,返回 null
return null;
}
注意:在解析元素过程中没有创建和实例化 Bean 对象,只是创建了 Bean 对象的定义类
BeanDefinition,将元素中的配置信息设置到 BeanDefinition 中作为记录,当依赖注入时才使
用这些记录信息创建和实例化具体的 Bean 对象
接下来BeanDefinitionParserDelegate 会解析元素以及子元素、解析子元素。
通过 Spring IOC 容器对 Bean 定义资源的解析后,IOC 容器大致完成了管理 Bean 对象的准备工作,即
初始化过程,但是***最为重要的依赖注入还没有发生***,现在在 IOC 容器中 BeanDefinition 存储的只是一
些静态信息,接下来需要向容器注册 Bean 定义信息才能全部完成 IoC 容器的初始化过程
2.2.2.11 解析过后的 BeanDefinition 在 IOC 容器中的注册
第9步后面还有注册任务,BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
//将解析的 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 alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
当调用 BeanDefinitionReaderUtils 向 IOC 容器注册解析的 BeanDefinition 时,真正完成注册功能的是 DefaultListableBeanFactory。
2.2.2.12 DefaultListableBeanFactory 向 IOC 容器注册解析后的 BeanDefinition
DefaultListableBeanFactory 中使用一个 HashMap 的集合对象存放 IOC 容器中注册解析的
BeanDefinition,向 IOC 容器注册的主要源码如下:
//存储注册信息的 BeanDefinition
private static final Map<String,
Reference<DefaultListableBeanFactory>> serializableFactories
= new ConcurrentHashMap<>(8);
//向 IoC 容器注册解析的 BeanDefiniton
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
//校验解析的 BeanDefiniton
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
}
//检查是否有同名的 BeanDefinition 已经在 IOC 容器中注册,如果已经注册,
//并且不允许覆盖已注册的 Bean,则抛出注册失败异常
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(
beanName,
beanDefinition,
existingDefinition);
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {//创建bean 已经完成
//注册的过程中需要线程同步,以保证数据的一致性
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions =
new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
Set<String> updatedSingletons =
new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
//IOC 容器中没有已经注册同名的 Bean,按正常注册流程注册
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
//重置所有已经注册过的 BeanDefinition 的缓存
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
至此,Bean 定义资源文件中配置的 Bean 被解析过后,已经注册到 IOC 容器中,被容器管理起来,真正
完成了 IOC 容器初始化所做的全部工作。现在 IOC 容器中已经建立了整个 Bean 的配置信息,这些
BeanDefinition 信息已经可以使用,并且可以被检索,IOC 容器的作用就是对这些注册的 Bean 定义信
息进行处理和维护。这些的注册的 Bean 定义信息是 IoC 容器控制反转的基础,正是有了这些注册的数
据,容器才可以进行依赖注入。
此外,在使用 SpringIOC 容器的时候我们还需要区别两个概念:
- BeanFactory 其中 BeanFactory 指的是 IOC 容器的编程抽象,比如ApplicationContext,XmlBeanFactory 等,这些都是 IOC 容器的具体表现,需要使用什么样的容器由客户决定,但 Spring 为我们提供了丰富的选择。
- FactoryBean,FactoryBean 只是一个可以在 IOC 而容器中被管理的一个bean,是对各种处理过程和资源使用的抽象,FactoryBean 在需要时产生另一个对象,而不返回FactoryBean 本身,我们可以把它***看成是一个抽象工厂,对它的调用返回的是工厂生产的产品***。Spring 包括了大部分的通用资源和服务访问抽象的 FactoryBean 的实现,其中包括:对 JNDI 查询的处理,对代理对象的处理,对事务性代理的处理,对 RMI 代理的处理等,这些我们都可以看成是具体的工厂,看成是 Spring 为我们建立好的工厂。也就是说 Spring 通过使用抽象工厂模式为我们准备了一系列工厂来生产一些特定的对象,免除我们手工重复的工作,我们要使用时只需要在 IOC 容器里配置好就能很方便的使用了。