Spring源码解析-自定义标签的解析

首先来讲一下这个自定义标签怎么用:

应用

①创建一个需要拓展的组件,这里我们创建一个pojo类

public class User {
    private String userName;
    private String email;

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }
}

②定义一个XSD结构描述文件,命名为Spring-test.xsd

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.corefuture.cn/schema/user"
        elementFormDefault="qualified">
    <element name="user">
        <complexType>
            <attribute name="id" type="string"/>
            <attribute name="userName" type="string"/>
            <attribute name="email" type="string"/>
        </complexType>
    </element>
</schema>

③创建一个类,实现BeanDefinitionPaser接口,用来解析我们自己定义的标签

package parser;

import bean.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;

/**
 * Created by WHZ on 2017/8/15 0015.
 */
public class UserBeanDefinitionPaser extends AbstractSingleBeanDefinitionParser{
    @Override
    protected Class getBeanClass(Element element){
        return User.class;
    }

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        //获取属性userName
        String userName = element.getAttribute("userName");
        //获取属性email
        String email = element.getAttribute("email");
        //添加到BeanDefinitionBuilder
        if(StringUtils.hasText(userName)){
            builder.addPropertyValue("userName",userName);
        }
        if(StringUtils.hasText(email)){
            builder.addPropertyValue("email",email);
        }
    }
}

④创建一个继承了NamespaceHandlerSupport的类,用来将之前的解析器注册到spring容器中

package handler;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import parser.UserBeanDefinitionPaser;

/**
 * Created by Administrator on 2017/8/15 0015.
 */
public class MyNamespaceHandler extends NamespaceHandlerSupport {
    public void init() {
        registerBeanDefinitionParser("user",new UserBeanDefinitionPaser());
    }
}

⑤在工程文件的META-INF下添加两个文件,如果没有META-INF就自己创建,创建一个spring.hadlers

http\://www.corefuture.cn/schema/user=handler.MyNamespaceHandler

创建一个spring.schemas

http\://www.corefuture.cn/schema/user.xsd=META-INF/Spring-test.xsd

记住这两个文件名最好要小写,因为这样idea这个编译环境能识别这两个文件,然后可以检查出文件内容的错误。
⑥接着就是配置spring的配置文件

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:task="http://www.springframework.org/schema/task"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:myname="http://www.corefuture.cn/schema/user"
       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.xsd
        http://www.springframework.org/schema/task
        http://www.springframework.org/schema/task/spring-task.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.corefuture.cn/schema/user
        http://www.corefuture.cn/schema/user.xsd">

    <myname:user id="aa" userName="aaaa" email="aaaa"/>
</beans>

⑦测试

package client;

import bean.User;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

/**
 * Created by Administrator on 2017/8/14 0014.
 */
public class BeanFactoryTest {

    @Test
    public void testSimpleLoad(){
        BeanFactory bf =new XmlBeanFactory(new ClassPathResource(("beanFactoryTest.xml")));
        User myTestBean = (User)bf.getBean("aa");
        System.out.println(myTestBean.getEmail());
    }
}
结果
aaaa

接着来看对自定义标签的解析过程

解析过程

回到之前的代码

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配置文件时,SAX会通过我们在spring.schemas中定义的约束文件的位置,对标签进行校验,接下来来看一下自定义标签的解析。进入parseCustomElement方法。

 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 {
            //解析
            return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
        }
    }

我们来看一下resolve这个方法
这个是接口方法,所以需要找到实体类,在readerContext初始化时这个NamespaceHandlerResoler已经被初始化为DefaultNamespaceHandlerResolver,然后我们进入这个类的resolve方法。

public NamespaceHandler resolve(String namespaceUri) {
        //获取所有已经配置的handler映射
        Map<String, Object> handlerMappings = this.getHandlerMappings();
        //根据命名空间找到对应的handler或者类名
        Object handlerOrClassName = handlerMappings.get(namespaceUri);
        if(handlerOrClassName == null) {
            return null;
        } else if(handlerOrClassName instanceof NamespaceHandler) {
            //如果是NamespaceHandler,说明已经调用了init方法,注册自定义的解析器,那么直接返回
            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 {
                    //通过反射机制实例化handler
                    NamespaceHandler namespaceHandler = (NamespaceHandler)BeanUtils.instantiateClass(handlerClass);
                    //初始化,就是调用我们自己handler类的方法,将我们自定义的解析器注册到handler中
                    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);
            }
        }
    }

接着我们来看之前的handler.parse方法

public BeanDefinition parse(Element element, ParserContext parserContext) {
        //寻找解析器并解析
        return this.findParserForElement(element, parserContext).parse(element, parserContext);
    }

    private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
        //获取元素名称也就是<myname:user>的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方法,我们定义的解析器没有parse方法,所以肯定在父类或者祖父类中,最后在AbstractBeanDefinitionParser中找到parse方法

public final BeanDefinition parse(Element element, ParserContext parserContext) {
        //对元素进行解析 然后获取beandefiniton
        AbstractBeanDefinition definition = this.parseInternal(element, parserContext);

我们进入这个方法查看,这是个模板方法,模板方法由子类实现,我们要在AbstractSingleBeanDefinitionParser寻找

protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
        //得到一个genericBeanDefinition
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
        String parentName = this.getParentName(element);
        if(parentName != null) {
            builder.getRawBeanDefinition().setParentName(parentName);
        }
        //解析调用我们定义的解析器paser的getBeanClass方法
        Class<?> beanClass = this.getBeanClass(element);
        if(beanClass != null) {
        //设置class          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);
        }
        //调用我们的doPase方法
        this.doParse(element, parserContext, builder);
        //将装载完属性的beandefinition返回
        return builder.getBeanDefinition();
    }

继续看原来的方法

        if(definition != null && !parserContext.isNested()) {
            try {
                //获取id
                String id = this.resolveId(element, definition, parserContext);
                //在这里一定要有id,说明我们的标签一定要有id属性。否则就会报错,也就意味着beanName不能被name所代替
                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 = new String[0];
                   //解析name属性,当然主要还是为了获取别名
                String name = element.getAttribute("name");
                if(StringUtils.hasLength(name)) {
                    aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
                }
                //装配BeanDefinitionHolder 
                BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
                //然后注册到spring容器中,当然这个注册和默认标签bean注册是一样的
                this.registerBeanDefinition(holder, parserContext.getRegistry());
                //如果需要就通知监听器
                if(this.shouldFireEvents()) {
                    BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
                    this.postProcessComponentDefinition(componentDefinition);
                    parserContext.registerComponent(componentDefinition);
                }
            } catch (BeanDefinitionStoreException var9) {
                parserContext.getReaderContext().error(var9.getMessage(), element);
                return null;
            }
        }

        return definition;
    }

总结

①通过命名空间,从handlermap映射中获取handler,如果取到了handler类,那么直接返回,如果没有,那返回的肯定是全类名,然后通过全类名用反射机制进行实例化,调用这个handler的init方法注册我们定义的解析器,再放回handlermap中。
②调用handler的parse方法,先找到对应的解析器,然后对元素进行解析,先解析class等一些属性,然后调用我们自己的doParse方法,最后返回承载了各种属性的beandefinition,然后得到元素的id作为beanName,然后将beandefinition,beanName,别名集合装配到beanDefinitionHolder中
③最后将beanDefinitionHolder注册到容器中,正确说是将beanDefinitionHolder中的beandefinition注册到容器中,当然还有别名和beanName之间的映射。这和之前讲过的默认标签的bean注册的过程是一样的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值