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于图书馆

相关文章
相关标签/搜索