zoukankan      html  css  js  c++  java
  • spring源码阅读(三) Bean加载之自定义标签加载

    紧接着上一篇关于spring默认标签加载,这一篇来看下自定义标签的加载

    继续从 DefaultBeanDefinitionDocumentReader来看

    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的源码里可谓是用的淋漓尽致,尤其单一职责/接口隔离,这两个翻看源码的时候尤其有体会,之前自己在项目开发中,其实根本没有在一这些事情,只是按照业务划分进行接口的拆分,并不在意是否是单一职责/是否接口隔离这些事情,其实单一职责能让我们更好的去拓展和维护我们的代码。包括接口隔离其实都是这样的目的。能够符合这些规则,我们的代码就能更大限度的符合高可维护性/低耦合性这些要求,也就能实现最大限度的优化开发效率这件事情。

    好了,扯了些题外话,我们继续自定义标签的解析工作:

    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));
            }
        }

     看到这里有点儿蒙圈,如果没有用过自定义标签的话,会有些蒙,那我们不妨在第一篇的例子上我们搞一个自定义标签来试试。

     2019.07.06 14:29分 于图书馆继续(在家带了三天孩子,今天继续,上午继续看《架构整洁之道》不过有点儿瞌睡,中间看了一个小时的曾国藩家书,曾国藩说敬字/勤字,个个方面来说都要早起,而且早起更是曾氏一门,延续了好几代人的好习惯,并要求子女家人也要这样,是啊,想想,业精于勤荒于嬉,不说的都是这个意思吗?)早上起的太晚,习惯了现在连孩子都是这样了,这样下去下一代也还将延续我的毛病,如果现在不痛下决心就很难改变了。

    好了,穿插下想说的话,现在继续。刚才翻了之前的两篇博客,写的还是不够好,自己看起来都有点儿晕,不过很快串联起来就好了,这两篇默认标签和自定义标签包括第一篇的xml文件到Doc的解析,这些相关的内容无非就是Bean的解析过程,认准了这个核心,带着这个核心去看就不难明白这其中的流程了。

    前文提到,我们自己构建一个例子来看下自定义标签到底是如何使用的。这里就来展示下之前构建的例子。

    创建一个自定义标签的步骤如下:

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

  • 相关阅读:
    python 数据分析3
    python 数据分析2
    Python 数据分析1
    Python18 Django 基础
    Python 17 web框架&Django
    一只救助犬的最后遗言
    With As 获取 id parentId 递归获取所有
    分布式事物
    div 浮动框
    sql时间比较
  • 原文地址:https://www.cnblogs.com/aquariusm/p/11122227.html
Copyright © 2011-2022 走看看