Spring 是一个开源的轻量级 Java SE(Java 标准版本)/Java EE(Java 企业版本)开发应用框架,其目的是用于
简化企业级应用程序开发 。
Spring 的特点:
Spring 简化Java开发,主要采用了4个关键策略:
主要是通过:面向 Bean、依赖注入以及面向切面这三种方式来达成的
@AutoWrite
private InterfaceA a;//自动把实现类注入进来
@Resource("aaa")
private A a;//IOC容器中类的id为aaa对象自动注入进来,可以区分父子类
@AutoWrite
private A a;//根据类型自动注入
以下是 Spring 4 的系统架构图。
java.lang.instrument
进行设计的,应该算是Aop 的一个支援模块,主要作用是在 JVM 启用时,生成一个代理类,程序员通过代理类在运行时修改类的字节,从而改变一个类的功能,实现 Aop 的功能)组成源码解析从 spring-core 入手,其次是 spring-beans 和spring-aop,随后是 spring-context,再其次是 spring-tx 和 spring-orm,最后是 spring-web 和其他部分。
先来看下两个基本概念:
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。依赖关系如下图所示:
Spring是面向Bean的编程,所以解析Spring以围绕Bean展开,首先说下Bean的作用域,有以下几种:
singleton(IOC容器每次都返回同一个实例)
prototype(IOC容器每次产生一个新的实例)
request(每次HTTP请求都会创建一个新的Bean,适用于WebApplicationContext环境)
session(同一个Session共享一个Bean实例。不同Session使用不同的实例)
global-session(所有的Session共享一个Bean实例)
BeanFactory 作为最顶层的一个接口类,它定义了 IOC 容器的基本功能规范,有三个子类:
另外,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 容器的基本功能外,还为用户提供了以下的附加服务,比如:
SpringIOC 容器管理了我们定义的各种 Bean 对象及其相互的关系,Bean 对象在 Spring 实现中是以
BeanDefinition 来描述的,其继承体系如下:
Bean 的解析主要就是对 Spring 配置文件的解析。这个解析过程主要通过下图中的类完成:
IOC 容器的初始化包括 BeanDefinition 的 Resource **定位、载入和注册**这三个基本的过程。以
ApplicationContext 为例讲解,XmlWebApplicationContext 、ClasspathXmlApplicationContext就属于这个继承体系。
ApplicationContext 允许上下文嵌套,通过保持父上下文可以维持一个上下文体系。对于 bean 的查找
可以在这个上下文体系中发生,首先检查当前上下文,其次是父上下文,逐级向上,这样为不同的 Spring
应用提供了一个共享的 bean 定义环境。下面解析一下四种加载该文件方式的过程:
通过 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);
此处依据 FileSystemXmlApplicationContext为例进行解析,构造函数如下:
public FileSystemXmlApplicationContext(
String[] configLocations,
boolean refresh,
@Nullable ApplicationContext parent) throws BeansException {
//设置 Bean 资源加载器
super(parent);
//设置 Bean 定义资源文件的定位路径
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
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;
}
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;
}
}
}
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)分支。
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 方法源码分析可以看出该方法做了以下
两件事:
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 等来供我们使用。
重新回到 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 实现。
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);
}
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;
}
//根据 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) {
//如果元素节点是导入元素,进行导入解析
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
//如果元素节点是别名元素,进行别名解析
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
//元素节点既不是导入元素,也不是别名元素,即普通的元素,
//按照 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 对象中元素的解析
//由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));
}
}
Bean 定义资源文件中的和元素解析在 DefaultBeanDefinitionDocumentReader 中已经完成,对 Bean 定义资源文件中使用最多的元素交由 BeanDefinitionParserDelegate 来解析,其解析实现的源码如下:
//解析 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<>();
//将元素中的所有 name 属性值存放到别名中
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr,
MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
//如果元素中没有配置 id 属性时,将别名中的第一个值赋值给 beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
}
//检查元素所配置的 id 或者 name 的唯一性,
//containingBean 标识元素中是否包含子元素
if (containingBean == null) {
//检查元素所配置的 id、name 或者别名是否重复
checkNameUniqueness(beanName, aliases, ele);
}
//详细对元素中配置的 Bean 定义进行解析的地方
AbstractBeanDefinition beanDefinition =
parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
//如果元素中没有配置 id、别名或者 name,且没有包含子元素
//元素,为解析的 Bean 生成一个唯一 beanName 并注册
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
//如果元素中没有配置 id、别名或者 name,且包含了子元素
//元素,为解析的 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 的 id、name 和别名等属性进行了处理,
//该方法中主要处理除这三个以外的其他属性数据
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele,
String beanName,
@Nullable BeanDefinition containingBean) {
//记录解析的
this.parseState.push(new BeanEntry(beanName));
//这里只读取元素中配置的 class 名字,然后载入到 BeanDefinition 中去
//只是记录配置的 class 名字,不做实例化,对象的实例化在依赖注入时完成
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
String parent = null;
//如果元素中配置了 parent 属性,则获取 parent 属性的值
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
//根据元素配置的 class 名称和 parent 属性值创建 BeanDefinition
//为载入 Bean 定义信息做准备
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//对当前的元素中配置的一些属性进行解析和设置,如配置的单态(singleton)属性等
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
//为元素解析的 Bean 设置 description 信息
bd.setDescription(
DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//对元素的 meta(元信息)属性解析
parseMetaElements(ele, bd);
//对元素的 lookup-method 属性解析
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//对元素的 replaced-method 属性解析
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析元素的构造方法设置
parseConstructorArgElements(ele, bd);
//解析元素的设置
parsePropertyElements(ele, bd);
//解析元素的 qualifier 属性
parseQualifierElements(ele, bd);
//为当前解析的 Bean 设置所需的资源和依赖对象
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
//解析元素出错时,返回 null
return null;
}
注意:在解析元素过程中没有创建和实例化 Bean 对象,只是创建了 Bean 对象的定义类
BeanDefinition,将元素中的配置信息设置到 BeanDefinition 中作为记录,当依赖注入时才使
用这些记录信息创建和实例化具体的 Bean 对象
接下来BeanDefinitionParserDelegate 会解析元素以及子元素、解析子元素。
通过 Spring IOC 容器对 Bean 定义资源的解析后,IOC 容器大致完成了管理 Bean 对象的准备工作,即
初始化过程,但是***最为重要的依赖注入还没有发生***,现在在 IOC 容器中 BeanDefinition 存储的只是一
些静态信息,接下来需要向容器注册 Bean 定义信息才能全部完成 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。
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 容器的时候我们还需要区别两个概念: