Spring的命名空间解析过程
前言
上一篇,动手写了一个动态代理 的使用,本来想接着继续看 Spring的动态代理是如何实现的 , 但是发现如果不对 命名空间 做一个简单的学习,很难找到 Spring Aop 的切入点,于是有了本文。
Spring 通过引入 NameSpace 来降低了其他框架接入 Spring 的难处,同时通过使用 NameSpace 也简化了 Spring 引入其他组件的难度,甚至我们可以假想成Spring的其他组件都都可以是一个个 单独的框架,只不过这个 单独的框架 需要依赖 Spring 的核心组件— core,bean,通过这种想法我们会了解到 Spring 是如何通过命名空间 context:component-scan 对bean进行扫描的,是如何一键开启 Aop 的,我们还可以通过自定义命名空间来让我们自定义的 命名空间 bean对象让 Spring 接管,例如 <chenshun00:me /> 这种模式,也能学习到dubbo等等 RPC 是如何支持 Spring 的
自定义命名空间需要什么准备?
需要什么准备什么,Spring 都为我们准备好了,打开我们下载好的 Spring源码 ,随便找一个Spring的组件,例如我找的是 Spring-aop-5.0.7.RELEASE,打开即可发现闪亮的 spring.handles 和 spring.schemas。
spring.handles找到处理自定义命名空间的类spring.schemas申明使用的xsd文件在何处
1、第一步,在 maven 项目的 resources 目录下建立一个 META-INF 文件夹,分别建立3个文件 spring.handlers,spring.schemas,my.xsd[你可以随便填,我这里填的 my,如果是第一次建议跟我一致,防止出错],写入如下数据
spring.handlers ----> http\://test.top/schema/my=top.huzhurong.cmdb.handlers.MyNamespaceHandlerspring.schemas ----> http\://test.top/schema/my.xsd=META-INF/my.xsd
ps : 这里的 http: 是转义 ,
spring.schemas说明在那里可以找到xsd文件
my.xsd文件如下
<?xml version="1.0" encoding="UTF-8"?><xsd:schema xmlns="http://test.top/schema/my"xmlns:xsd="http://www.w3.org/2001/XMLSchema"targetNamespace="http://test.top/schema/my"elementFormDefault="qualified" attributeFormDefault="unqualified"><xsd:import namespace="http://www.springframework.org/schema/beans"/><xsd:element name="user"><xsd:complexType><xsd:attribute name="name" type="xsd:string" use="required"><xsd:annotation><xsd:documentation><![CDATA[姓名]]></xsd:documentation></xsd:annotation></xsd:attribute><xsd:attribute name="age" type="xsd:integer" use="required"><xsd:annotation><xsd:documentation><![CDATA[年纪]]></xsd:documentation></xsd:annotation></xsd:attribute><xsd:attribute name="birthday" type="xsd:date"><xsd:annotation><xsd:documentation><![CDATA[生日]]></xsd:documentation></xsd:annotation></xsd:attribute></xsd:complexType></xsd:element></xsd:schema>
到这一步,环境都准备好了,新手最害怕的一步即将度过.
自定义 MyNamespaceHandler
从上边的handlers得到我们的handler在 top.huzhurong.cmdb.handlers 包下,代码如下:
package top.huzhurong.cmdb.handlers;import org.springframework.beans.factory.xml.NamespaceHandlerSupport;/*** @author luobo.cs@raycloud.com* @since 2018/8/29*/public class MyNamespaceHandler extends NamespaceHandlerSupport {@Overridepublic void init() {//初始化,注册了解析user的MyDefinitionParser类,如果有其他的就要相应的增加解析类registerBeanDefinitionParser("user", new MyDefinitionParser(User.class));}}
handler中只是注册了 解析器 在那里,所以真正的处理应该是在 MyDefinitionParser 类当中。解析的也很简单,就是按照节点的属性一个一个的解析即可。
package top.huzhurong.cmdb.handlers;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;import java.text.DateFormat;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;/*** @author luobo.cs@raycloud.com* @since 2018/8/29*/public class MyDefinitionParser implements BeanDefinitionParser {private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");private Class<?> aClass;public MyDefinitionParser(Class<?> aClass) {this.aClass = aClass;}@Overridepublic BeanDefinition parse(Element element, ParserContext parserContext) {//在Spring当中,每一个bean都是RootBeanDefinition或者ChildBeanDefinition代表RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();rootBeanDefinition.setParentName(null);rootBeanDefinition.setBeanClass(this.aClass);rootBeanDefinition.setTargetType(this.aClass);rootBeanDefinition.setLazyInit(false);String name = element.getAttribute("name");//name属性Integer age = Integer.valueOf(element.getAttribute("age"));//age属性Date date;if (element.hasAttribute("birthday")) {//可选的birthday属性try {date = dateFormat.parse(element.getAttribute("birthday"));rootBeanDefinition.getPropertyValues().addPropertyValue("birthday", date);} catch (ParseException e) {e.printStackTrace();}}//设置属性值rootBeanDefinition.getPropertyValues().addPropertyValue("name", name);rootBeanDefinition.getPropertyValues().addPropertyValue("age", age);//注册,等写完aop和事务之后会写Spring的解析(xml和annotation注解的处理)parserContext.getRegistry().registerBeanDefinition("user",rootBeanDefinition);return rootBeanDefinition;}}
到这一步,对于我们来说就算是处理完毕,就当处理好了,当时实际上Spring得等到所有的beandefinition都好了,才会去实例化,避免注入失败,当然这个等到日后会继续分析Spring的解析。
现在再说一下xsd文件,其实xsd文件还是蛮好理解,我上述定义了一个element为user的节点,然后user里边包含了几个属性,他们的类型是什么,是必须的还是可选的,这个不是重点,应该很好理解的,要注意到的是xsd文件的systemId,因为后续Spring会根据这个systemId去加载xml文件,试想如果要你去写Spring,你会怎么去解析xml文件呢?肯定是一个bean节点,一个bean节点的解析,Spring也是一样的,并且同时利用了xsd的systemid的特性,这里只是小提一下,等到分析Spring的xml解析会说明。
到这一步未知,我们自定义的命名空间就完成了,只要在bean.xml中写入一个bean即可,如下所示:
<my:user name="chenshun00" age="22"/>
后续分析文章
- Spring是如何开启aop,bean是如何被代理的
- Spring解析xml的几个步骤
- 事务的初步理解
Spring的声明式事务
2018-08-29
