Spring源码解析笔记3——自定义标签的解析

继续笔记1中结尾处的自定义标签解析,delegate.parseCustomElement(ele)方法。

自定义标签:Spring提供了一个可扩展Schema的支持,扩展Spring自定义标签的步骤如下。

  • 创建一个需要扩展的组件。
  • 定义一个XSD文件描述组件内容。
  • 创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义。
  • 创建一个Handler文件,扩展自NamespaceHandlerSupport,目的是将组件注册到Spring容器。
  • 编写Spring.handlers和Spring.schemas。

1.第一步,创建一个普通的JavaBean。

public class User {

    private String username;
    private String email;

    //get/set方法。
}

2.定义一个XSD文件描述组件。

<?xml version="1.0" encoding="utf-8" ?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.lexueba.com/schema/user"
        xmlns:tns="http://www.lexueba.com/schema/user"
        elementFormDefault="qualified">
    <element name="user">
        <complexType>
            <attribute name="id" type="string"></attribute>
            <attribute name="username" type="string"></attribute>
            <attribute name="email" type="string"></attribute>
        </complexType>
    </element>
</schema>

3.创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义。

public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    protected  Class getBeanClass(Element element){
        return User.class;
    }

    protected void doParse(Element element, BeanDefinitionBuilder bean){
        String username = element.getAttribute("username");
        String email = element.getAttribute("email");

        if(StringUtils.hasText(username)){
            bean.addPropertyValue("username",username);
        }

        if(StringUtils.hasText(email)){
            bean.addPropertyValue("email",email);
        }
    }
}

4.创建一个Handler文件,扩展自NamespaceHandlerSupport,目的是将组件注册到Spring容器。

package com.msmk.cloud.spring.demo01;

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文件,默认位置在工程的/META-INF/文件夹下,当然,你可以通过Spring的扩展或者修改源码的方式改变路径。

//Spring.handlers
http://www.lexueba.com.schema/user=com.msmk.cloud.spring.demo01.MyNamespaceHandler


//Spring.schemas
http://www.lexueba.com.schema/user.xsd=META-INF/Spring-test.xsd

6.创建测试的配置文件,在配置文件中引入对应的命名空间和对应的XSD。

<?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.lexueba.com/schema/user"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
       http://www.lexueba.com/schema/user http://www.lexueba.com/schema/user.xsd" >

    <myname:user id="testbean" username="aaa" email="bbb"/>

</beans>

7.测试。

public static void main(String[] args) {
        ApplicationContext bf = new ClassPathXmlApplicationContext("test.xml");
        User user = (User)bf.getBean("testBean");
        System.out.println(user.getUsername() + "##" + user.getEmail());
    }

继续笔记1中结尾处的自定义标签解析。通过delegate.parseCustomElement(ele)方法进行跟踪。

public BeanDefinition parseCustomElement(Element ele) {
        return this.parseCustomElement(ele, (BeanDefinition)null);
}

继续跟踪:

 public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {

        //获取对应的命名空间。
        String namespaceUri = this.getNamespaceURI(ele);

        //根据命名空间找到对应的NamespaceHandler
        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 {

            //调用自定义的NamespaceHandler进行解析。
            return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
        }
    }

分别对每一个方法进行跟踪:

  • getNamespaceURI,主要用于区分默认标签和自定义标签都是以命名空间为基础,如何根据对应的命名空间去提取元素,spring是直接调用org.w3c.dom.Node中已经提供了的方法。

    public String getNamespaceURI(Node node) {
        return node.getNamespaceURI();
    }
    
  • resolve方法

    //以下代码调用了resolve方法。
    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    
    //继续跟踪resolve方法。
    public NamespaceHandler resolve(String namespaceUri) {
    
        //获取所有已经配置的handler映射
        Map 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 err = ClassUtils.forName(className, this.classLoader);
                if(!NamespaceHandler.class.isAssignableFrom(err)) {
                    throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
                } else {
    
                    //初始化类
                    NamespaceHandler namespaceHandler = (NamespaceHandler)BeanUtils.instantiateClass(err);
    
                    //调用自定义的NamespaceHandlerder的初始化方法。
                    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);
            }
        }
    }
    

