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

来源:互联网 发布:中国第一家网络银行 编辑:程序博客网 时间:2024/05/22 06:38

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

应用

①创建一个需要拓展的组件,这里我们创建一个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注册的过程是一样的。