02-Spring配置文件加载
获取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配置文件加载的更多相关文章
- Spring配置文件加载流程
http://blog.csdn.net/dy_paradise/article/details/6038990
- Spring中加载xml配置文件的六种方式
Spring中加载xml配置文件的六种方式 博客分类: Spring&EJB XMLSpringWebBeanBlog 因为目前正在从事一个项目,项目中一个需求就是所有的功能都是插件的形式装 ...
- Spring使用环境变量控制配置文件加载
项目中需要用到很多配置文件,不同环境的配置文件是不一样的,因此如果只用一个配置文件,势必会造成配置文件混乱,这里提供一种利用环境变量控制配置文件加载的方法,如下: 一.配置环境变量 如果是window ...
- Spring中加载配置文件的方式
原文:http://blog.csdn.net/snowjlz/article/details/8158560 Spring 中加载XML配置文件的方式,好像有3种, XML是最常见的Spring 应 ...
- Spring如何加载log4j配置文件
今天有朋友在群里问了这个问题,于是写了这篇文章进行整理. 问题如下: 在项目中添加了log4j.properties配置文件,并没有在Spring配置文件中配置,也没有在web.xml中配置,但是代码 ...
- Spring Boot加载配置文件
问题1:Spring如何加载配置,配置文件位置? 1.默认位置: Spring Boot默认的配置文件名称为application.properties,SpringApplication将从以下位置 ...
- Spring使用环境变量控制配置文件加载(转)
项目中需要用到很多配置文件,不同环境的配置文件是不一样的,因此如果只用一个配置文件,势必会造成配置文件混乱,这里提供一种利用环境变量控制配置文件加载的方法,如下: 一.配置环境变量 如果是window ...
- Spring Cloud配置文件加载优先级简述
Spring Cloud中配置文件的加载机制与其它的Spring Boot应用存在不一样的地方:如它引入了bootstrap.properties的配置文件,同时也支持从配置中心中加载配置文件等:本文 ...
- Spring之配置文件加载方式
spring在org.springframework.core.io包中提供了多种配置文件加载方式.无论是XML.URL还是文件,都有很好的支持.比如基于URL的UrlResource.基于输入流的I ...
- Spring Boot加载application.properties配置文件顺序规则
SpringApplication会从以下路径加载所有的application.properties文件: 1.file:./config/(当前目录下的config文件夹) 2.file:./(当前 ...
随机推荐
- 国产网络测试仪MiniSMB - 如何3秒内创建出16,000条UDP/TCP端口号递增流
国产网络测试仪MiniSMB(www.minismb.com)是复刻smartbits的IP网络性能测试工具,是一款专门用于测试智能路由器,网络交换机的性能和稳定性的软硬件相结合的工具.可以通过此以太 ...
- 通过修改etcd来设置或修改节点flannel子网信息
在首次启动flannel服务的时候可以手动指定subnet.env文件,配置所在节点的flannel子网网段,如果不指定配置文件,flannel将自动分配一个子网网段并生成配置文件 /var/run/ ...
- Oracle数据库故障处理方法
1.启动数据库报错:ORA-01102:cannot mount database in EXCLUSIVE mode 给客户处理oracle故障,遇到如下报错: 以sys登录至数据库,执行shutd ...
- HDU 3920 Clear All of Them I(状压DP)题解
题意:2n个点,一个起点,开n枪,每枪必须打两个点,花费为起点到其中一点距离加上两点距离.问打完2n个点的最小花费. 思路:很显然应该dp状态,然后枚举i j两个空位置去填,那么复杂度$O(20 * ...
- vue watch route params change
vue watch route params change watch: { '$route.params.menuKey' (val, oldVal) { console.log('new rout ...
- 为什么国内的好多具备 HTTPS 的网站却没有使用 HTTPS 重定向功能
为什么国内的好多具备 HTTPS 的网站却没有使用 HTTPS 重定向功能 HTTPS 重定向 good demos ️ HTTPS http://www.xgqfrms.xyz/ https://w ...
- Flutter CodePen challenges
Flutter CodePen challenges 挑战赛 https://mp.weixin.qq.com/s/qIYokWN9SVgr-F7YxbJuOQ CodePen Flutter 编辑器 ...
- Regular Expressions all in one
Regular Expressions all in one Regular Expressions Cheatsheet https://developer.mozilla.org/en-US/do ...
- 「NGK每日快讯」2021.2.3日NGK公链第92期官方快讯!
- BGV暴涨千倍,未来或将超越YFI领跑DeFi全场!
毫无疑问,YFI在2020年上半年以一己之力掀翻了DeFi市场的热潮.迄今为止,YFI的新鲜资讯从不缺席,最近也是频频登上各大知名媒体热搜.其币价远远超过比特币价格,也让资本市场注意到DeFi市场原来 ...