解释上面resolve方法中的每一个方法:

  • this.getHandlerMappings()

    private Map<String, Object> getHandlerMappings() {
    
        //如果没缓存则开始进行缓存。
        if(this.handlerMappings == null) {
            synchronized(this) {
                if(this.handlerMappings == null) {
                    try {
    
                        //在构造函数中已经被初始化为:META-INF/Spring.handlers
                        Properties ex = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
                        if(this.logger.isDebugEnabled()) {
                            this.logger.debug("Loaded NamespaceHandler mappings: " + ex);
                        }
    
                        ConcurrentHashMap handlerMappings = new ConcurrentHashMap(ex.size());
    
                        //将Properties格式文件合并到Map格式的handlerMappings中
                        CollectionUtils.mergePropertiesIntoMap(ex, handlerMappings);
                        this.handlerMappings = handlerMappings;
                    } catch (IOException var5) {
                        throw new IllegalStateException("Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", var5);
                    }
                }
            }
        }
    
        return this.handlerMappings;
    }

分析一下MyNamespaceHandler父类NamespaceHandlerSupport中的parse方法:

public BeanDefinition parse(Element element, ParserContext parserContext) {
        return this.findParserForElement(element, parserContext).parse(element, parserContext);
    }


//继续追踪findParserForElement方法:
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {

        //获取元素名称,也就是<myname:user中的user,此时localName为user。
        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;
    }


//继续追踪parse方法:
public final BeanDefinition parse(Element element, ParserContext parserContext) {

        //此处的parseInternal才是真正做事的方法。
        AbstractBeanDefinition definition = this.parseInternal(element, parserContext);
        if(definition != null && !parserContext.isNested()) {
            try {
                String ex = this.resolveId(element, definition, parserContext);
                if(!StringUtils.hasText(ex)) {
                    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 holder = element.getAttribute("name");
                    if(StringUtils.hasLength(holder)) {
                        aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(holder));
                    }
                }

                //将AbstractBeanDefinition转换为BeanDefinitionHolder并注册
                BeanDefinitionHolder holder1 = new BeanDefinitionHolder(definition, ex, aliases);
                this.registerBeanDefinition(holder1, parserContext.getRegistry());
                if(this.shouldFireEvents()) {

                    //需要通知监听器进行处理。
                    BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder1);
                    this.postProcessComponentDefinition(componentDefinition);
                    parserContext.registerComponent(componentDefinition);
                }
            } catch (BeanDefinitionStoreException var8) {
                parserContext.getReaderContext().error(var8.getMessage(), element);
                return null;
            }
        }

        return definition;
    }

上面追踪parse方法中的核心parseInternal方法源码如下。

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,此时会调用自定义解析器如UserBeanDefinitionParser中的getBeanClass方法。
        Class beanClass = this.getBeanClass(element);
        if(beanClass != null) {
            builder.getRawBeanDefinition().setBeanClass(beanClass);
        } else {

            //若子类没有重写getBeanClass方法则尝试检查子类是否重写getBeanClass方法。
            String beanClassName = this.getBeanClassName(element);
            if(beanClassName != null) {
                builder.getRawBeanDefinition().setBeanClassName(beanClassName);
            }
        }

        builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
        if(parserContext.isNested()) {
                //若存在父类则使用父类的scope属性。
                builder.setScope(parserContext.getContainingBeanDefinition().getScope());
        }

        if(parserContext.isDefaultLazyInit()) {
            //配置懒加载。
            builder.setLazyInit(true);
        }

        //调用子类重写的doParse方法进行解析。
        this.doParse(element, parserContext, builder);
        return builder.getBeanDefinition();
    }

//继续跟踪doParse方法。
 protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
        this.doParse(element, builder);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值