spring拓展之如何定义自己的namespace

目录
  • spring拓展 定义自己的namespace
    • 1.查看源码认识spring是怎么加载xml配置的
    • 2.定义自己的namespace
  • spring-namespace实现自定义标签类
    • 1.配置java Bean
    • 2.编写xsd文件
    • 3.编写BeanDefinationParse标签解析类
    • 4.编写调用标签解析类的NamespaceHandler类
    • 5.编写spring.handlers和spring.schemas以供spring读取
    • 6.打包
    • 7.在其他项目中使用

spring拓展 定义自己的namespace

1.查看源码认识spring是怎么加载xml配置的

1.1 spring是怎么创建对象的?

查看spring beanFactory的继承关系

通过查看源码可以得知,BeanFactory 中的对象创建是实际是根据RootBeanDefinition创建的, 在AbstractAutowireCapableBeanFactory中有具体的实现,包括创建实例,

利用Spring拓展

java的内省实现BeanWrapperImpl,创建对象的包装类,使用反射给对象填充属性,并实现依赖注入DI 。具体可以自行参阅源码。

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    .....
    protected abstract Object createBean(String beanName, RootBeanDefinition mbd, Object[] args)
            throws BeanCreationException;
}

而RootBeanDefination定义的是什么呢?查看AbstractBeanDefination类。

可以看到这里就是Spring对对象属性的封装,包括类名,属性,加载策略等等,其实也就是我们在xml里 配置的对象。

1.2 spring是怎么将xml里配置的对象读到BeanFactory中的?

在查看spring容器的源码时,得知spring 是使用 org.springframework.beans.factory.xml.XmlBeanDefinitionReader 进行xml解析的

    public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
        .....
        protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
                    throws BeanDefinitionStoreException {
                try {
                    //读取xml
                    Document doc = doLoadDocument(inputSource, resource);
                    //解析并注册xml中定义的BeanDefination
                    return registerBeanDefinitions(doc, resource);
                }
                catch (BeanDefinitionStoreException ex) {
                    xxx
                }
            }
        .....
    }

接下来查看对dom 解析部分的源码

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {
@Override
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();
        doRegisterBeanDefinitions(root);
    }
    protected void doRegisterBeanDefinitions(Element root) {
        //为了实现进行递归解析
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);
        if (this.delegate.isDefaultNamespace(root)) {
            //对profile的支持
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    return;
                }
            }
        }
        preProcessXml(root);
        //开始解析dom树
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);
        this.delegate = parent;
    }
    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)) {
                        //spring的基础命名元素解析(import、bean、beans、alias)其中在
                        //beans嵌套时会进行递归解析
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        //拓展元素解析
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }
}

查看spring是怎么实现拓展元素的解析的

public class BeanDefinitionParserDelegate {
    public BeanDefinition parseCustomElement(Element ele) {
        return parseCustomElement(ele, null);
    }
    public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
        //获取当前节点命名空间URI
        String namespaceUri = getNamespaceURI(ele);
        //根据命名空间解析到自定义的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;
        }
        //使用拓展的Handler对当前节点进行解析
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }
}

其中NamespaceHandlerResolver 会去查找当前项目中classpath 下的META-INF目录下所有文件名为 spring.handlers配置文件,定位到自定义namespace的解析器实现类。

其中在namespace 的处理器中可以通过进行BeanDefination的注册,注册过的BeanDefination会用来给BeanFactory创建对象使用,将解析好的BeanDefination注册到parserContext.getRegistry()中即可。其实DefaultListableBeanFactory 就是一个BeanDefinitionRegistry。

@Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        ....
        parserContext.getRegistry().registerBeanDefinition(beanName, mbd);
    }

2.定义自己的namespace

2.1 定义schema约束xsd文件

将自定义的xsd文件放到项目的 META-INF 目录下。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://xxx.xxx.com/schema/myns"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:tool="http://www.springframework.org/schema/tool"
    targetNamespace="http://xxx.xxx.com/schema/myns">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
    <xsd:import namespace="http://www.springframework.org/schema/beans"/>
    <xsd:import namespace="http://www.springframework.org/schema/tool"/>
    <xsd:annotation>
        <xsd:documentation><![CDATA[ Namespace support for the myns test. ]]></xsd:documentation>
    </xsd:annotation>
    <xsd:complexType name="mybeanType">
        <xsd:attribute name="id" type="xsd:ID">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="name" type="xsd:string" use="required">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The mybean name. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="class" type="xsd:string" use="required">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The version. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
    </xsd:complexType>
    <xsd:element name="mybean" type="mybeanType">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ The mybean config ]]></xsd:documentation>
        </xsd:annotation>
    </xsd:element>
