获取IOC容器

加载、解析xml文件,形成GenericBeanDefinition,供后续实例化剩下的所有 Bean 使用。

obtainFreshBeanFactory()

获取IOC容器

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 初始化BeanFactory,并进行XML文件读取,并将得到的BeanFactory记录在当前实体的属性中
refreshBeanFactory();
// 返回当前实体的beanFactory属性
return getBeanFactory();
}

refreshBeanFactory()

刷新IOC容器,此类是一个抽象类,在前面初始化的对象,不同,此类方法执行的不同。这里主要执行的是 AbstractRefreshableApplicationContext中实现的方法。

protected final void refreshBeanFactory() throws BeansException {
// 如果存在beanFactory,则销毁beanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 创建DefaultListableBeanFactory对象
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 为了序列化指定id,可以从id反序列化到beanFactory对象
beanFactory.setSerializationId(getId());
// 定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖
customizeBeanFactory(beanFactory);
// 初始化documentReader,并进行XML文件读取及解析,默认命名空间的解析,自定义标签的解析
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}

refreshBeanFactory() 方法的另一个实现是在 GenericApplicationContext 中,Spring Boot 的纯注解就是调用这个方法,因为在 AnnotationConfigServletWebServerApplicationContext 的时候就将 IOC容器已经创建完毕了。

protected final void refreshBeanFactory() throws IllegalStateException {
if (!this.refreshed.compareAndSet(false, true)) {
throw new IllegalStateException(
"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
}
this.beanFactory.setSerializationId(getId());
}

createBeanFactory()

在 createBeanFactory() 的方法中 new DefaultListableBeanFactory(),往其父类构造方法中能够找到这样一段代码,是 AbstractAutowireCapableBeanFactory 的构造方法。

public AbstractAutowireCapableBeanFactory() {
super();
// 忽略要依赖的接口,此处忽略的接口会在后面创建 Bean 的时候,判断是否进行回调。
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}

customizeBeanFactory()

在 IoC 容器中设置的两个属性,在后面会使用到这两个参数。

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
// 如果属性allowBeanDefinitionOverriding不为空,设置给beanFactory对象相应属性,是否允许覆盖同名称的不同定义的对象
if (this.allowBeanDefinitionOverriding != null) {
beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 如果属性allowCircularReferences不为空,设置给beanFactory对象相应属性,是否允许bean之间存在循环依赖
if (this.allowCircularReferences != null) {
beanFactory.setAllowCircularReferences(this.allowCircularReferences);
}
}

loadBeanDefinitions(beanFactory)

这个方法有很多的重载方法,很容易迷,在这里简单说一下,loadBeanDefinition 方法主要有加载 String 路径形式的,Resource 资源形式的,还有数组类型的,但是数组类型最终会调回单个的形式,String 路径形式的,会封装成 Resource 资源的形式来进行加载。在此方法中会通过创建一个 BeanDefinitionReader 形式的读取器,来对资源文件进行,将解析后的资源形成 BeanDefinition,供后续创建 Bean 对象使用。

protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// 创建一个xml的beanDefinitionReader,并通过回调设置到beanFactory中
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's
// resource loading environment.
// 给reader对象设置环境对象
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
// 初始化beanDefinitionReader对象,此处设置配置文件是否要进行验证
initBeanDefinitionReader(beanDefinitionReader);
// 开始完成beanDefinition的加载
loadBeanDefinitions(beanDefinitionReader);
}

getConfigLocations()里面的数据来源于前面 setConfigLocations();

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
// 以Resource的方式获得配置文件的资源位置
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
// 以String的形式获得配置文件的位置
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}

reader.loadBeanDefinitions()

经过一系列的重载方法的处理,最终会调用 doLoadBeanDefinitions(inputSource, encodedResource.getResource()) 对其资源文件进行处理,当然前面有一步涉及到将 String 类型的文件名转换为 Resource,然后进行处理

/** 转换的核心方法,这个地方的资源解析器来源于前面的构造方法 */
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);

