Spring中的NamespaceHandler加載過程源碼詳解
NamespaceHandler加載過程流程

流程圖中羅列了Spring加載配置的整個(gè)過程,其中,從BeanDefinitionParserDelegate.parseCustomElement(...)開始,即開始了對命名空間NamespaceHandler的解析,本文對這部分內(nèi)容進(jìn)行解析。
NamespaceHandler加載過程解析
1) BeanDefinitionParserDelegate.parseCustomElement(…)方法
① 取得指定元素的命名空間。
② 通過DefaultNamespaceHandlerResolver解析指定元素命名空間對應(yīng)的NamespaceHandler。
③ 通過查找的NamespaceHandler解析指定元素,取得Bean定義實(shí)例。
/**
* 解析自定義元素.
* @param ele 元素.
* @param containingBd Bean定義.
* @return 解析Bean定義.
*/
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 獲取元素命名空間.
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 解析元素命名空間對應(yīng)的NamespaceHandler.
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// 解析當(dāng)前元素.
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}2) DefaultNamespaceHandlerResolver.resolve(…)方法
① 加載所有依賴的META-INF/spring.handlers文件,取得命名空間對應(yīng)處理器映射。
② 根據(jù)命名空間從①的映射中查找對應(yīng)的NamespaceHandler類。
③ 映射配置的NamespaceHandler類必須實(shí)現(xiàn)NamespaceHandler接口,否則拋出異常。
④ 根據(jù)配置映射的NamespaceHandler類路徑進(jìn)行實(shí)例化。
⑤ 調(diào)用NamespaceHandler的init()方法,指定命名空間元素對應(yīng)的解析器。
/**
* 從配置映射中查找所提供命名空間URI的NamespaceHandler.
* @param namespaceUri 相關(guān)命名空間URI.
* @return NamespaceHandler,若未找到,則為null.
*/
@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
// 加載處理器映射.
Map<String, Object> handlerMappings = getHandlerMappings();
// 根據(jù)namespaceUri查找處理器類路徑.
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
String className = (String) handlerOrClassName;
try {
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
// 處理器需實(shí)現(xiàn)NamespaceHandler接口.
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
// NamespaceHandler初始化,指定命名空間下各元素對應(yīng)的解析器.
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
catch (ClassNotFoundException ex) {
throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
"] for namespace [" + namespaceUri + "]", ex);
}
catch (LinkageError err) {
throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
className + "] for namespace [" + namespaceUri + "]", err);
}
}
}3) DefaultNamespaceHandlerResolver.getHandlerMappings()方法
① 加載所有依賴的META-INF/spring.handlers文件,取得命名空間對應(yīng)處理器映射,存儲在Properties實(shí)例中。
② 將數(shù)據(jù)由Properties轉(zhuǎn)存到Map中。
/**
* 延遲加載指定的NamespaceHandler映射.
*/
private Map<String, Object> getHandlerMappings() {
Map<String, Object> handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
synchronized (this) {
handlerMappings = this.handlerMappings;
if (handlerMappings == null) {
try {
// 搜索META-INF/spring.handlers文件.
Properties mappings =
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map<String, Object> mappingsToUse = new ConcurrentHashMap<>(mappings.size());
// 將Properties數(shù)據(jù)轉(zhuǎn)換到Map中.
CollectionUtils.mergePropertiesIntoMap(mappings, mappingsToUse);
handlerMappings = mappingsToUse;
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
}
}
}
return handlerMappings;
}4) NamespaceHandlerSupport.parse(…)方法
① 使用提供元素的本地名稱查找BeanDefinitionParser。
② 調(diào)用BeanDefinitionParser實(shí)現(xiàn)進(jìn)行解析。
/**
* 通過委托給為Element注冊的BeanDefinitionParser來解析提供的Element.
*/
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 使用提供元素的本地名稱查找BeanDefinitionParser.
BeanDefinitionParser parser = findParserForElement(element, parserContext);
// 調(diào)用BeanDefinitionParser實(shí)現(xiàn)進(jìn)行解析.
return (parser != null ? parser.parse(element, parserContext) : null);
}5) NamespaceHandlerSupport.findParserForElement(…)方法
① 通過指定元素,取得元素的本地參數(shù)名稱。
② 根據(jù)本地名稱查找已注冊的BeanDefinitionParser。
/**
* 使用提供的元素的本地名稱查找BeanDefinitionParser.
*/
@Nullable
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
// 獲取提供元素的本地名稱.
String localName = parserContext.getDelegate().getLocalName(element);
// 根據(jù)本地名稱查找BeanDefinitionParser.
BeanDefinitionParser parser = this.parsers.get(localName);
if (parser == null) {
parserContext.getReaderContext().fatal(
"Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
}
return parser;
}6) BeanDefinitionParser.parse(…)方法
BeanDefinitionParser.parse(...)是由具體開發(fā)進(jìn)行編寫的,其中涉及元素解析、元素校驗(yàn)、注冊對應(yīng)Beam。
總結(jié)
‘工欲善其事必先利其器’,只有當(dāng)對原理的了解達(dá)到一定程度,才能在實(shí)際遇到問題,簡潔、快速、高效的解決問題。
源碼解析基于spring-framework-5.0.5.RELEASE版本源碼。
若文中存在錯(cuò)誤和不足,歡迎指正!
到此這篇關(guān)于Spring中的NamespaceHandler加載過程源碼詳解的文章就介紹到這了,更多相關(guān)NamespaceHandler加載過程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java連接池Druid獲取連接getConnection示例詳解
這篇文章主要為大家介紹了java連接池Druid獲取連接getConnection示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
SpringBoot中創(chuàng)建bean的7種方式總結(jié)
Spring是一款廣泛應(yīng)用于企業(yè)級應(yīng)用程序開發(fā)的Java框架,其 IOC 和 DI 特性可以有效地管理應(yīng)用程序中的對象,提高了應(yīng)用程序的可維護(hù)性和可擴(kuò)展性,那你知道spring有哪些方式將bean放入容器嘛,今天就給大家總結(jié)一下2023-07-07
SpringMVC源碼之HandlerMapping處理器映射器解析
這篇文章主要介紹了SpringMVC源碼之HandlerMapping處理器映射器解析,在Spring?MVC中,HandlerMapping處理器映射器用于確定請求處理器對象,請求處理器可以是任何對象,只要它們使用了@Controller注解或注解@RequestMapping,需要的朋友可以參考下2023-08-08
springboot整合gateway實(shí)現(xiàn)網(wǎng)關(guān)功能的示例代碼
本文主要介紹了springboot整合gateway實(shí)現(xiàn)網(wǎng)關(guān)功能的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
十五道tomcat面試題,為數(shù)不多的機(jī)會(huì)!
這篇文章主要介紹了十五道tomcat面試題,Tomcat的本質(zhì)是一個(gè)Servlet容器。一個(gè)Servlet能做的事情是:處理請求資源,并為客戶端填充response對象,需要的朋友可以參考下2021-08-08
Java中CaffeineCache自定義緩存時(shí)間的實(shí)現(xiàn)
本文主要介紹了Java中CaffeineCache自定義緩存時(shí)間的實(shí)現(xiàn),通過聲明緩存value值holder對象并創(chuàng)建緩存容器,可以為不同的key值指定不同的過期時(shí)間,具有一定的參考價(jià)值,感興趣的可以了解一下2025-02-02