</xsd:schema>

更多xsd写法可以参阅xml的相关资料。

2.2创建自定义namespace的NamespaceHandler

使用NamespaceHandlerSupport来实现我们定义的NamespaceHandler。在init时去提供具体的标签的 解析器。

BeanDefinitionParser

public class MybeanParser implements BeanDefinitionParser {
    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        RootBeanDefinition mbd =  new RootBeanDefinition();
        mbd.setBeanClassName(element.getAttribute("class"));
        String beanName = element.getAttribute("id");
        MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
        mutablePropertyValues.add("name", element.getAttribute("name"));
        mbd.setPropertyValues(mutablePropertyValues);
        parserContext.getRegistry().registerBeanDefinition(beanName, mbd);
        return mbd;
    }
}

实现自定义的NamespaceHandler

public class MynsNameSpaceHandler extends NamespaceHandlerSupport{
    @Override
    public void init() {
        registerBeanDefinitionParser("mybean", new MybeanParser());
    }
}

这里的mybean是myns namespace下的元素标签的具体的解析实现

如:

<myns:mybean></myns:mybean>

2.3配置自定义的NamespaceHandler映射

在META-INF下创建文件 spring.handlers

http\://xxx.xxx.com/schema/myns=com.xxx.MynsNameSpaceHandler

2.4使用自定义的Namespace

<?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:myns="http://xxx.xxx.com/schema/myns"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://xxx.xxx.com/schema/myns http://xxx.xxx.com/schema/myns.xsd
        ">
        <myns:mybean class="com.xxx.People" id="mybean123" name="testMybean"></myns:mybean>
</beans>

2.5测试

public class MybeanNamespaceTestCase {
    @SuppressWarnings("resource")
    @Test
    public void testGetBean(){
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext(new String[]{"myapp.xml"},false);
        ac.setValidating(false);
        ac.refresh();
        People bean = ac.getBean("mybean123",People.class);
        System.out.println(bean.getName());
    }
}

这里设置ac.setValidating(false); 是因为如果开启xml约束检查,需要配置schema的位置,

也是在META-INF 下新建spring.schemas

并加入:

http\://xxx.xxx.com/schema/myns.xsd=META-INF/myns.xsd

这样spring 在xml解析时会调用org.springframework.beans.factory.xml.PluggableSchemaResolver

进行获取schema文件进行约束检查,

这样配置完毕后就可以ac.setValidating(true);啦,如果文件内容不符合规范,会启动时抛出异常。

此外,如果想要在使用过程开发工具能够像使用spring 自身的一些配置时有提升功能,可以将schema文件

上传到文件服务器上,能够通过http 访问到xsi:schemaLocation的地方,或者配置开发工具中的xml 约束映射,将地址映射到本 地磁盘中,这样就能

spring-namespace实现自定义标签类

介绍如何通过spring namespace的方式进行bean的配置

最终要达到的目的如下:

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

    <user:self-user id="user2" userId="12" name="aaa"/>
    <bean id="user1" class="com.xueyou.User">
        <property name="userId" value="33"/>
        <property name="name" value="xiaoming"/>
    </bean>
</beans>

好,下面上货。

1.配置java bean

2.编写xsd文件

3.编写BeanDefinationParse标签解析类

4.编写调用标签解析类的NamespaceHandler类

5.编写spring.handlers和spring.schemas供spring读取

6.在spring中使用

目录结构

1.配置java Bean

