阿里一面如何实现Spring可扩展Schema


引言

自从SpringBoot时代的到来,去除了Spring的各种繁琐的XML配置,让我们可以腾出双手以便于更加专注的搬砖。
记得那时候刚学Spring的时候,每天被Spring的各种XMl配置文件折磨的不行,每引入一个新的框架,最担心的就是jar冲突、哪个配置文件又配的不对、配置文件没有起作用。所以每次搭建好一个项目就把配置文件用小笔记记录下来,
方便下次在整合项目的时候直接copy复制就好。下面我们就以Spring整合dubbo的事例看下

beans xmlnsxsi=httpwww.w3.org2001XMLSchema-instance
       xmlnsdubbo=httpdubbo.apache.orgschemadubbo
       xmlns=httpwww.springframework.orgschemabeans
       xsischemaLocation=httpwww.springframework.orgschemabeans httpwww.springframework.orgschemabeansspring-beans.xsd
       httpdubbo.apache.orgschemadubbo httpdubbo.apache.orgschemadubbodubbo.xsd
    dubboapplication name=demo-provider
    dubboregistry address=zookeeper127.0.0.12181
    dubboprotocol name=dubbo port=20890
    bean id=demoService class=org.apache.dubbo.samples.basic.impl.DemoServiceImpl
    dubboservice interface=org.apache.dubbo.samples.basic.api.DemoService ref=demoService
beans

上述代码中我们有看到dubbo自定义了一套自己的标签,dubboapplication ,dubboregistry ,dubboprotocol,dubboservice
我们心中是不是有点小疑问:这些标签在Spring项目启动的时候是如何被Spring管理的?是怎样被Spring来识别的?
如果我们自己随便定义一个标签Spring是否能够识别?我们去翻翻Spring的官网发现这玩意其实就是Spring提供的 XML schema 的扩展支持。只要按照它的步骤来,我们就可以配置任何我们自定义的标签。XML schema 扩展机制是什么?这个也许好多人没听过:
Spring 为基于 XML 构建的应用提供了一种扩展机制,用于定义和配置 Bean。 它允许使用者编写自定义的 XML bean 解析器,并将解析器本身以及最终定义的 Bean 集成到 Spring IOC 容器中。

我们可以看看官网httpsdocs.spring.iospring-frameworkdocscurrentreferencehtmlcore.html#xml-custom
10.2. XML Schema Authoring 这个是主要介绍它的。

如何实现一个自定义 XML 扩展

官网有介绍,要实现一个自定义的XML Schema 总共需要4步:

  • 编写一个 XML schema 文件描述的你节点元素。
  • 编写一个 NamespaceHandler 的实现类
  • 编写一个或者多个 BeanDefinitionParser 的实现 (关键步骤).
  • 注册上述的 schema 和 handler。

既然只要按照这四步来,那我们就照着这个文档来自己实现一个。

Authoring the Schema

编写一个javajr.xsd 放入项目的resourcesMETA-INF文件夹里面(这个也可以是其他路劲)

xml version=1.0 encoding=UTF-8 standalone=no
xsdschema xmlnsxsd=httpwww.w3.org2001XMLSchema
            xmlnsbeans=httpwww.springframework.orgschemabeans
            xmlnstool=httpwww.springframework.orgschematool
            xmlns=httpswww.javajr.cnschemajavajr
            targetNamespace=httpswww.javajr.cnschemajavajr

    xsdimport namespace=httpwww.springframework.orgschemabeans

    xsdelement name=application
        xsdcomplexType
            xsdcomplexContent
                xsdextension base=beansidentifiedType
                    xsdattribute name=website type=xsdstring use=required
                    xsdattribute name=weixin type=xsdstring use=required
                xsdextension
            xsdcomplexContent
        xsdcomplexType
    xsdelement
xsdschema
  • targetNamespace=httpswww.javajr.cnschemajavajr 这里targetNamespace的地址后面有用到。
  • 这里我们就定义了一个元素application 里面有两个属性分别为websiteweixin
    编写一个 NamespaceHandler
    package org.spring.demo.schema;
    import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
    public class MyNamespaceHandler extends NamespaceHandlerSupport {
      @Override
      public void init() {
           这个名字也不是随便取的,上面编写xsd的根节点元素的name, xsdelement name=application
          registerBeanDefinitionParser(application, new MyBeanDefinitionParser());
      }
    }
    这个NamespaceHandler 就是将一个 XML 节点解析成 IOC 容器中的一个实体类。也就是说相当于在xml里面的配置的对象,通过Spring ioc 容器管理起来了
    编写 BeanDefinitionParser 的实现类
    package org.spring.demo.schema;
    import org.spring.demo.domain.JavajrDomain;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
    import org.springframework.util.StringUtils;
    import org.w3c.dom.Element;
    

public class MyBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

