spring源碼閱讀(三) Bean加載之自定義標籤加載

 

緊接着上一篇關於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於圖書館

相關文章
相關標籤/搜索