package com.xueyou;
public class User {
    private int userId;
    private String name;
    public User() {
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "userId='" + userId + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

2.编写xsd文件

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.wuxueyou.cn/schema/user"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:beans="http://www.springframework.org/schema/beans"
            targetNamespace="http://www.wuxueyou.cn/schema/user"
            elementFormDefault="qualified">
    <xsd:import namespace="http://www.springframework.org/schema/beans"
                schemaLocation="http://www.springframework.org/schema/beans/spring-beans.xsd"/>
    <xsd:element name="self-user">
        <xsd:complexType>
            <!--这里的最外层的id是spring用的,用来定义bean的名称用的,不要和类中的id混淆了-->
            <xsd:attribute name="id" type="xsd:string" use="required"/>
            <xsd:attribute name="userId" type="xsd:int" use="required"/>
            <xsd:attribute name="name" type="xsd:string" use="required"/>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

3.编写BeanDefinationParse标签解析类

package com.xueyou.parser;
import com.xueyou.User;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.w3c.dom.Element;

public class UserDefinationParser extends AbstractSimpleBeanDefinitionParser {
    @Override
    protected Class<?> getBeanClass(Element element) {
        return User.class;
    }

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        int userId = Integer.valueOf(element.getAttribute("userId"), 0);
        String name = element.getAttribute("name");
        builder.addPropertyValue("userId", userId);
        builder.addPropertyValue("name", name);
    }
}

4.编写调用标签解析类的NamespaceHandler类

package com.xueyou.handler;
import com.xueyou.parser.UserDefinationParser;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class UserNamespaceHandler extends NamespaceHandlerSupport {
    public void init() {
        registerBeanDefinitionParser("self-user", new UserDefinationParser());
    }
}

5.编写spring.handlers和spring.schemas以供spring读取

spring.handlers

http\://www.wuxueyou.cn/schema/user=com.xueyou.handler.UserNamespaceHandler

spring.schemas

http\://www.wuxueyou.cn/schema/user.xsd=namespace/user.xsd

6.打包

首先把刚才的打成一个jar包,需要注意在maven的plugin中添加如下内容, 这个shade插件能够合并指定的内容,比如spring.schema等等

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.2.0</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                <resource>META-INF/spring.handlers</resource>
                            </transformer>
                            <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                                <resource>META-INF/spring.schemas</resource>
                            </transformer>
                            <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass></mainClass>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

7.在其他项目中使用

在一个web项目中使用这个类

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

    <user:self-user id="user2" userId="12" name="aaa"/>
    <bean id="user1" class="com.xueyou.User">
        <property name="userId" value="33"/>
        <property name="name" value="xiaoming"/>
    </bean>
</beans>

在controller中进行测试

package com.example.demo.controller;
import com.xueyou.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;

@RestController
@RequestMapping("/namespacetest")
public class NamespaceController {

    @Resource(name = "user2")
    private User user;

    @RequestMapping("/user")
    public User namespacetest() {
        return user;
    }
}

运行结果:

运行结果

最终,我们可以使用spring-namespace对bean进行配置了。这样比<bean>标签要好的多。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • spring注入在有常量的情况下使用@AllArgsConstructor操作

    目录 spring注入在有常量的情况下使用@AllArgsConstructor 会报错 解决方案 @AllArgsConstructor注解作用 spring注入在有常量的情况下使用@AllArgsConstructor 会报错 如下所示: Consider defining a bean of type 'java.lang.String' in your configuration. 这是github上作者的回答 大致意思是,他们不用spring..... 解决方案 使用@Required

  • Spring如何自定义XML配置扩展

    在Spring中,我们定义一个自己的标签有如下步骤: 自己定义一个XSD文件. 定义一个和XSD文件所对应的实体类. 创建实现了BeanDefinitionParser的类(其实更好的做法是继承抽象类AbstractBeanDefinitionParser),去解析我们的自定义标签. 创建一个继承了NamespaceHandlerSupport的类,去将我们创建的类注册到spring容器. 编写自己的Spring.handlers和Spring.schemas 一.定义一个XSD文件 首先我们在

  • 关于Spring不同类型的注入方式 p-namespace,c-namespace

    目录 Spring不同类型的注入方式 1.不同类型的注入方式 2.p-namespace以及c-namespace Spring p-namespace和c-namespace用法 1.p-namespace 2.c-namespace Spring不同类型的注入方式 spring官网代码示例 1.不同类型的注入方式 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://w

  • 通过Spring自定义NamespaceHandler实现命名空间解析(推荐)

    spring中在使用xml进行bean配置时,我们经常出现<context:annotation-config/>这样的配置,或是在使用dubbo时,暴露服务时,使用<dubbo:service interface="xxx" ref="yyy" />,我们知道仅仅通过这些简单的配置,其实完成了很多工作,那么我们能不能也实现这种功能,仅通过简单的配置,实现bean定义加载的过程中细节的隐藏,但完成复杂的功能呢? 答案是肯定的,方法是我们使用自