异常信息给去掉了

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException { try {
// 此处获取xml文件的document对象,这个解析过程是由documentLoader完成的,从String[] -string-Resource[]- resource,最终开始将resource读取成一个document文档,根据文档的节点信息封装成一个个的 BeanDefinition 对象
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}
}

registerBeanDefinitions(doc, resource)

具体的解析,注册逻辑,doLoadDocument 主要将 xml 对象解析封装为 Document 对象,这里主要说明 BeanDefinition 具体的解析逻辑。此处还涉及到后续自定义标签解析的一部分工作,在 createReaderContext(resource)方法中会创建 DefaultNamespaceHandlerResolver,此方法会在后续的时候加载 META-INF/spring.handlers 中的 uri 和解析器的一个对应关系。

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 对xml的beanDefinition进行解析
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
// 完成具体的解析过程
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}

调用的是默认的 DefaultBeanDefinitionDocumentReader#registerBeanDefinition

doRegisterBeanDefinitions()

具体的注册解析逻辑

protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
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;
}
}
} /** 处理 xml 之前,可以对其进行扩展 */
preProcessXml(root);
/** 解析 xml 形成 BeanDefinition */
parseBeanDefinitions(root, this.delegate);
/** 处理 xml 之后,可以对其进行扩展 */
postProcessXml(root); this.delegate = parent;
}

parseBeanDefinitions

里面包含默认的 bean 标签的解析和其余自定义的标签的处理,这里暂时不对自定义标签的解析其进行具体的解析,在随后的一篇文章中说明。

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);
}
}

parseDefaultElement()

解析默认的标签,包含 import,alias、beans、bean等标签的解析,注意: beans标签的解析会是一个递归的过程,又会返回去调用 doRegisterBeanDefinitions(ele) 方法。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}

processBeanDefinition(ele, delegate)

处理 BeanDefinition,主要是用 BeanDefinitionParserDelegate 策略对其进行解析,解析之后形成 BeanDefinitionHolder。

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// beanDefinitionHolder是beanDefinition对象的封装类,封装了BeanDefinition,bean的名字和别名,用它来完成向IOC容器的注册
// 得到这个beanDefinitionHolder就意味着beandefinition是通过BeanDefinitionParserDelegate对xml元素的信息按照spring的bean规则进行解析得到的
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
// 向ioc容器注册解析得到的beandefinition的地方
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
// 在beandefinition向ioc容器注册完成之后,发送消息
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

registerBeanDefinition()

将得到的 BeanDefinition 注册到 IOC 中。

