Spring通過<import>標簽導(dǎo)入外部配置文件
示例
我們先來看下配置文件是怎么導(dǎo)入外部配置文件的?先定義一個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聲明了導(dǎo)入的資源文件為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標簽并加載這個導(dǎo)入的配置文件的。首先我們到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); } }
這里就是我們要找的導(dǎo)入外部配置文件加載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? //絕對路徑 也會調(diào)用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é)點 內(nèi)部會調(diào)用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.接下來判斷是否是絕對路徑,通過調(diào)用ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();來判斷,劃重點:以 classpath*: 或者 classpath: 開頭為絕對路徑,或者可以生成一個URI實例就是當作絕對路徑,或者也可以URI的isAbsolute來判斷
3.如果是絕對路徑那么我們通過getReaderContext().getReader()獲取到XmlBeanDefinitionReader然后調(diào)用它的loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources)函數(shù)
4.如果不是絕對路徑那么我們嘗試生成相對當前資源的路徑(這點很重要),再通過loadBeanDefinitions方法來加載這個配置文件中的BeanDefinitions。這里有個細節(jié)需要我們注意,就是它為什么要嘗試去判斷資源是否存在?就是如果存在的話那么直接調(diào)用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定義。如果不存在,那么就跟絕對路徑一樣會調(diào)用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。
總結(jié)
從源碼可以看出從外部導(dǎo)入配置文件也就是給了通過一個總的配置文件來加載各個單一配置文件擴展的機會。
以上就是Spring通過<import>標簽導(dǎo)入外部配置文件的詳細內(nèi)容,更多關(guān)于Spring 導(dǎo)入外部配置文件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Javaweb實現(xiàn)完整個人博客系統(tǒng)流程
這篇文章主要介紹了怎樣用Java來實現(xiàn)一個完整的個人博客系統(tǒng),我們通過實操上手的方式可以高效的鞏固所學(xué)的基礎(chǔ)知識,感興趣的朋友一起來看看吧2022-03-03SpringBoot整合Netty+Websocket實現(xiàn)消息推送的示例代碼
WebSocket使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡單,允許服務(wù)端主動向客戶端推送數(shù)據(jù),本文主要介紹了SpringBoot整合Netty+Websocket實現(xiàn)消息推送的示例代碼,具有一定的參考價值,感興趣的可以了解一下2024-01-01java中Map、Set、List的簡單使用教程(快速入門)
這篇文章主要給大家介紹了關(guān)于java中Map、Set、List簡單使用教程,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01Java中的讀寫鎖ReentrantReadWriteLock源碼分析
這篇文章主要介紹了Java中的讀寫鎖ReentrantReadWriteLock源碼分析,ReentrantReadWriteLock 分為讀鎖和寫鎖兩個實例,讀鎖是共享鎖,可被多個線程同時使用,寫鎖是獨占鎖,持有寫鎖的線程可以繼續(xù)獲取讀鎖,反之不行,需要的朋友可以參考下2023-12-12Java數(shù)字格式類(NumberFormat類和DecimalFormat類)用法詳解
NumberFormat類是Java提供的一個格式化數(shù)字的類,可以將一串數(shù)字轉(zhuǎn)化成自己想要的數(shù)據(jù)格式,也可以將字符串轉(zhuǎn)化成數(shù)值,下面這篇文章主要給大家介紹了關(guān)于Java數(shù)字格式類(NumberFormat類和DecimalFormat類)用法的相關(guān)資料,需要的朋友可以參考下2022-07-07