在学些Spring Boot 自定义Namespace之前,先来看一个简单的案例。在Spring Boot出现之前,所有的bean都是在XML文件的格式
中定义。为了管理方便,一些大型复杂的应用系统,通常定个多个xml文件来共同满足业务需求,如业务bean、datasource 定义、业务拦
截bean定义等等。
为了弄清楚Spring Boot Namespace的运行原理,先从一个最简单的demo开始
定义一个普通的学员类,包含学员id、姓名、年龄、专业属性,并使用lombok生成get、set方法
@Data
@ToString
public class Student implements Serializable {private Long id;// 姓名private String name;// 年龄private byte age;// 专业private String major;
}
在resources目录下创建 xml/spring-beans.xml 文件,内容如下
在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命名空间, 完成bean的装载与逻辑处理。那么我们了解其内部原理后,也可以开发出自己的开源框架了。
点击右侧的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 ----> 定义解析处理类
需要注意的是,dubbo中的每个模块都一一对应一个xml 解析器,这里如果不一致则会报以下错误
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Cannot locate BeanDefinitionParser for element [application]
接下来,我们依葫芦画瓢实现一个自定义的Namespace处理流程
http\://xml.definition.com/schema/custom.xsd=META-INF/student.xsd
http\://xml.definition.com/schema/custom=com.springboot.study.springbootstudy.handler.CustomNamespaceHandler
CustomNamespaceHandler - 自定义的Namespace的处理代码如下
public class CustomNamespaceHandler extends NamespaceHandlerSupport {/*** 自定义标签解析 生成bean 对象*/@Overridepublic 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) {//beanDefinitionRootBeanDefinition beanDefinition = new RootBeanDefinition();beanDefinition.setBeanClass(Student.class);beanDefinition.setLazyInit(false);//beanDefinition.setScope();//解析idString id = element.getAttribute("id");beanDefinition.getPropertyValues().add("id", id);//解析namebeanDefinition.getPropertyValues().add("name",element.getAttribute("name"));//解析 agebeanDefinition.getPropertyValues().add("age",element.getAttribute("age"));//majorbeanDefinition.getPropertyValues().add("major",element.getAttribute("major"));parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);return beanDefinition;}
}
至此已经完成了一个简单版的自定义Namespace所有功能开发,接下来测试一下。
修改spring-beans.xml 文件,现在使用自定义Namespace的方式注册bean
然后重新启动应用程序,依然可以获取xml中 student bean 定义的内容。
源码地址