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.MyNamespaceHandler
spring.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 {
@Override
public 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;
}
@Override
public 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