spring 自定义命名空间

目录

1.定义和xsd文件相对应的配置类,来承载配置

2.定义xsd文件

 3.自定义与命名空间相对应的handler处理类

4.定义handler中注册的对应的BeanDefinitionParser,解析命名空间中的属性

5.定义spring.handlers和 spring.schemas文件。

6.spring-config.xml文件中引入自定义的people命名空间

7.编写测试类:

8 原理:



1.定义和xsd文件相对应的配置类,来承载配置

package com.chr.test.namespace;

/**
 * @author chenhairong3
 * @description 定义一个和xsd配置相对应的Java对象,来承载配置
 * @date 2020/12/15
 */
public class People {

    //id属性是必须的
    private String id;

    private String name;


    private int age;


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

2.定义xsd文件

id字段 不用在xsd中定义,默认就有此字段

 3.自定义与命名空间相对应的handler处理类

package com.chr.test.namespace;

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

/**
 * @author chenhairong3
 * @description 自定义的people命名空间的handler处理类
 * @date 2020/12/15
 */
public class PeopleNamespaceHandler extends NamespaceHandlerSupport {

    public PeopleNamespaceHandler() {
    }


    public void init() {
        this.registerBeanDefinitionParser("config", new PeopleConfigBeanDefinitionParser());
    }
}

4.定义handler中注册的对应的BeanDefinitionParser,解析命名空间中的属性

package com.chr.test.namespace;

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 chenhairong3
 * @description people命名空间下config元素对应的解析类
 * @date 2020/12/15
 */
public class PeopleConfigBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
    @Override
    protected Class getBeanClass(Element element) {
        return People.class;
    }

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

5.定义spring.handlers和 spring.schemas文件。

spring.handlers 把命名空间url和handler处理类关联起来。

 spring.schemas 把命名空间和对应的xsd文件关联起来。

spring.handlers文件如下:

http\://www.springframework.org/schema/people=com.chr.test.namespace.PeopleNamespaceHandler

spring.schemas 文件如下:

http\://www.springframework.org/schema/people=META-INF/people.xsd

 

6.spring-config.xml文件中引入自定义的people命名空间

<?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:util="http://www.springframework.org/schema/util"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:people="http://www.springframework.org/schema/people"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
       http://www.springframework.org/schema/util
       http://www.springframework.org/schema/util/spring-util-3.2.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/people
       file:D:/idea-workspace/test-spring/test-spring-configuration/src/main/resources/META-INF/people.xsd"
       default-autowire="byName">

   
    <people:config id="cutesource" name="袁志俊" age="27"/>

</beans>
http://www.springframework.org/schema/people
这里的路径我写的是我本地的绝对文件路径,我的电脑是windows系统的。使用时,请更换到自己xsd文件所对应的的目录。
file:///D://idea-workspace//test-spring//test-spring-configuration//src//main//resources//META-INF//people.xsd

 

7.编写测试类:

/**
 * @author chenhairong3
 * @description
 * @date 2020/12/15
 */
public class NamespaceTest {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring/spring-config.xml");
        People people = (People) context.getBean("cutesource");
        System.out.println("people = " + people.getId() +",name:"+people.getName()+",age:"+people.getAge());
    }

}

 

输出:

people = cutesource,name:袁志俊,age:27

 

8 原理:

 

 下来我们介绍一下Spring提供的NamespaceHandler的处理机制.

接下来我们先看下Spring提供的NamespaceHandlerSupport抽象类中的parse方法。

public abstract class NamespaceHandlerSupport implements NamespaceHandler {
    private final Map<String, BeanDefinitionParser> parsers = new HashMap();
    private final Map<String, BeanDefinitionDecorator> decorators = new HashMap();
    private final Map<String, BeanDefinitionDecorator> attributeDecorators = new HashMap();

    public NamespaceHandlerSupport() {
    }

    @Nullable
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        BeanDefinitionParser parser = this.findParserForElement(element, parserContext);
        return parser != null ? parser.parse(element, parserContext) : null;
}

//此次省略掉其他无关代码

}

parse方法中,会找到与命名空间相对应的BeanDefinitionParser,然后调用BeanDefinitionParser中的parse方法进行处理。