public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException { // Register bean definition under primary name.
// 使用beanName做唯一标识注册
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any.
// 注册所有的别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}

registerBeanDefinition

方法存在于 DefaultListableBeanFactory 类中,主要存放在两个地方,一个为 beanDefinitionMap,beanName 为 key,BeanDefinition 为 value,beanDefinitionNames 中存放所有的 Bean 名称,在后续实例化所有剩下的 bean 中会使用,注意:beanDefinitionNames 中存放的名称是有顺序的。

至此 解析形成的 xml 已经注册到 IOC 中。

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

02-Spring配置文件加载的更多相关文章

  1. Spring配置文件加载流程

    http://blog.csdn.net/dy_paradise/article/details/6038990

  2. Spring中加载xml配置文件的六种方式

    Spring中加载xml配置文件的六种方式 博客分类: Spring&EJB XMLSpringWebBeanBlog  因为目前正在从事一个项目,项目中一个需求就是所有的功能都是插件的形式装 ...

  3. Spring使用环境变量控制配置文件加载

    项目中需要用到很多配置文件,不同环境的配置文件是不一样的,因此如果只用一个配置文件,势必会造成配置文件混乱,这里提供一种利用环境变量控制配置文件加载的方法,如下: 一.配置环境变量 如果是window ...

  4. Spring中加载配置文件的方式

    原文:http://blog.csdn.net/snowjlz/article/details/8158560 Spring 中加载XML配置文件的方式,好像有3种, XML是最常见的Spring 应 ...

  5. Spring如何加载log4j配置文件

    今天有朋友在群里问了这个问题,于是写了这篇文章进行整理. 问题如下: 在项目中添加了log4j.properties配置文件,并没有在Spring配置文件中配置,也没有在web.xml中配置,但是代码 ...

  6. Spring Boot加载配置文件

    问题1:Spring如何加载配置,配置文件位置? 1.默认位置: Spring Boot默认的配置文件名称为application.properties,SpringApplication将从以下位置 ...

  7. Spring使用环境变量控制配置文件加载(转)

    项目中需要用到很多配置文件,不同环境的配置文件是不一样的,因此如果只用一个配置文件,势必会造成配置文件混乱,这里提供一种利用环境变量控制配置文件加载的方法,如下: 一.配置环境变量 如果是window ...

  8. Spring Cloud配置文件加载优先级简述

    Spring Cloud中配置文件的加载机制与其它的Spring Boot应用存在不一样的地方:如它引入了bootstrap.properties的配置文件,同时也支持从配置中心中加载配置文件等:本文 ...

  9. Spring之配置文件加载方式

    spring在org.springframework.core.io包中提供了多种配置文件加载方式.无论是XML.URL还是文件,都有很好的支持.比如基于URL的UrlResource.基于输入流的I ...

  10. Spring Boot加载application.properties配置文件顺序规则

    SpringApplication会从以下路径加载所有的application.properties文件: 1.file:./config/(当前目录下的config文件夹) 2.file:./(当前 ...

随机推荐

  1. InnoDB 存储引擎简介

    InnoDB 核心特性 MVCC(Multi-Version Concurrency Control,多版本并发控制),事务处理,行级锁,热备份,自动故障恢复( Crash Safe Recovery ...

  2. NFS 共享存储

    目录 环境准备 NFS服务端 NFS客户端 部署时常见报错 httpd服务 NFS 共享存储的坑 环境准备 主机名 WanIP(Wide Area Network) LanIP(Local Area ...

  3. P类问题,NP,NPC,HPHard,coNP,NPI问题 的简单认识

    参考<算法设计技巧与分析>--沙特 问题可以分为判定类问题和最优化问题,判定类问题可以转化为最优化问题,所以下面讨论的是判定类的问题. P类问题是可以在多项式时间  采用确定性算法给出解 ...

  4. Leetcode(23)-合并K个排序链表

    合并 k 个排序链表,返回合并后的排序链表.请分析和描述算法的复杂度. 示例: 输入: [   1->4->5,   1->3->4,   2->6 ] 输出: 1-&g ...

  5. SDN总结

    之前做项目用到了SDN,知道其作用,但是对其不是特别熟悉,今天特来总结一下相关知识点: 1. SDN的典型架构分为哪三层 主要分为应用层,控制层,和基础设施层: 2. SDN技术的关键点是 控制平面和 ...

  6. Mybatis-02 CRUD

    Mybatis-02 CRUD CRUD 先来简单回顾一下之前的准备步骤: 创建一个数据库,并加入数据 创建一个Maven项目 导入对应的依赖 创建Pojo类和Dao类 写出Mybatis工具类 配置 ...

  7. Sentry React SourceMaps All In One

    Sentry React SourceMaps All In One React https://docs.sentry.io/platforms/javascript/guides/react/ h ...

  8. CDN maxcdn for speed up

    https://www.maxcdn.com/ The Next Generation CDN.Plug into a whole new level of speed.From: https://w ...

  9. 知乎 bug

    知乎 bug shit zhihu https://zhuanlan.zhihu.com/p/111809590 无法展开评论 https://unpkg.zhimg.com/@cfe/sentry- ...

  10. DB-Engines Ranking : Redis, MongoDB, MySQL

    DB-Engines Ranking http://db-engines.com/en/ranking The DB-Engines Ranking ranks database management ...