  • spring拓展之如何定义自己的namespace

    目录 spring拓展 定义自己的namespace 1.查看源码认识spring是怎么加载xml配置的 2.定义自己的namespace spring-namespace实现自定义标签类 1.配置java Bean 2.编写xsd文件 3.编写BeanDefinationParse标签解析类 4.编写调用标签解析类的NamespaceHandler类 5.编写spring.handlers和spring.schemas以供spring读取 6.打包 7.在其他项目中使用 spring拓展 定义

  • Spring实战之Bean定义中的SpEL表达式语言支持操作示例

    本文实例讲述了Spring实战之Bean定义中的SpEL表达式语言支持操作.分享给大家供大家参考,具体如下: 一 配置 <?xml version="1.0" encoding="GBK"?> <!-- 指定Spring配置文件的根元素和Schema 导入p:命名空间和util:命名空间的元素 --> <beans xmlns="http://www.springframework.org/schema/beans"

  • Spring Boot之FilterRegistrationBean-自定义Filter详解

    Spring Boot之FilterRegistrationBean-自定义Filter 项目老的用spring写的,新的升级到了springboot,原代码中有在web.xml中定义过滤器,在boot中,自然没法这样用了,因而看了看boot如何使用自定义过滤器. 在springboot 中,主要是靠FilterRegistrationBean 这个类来提供这样的功能. 自定义 Filter 我们常常在项目中会使用 filters 用于录调用日志.排除有 XSS 威胁的字符.执行权限验证等等.S

  • spring boot使用properties定义短信模板的方法教程

    前言 通常我们做开发时候会遇到短信发送邮件发送之类的需求,发送内容往往会由客户提供一个模板,如果我们是在程序里拼接字符串来搞定这个模板,很明显是一种坑队友的做法.一般将模板放入properties文件中,使用的时候替换其中的一些变量即可. 本文我们使用springboot来实现根据模板发送短信验证码的功能,下面话不多说了,来一起看看详细的介绍吧. tips: 1.正则表达式 2.springboot读取properties文件 模板定义 将需要定义的短信模板都定义在msg.properties文

  • Spring Boot中Bean定义方调用方式解析

    我们知道如果我们要在一个类使用spring提供的bean对象,我们需要把这个类注入到spring容器中,交给spring容器进行管理,但是在实际当中,我们往往会碰到在一个普通的Java类中,想直接使用spring提供的其他对象或者说有一些不需要交给spring管理,但是需要用到spring里的一些对象.如果这是spring框架的独立应用程序,我们通过 ApplicationContextac=newFileSystemXmlApplicationContext("applicationConte

  • spring框架学习总结

    目录 Spring 框架概述 Spring优点 Spring体系结构 Spring拓展 Spring Boot与Spring Cloud Spring IoC 容器 (IoC 也称为依赖项注入(DI),或DI是实现IoC的一种方法) IoC容器概述 Spring入门程序 IoC创建对象的三种方式 通过无参构造(要提供set方法) 通过有参构造(要提供get方法) 通过工厂类 Spring依赖注入(DI)和Bean的作用域 Spring 常用配置及属性 Spring自动装配 Spring注解开发

  • Spring的编程式事务和声明式事务详解

    入口(了解一些基本概念) Spring事务属性(事务的属性有哪些?) 我们都知道事务有开始,保存点,提交,回滚,隔离级别等属性.那么Spring对于事务属性定义有哪些呢?通过TransactionDefinition接口我们可以了解到: public interface TransactionDefinition{ int getIsolationLevel(); int getPropagationBehavior(); int getTimeout(); boolean isReadOnly

  • 详解Spring框架入门

    一.什么是Spring Spring框架是由于软件开发的复杂性而创建的.Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情.然而,Spring的用途不仅仅限于服务器端的开发.从简单性.可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益.Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架. ◆目的:解决企业应用开发的复杂性 ◆功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能 ◆范围:任何Java应用 二.

  • Spring整合MyBatis的三种方式

    1.整合之前的环境准备 导入相关的jar包 Junit测试 <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> MyBatis <dependency> <groupId

  • 详解Spring如何解析占位符

    目录 什么是Spring的占位符? Spring什么时候去解析并占位符 什么是Spring的占位符? 在以前的Spring Xml配置中我们可能会有如下配置: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001

随机推荐