那么namespaceHandler在spring容器初始化的时候是怎么被调用的呢?

我们回到ClassPathXmlApplicationContext中的refresh 容器刷新方法:

public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            //在这里进行配置文件的加载,并把配置解析成BeanDefinition并放入map集合中
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }
我们继续进入这个方法:ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //在这里完成BeanDefinition的加载,这是个抽象方法,最终会调用到子类AbstractRefreshableApplicationContext
        this.refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Bean factory for " + this.getDisplayName() + ": " + beanFactory);
        }

        return beanFactory;
    }

//在这里完成BeanDefinition的加载,这是个抽象方法,最终会调用到子类AbstractRefreshableApplicationContext
        this.refreshBeanFactory();

我们继续跟踪AbstractRefreshableApplicationContext类中的refreshBeanFactory()方法,

protected final void refreshBeanFactory() throws BeansException {
        if (this.hasBeanFactory()) {
            this.destroyBeans();
            this.closeBeanFactory();
        }

        try {
            DefaultListableBeanFactory beanFactory = this.createBeanFactory();
            beanFactory.setSerializationId(this.getId());
            this.customizeBeanFactory(beanFactory);
            //在这里完成bean定义的加载,这里会调用到子类的AbstractXmlApplicationContext中的此方法
            this.loadBeanDefinitions(beanFactory);
            synchronized(this.beanFactoryMonitor) {
                this.beanFactory = beanFactory;
            }
        } catch (IOException var5) {
            throw new ApplicationContextException("I/O error parsing bean definition source for " + this.getDisplayName(), var5);
        }
    }

  //在这里完成bean定义的加载,这里会调用到子类的AbstractXmlApplicationContext中的此方法
            this.loadBeanDefinitions(beanFactory);

进入AbstractXmlApplicationContext类中的loadBeanDefinitions方法:

 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        this.initBeanDefinitionReader(beanDefinitionReader);
        //通过XmlBeanDefinitionReader完成对bean定义的解析加载
        this.loadBeanDefinitions(beanDefinitionReader);
    }

  //通过XmlBeanDefinitionReader完成对bean定义的解析加载
        this.loadBeanDefinitions(beanDefinitionReader);

 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
        Resource[] configResources = this.getConfigResources();
        if (configResources != null) {
            reader.loadBeanDefinitions(configResources);
        }

        String[] configLocations = this.getConfigLocations();
        if (configLocations != null) {
            //完成bean定义的加载
            reader.loadBeanDefinitions(configLocations);
        }

    }
进入AbstractBeanDefinitionReader中的loadBeanDefinitions()方法:
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
    Assert.notNull(locations, "Location array must not be null");
    int counter = 0;
    String[] var3 = locations;
    int var4 = locations.length;

    for(int var5 = 0; var5 < var4; ++var5) {
        String location = var3[var5];
        counter += this.loadBeanDefinitions(location);
    }

    return counter;
}

 

最终追踪到AbstractBeanDefinitionReader中的下面方法:

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
    ResourceLoader resourceLoader = this.getResourceLoader();
    if (resourceLoader == null) {
        throw new BeanDefinitionStoreException("Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
    } else {
        int loadCount;
        if (!(resourceLoader instanceof ResourcePatternResolver)) {
            Resource resource = resourceLoader.getResource(location);
            loadCount = this.loadBeanDefinitions((Resource)resource);
            if (actualResources != null) {
                actualResources.add(resource);
            }

            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
            }

            return loadCount;
        } else {
            try {
                Resource[] resources = ((ResourcePatternResolver)resourceLoader).getResources(location);
                //进行配置的加载
                loadCount = this.loadBeanDefinitions(resources);
                if (actualResources != null) {
                    Resource[] var6 = resources;
                    int var7 = resources.length;

                    for(int var8 = 0; var8 < var7; ++var8) {
                        Resource resource = var6[var8];
                        actualResources.add(resource);
                    }
                }

                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
                }

                return loadCount;
            } catch (IOException var10) {
                throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", var10);
            }
        }
    }
}

最终追踪到DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法:

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;
    this.logger.debug("Loading bean definitions");
    Element root = doc.getDocumentElement();
    //开始注册bean定义
    this.doRegisterBeanDefinitions(root);
}