@Override
protected Class getBeanClass(Element element) {
    return JavajrDomain.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder bean) {
     this however is an optional property
    String website = element.getAttribute(website);
    if (StringUtils.hasText(website)) {
        bean.addPropertyValue(website,website);
    }
    String weiXin = element.getAttribute(weixin);
    if (StringUtils.hasText(weiXin)) {
        bean.addPropertyValue(weixin,weiXin);
    }
}

}

上面在这个实现类只是简单的做了一个赋值操作,你如果需要有自己的逻辑业务也可以自行来实现。上面还有一个JavajrDomain这个实体类就不贴代码,就一个简单的`javabean`里面包含了两个属性weixin和website。
##### 注册schema组件
最后在`resourcesMETA-INF`目录下添加两个配置文件(`spring.handler和spring.schema`)
- `resourcesMETA-INFspring.handlers`
```xml
httpswww.javajr.cnschemajavajr=org.spring.demo.schema.MyNamespaceHandler
  • resourcesMETA-INspring.schemas
    httpswww.javajr.cnschemajavajr.xsd=META-INFjavajr.xsd
    在这个地方的时候我们其实可以以版本号来进行命名,方便我们可以使用多个不同的版本,Spring-beans 就是这么玩的。
    在这里插入图片描述

测试自定义schema

  • resources 目录下新建一个applicationContext.xml文件
    在这里插入图片描述
    这个文件就是使用下我们我们自己自定义的schema,这个文件需要注意的就是上面标红的这几行,一般如果我们有引入过第三方的框架,比如mq、或者dubbo等它们都有自定义的这些玩意。

  • 编写一个启动类

    public static void main(String[] args) {
          ApplicationContext ctx = new ClassPathXmlApplicationContext(classpathapplicationContext.xml);
          JavajrDomain bean = ctx.getBean(JavajrDomain.class);
          System.out.println(bean.toString());
      }

    我们可以看到控制台输出

    JavajrDomain{weixin='javajr8', website='javajr.cn'}
  • 到这里我们自己实现的一个 XML schema 就完成了,是不是很简单,只要照着官方文档撸就可以了。照着撸的过程可能有几个小细节需要注意下引入 XML schema 的时候需要注意下空格,或者一些特殊符号。
    上述代码已经提交到了gitee上httpsgitee.comjavajrspring-schema-demo 感兴趣的朋友可以直接下载下来run下,不过还是不建议这么玩,最好还是自己动手去尝试下,毕竟也就四步,照着文档来。

Dubbo 中的 XML schema 扩展

在文章开始的时候我们有介绍dubbo 自定义的XML schema ,下面我们一起打开dubbo源码看看它是如何来实现的,看下面这个截图,也是按照那四步来的。
在这里插入图片描述

SpringBoot的starter

现在有了SpringBoot 之后以前用这个 XML schema配置的框架,大多数都会有对应的starter来进行封装,starter的使用比起 XML schema的使用还是简单多了,开箱即用,无需编写很多的配置文件。如果不是很清楚SpringBootstarter的推荐去看看这两篇文章《面试高频题:springBoot自动装配的原理你能说出来吗?》《保姆级教程,手把手教你实现一个SpringBoot的starter》

总结

虽然现在XML schema 扩展用的不多了,但是应该也还有比较老的项目在使用把,如果还是比较老的项目,需要引入一个什么样的框架,我们至少需要知道需要怎么去引入,网上虽然有很多文章可以借鉴,但是我们也应该知其然知其所以然。而不是直接把配置文件单纯的copy过来。我们应该知道为啥需要copy这个xsd,为什么没有这个xsdidea不糊识别会报错。

结束

  • 由于自己才疏学浅,难免会有纰漏,假如你发现了错误的地方,还望留言给我指出来,我会对其加以修正。
  • 如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。
  • 感谢您的阅读,十分欢迎并感谢您的关注。
    巨人的肩膀摘苹果:
    httpsjavajr.cn
    httpsdocs.spring.iospring-frameworkdocscurrentreferencehtmlcore.html#xml-custom
    httpswww.cnkirito.moespring-xsd#%E5%89%8D%E8%A8%80

文章作者: java金融
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 java金融 !
 上一篇
记一次lombok踩坑经历 记一次lombok踩坑经历
引言今天中午正在带着耳机遨游在代码的世界里,被运营在群里@了,气冲冲的反问我最近有删生产的用户数据的吗?我肯定客气的回答道没有呀?生产的数据我怎么能随随便便可以删除,这可是公司的红线,再说了我也没有数据库的删除权限啊,不过查询权限还是有的。
2021-10-22
下一篇 
Spring的Xml和JavaConfig 扩展你选哪一个 Spring的Xml和JavaConfig 扩展你选哪一个
引言上一篇文章我们有怎么介绍到如何通过XML的形式来定义Spring的扩展《Spring面试高频题如何:自定义XML schema 扩展》,好多人都在吐槽现在都什么年代了,xml还有人再用吗?这玩意早就过时了吧,还有必要去掌握它吗?Spri
2021-10-22