Spring通过<import>标签导入外部配置文件
作者:沉迷Spring 发布时间:2023-03-04 06:43:58
目录
示例
原理
DefaultBeanDefinitionDocumentReader
parseDefaultElement
importBeanDefinitionResource
总结
示例
我们先来看下配置文件是怎么导入外部配置文件的?先定义一个spring-import配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="spring-config.xml"/>
<bean id="innerPerson" class="com.john.aop.Person">
</bean>
</beans>
我们看到里面定义了一个标签并用属性resource声明了导入的资源文件为spring-config.xml。我们再来看下spring-config.xml配置文件是怎样的:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="outerPerson" class="com.john.aop.Person">
<property name="firstName" value="john"/>
<property name="lastName" value="wonder"/>
</bean>
<bean id="ChineseFemaleSinger" class="com.john.beanFactory.Singer" abstract="true" >
<property name="country" value="中国"/>
<property name="gender" value="女"/>
</bean>
</beans>
然后我们可以实例化一个BeanFactory来加载spring-import这个配置文件了。
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("spring-import.xml");
Person outerPerson=(Person)context.getBean("outerPerson");
System.out.println(outerPerson);
}
如果没问题的话,我们可以获取到outerPerson这个bean并打印了。
Person [0, john wonder]
原理
我们来通过源码分析下Spring是如何解析import标签并加载这个导入的配置文件的。首先我们到DefaultBeanDefinitionDocumentReader类中看下:
DefaultBeanDefinitionDocumentReader
我们可以看到类里面定义了一个public static final 的IMPORT_ELEMENT变量:
public static final String IMPORT_ELEMENT = "import";
然后我们可以搜索下哪边用到了这个变量,并且定位到parseDefaultElement函数:
parseDefaultElement
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//todo 对 import 标签的解析 2020-11-17
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
}
这里就是我们要找的导入外部配置文件加载Bean定义的源代码,我们再重点看下importBeanDefinitionResource函数:
importBeanDefinitionResource
/**
* Parse an "import" element and load the bean definitions
* from the given resource into the bean factory.
*/
protected void importBeanDefinitionResource(Element ele) {
//// 获取 resource 的属性值
String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
//// 为空,直接退出
if (!StringUtils.hasText(location)) {
getReaderContext().error("Resource location must not be empty", ele);
return;
}
// Resolve system properties: e.g. "${user.dir}"
// // 解析系统属性,格式如 :"${user.dir}"
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
Set<Resource> actualResources = new LinkedHashSet<>(4);
// Discover whether the location is an absolute or relative URI
//// 判断 location 是相对路径还是绝对路径
boolean absoluteLocation = false;
try {
//以 classpath*: 或者 classpath: 开头为绝对路径
absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
}
catch (URISyntaxException ex) {
// cannot convert to an URI, considering the location relative
// unless it is the well-known Spring prefix "classpath*:"
}
// Absolute or relative?
//绝对路径 也会调用loadBeanDefinitions
if (absoluteLocation) {
try {
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
if (logger.isTraceEnabled()) {
logger.trace("Imported " + importCount + " bean definitions from URL location [" + location + "]");
}
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error(
"Failed to import bean definitions from URL location [" + location + "]", ele, ex);
}
}
else {
//如果是相对路径,则先计算出绝对路径得到 Resource,然后进行解析
// No URL -> considering resource location as relative to the current file.
try {
int importCount;
Resource relativeResource = getReaderContext().getResource().createRelative(location);
//如果相对路径 这个资源存在 那么就加载这个bean 定义
if (relativeResource.exists()) {
importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
actualResources.add(relativeResource);
}
else {
String baseLocation = getReaderContext().getResource().getURL().toString();
//todo import节点 内部会调用loadBeanDefinitions 操作 2020-10-17
importCount = getReaderContext().getReader().loadBeanDefinitions(
StringUtils.applyRelativePath(baseLocation, location), actualResources);
}
if (logger.isTraceEnabled()) {
logger.trace("Imported " + importCount + " bean definitions from relative location [" + location + "]");
}
}
}
}
我们来解析下这段代码是怎么一个流程:
1.首先通过resource标签解析到它的属性值,并且判读字符串值是否为空。
2.接下来它还会通过当前上下文环境去解析字符串路径里面的占位符,这点我们在之前文章中分析过。
2.接下来判断是否是绝对路径,通过调用ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();来判断,划重点:以 classpath*: 或者 classpath: 开头为绝对路径,或者可以生成一个URI实例就是当作绝对路径,或者也可以URI的isAbsolute来判断
3.如果是绝对路径那么我们通过getReaderContext().getReader()获取到XmlBeanDefinitionReader然后调用它的loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources)函数
4.如果不是绝对路径那么我们尝试生成相对当前资源的路径(这点很重要),再通过loadBeanDefinitions方法来加载这个配置文件中的BeanDefinitions。这里有个细节需要我们注意,就是它为什么要尝试去判断资源是否存在?就是如果存在的话那么直接调用loadBeanDefinitions(Resource resource)方法,也就是说这里肯定是加载单个资源文件,如方法注释所说:
/**
* Load bean definitions from the specified XML file.
* @param resource the resource descriptor for the XML file
* @return the number of bean definitions found
* @throws BeanDefinitionStoreException in case of loading or parsing errors
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
//load中去注册BeanDefinition
return loadBeanDefinitions(new EncodedResource(resource));
}
从指定的xml文件加载Bean定义。如果不存在,那么就跟绝对路径一样会调用loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources)函数,我们来看看这个函数的定义:
/**
* Load bean definitions from the specified resource location.
* <p>The location can also be a location pattern, provided that the
* ResourceLoader of this bean definition reader is a ResourcePatternResolver.
* @param location the resource location, to be loaded with the ResourceLoader
* (or ResourcePatternResolver) of this bean definition reader
* @param actualResources a Set to be filled with the actual Resource objects
* that have been resolved during the loading process(要填充加载过程中已解析的实际资源对象*的集合). May be {@code null}
* to indicate that the caller is not interested in those Resource objects.
*/
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
解释很清楚,这个location是指从指定资源路径加载BeanDefinitions。
总结
从源码可以看出从外部导入配置文件也就是给了通过一个总的配置文件来加载各个单一配置文件扩展的机会。
来源:https://mp.weixin.qq.com/s?__biz=MzI4OTUwNDU0MA==&mid=2247483916&idx=1&sn=693aaf6cee6112392143fa99dcf1b7c9&chksm=ec2f6e7fdb58e7694fb9bff06aea690744cbde6b29959de0d7e46c71e6161a2d0b9c87217868&cur_album_id=1650884114146787333