进入 //开始注册bean定义
    this.doRegisterBeanDefinitions(root);方法

protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
        if (this.delegate.isDefaultNamespace(root)) {
            String profileSpec = root.getAttribute("profile");
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
                if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (this.logger.isInfoEnabled()) {
                        this.logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
                    }

                    return;
                }
            }
        }
        //这里用到了模板方法模式,这个方法是抽象方法,由子类方法
        this.preProcessXml(root);
        //开始解析bean定义
        this.parseBeanDefinitions(root, this.delegate);
        //这里用到了模板方法模式,这个方法是抽象方法,由子类方法
        this.postProcessXml(root);
        this.delegate = parent;
    }

       进入此方法:
        this.parseBeanDefinitions(root, this.delegate);

   protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
//判断是否是spring默认的命名空间,默认的空间是bean
            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);
        }

    }

进入 //解析用户自定义的命名空间方法:
            delegate.parseCustomElement(root);

进入到

BeanDefinitionParserDelegate的
@Nullable
    public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
       //获取元素对应的命名空间urI
        String namespaceUri = this.getNamespaceURI(ele);
        if (namespaceUri == null) {
            return null;
        } else {
             //通过namespaceUri找到对应的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 {
               //调用handler的parse方法处理。NamespaceHandlerSupport的parse方法会找多对应handler的BeanDefinitionParse,从而调用BeanDefinitionParse的parse方法来处理命名空间数据
                return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
            }
        }
    }

进入 //通过namespaceUri找到对应的NamespaceHandler处理类
            NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);这个方法,来根据spring是如何根据namespaceUri来找对对应的namespaceHandler呢?

 @Nullable
    public NamespaceHandler resolve(String namespaceUri) {
       //从handlerMappings中根据namespaceUri找多对应的handler对象实例
        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 {
           //如果对象实例为空,则通过反射实例化handler对象,并放入到handlerMappings中。
            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("Could not find NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", var7);
            } catch (LinkageError var8) {
                throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", var8);
            }
        }
    }

那么重点来了,this.getHandlerMappings()是如何存入进去的呢?

进入

DefaultNamespaceHandlerResolver类:
private Map<String, Object> getHandlerMappings() {
        Map<String, Object> handlerMappings = this.handlerMappings;
        if (handlerMappings == null) {
            synchronized(this) {
                handlerMappings = this.handlerMappings;
                if (handlerMappings == null) {
                    try {
                       //加载配置文件,this.handlerMappingsLocation 这里的文件路径就是:"META-INF/spring.handlers",所以是通过此文件找多的。key就是namespaceuri,value就是对应的NamespaceHandler,最后放入map中
                        Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
                        if (this.logger.isDebugEnabled()) {
                            this.logger.debug("Loaded NamespaceHandler mappings: " + mappings);
                        }

                        Map<String, Object> mappingsToUse = new ConcurrentHashMap(mappings.size());
                        CollectionUtils.mergePropertiesIntoMap(mappings, mappingsToUse);
                        handlerMappings = mappingsToUse;
                        this.handlerMappings = mappingsToUse;
                    } catch (IOException var6) {
                        throw new IllegalStateException("Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", var6);
                    }
                }
            }
        }

        return (Map)handlerMappings;
    }

终于破案了,是通过 Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);

/加载配置文件,this.handlerMappingsLocation 这里的文件路径就是:"META-INF/spring.handlers",所以是通过此文件找多的。key就是namespaceuri,value就是对应的NamespaceHandler,最后放入map中
                       

所以自定义的命名空间是在spring的刷新容器的时候,在refresh里面,在loadBeanDefinitions的时候,解析自定义命名空间parseCustomElement()方法中解析中,解析到最后是通过读取"META-INF/spring.handlers"这个配置文件,此文件的key为自定义命名空间uri,value是对应的namespacehandler的全限定类名。然后调用对应的namespacehandler的parse方法,parse方法会找到handler中init方法注册的BeanDefinitionParser类,然后调用对应BeanDefinitionParser的parse方法进行自定义命名空间的属性数据解析等。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值