本系列大量参考Spring IOC 容器源码分析、 【死磕 Spring】—– IOC 总结
文章内容如下:
1.IOC的入口
1.1 ApplicationContext的介绍
1.2 ClassPathXmlApplicationContext
2. AbstractApplicationContext#refresh,初始化容器和bean
2.1 AbstractApplicationContext#prepareRefresh 标记“已启动”状态、处理配置文件中的占位符
2.2 AbstractApplicationContext#obtainFreshBeanFactory() 创建BeanFactory(包括解析得到BeanDefinition)
2.2.1 AbstractRefreshableApplicationContext#refreshBeanFactory 刷新(重建)BeanFactory
2.2.2 这里先解决,为什么创建的是DefaultListableBeanFactory?
2.2.3 来看一下BeanDefinition接口的实现
//加载 BeanDefinition 们
(1)AbstractXmlApplicationContext#loadBeanDefinitions(beanFactory)
//最后返回的是加载BeanDefinition 数量
(2)AbstractBeanDefinitionReader#loadBeanDefinitions(Resource... resources)
//避免加载自身的死循环,调用加载Bean的真正逻辑
(3)XmlBeanDefinitionReader#(Resource resource)
//获取XML Document实例,注册Bean信息
(4)XmlBeanDefinitionReader #doLoadBeanDefinitions(InputSource inputSource, Resource resource)
//执行注册BeanDefinition,解析文件
(5)DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions,
//根据命名空间解析
(6)DefaultBeanDefinitionDocumentReader#parseBeanDefinitions,
//解析默认命名空间标签,在调用的类中还会进行 BeanDefinition 的注册
(7)DefaultBeanDefinitionDocumentReader#parseDefaultElement
//解析bean标签,设置beanName
(8)BeanDefinitionParserDelegate #parseBeanDefinitionElement
//真正解析标签的方法
(9)BeanDefinitionParserDelegate#parseBeanDefinitionElement
【注,这两个(8)(9)是对(7)中调用的不同方法的分析,(8)(9)可类比为栈深】
// 通过beanName和alias来执行不同的方法注册bean
(8)BeanDefinitionReaderUtils#registerBeanDefinition
//真正通过beanName注册的方法
(9)DefaultListableBeanFactory#registerBeanDefinition
3.总结
1.IOC的入口
1.1 ApplicationContext的介绍
IOC 总体来说有两处地方最重要,一个是创建 Bean 容器,一个是初始化 Bean,先从启动Spring容器的例子来开始吧。从前文的组件我们了解到,容器有BeanFactor和ApplicationContext两类(注意是类而不是两种)。这边我们以介绍ApplicationContext为主。
对于Spring项目,一般我们在main方法里会这么写,
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationfile.xml");
}
1.2 ClassPathXmlApplicationContext
继续进去看看new的这个ClassPathXmlApplicationContext做了什么吧
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
this(new String[] {configLocation}, true, null);
}
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
这边就进入了refresh
方法的逻辑,此方法是核心方法,用以初始化容器,初始化bean
,后面有关IOC源码的分析都是围绕这个方法展开的。
2.AbstractApplicationContext#refresh,初始化容器和bean
public void refresh() throws BeansException, IllegalStateException {
// 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛
synchronized (this.startupShutdownMonitor) {
// 准备工作,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符
prepareRefresh();
// 这步比较关键,这步完成后,配置文件就会解析成一个个 Bean 定义,注册到 BeanFactory 中,
// 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了,
// 注册也只是将这些信息都保存到了注册中心(说到底核心是一个 beanName-> beanDefinition 的 map)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
// 这块待会会展开说
prepareBeanFactory(beanFactory);
try {
// 【这里需要知道 BeanFactoryPostProcessor 这个知识点,Bean 如果实现了此接口,
// 那么在容器初始化以后,Spring 会负责调用里面的 postProcessBeanFactory 方法。】
// 这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化
// 具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做点什么事
postProcessBeanFactory(beanFactory);
// 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
invokeBeanFactoryPostProcessors(beanFactory);
// 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别
// 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
// 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
registerBeanPostProcessors(beanFactory);
// 初始化当前 ApplicationContext 的 MessageSource,国际化这里就不展开说了,不然没完没了了
initMessageSource();
// 初始化当前 ApplicationContext 的事件广播器,这里也不展开了
initApplicationEventMulticaster();
// 从方法名就可以知道,典型的模板方法(钩子方法),
// 具体的子类可以在这里初始化一些特殊的 Bean(在初始化 singleton beans 之前)
onRefresh();
// 注册事件监听器,监听器需要实现 ApplicationListener 接口。这也不是我们的重点,过
registerListeners();
// 重点,重点,重点
// 初始化所有的 singleton beans
//(lazy-init 的除外)
finishBeanFactoryInitialization(beanFactory);
// 最后,广播事件,ApplicationContext 初始化完成
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
// 销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// 把异常往外抛
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
2.1 AbstractApplicationContext#prepareRefresh 标记“已启动”状态、处理配置文件中的占位符
protected void prepareRefresh() {
// 记录启动时间
this.startupDate = System.currentTimeMillis();
// 设置关闭状态为 false 、开启状态为 true,这两个都是AtomicBoolean
//这两个标记为当前Context的状态
this.closed.set(false);
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
} else {
logger.debug("Refreshing " + getDisplayName());
}
}
// 初始化 PropertySource ,由子类覆盖实现。默认实现为空。
initPropertySources();
// 验证所需属性,是否已经放到环境中
getEnvironment().validateRequiredProperties();
// 初始化早期的 ApplicationEvent 集合。
// 因为此时,ApplicationMulticaster 还没创建好。
this.earlyApplicationEvents = new LinkedHashSet<>();
}
这个方法只是简单的创建 Bean 容器前的准备工作
- 1.负责将spring容器设置相应的状态
- 2.初始化上下文环境,对系统的环境变量或者系统属性进行准备和校验,比如如环境变量中必须设置某个值才能运行,否则不能运行,这个时候可以在这里加这个校验,重写
initPropertySources
方法就好了。例子:spring4.1.8扩展实战之一:自定义环境变量验证- 3.在
initPropertySources
方法中设定值后,getEnvironment().validateRequiredProperties()
会从集合requiredProperties
中取出所有key(这是initPropertySources
方法注入进来的值),然后获取这些key
的环境变量(包括系统环境变量和进程环境变量),如果有一个key
对应的环境变量为空,就会抛出异常,导致spring容器初始化失败;也就是说这两行代码的联动的,在前面一行注入一个必要的值,后面一行对这个值进行校验
2.2 AbstractApplicationContext#obtainFreshBeanFactory() 创建BeanFactory(包括解析得到BeanDefinition)
我们回到refresh()
方法中的下一行 obtainFreshBeanFactory()
。
注意,这个方法是全文最重要的部分之一,这里将会初始化BeanFactory
、加载 Bean
、注册BeanDefinition
等等。也会在此延伸出去一些概念。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 刷新( 重建 ) BeanFactory
refreshBeanFactory();
// 获得 BeanFactory
return getBeanFactory();
}
这两个方法都是抽象方法,交由子类实现,先来看他的refreshBeanFactory
在他的子类里有如下代码:
2.2.1 AbstractRefreshableApplicationContext#refreshBeanFactory 刷新(重建)BeanFactory
protected final void refreshBeanFactory() throws BeansException {
// 若已有 BeanFactory ,销毁它的 Bean 们,并销毁 BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 创建 BeanFactory 对象
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 指定序列化编号
beanFactory.setSerializationId(getId());
// 设置 BeanFactory 的两个配置属性:是否允许 Bean 覆盖、是否允许循环引用(后面再讲)
customizeBeanFactory(beanFactory);
// 加载 BeanDefinition 们
loadBeanDefinitions(beanFactory);
// 设置 Context 的 BeanFactory
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
} catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
- 判断当前容器是否存在一个
BeanFactory
,如果存在则对其进行销毁和关闭
- 调用
createBeanFactory()
创建一个BeanFactory
实例,其实就是DefaultListableBeanFactory
- 自定义
BeanFactory
的属性
- 加载
BeanDefinition
- 将创建好的 bean 工厂的引用交给的
ApplicationContext
来管理
2.2.2 这里先解决,为什么创建的是DefaultListableBeanFactory?
看这链路就很明显了,他一个人把这几个接口、抽象类都继承了,拥有所有的功能。
而设置完BeanFactory
的属性后调用了loadBeanDefinitions(beanFactory)
来加载BeanDefinition,
在此之前我们仍需先介绍一下
什么是BeanDefinition
?
BeanDefinition
中保存了我们的 Bean 信息,比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等。
Bean 在代码层面上可以简单认为是BeanDefinition
的实例,注意哦,此时Bean还没实例化
2.2.3 来看一下BeanDefinition接口的实现
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
// 我们可以看到,默认只提供 sington 和 prototype 两种,
// 很多读者可能知道还有 request, session, globalSession, application, websocket 这几种,
// 不过,它们属于基于 web 的扩展。
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
// 比较不重要,直接跳过吧
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
// 设置父 Bean,这里涉及到 bean 继承,不是 java 继承。请参见附录的详细介绍
// 一句话就是:继承父 Bean 的配置信息而已
void setParentName(String parentName);
// 获取父 Bean
String getParentName();
// 设置 Bean 的类名称,将来是要通过反射来生成实例的
void setBeanClassName(String beanClassName);
// 获取 Bean 的类名称
String getBeanClassName();
// 设置 bean 的 scope
void setScope(String scope);
String getScope();
// 设置是否懒加载
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
// 设置该 Bean 依赖的所有的 Bean,注意,这里的依赖不是指属性依赖(如 @Autowire 标记的),
// 是 depends-on="" 属性设置的值。
void setDependsOn(String... dependsOn);
// 返回该 Bean 的所有依赖
String[] getDependsOn();
// 设置该 Bean 是否可以注入到其他 Bean 中,只对根据类型注入有效,
// 如果根据名称注入,即使这边设置了 false,也是可以的
void setAutowireCandidate(boolean autowireCandidate);
// 该 Bean 是否可以注入到其他 Bean 中
boolean isAutowireCandidate();
// 主要的。同一接口的多个实现,如果不指定名字的话,Spring 会优先选择设置 primary 为 true 的 bean
void setPrimary(boolean primary);
// 是否是 primary 的
boolean isPrimary();
// 如果该 Bean 采用工厂方法生成,指定工厂名称。对工厂不熟悉的读者,请参加附录
// 一句话就是:有些实例不是用反射生成的,而是用工厂模式生成的
void setFactoryBeanName(String factoryBeanName);
// 获取工厂名称
String getFactoryBeanName();
// 指定工厂类中的 工厂方法名称
void setFactoryMethodName(String factoryMethodName);
// 获取工厂类中的 工厂方法名称
String getFactoryMethodName();
// 构造器参数
ConstructorArgumentValues getConstructorArgumentValues();
// Bean 中的属性值,后面给 bean 注入属性值的时候会说到
MutablePropertyValues getPropertyValues();
// 是否 singleton
boolean isSingleton();
// 是否 prototype
boolean isPrototype();
// 如果这个 Bean 是被设置为 abstract,那么不能实例化,
// 常用于作为 父bean 用于继承,其实也很少用......
boolean isAbstract();
int getRole();
String getDescription();
String getResourceDescription();
BeanDefinition getOriginatingBeanDefinition();
}
这里接口虽然那么多,但是没有类似 getInstance()
这种方法来获取我们定义的类的实例,真正的我们定义的类生成的实例到哪里去了呢?别着急,这个要很后面才能讲到。
先来看这个 loadBeanDefinitions(beanFactory)
,同样的这个也是抽象方法,交由子类实现,
在AbstractXmlApplicationContext
中会有其实现方法,
(1) AbstractXmlApplicationContext# loadBeanDefinitions(beanFactory) 加载 BeanDefinition 们
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// 创建 XmlBeanDefinitionReader 对象
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 对 XmlBeanDefinitionReader 进行环境变量的设置
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 对 XmlBeanDefinitionReader 进行设置,可以进行覆盖
initBeanDefinitionReader(beanDefinitionReader);
// 从 Resource 们中,加载 BeanDefinition 们
loadBeanDefinitions(beanDefinitionReader);
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
// 从配置文件 Resource 中,加载 BeanDefinition 们
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
// 从配置文件地址中,加载 BeanDefinition 们
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
至此我们知道了,在这一步就会加载BeanDefinition
了,本篇接下来的内容就是简单分析是如何读取XML文件来加载成一个BeanDefinition
的。
上面虽有两个分支,属于重载方法,但是最后都是会调用到
AbstractBeanDefinitionReader#loadBeanDefinitions(Resource... resources)
的,接下来就来分析这个方法
(2) AbstractBeanDefinitionReader#loadBeanDefinitions(Resource... resources) 最后返回的是加载BeanDefinition 数量
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int count = 0;
// 注意这里是个 for 循环,也就是每个文件是一个 resource
for (Resource resource : resources) {
count += loadBeanDefinitions(resource);
}
// 最后返回 counter,表示总共加载了多少的 BeanDefinition
return count;
}
//省略其他代码
}
会接着调用
(3) XmlBeanDefinitionReader#loadBeanDefinitions(Resource resource) 避免加载自身的死循环,调用加载Bean的真正逻辑
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}
// 获取已经加载过的资源
Set currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
// 将当前资源加入记录中。如果已存在,抛出异常
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
// 从 EncodedResource 获取封装的 Resource ,并从 Resource 中获取其中的 InputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) { // 设置编码
inputSource.setEncoding(encodedResource.getEncoding());
}
// 核心逻辑部分,执行加载 BeanDefinition
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
inputStream.close();
}
} catch (IOException ex) {
throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), ex);
} finally {
// 从缓存中剔除该资源
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
//省略其他代码
}
这边解释下为什么当资源加入记录,已存在的时候回抛出异常,这是为了避免一个 EncodedResource
在加载时,还没加载完成,又加载自身,从而导致死循环。
也因此,当一个EncodedResource
加载完成后,需要从缓存中剔除。
这个方法从encodedResource
获取封装的Resource
资源,并从Resource
中获取相应的 InputStream
,然后将InputStream
封装为 InputSource
,最后调用 XmlBeanDefinitionReader#doLoadBeanDefinitions(InputSource inputSource, Resource resource)
方法,执行加载 Bean Definition
的真正逻辑
(4) XmlBeanDefinitionReader #doLoadBeanDefinitions(InputSource inputSource, Resource resource),获取XML Document实例,注册Bean信息
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 获取 XML Document 实例
Document doc = doLoadDocument(inputSource, resource);
// 根据 Document 实例,注册 Bean 信息
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
} catch (BeanDefinitionStoreException ex) {
throw ex;
} catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
} catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
} catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
} catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
} catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 创建 BeanDefinitionDocumentReader 对象
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
// 获取已注册的 BeanDefinition 数量
int countBefore = getRegistry().getBeanDefinitionCount();
// 创建 XmlReaderContext 对象
// 注册 BeanDefinition
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
// 计算新注册的 BeanDefinition 数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
//省略其他代码
}
这边就主要是两个方法,
-
doLoadDocument()
根据 xml 文件,获取Document
实例。 -
registerBeanDefinitions()
一个根据获取的Document
实例,注册Bean
信息。
这边对于
doLoadDocument()
方法做一个简单的描述:
其调用的getValidationModeForResource
方法主要是为了获取指定资源(xml)的验证模式。XML
文件的验证模式保证了XML
文件的正确性。常见的有DTD和XSD两种
而后通过解析器(EntityResolver
)来解析InputSource
,返回Document
对象。在这里多提一点,在
XML
文件上的约束,就是下图这个,所以我们虽然在Spring
的XML
配置中看到的约束文件是一个在线地址,实际上约束文件是从本地 jar 中读取的,EntityResolver
也是根据这个来解析的。参考链接:Spring 源码第三弹!EntityResolver 是个什么鬼?
说过了doLoadDocument(inputSource, resource)
,再来聊聊registerBeanDefinitions(doc, resource)
是怎么注册bean信息的,这边的注释已经比较清晰了,主要注意这个同名的方法,他会调用
DefaultBeanDefinitionDocumentReader
里的方法
(5)DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions,执行注册BeanDefinition,解析文件
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
// 获得 XML Document Root Element
// 执行注册 BeanDefinition
// 从 xml 根节点开始解析文件
doRegisterBeanDefinitions(doc.getDocumentElement());
}
protected void doRegisterBeanDefinitions(Element root) {
// 记录老的 BeanDefinitionParserDelegate 对象(为了递归处理)
//负责解析 XML Element 的各种方法
// 因为 内部是可以定义 的,所以这个方法的 root 其实不一定就是 xml 的根节点,
//也可以是嵌套在里面的 节点,从源码分析的角度,我们当做根节点就好了
BeanDefinitionParserDelegate parent = this.delegate;
// 创建 BeanDefinitionParserDelegate 对象,并进行设置到 delegate
this.delegate = createDelegate(getReaderContext(), root, parent);
//检查 根标签的命名空间是否为空
if (this.delegate.isDefaultNamespace(root)) {
// 处理 profile 属性。
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
// 使用分隔符切分,可能有多个 profile 。
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// 如果所有 profile 都无效,则不进行注册
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
// 解析前处理
preProcessXml(root);
// 解析
parseBeanDefinitions(root, this.delegate);
// 解析后处理
postProcessXml(root);
// 设置 delegate 回老的 BeanDefinitionParserDelegate 对象
this.delegate = parent;
}
//省略其他代码
}
对于代码里的profile
不熟悉的可以参见这篇博客 Spring3自定义环境配置
这个方法只是对profile
进行了处理,真正的执行逻辑还是在parseBeanDefinitions(root, this.delegate);
中,另外解析前后的处理是空方法,可以交由子类去自己实现。这边就不看了。
(6)DefaultBeanDefinitionDocumentReader#parseBeanDefinitions,根据命名空间解析
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
// 如果根节点使用默认命名空间,执行默认解析
if (delegate.isDefaultNamespace(root)) {
// 遍历子节点
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
// 如果该节点使用默认命名空间,执行默认解析
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
// 如果该节点非默认命名空间,执行自定义解析
} else {
delegate.parseCustomElement(ele);
}
}
}
// 如果根节点非默认命名空间,执行自定义解析
} else {
delegate.parseCustomElement(root);
}
}
//省略其他代码
}
这边有默认命名空间和非默认之分,默认的是
这四个节点,这是由于他们处于http://www.springframework.org/schema/beans
这个 namespace
下。
如果需要使用"非 default"
标签,那么上面的xml
头部的地方也要引入相应的 namespace 和 .xsd 文件的路径,如下面代码所示,还引入了
等标签。
至此,我们主要来分析下默认命名空间下的bean标签
(7) DefaultBeanDefinitionDocumentReader#parseDefaultElement 解析默认命名空间标签
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // import
importBeanDefinitionResource(ele);
} else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { // alias
processAliasRegistration(ele);
} else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { // bean
processBeanDefinition(ele, delegate);
} else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // beans,需要执行递归
doRegisterBeanDefinitions(ele);
}
}
// 进行 bean 元素解析。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 如果解析成功,则返回 BeanDefinitionHolder 对象。而 BeanDefinitionHolder 为 name 和 alias 的 BeanDefinition 对象
// 如果解析失败,则返回 null 。
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 进行自定义标签处理
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 进行 BeanDefinition 的注册
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
} catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// 发出响应事件,通知相关的监听器,已完成该 Bean 标签的解析。
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
//省略其他代码
}
很明显,这个processBeanDefinition
方法也不是真正的解析标签的方法,需要到
parseBeanDefinitionElement
方法里去
(7) BeanDefinitionParserDelegate #parseBeanDefinitionElement解析bean标签,设置beanName
public class BeanDefinitionParserDelegate {
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
// 解析 id 和 name 属性
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
// 计算别名集合
// 将 name 属性的定义按照 “逗号、分号、空格” 切分,形成一个 别名列表数组,
// 当然,如果你不定义 name 属性的话,就是空的了
List aliases = new ArrayList<>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
// beanName ,优先,使用 id
String beanName = id;
// 如果没有指定id, 那么用别名列表的第一个名字作为beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0); // 移除出别名集合
if (logger.isTraceEnabled()) {
logger.trace("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
// 检查 beanName 的唯一性
checkNameUniqueness(beanName, aliases, ele);
}
// 根据 ... 中的配置创建 BeanDefinition,然后把配置中的信息都设置到实例中
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
// 到这里,整个 标签就算解析结束了,一个 BeanDefinition 就形成了。
if (beanDefinition != null) {
// 如果都没有设置 id 和 name,那么此时的 beanName 就会为 null,进入下面这块代码产生
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
// 生成唯一的 beanName
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
} else {
// 如果我们不定义 id 和 name,那么我们下图里的那个例子:
// 1. beanName 为:com.javadoop.example.MessageServiceImpl#0
// 2. beanClassName 为:com.javadoop.example.MessageServiceImpl
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
// 把 beanClassName 设置为 Bean 的别名
aliases.add(beanClassName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
} catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
// 返回 BeanDefinitionHolder 对象
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
//省略其他代码
}
这边的代码就是对于BeanDefinition
设置beanName
,配置规则如下图,但是从解析的逻辑仍需我们更进一层,去parseBeanDefinitionElement
方法中才能了解到
(9) BeanDefinitionParserDelegate#parseBeanDefinitionElement 真正解析标签的方法
public class BeanDefinitionParserDelegate {
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
// 解析 class 属性
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
// 解析 parent 属性
String parent = null;
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
try {
// 创建 BeanDefinition,然后设置类信息
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 解析默认 bean 的各种属性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 提取 description
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// tips:
// 下面的一堆是解析 ...... 内部的子元素,
// 解析出来以后的信息都放到 bd 的属性中
// 解析元数据
parseMetaElements(ele, bd);
// 解析 lookup-method 属性
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 解析 replaced-method 属性
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
// 解析构造函数参数
parseConstructorArgElements(ele, bd);
// 解析 property 子元素
parsePropertyElements(ele, bd);
// 解析 qualifier 子元素
parseQualifierElements(ele, bd);
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();
}
return null;
}
//省略其他代码
}
到这里,我们已经完成了根据
配置创建了一个BeanDefinitionHolder
实例。注意,是一个。
其成员变量也很简单
至此,我们可以回到(7)中的DefaultBeanDefinitionDocumentReader#processBeanDefinition
,来进行下一步BeanDefinition
的注册了
(8)BeanDefinitionReaderUtils#registerBeanDefinition 通过beanName和alias注册bean
public abstract class BeanDefinitionReaderUtils {
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
String beanName = definitionHolder.getBeanName();
// 注册这个 Bean
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 如果还有别名的话,也要根据别名全部注册一遍,不然根据别名就会找不到 Bean 了
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
// alias -> beanName 保存它们的别名信息,这个很简单,用一个 map 保存一下就可以了,
// 获取的时候,会先将 alias 转换为 beanName,然后再查找
registry.registerAlias(beanName, alias);
}
}
}
//省略其他代码
}
这个方法就是通过beanName
和alias
注册bean
,真正的代码逻辑要调用 BeanDefinitionRegistry
的实现类,这边选了个DefaultListableBeanFactory
来看
(9)DefaultListableBeanFactory#registerBeanDefinition 真正通过beanName注册的方法
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// 校验 beanName 与 beanDefinition 非空
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
// 校验 BeanDefinition 。
// 这是注册前的最后一次校验了,主要是对属性 methodOverrides 进行校验。
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
} catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// 从缓存中获取指定 beanName 的 BeanDefinition
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
// 如果已经存在
if (existingDefinition != null) {
// 如果存在但是不允许覆盖,抛出异常
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
// 覆盖 beanDefinition 大于 被覆盖的 beanDefinition 的 ROLE ,打印 info 日志
} else if (existingDefinition.getRole() < beanDefinition.getRole()) {
//打印的意思是用框架定义的 Bean 覆盖用户自定义的 Bean
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
// 覆盖 beanDefinition 与 被覆盖的 beanDefinition 不相同,打印 debug 日志
} else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
// 其它,打印 debug 日志
} else {
if (logger.isTraceEnabled()) {
//用同等的 Bean 覆盖旧的 Bean,这里指的是 equals 方法返回 true 的 Bean
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
// 允许覆盖,直接覆盖原有的 BeanDefinition 到 beanDefinitionMap 中。
this.beanDefinitionMap.put(beanName, beanDefinition);
// 如果未存在
} else {
// 判断是否已经有其他的 Bean 开始初始化了.如果开始了则需要对 beanDefinitionMap 进行并发控制
// 注意,"注册Bean" 这个动作结束,Bean 依然还没有初始化,我们后面会有大篇幅说初始化过程,
// 在 Spring 容器启动的最后,会 预初始化 所有的 singleton beans
if (hasBeanCreationStarted()) {
// beanDefinitionMap 为全局变量,避免并发情况
synchronized (this.beanDefinitionMap) {
// 添加到 BeanDefinition 到 beanDefinitionMap 中。
this.beanDefinitionMap.put(beanName, beanDefinition);
// 添加 beanName 到 beanDefinitionNames 中
List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
// 从 manualSingletonNames 移除 beanName
if (this.manualSingletonNames.contains(beanName)) {
Set updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
} else {
// 添加到 BeanDefinition 到 beanDefinitionMap 中。
//这个 map 保存了所有的 BeanDefinition
this.beanDefinitionMap.put(beanName, beanDefinition);
// 添加 beanName 到 beanDefinitionNames 中
//// 这是个 ArrayList,所以会按照 bean 配置的顺序保存每一个注册的 Bean 的名字
this.beanDefinitionNames.add(beanName);
// 从 manualSingletonNames 移除 beanName
//这是个 LinkedHashSet,代表的是手动注册的 singleton bean,
// 注意这里是 remove 方法,到这里的 Bean 当然不是手动注册的
// 手动指的是通过调用以下方法注册的 bean :
// registerSingleton(String beanName, Object singletonObject)
// 这不是重点,解释只是为了不让大家疑惑。Spring 会在后面"手动"注册一些 Bean,
// 如 "environment"、"systemProperties" 等 bean,我们自己也可以在运行时注册 Bean 到容器中的
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
// 重新设置 beanName 对应的缓存
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
//省略其他代码
}
总结一下,到这里已经初始化了Bean
容器(DefaultListableBeanFactory
),
配置也相应的转换为了一个个 BeanDefinition
(先变成Document
,根据标签挨个解析成BeanDefinitionHolder
),然后注册了各个 BeanDefinition
到注册中心(一个ConcurrentHashMap
),并且发送了注册事件[(7)中的DefaultBeanDefinitionDocumentReader#processBeanDefinition
] 。
3. 总结
可以借助一张图来总结一下这个流程: