Spring Boot自定义Namespace

Spring Boot 自定义Namespace

在学些Spring Boot 自定义Namespace之前,先来看一个简单的案例。在Spring Boot出现之前,所有的bean都是在XML文件的格式

中定义。为了管理方便,一些大型复杂的应用系统,通常定个多个xml文件来共同满足业务需求,如业务bean、datasource 定义、业务拦

截bean定义等等。

为了弄清楚Spring Boot Namespace的运行原理,先从一个最简单的demo开始

基础应用

bean class

定义一个普通的学员类,包含学员id、姓名、年龄、专业属性,并使用lombok生成get、set方法

@Data
@ToString
public class Student implements Serializable {

    private Long id;

    // 姓名
    private String name;

    // 年龄
    private byte age;

    // 专业
    private String major;
}

bean xml 定义

在resources目录下创建 xml/spring-beans.xml 文件,内容如下


<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
    <bean id="student" class="com.springboot.study.springbootstudy.bean.Student">
        <property name="id" value="1000"/>
        <property name="name" value="科比"/>
        <property name="age" value="38"/>
        <property name="major" value="篮球"/>
    bean>
beans>

bean 应用

在Spring Boot 启动类中使用 @ImportResource 引入xml 配置文件,就可以使用bean了

@SpringBootApplication
@ImportResource(locations= {"classpath:xml/spring-beans.xml"})

public class SpringbootStudyApplication {

	public static void main(String[] args) {
		ConfigurableApplicationContext app = SpringApplication.run(SpringbootStudyApplication.class, args);
		
		final Student student = app.getBean(Student.class);
		System.out.println("Spring Boot Student =================: " + student);
    // 输出如下
    // Spring Boot Student =================: Student(id=1000, name=科比, age=38, major=篮球)
	}
}

至此,已经完成了bean的定义到应用的基本流程,简单吧。相信学习过dubbo框架的同学对下面的代码特别熟悉

<dubbo:application name="demo-consumer"/>

<dubbo:registry address="zookeeper://localhost:2181"/>

<dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"/>

通过自定义dubbo命名空间, 完成bean的装载与逻辑处理。那么我们了解其内部原理后,也可以开发出自己的开源框架了。

bean 解析原理

Spring Boot自定义Namespace_第1张图片

点击右侧的URL后,定位到dubbo.xsd文件和以两个spring开头的文件信息

  • dubbo.xsd - 定义了以dubbo为命名空间的语法,对里面的内容做了限定

  • spring.handlers - 里面定义了dubbo.xsd 解析类,在运行时使用SPI的方式动态加载

    http\://dubbo.apache.org/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
    http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
    
  • spring.schemas - 将xsd文件、与命名空间关联起来

    http\://dubbo.apache.org/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd
    http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/compat/dubbo.xsd
    

现在来梳理下这三部分之间的关系

定义xsd文件 —> xsd文件关联schemas ----> 定义解析处理类

Spring Boot自定义Namespace_第2张图片

需要注意的是,dubbo中的每个模块都一一对应一个xml 解析器,这里如果不一致则会报以下错误

org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Cannot locate BeanDefinitionParser for element [application]

接下来,我们依葫芦画瓢实现一个自定义的Namespace处理流程

自定义Namespace

xsd 定义


<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:beans="http://www.springframework.org/schema/beans"
            xmlns:tool="http://www.springframework.org/schema/tool"
            xmlns="http://xml.definition.com/schema/custom"
            targetNamespace="http://xml.definition.com/schema/custom">

    <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:complexType name="applicationType">
        <xsd:attribute name="id" type="xsd:long"/>
        <xsd:attribute name="name" type="xsd:string" use="required"/>
        <xsd:attribute name="age" type="xsd:byte"/>
        <xsd:attribute name="major" type="xsd:string"/>
    xsd:complexType>

    <xsd:element name="application" type="applicationType"/>
xsd:schema>

schemas 定义

http\://xml.definition.com/schema/custom.xsd=META-INF/student.xsd

handlers 定义

http\://xml.definition.com/schema/custom=com.springboot.study.springbootstudy.handler.CustomNamespaceHandler

关联代码

  • CustomNamespaceHandler - 自定义的Namespace的处理代码如下

    public class CustomNamespaceHandler extends NamespaceHandlerSupport {
        /**
         * 自定义标签解析 生成bean 对象
         */
        @Override
        public void init() {
            System.out.println("==================================== CustomNamespaceHandler execute");
            this.registerBeanDefinitionParser("application",new CustomBeanDefinitionParser());
        }
    }
    
  • CustomBeanDefinitionParser - xml 解析代码如下

    package com.springboot.study.springbootstudy.handler;
    
    import com.springboot.study.springbootstudy.bean.Student;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.support.RootBeanDefinition;
    import org.springframework.beans.factory.xml.BeanDefinitionParser;
    import org.springframework.beans.factory.xml.ParserContext;
    import org.w3c.dom.Element;
    
    public class CustomBeanDefinitionParser implements BeanDefinitionParser {
    
        /**
         * 解析xml文件 动态注册bean 至ioc容器中
         * @param element
         * @param parserContext
         * @return
         */
        public BeanDefinition parse(Element element, ParserContext parserContext) {
            //beanDefinition
            RootBeanDefinition beanDefinition = new RootBeanDefinition();
            beanDefinition.setBeanClass(Student.class);
            beanDefinition.setLazyInit(false);
            //beanDefinition.setScope();
            //解析id
            String id = element.getAttribute("id");
            beanDefinition.getPropertyValues().add("id", id);
            //解析name
            beanDefinition.getPropertyValues().add("name",
                    element.getAttribute("name"));
            //解析 age
            beanDefinition.getPropertyValues().add("age",
                    element.getAttribute("age"));
            //major
            beanDefinition.getPropertyValues().add("major",
                    element.getAttribute("major"));
    
            parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
            return beanDefinition;
    
        }
    }
    

    至此已经完成了一个简单版的自定义Namespace所有功能开发,接下来测试一下。

测试验证

修改spring-beans.xml 文件,现在使用自定义Namespace的方式注册bean


<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:custom="http://xml.definition.com/schema/custom"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
        http://xml.definition.com/schema/custom
        http://xml.definition.com/schema/custom.xsd">







	<custom:application id="1000" name="科比" age="38" major="篮球"/>
beans>

然后重新启动应用程序,依然可以获取xml中 student bean 定义的内容。

源码地址

你可能感兴趣的:(java,基础,Spring,命名空间,Spring,Boot命名空间)