緊接着上一篇關於spring默認標籤加載,這一篇來看下自定義標籤的加載node
繼續從 DefaultBeanDefinitionDocumentReader來看spring
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)) { this.parseDefaultElement(ele, delegate); // 默認標籤解析 } else { delegate.parseCustomElement(ele); // 自定義標籤解析 } } } } else { delegate.parseCustomElement(root); // 自定義標籤解析 } }
寫在前邊的東西,最近結合着《架構整潔之道》和《spring源碼深度解析》這兩本書一起看着,架構整潔之道里描述的一些面向對象的開發原則,接口隔離/單一職責/開閉幕式/依賴反轉/裏式替換架構
這些原則,在spring的源碼裏可謂是用的淋漓盡致,尤爲單一職責/接口隔離,這兩個翻看源碼的時候尤爲有體會,以前本身在項目開發中,其實根本沒有在一這些事情,只是按照業務劃分進行接口的拆分,並不在乎是不是單一職責/是否接口隔離這些事情,其實單一職責能讓咱們更好的去拓展和維護咱們的代碼。包括接口隔離其實都是這樣的目的。可以符合這些規則,咱們的代碼就能更大限度的符合高可維護性/低耦合性這些要求,也就能實現最大限度的優化開發效率這件事情。app
好了,扯了些題外話,咱們繼續自定義標籤的解析工做:dom
public BeanDefinition parseCustomElement(Element ele) { return this.parseCustomElement(ele, (BeanDefinition)null); }
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { String namespaceUri = this.getNamespaceURI(ele); NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } else { return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); } }
看到這裏有點兒蒙圈,若是沒有用過自定義標籤的話,會有些蒙,那咱們不妨在第一篇的例子上咱們搞一個自定義標籤來試試。ide
2019.07.06 14:29分 於圖書館繼續(在家帶了三天孩子,今天繼續,上午繼續看《架構整潔之道》不過有點兒瞌睡,中間看了一個小時的曾國藩家書,曾國藩說敬字/勤字,個個方面來講都要早起,並且早起更是曾氏一門,延續了好幾代人的好習慣,並要求子女家人也要這樣,是啊,想一想,業精於勤荒於嬉,不說的都是這個意思嗎?)早上起的太晚,習慣瞭如今連孩子都是這樣了,這樣下去下一代也還將延續個人毛病,若是如今不痛下決心就很難改變了。函數
好了,穿插下想說的話,如今繼續。剛纔翻了以前的兩篇博客,寫的仍是不夠好,本身看起來都有點兒暈,不過很快串聯起來就行了,這兩篇默認標籤和自定義標籤包括第一篇的xml文件到Doc的解析,這些相關的內容無非就是Bean的解析過程,認準了這個核心,帶着這個核心去看就不難明白這其中的流程了。post
前文提到,咱們本身構建一個例子來看下自定義標籤究竟是如何使用的。這裏就來展現下以前構建的例子。學習
建立一個自定義標籤的步驟以下:測試
1:建立一個須要擴展的組件
2:定義一個XSD文件描述組件內容
3:建立一個文件,實現BeanDefinitionParser接口,用來解析XSD文件重的定義和組建定義
4:建立一個Handle文件,擴展自NamespaceHandlerSupport目的是將組件註冊到Spring容器
5:編寫spring.handlers和spring.schemas文件
OK,根據這個步驟咱們來操做:
1:建立組件
public class User { private String userName; private String email; // ... 省略get set方法 }
2:定義XSD文件
建立了spring-test.xsd文件在META-INF下:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <xsd:schema xmlns="http://www.zaojiaoshu.com/schema/user" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.zaojiaoshu.com/schema/user" elementFormDefault="qualified" > <xsd:element name="user"> <xsd:complexType> <xsd:attribute name="id" type="xsd:string"/> <xsd:attribute name="userName" type="xsd:string"/> <xsd:attribute name="email" type="xsd:string"/> </xsd:complexType> </xsd:element> </xsd:schema>
目錄
3:建立BeanDefinition接口實例,來解析XSD文件
package com.zaojiaoshu.learn.selfTag; import com.zaojiaoshu.learn.entity.User; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.util.StringUtils; import org.w3c.dom.Element; public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { protected Class<?> getBeanClass(Element element) { return User.class; } protected void doParse(Element element, BeanDefinitionBuilder builder) { String userName = element.getAttribute("userName"); String email = element.getAttribute("email"); if(StringUtils.hasText(userName)){ builder.addPropertyValue("userName", userName); } if(StringUtils.hasText(email)){ builder.addPropertyValue("email", email); } } }
4:建立Handle文件,擴展自NamespaceHandlerSupport,目的是將組件註冊到Spring容器
package com.zaojiaoshu.learn.selfTag; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; public class MyNameSpaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("user", new UserBeanDefinitionParser()); } }
5:建立spring.handlers和spring.schemas文件
spring.handlers
http\://www.zaojiaoshu.com/schema/user=com.zaojiaoshu.learn.selfTag.MyNameSpaceHandler
spring.schemas
http\://www.zaojiaoshu.com/schema/user.xsd=META-INF/spring-test.xsd
至此,一個自定義標籤須要的工做,就都完成了。咱們來作個測試在咱們項目的ioc.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" xmlns:myname="http://www.zaojiaoshu.com/schema/user" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.zaojiaoshu.com/schema/user http://www.zaojiaoshu.com/schema/user.xsd"> <myname:user id="testSelfBean" userName="aaa" email="bbb"/> </beans>
測試代碼:
public class ServerMain { public static void main(String args[]){ BeanFactory context = new XmlBeanFactory(new ClassPathResource("ioc.xml")); User user = (User) context.getBean("testSelfBean"); System.out.println(user.getUserName() + "" + user.getEmail() + "米"); }
執行結果:
aaabbb米
好了,這樣咱們的一個自定義標籤的例子就完成了,那麼咱們就帶着問題來翻看自定義標籤的解析源碼吧。首先第一個問題,關於爲何META-INF/spring.handlers和META-INF/spring.schemas這兩個文件,爲何會被加載到?
對,這是我本能的第一個好奇,why?你告訴我在這裏寫的,可是爲何?
上文的自定義解析的代碼以下:
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { String namespaceUri = this.getNamespaceURI(ele); NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } else { return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); } }
咱們來看這一句,看上去就是根據Namespace找到對應的Resolver來進行resolve。其實就是這一句起做用。
rederContext.getNamespaceHandlerResolver()方法,返回的實例對象是:DefaultNamespaceHandlerResolver,這個在構建BeanDefinitionDocuemntReader的時候能夠看到。
public XmlReaderContext createReaderContext(Resource resource) { return new XmlReaderContext(resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, this.getNamespaceHandlerResolver()); } public NamespaceHandlerResolver getNamespaceHandlerResolver() { if (this.namespaceHandlerResolver == null) { this.namespaceHandlerResolver = this.createDefaultNamespaceHandlerResolver(); } return this.namespaceHandlerResolver; } protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() { return new DefaultNamespaceHandlerResolver(this.getResourceLoader().getClassLoader()); }
知道了是DefaultNamespaceHandlerResolver對象,那麼咱們來看它的resolve方法
public NamespaceHandler resolve(String namespaceUri) { Map<String, Object> handlerMappings = this.getHandlerMappings(); 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); if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } else { NamespaceHandler namespaceHandler = (NamespaceHandler)BeanUtils.instantiateClass(handlerClass); namespaceHandler.init(); handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } } catch (ClassNotFoundException var7) { throw new FatalBeanException("NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "] not found", var7); } catch (LinkageError var8) { throw new FatalBeanException("Invalid NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]: problem with handler class file or dependent class", var8); } } }
頭兩句,一句獲取handlerMappings映射關係,第二句根據映射關係拿到對應的處理類。
第一句:
private Map<String, Object> getHandlerMappings() { if (this.handlerMappings == null) { synchronized(this) { if (this.handlerMappings == null) { try { Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (this.logger.isDebugEnabled()) { this.logger.debug("Loaded NamespaceHandler mappings: " + mappings); } Map<String, Object> handlerMappings = new ConcurrentHashMap(mappings.size()); CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); this.handlerMappings = handlerMappings; } catch (IOException var5) { throw new IllegalStateException("Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", var5); } } } } return this.handlerMappings; }
看到,是個加載屬性文件的方法,裏邊的this.handlerMappingLocation咱們留意下。這個location其實在上邊構造函數裏其實已經指定了。咱們來看下DefaultNamespasceHandlerResolver的構造函數。
public DefaultNamespaceHandlerResolver() { this((ClassLoader)null, "META-INF/spring.handlers"); } public DefaultNamespaceHandlerResolver(ClassLoader classLoader) { this(classLoader, "META-INF/spring.handlers"); } public DefaultNamespaceHandlerResolver(ClassLoader classLoader, String handlerMappingsLocation) { this.logger = LogFactory.getLog(this.getClass()); Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null"); this.classLoader = classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader(); this.handlerMappingsLocation = handlerMappingsLocation; }
兩個構造函數都是默認的文件位置,因此這個spring.handlers文件咱們必定是已經加載了的。
因此這裏的mapping裏必定有,咱們spring.handlers文件裏的定義
http\://www.zaojiaoshu.com/schema/user=com.zaojiaoshu.learn.selfTag.MyNameSpaceHandler
那麼,上邊的第二句代碼user這個element的namespace就是 http://www.zaojiaoshu.com/schema/user ,那麼獲取的class就是:com.zaojiaoshu.learn.selfTag.MyNameSpaceHandler類。
接下來作的事情就是:加載這個類,實例化這個類,調用這個類的init()方法。咱們在上邊的代碼上能表清晰的看到這些步驟。而init方法是咱們在實例代碼裏MyNameSpaceHanlder類裏的惟一一個方法,目的就是註冊解析類。
同時,resolve方法返回的是一個NamespaceHandler對象。
咱們繼續上文parseCustomeElement的代碼,調用handler的parse方法:
public BeanDefinition parse(Element element, ParserContext parserContext) { return this.findParserForElement(element, parserContext).parse(element, parserContext); } private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) { String localName = parserContext.getDelegate().getLocalName(element); BeanDefinitionParser parser = (BeanDefinitionParser)this.parsers.get(localName); if (parser == null) { parserContext.getReaderContext().fatal("Cannot locate BeanDefinitionParser for element [" + localName + "]", element); } return parser; }
第一句兩件事情,1:根據element找到對應的parser,而後調用parser的parse方法
查看findParserForElement 的代碼,能夠看到,parsers里根據localName獲取的。這裏這個localName對於咱們的標籤來講確定就是:user了。
咱們還記得MyNamespaceHandler的init代碼:
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
調用的無非就是:NamespaceHandlerSupport 裏的:
protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator dec) { this.decorators.put(elementName, dec); }
那就能夠看到了,咱們自定義的Handler裏作的就是註冊user這個key到NamespaceHandlerSupport 的parsers列表裏,而後在解析的時候,這裏獲取的就是這個parser了,相應的調用的就是咱們自定義的那個parser的parse方法了,返回了BeanDefinition對象。
繼續解析的parse方法:
public final BeanDefinition parse(Element element, ParserContext parserContext) { AbstractBeanDefinition definition = this.parseInternal(element, parserContext); if (definition != null && !parserContext.isNested()) { try { String id = this.resolveId(element, definition, parserContext); if (!StringUtils.hasText(id)) { parserContext.getReaderContext().error("Id is required for element '" + parserContext.getDelegate().getLocalName(element) + "' when used as a top-level tag", element); } String[] aliases = null; if (this.shouldParseNameAsAliases()) { String name = element.getAttribute("name"); if (StringUtils.hasLength(name)) { aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name)); } } BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases); this.registerBeanDefinition(holder, parserContext.getRegistry()); if (this.shouldFireEvents()) { BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder); this.postProcessComponentDefinition(componentDefinition); parserContext.registerComponent(componentDefinition); } } catch (BeanDefinitionStoreException var8) { parserContext.getReaderContext().error(var8.getMessage(), element); return null; } } return definition; }
這個parser固然就是咱們以前建立的自定義的parser對象,咱們繼承了
AbstractSimpleBeanDefinitionParser
這個類。咱們來看下類圖:
因此第一句partnerInternal調用的是:AbstractSingleBeanDefinitionParser也即子類的實現
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); String parentName = this.getParentName(element); if (parentName != null) { builder.getRawBeanDefinition().setParentName(parentName); } Class<?> beanClass = this.getBeanClass(element); if (beanClass != null) { builder.getRawBeanDefinition().setBeanClass(beanClass); } else { String beanClassName = this.getBeanClassName(element); if (beanClassName != null) { builder.getRawBeanDefinition().setBeanClassName(beanClassName); } } builder.getRawBeanDefinition().setSource(parserContext.extractSource(element)); if (parserContext.isNested()) { builder.setScope(parserContext.getContainingBeanDefinition().getScope()); } if (parserContext.isDefaultLazyInit()) { builder.setLazyInit(true); } this.doParse(element, parserContext, builder); return builder.getBeanDefinition(); } protected String getParentName(Element element) { return null; } protected Class<?> getBeanClass(Element element) { return null; } protected String getBeanClassName(Element element) { return null; } protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { this.doParse(element, builder); } protected void doParse(Element element, BeanDefinitionBuilder builder) { }
這裏一看就是明顯的模版方法,慣用的套路,咱們實現的就是最下邊那個方法doParse(Element element, BeanDefinitionBuilder builder) ,層層往上,咱們只是實現了一個自定義的parser。
還記得咱們作了什麼事情嗎?來看下咱們的UserBeanDefinition:
protected void doParse(Element element, BeanDefinitionBuilder builder) { String userName = element.getAttribute("userName"); String email = element.getAttribute("email"); if(StringUtils.hasText(userName)){ builder.addPropertyValue("userName", userName); } if(StringUtils.hasText(email)){ builder.addPropertyValue("email", email); } }
作的事情無非是把解析到的value值添加到builder的propertyValue屬性裏。而後返回了BeanDefinition,回到最初的調用鏈 AbstractBeanDefinitionParser的parse方法裏。
接下去作的事情就比較熟悉了,尤爲是那句
this.registerBeanDefinition(holder, parserContext.getRegistry());
無非就是跟以前的默認標籤的最終操做同樣,把咱們的解析到的BeanDefinition封裝成BeanDefinitionHolder而後,註冊到咱們的註冊器中,最終添加到DefaultListableBeanFactory的那個concurrentHashMap裏。
至此,咱們的自定義標籤的解析就結束了,相對默認標籤的解析,這裏的工做由於是使用自定義的Handler和parser,複雜程度,就主要在customer的自定義複雜程度上了,自己的解析複雜度,由於有了以前默認標籤的解析對比,這裏就輕鬆多了。
通過了這三篇的學習,咱們把spring的xml通過
xml --> Doc --> BeanDefinition --> 註冊到BeanFactory裏,這幾個步驟就完成了,咱們接下來的工做就進入到最最核心的加載步驟了。期待。。。
2019-07-06 17:19於圖書館