猜你喜欢
- 本文实例讲述了C#访问SqlServer设置链接超时的方法。分享给大家供大家参考。具体实现方法如下:下面这段代码设置超时时间为60秒,默认为
- 1.下载idea的 《.ignore》 插件,重启idea生效2.添加自己想要忽略的文件夹及文件,一般选这个就够了3.如果想要忽略提交的文件
- Android Studio 运行后出现了下面的错误Emulator: Process finished with exit code 1E
- Pattern类定义 public final class Pattern
- 本文介绍了android视频截屏&手机录屏实现代码,分享给大家,希望对大家有帮助问题在android中有时候我们需要对屏幕进行截屏操
- 倒序拼接字符串@ApiOperation("分页查询") @GetMapping(value
- 配置如下: <!--邀请用户送优惠券规则{邀请人规则:[{邀请人:优惠券ID}],使用邀请码人:优惠券ID},按照邀请人数从小到大配置
- 程序结构:一、配置 1. 在pom.xml中添加依赖pom.xml文件如下:<?xml version="1.0&
- springboot远程debug调试1.首先去编辑器打开项目2.打开Edit Configurations 选择remote选项
- import java.io.BufferedReader;import java.io.IOException;import java.i
- 安卓的三种本地的典型数据存储方式SharedPreferences以文件格式保存在本地存储中SQL数据库IDE : Android Stud
- 概述SpringMVC的处理器 * 类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦
- 1.通过用FTP进行上传文件,首先要实现建立FTP连接,一般建立FTP连接,需要知道FTP配置有关的信息。一般要在Bean中建立一个Serv
- 1.比较两个字符串时使用“==”还是equals()方法?当然是equals方法。“==”测试的是两个对象的引用是否相同,而equals()
- C# WinForm控件的拖动和缩放是个很有用的功能。实现起来其实很简单的,主要是设计控件的MouseDown、MouseLeave、Mou
- 这篇文章主要介绍了Spring Cache手动清理Redis缓存,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
- 目录首先必须要有一个个人微信公众号效果图后台路由代码完整代码首先必须要有一个个人微信公众号个人微信公众号相关的接口权限有限,不过用于个人学习
- 简介netty中的数据是通过ByteBuf来进行传输的,一个ByteBuf中可能包含多个有意义的数据,这些数据可以被称作frame,也就是说
- 在 Java 中,方法调用一般通过 Virtual Call 还有 Classic Call。Classic Call 就是直接指向方法的地
- 在C#中,当引用类型需要转换的时候,经常会用到关键字is、as以及显式强转。本篇来体验这三者的用法。先来梳理.NET引用类型转换的"