Spring通過<import>標簽導入外部配置文件
示例
我們先來看下配置文件是怎么導入外部配置文件的?先定義一個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函數(shù):
parseDefaultElement
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//todo 對 import 標簽的解析 2020-11-17
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
}
這里就是我們要找的導入外部配置文件加載Bean定義的源代碼,我們再重點看下importBeanDefinitionResource函數(shù):
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}"
// // 解析系統(tǒng)屬性,格式如 :"${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節(jié)點 內部會調用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.接下來它還會通過當前上下文環(huán)境去解析字符串路徑里面的占位符,這點我們在之前文章中分析過。
2.接下來判斷是否是絕對路徑,通過調用ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();來判斷,劃重點:以 classpath*: 或者 classpath: 開頭為絕對路徑,或者可以生成一個URI實例就是當作絕對路徑,或者也可以URI的isAbsolute來判斷
3.如果是絕對路徑那么我們通過getReaderContext().getReader()獲取到XmlBeanDefinitionReader然后調用它的loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources)函數(shù)
4.如果不是絕對路徑那么我們嘗試生成相對當前資源的路徑(這點很重要),再通過loadBeanDefinitions方法來加載這個配置文件中的BeanDefinitions。這里有個細節(jié)需要我們注意,就是它為什么要嘗試去判斷資源是否存在?就是如果存在的話那么直接調用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)函數(shù),我們來看看這個函數(shù)的定義:
/**
* 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。
總結
從源碼可以看出從外部導入配置文件也就是給了通過一個總的配置文件來加載各個單一配置文件擴展的機會。
以上就是Spring通過<import>標簽導入外部配置文件的詳細內容,更多關于Spring 導入外部配置文件的資料請關注腳本之家其它相關文章!
相關文章
SpringBoot安全策略開發(fā)之集成數(shù)據(jù)傳輸加密
這篇文章主要介紹了SpringBoot集成數(shù)據(jù)傳輸加密,近期在對開發(fā)框架安全策略方面進行升級優(yōu)化,提供一些通用場景的解決方案,本文針對前后端數(shù)據(jù)傳輸加密進行簡單的分享2023-01-01
使用Swagger2實現(xiàn)自動生成RESTful?API文檔
在開發(fā)?RESTful?API?的過程中,文檔是非常重要的一部分,可以幫助開發(fā)者了解?API?的功能和使用方法,本文將使用Swagger2?實現(xiàn)自動生成?RESTful?API?文檔,需要的可以參考一下2023-06-06
maven項目后出現(xiàn)‘parent.relativePath’ of POM錯誤時的解決方法
在Springboot項目啟動時,項目報錯‘parent.relativePath’ of POM問題,項目無法正常啟動,本文就來介紹一下解決方法,感興趣的可以了解一下2023-10-10
springmvc無法訪問/WEB-INF/views下的jsp的解決方法
本篇文章主要介紹了springmvc無法訪問/WEB-INF/views下的jsp的解決方法,非常具有實用價值,需要的朋友可以參考下2017-10-10
idea中開啟Run Dashboard 和 快速復制項目并改變端口的方法
這篇文章主要介紹了idea中開啟Run Dashboard 和 快速復制項目并改變端口的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08

