资讯专栏INFORMATION COLUMN

Spring源码阅读——ClassPathXmlApplicationContext(四)

ixlei / 1305人阅读

摘要:在的方法中,遍历每一个节点,判断是否为默认命名空间中的节点,如果是非默认命名空间的,调用方法进行处理。在学习自定义标签解析之前,先写一个自定义标签的。

在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法中,遍历每一个节点,判断是否为默认命名空间中的节点,如果是非默认命名空间的,调用delegate.parseCustomElement(ele)方法进行处理。在学习自定义标签解析之前,先写一个自定义标签的demo。

一、自定义标签示例

定义POJO

下面定义一个Person类

package com.demo.beans.custom;

/**
 *@author zhzhd
 *@date 2018/6/11
 *@package 
 *@describe
 **/
public class Person {
    private String userName;
    private String sex;
    private Integer age;

    public Person(String name){
        this.userName = name;
    }
   ......省略setter和getter
}

定义一个文件描述组件

在webapp下创建person.xsd文件,内容如下:



    
        
            
                
                    
                
            
        
        
            
                
                    
                
            
        
        
            
                
                    
                
            
        
        
            
                
                    
                
            
        
    

    
        
            
        
    

自定义NamespaceHandler

自定义MyNamespaceHandler,继承NamespaceHandlerSupport,并且重写init()方法,实现如下:

package com.demo.beans.custom;

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

/**
 *@author zhzhd
 *@date 2018/6/11
 *@package com.demo.beans.custom
 *@describe
 **/
public class MyNamespaceHandler extends NamespaceHandlerSupport{
    public void init() {
        registerBeanDefinitionParser("", new PersonBeanDefinitionParser());
    }
}

实现自定义PersonBeanDefinitionParser

自定义实现PersonBeanDefinitionParser,并且重写Class getBeanClass(Element element)和doParse(Element element, BeanDefinitionBuilder bean)方法。getBeanClass方法返回当前bean的class,doParse解析自定义元素属性,实现如下:

package com.demo.beans.custom;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

/**
 *@author zhzhd
 *@date 2018/6/11
 *@package com.demo.beans.custom
 *@describe
 **/
public class PersonBeanDefinitionParser extends AbstractSingleBeanDefinitionParser{

    @Override
    protected Class getBeanClass(Element element){
        return Person.class;
    }

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder bean){
        String name = element.getAttribute("name");
        bean.addConstructorArgValue(name);
        if (StringUtils.hasText(name)){
            bean.addConstructorArgValue(name);
        }

        String age = element.getAttribute("age");
        String sex = element.getAttribute("sex");
        if (StringUtils.hasText(age)){
            bean.addPropertyValue("age", Integer.parseInt(age));
        }
        if (StringUtils.hasText(age)){
            bean.addPropertyValue("sex", sex);
        }
    }
}

创建spring.handlers和spring.schemas

http://www.zhzhd.com/schema/person=com.demo.beans.custom.MyNamespaceHandler
http://www.zhzhd.com/schema/person.xsd=person.xsd

在XML中配置bean以及测试

接下来,需要在spring的配置文件中加入命名空间信息,并且配置自定义bean,实现如下:



    

测试demo如下:

    public static void main(String[] args) {
        BeanFactory beanFactory1 = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        ApplicationContext beanFactory = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"},true);
        Person person = beanFactory.getBean("testPerson", Person.class);
        System.out.println(JSON.toJSONString(person));
    }

从上面的示例可以看到,我们定义了自定义节点的handler,spring在解析xml中节点或属性的时候,当遇到自定义节点和属性时,会调用响应的handler进行解析,下面具体分析自定义节点和属性解析的源码。

二、spring解析自定义标签的源码分析

首先,从xml元素的解析开始分析,在parseBeanDefinitions()方法中,判断如果是xml中节点或属性是自定义的,则调用BeanDefinitionParserDelegate的parseCustomElement()方法处理,下面是parseCustomElement()的实现:

    public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
        // 读取命名空间url
        String namespaceUri = getNamespaceURI(ele);
        if (namespaceUri == null) {
            return null;
        }
        // 解析命名空间,返回NamespaceHandler实例
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        }
        // 解析自定义标签
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }

从Element中获取到自定义命名空间uri后,交给DefaultNamespaceHandlerResolver的resolve()方法解析并实例化NamespaceHandler实例,具体实现如下:

public NamespaceHandler resolve(String namespaceUri) {
        // 获取命名空间uri和handler类名关系map
        Map handlerMappings = 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");
                }
                // 实例化NamespaceHandler
                NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
                // 初始化NamespaceHandler
                namespaceHandler.init();
                handlerMappings.put(namespaceUri, namespaceHandler);
                return namespaceHandler;
            }
            catch (ClassNotFoundException ex) {
                throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
                        "] for namespace [" + namespaceUri + "]", ex);
            }
            catch (LinkageError err) {
                throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
                        className + "] for namespace [" + namespaceUri + "]", err);
            }
        }
    }

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/76390.html

相关文章

  • Spring源码阅读——ClassPathXmlApplicationContext

    摘要:在的方法中,遍历每一个节点,判断是否为默认命名空间中的节点,如果是非默认命名空间的,调用方法进行处理。在学习自定义标签解析之前,先写一个自定义标签的。 在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法中,遍历每一...

    silenceboy 评论0 收藏0
  • Spring源码阅读——ClassPathXmlApplicationContext

    摘要:在的方法中,遍历每一个节点,判断是否为默认命名空间中的节点,如果是非默认命名空间的,调用方法进行处理。在学习自定义标签解析之前,先写一个自定义标签的。 在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)方法中,遍历每一...

    wmui 评论0 收藏0
  • Spring源码阅读——ClassPathXmlApplicationContext(一)

    摘要:的继承关系继承了,实现了接口。是所有容器的顶级接口,中所有容器都是基于的。方法创建一个新的容器。在本方法中,最重要的是,调用这个方法解析配置文件,注册。 ClassPathXmlApplicationContext的继承关系 ClassPathXmlApplicationContext继承了AbstractXmlApplicationContext,实现了ApplicationCont...

    taowen 评论0 收藏0
  • Spring源码阅读——ClassPathXmlApplicationContext(三)

    摘要:在上一篇源码阅读二文章的最后,需要解析元素,创建实例完成必须的装配和进行最终的注册来完成元素的解析和注册,下面分别阅读三步的源码。 在上一篇Spring源码阅读——ClassPathXmlApplicationContext(二)文章的最后,需要解析bean元素,创建BeanDefinitionHolder实例、完成必须的装配和进行最终的注册bean来完成bean元素的解析和注册,下面...

    AndroidTraveler 评论0 收藏0
  • Spring源码阅读——ClassPathXmlApplicationContext(三)

    摘要:在上一篇源码阅读二文章的最后,需要解析元素,创建实例完成必须的装配和进行最终的注册来完成元素的解析和注册,下面分别阅读三步的源码。 在上一篇Spring源码阅读——ClassPathXmlApplicationContext(二)文章的最后,需要解析bean元素,创建BeanDefinitionHolder实例、完成必须的装配和进行最终的注册bean来完成bean元素的解析和注册,下面...

    gecko23 评论0 收藏0

发表评论

0条评论

ixlei

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<