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.handlesspring.schemas

  • spring.handles 找到处理 自定义命名空间的类
  • spring.schemas 申明使用的 xsd 文件在何处

1、第一步,在 maven 项目的 resources 目录下建立一个 META-INF 文件夹,分别建立3个文件 spring.handlers,spring.schemas,my.xsd[你可以随便填,我这里填的 my,如果是第一次建议跟我一致,防止出错],写入如下数据

  1. spring.handlers ----> http\://test.top/schema/my=top.huzhurong.cmdb.handlers.MyNamespaceHandler
  2. spring.schemas ----> http\://test.top/schema/my.xsd=META-INF/my.xsd

ps : 这里的 http: 是转义 , spring.schemas说明在那里可以找到xsd文件

my.xsd文件如下

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <xsd:schema xmlns="http://test.top/schema/my"
  3. xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  4. targetNamespace="http://test.top/schema/my"
  5. elementFormDefault="qualified" attributeFormDefault="unqualified">
  6. <xsd:import namespace="http://www.springframework.org/schema/beans"/>
  7. <xsd:element name="user">
  8. <xsd:complexType>
  9. <xsd:attribute name="name" type="xsd:string" use="required">
  10. <xsd:annotation>
  11. <xsd:documentation><![CDATA[姓名]]></xsd:documentation>
  12. </xsd:annotation>
  13. </xsd:attribute>
  14. <xsd:attribute name="age" type="xsd:integer" use="required">
  15. <xsd:annotation>
  16. <xsd:documentation><![CDATA[年纪]]></xsd:documentation>
  17. </xsd:annotation>
  18. </xsd:attribute>
  19. <xsd:attribute name="birthday" type="xsd:date">
  20. <xsd:annotation>
  21. <xsd:documentation><![CDATA[生日]]></xsd:documentation>
  22. </xsd:annotation>
  23. </xsd:attribute>
  24. </xsd:complexType>
  25. </xsd:element>
  26. </xsd:schema>

到这一步,环境都准备好了,新手最害怕的一步即将度过.

自定义 MyNamespaceHandler

从上边的handlers得到我们的handler在 top.huzhurong.cmdb.handlers 包下,代码如下:

  1. package top.huzhurong.cmdb.handlers;
  2. import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
  3. /**
  4. * @author luobo.cs@raycloud.com
  5. * @since 2018/8/29
  6. */
  7. public class MyNamespaceHandler extends NamespaceHandlerSupport {
  8. @Override
  9. public void init() {
  10. //初始化,注册了解析user的MyDefinitionParser类,如果有其他的就要相应的增加解析类
  11. registerBeanDefinitionParser("user", new MyDefinitionParser(User.class));
  12. }
  13. }

handler中只是注册了 解析器 在那里,所以真正的处理应该是在 MyDefinitionParser 类当中。解析的也很简单,就是按照节点的属性一个一个的解析即可。

  1. package top.huzhurong.cmdb.handlers;
  2. import org.springframework.beans.factory.config.BeanDefinition;
  3. import org.springframework.beans.factory.support.RootBeanDefinition;
  4. import org.springframework.beans.factory.xml.BeanDefinitionParser;
  5. import org.springframework.beans.factory.xml.ParserContext;
  6. import org.w3c.dom.Element;
  7. import java.text.DateFormat;
  8. import java.text.ParseException;
  9. import java.text.SimpleDateFormat;
  10. import java.util.Date;
  11. /**
  12. * @author luobo.cs@raycloud.com
  13. * @since 2018/8/29
  14. */
  15. public class MyDefinitionParser implements BeanDefinitionParser {
  16. private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
  17. private Class<?> aClass;
  18. public MyDefinitionParser(Class<?> aClass) {
  19. this.aClass = aClass;
  20. }
  21. @Override
  22. public BeanDefinition parse(Element element, ParserContext parserContext) {
  23. //在Spring当中,每一个bean都是RootBeanDefinition或者ChildBeanDefinition代表
  24. RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
  25. rootBeanDefinition.setParentName(null);
  26. rootBeanDefinition.setBeanClass(this.aClass);
  27. rootBeanDefinition.setTargetType(this.aClass);
  28. rootBeanDefinition.setLazyInit(false);
  29. String name = element.getAttribute("name");//name属性
  30. Integer age = Integer.valueOf(element.getAttribute("age"));//age属性
  31. Date date;
  32. if (element.hasAttribute("birthday")) {//可选的birthday属性
  33. try {
  34. date = dateFormat.parse(element.getAttribute("birthday"));
  35. rootBeanDefinition.getPropertyValues().addPropertyValue("birthday", date);
  36. } catch (ParseException e) {
  37. e.printStackTrace();
  38. }
  39. }
  40. //设置属性值
  41. rootBeanDefinition.getPropertyValues().addPropertyValue("name", name);
  42. rootBeanDefinition.getPropertyValues().addPropertyValue("age", age);
  43. //注册,等写完aop和事务之后会写Spring的解析(xml和annotation注解的处理)
  44. parserContext.getRegistry().registerBeanDefinition("user",rootBeanDefinition);
  45. return rootBeanDefinition;
  46. }
  47. }

到这一步,对于我们来说就算是处理完毕,就当处理好了,当时实际上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即可,如下所示:

  1. <my:user name="chenshun00" age="22"/>

后续分析文章

  • Spring是如何开启aop,bean是如何被代理的
  • Spring解析xml的几个步骤
  • 事务的初步理解
    1. Spring的声明式事务

2018-08-29