一、前言

Spring 为基于 XML 构建的应用提供了一种扩展机制,用于定义和配置 Bean。 它允许使用者编写自定义的 XML bean 解析器,并将解析器本身以及最终定义的 Bean 集成到 Spring IOC 容器中。

二、自定义 XML Schema 扩展

为了搞懂 Spring 的 XML 扩展机制,最直接的方式便是实现一个自定义的扩展。实现的步骤也为四步:

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

1. 编写 resources/META-INF/Car.xsd

 1 <?xml version="1.0" encoding="UTF-8"?>
2 <xsd:schema xmlns="http://www.mycompany.com/schema/my"
3 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
4 xmlns:beans="http://www.springframework.org/schema/beans"
5 targetNamespace="http://www.mycompany.com/schema/my1"
6 elementFormDefault="qualified"
7 attributeFormDefault="unqualified">
8
9 <xsd:import namespace="http://www.springframework.org/schema/beans"/>
10
11 <xsd:element name="car">
12 <xsd:complexType>
13 <xsd:complexContent>
14 <xsd:extension base="beans:identifiedType">
15 <xsd:attribute name="brand" type="xsd:string" use="required"/>
16 <xsd:attribute name="engine" type="xsd:float"/>
17 <xsd:attribute name="horsePower" type="xsd:int"/>
18 </xsd:extension>
19 </xsd:complexContent>
20 </xsd:complexType>
21 </xsd:element>
22 </xsd:schema>

这里,targetNamespace对Car标签很重要,比如说注册一个bean

1 <my1:car id="magic" brand="Magic" engine="4.5" horsePower="605" />

2. 编写 CarNamespaceHandler

 1 public class CarNamespaceHandler extends NamespaceHandlerSupport {
2
3 @Override
4 public void init() {
5 //遇到car元素的时候交给CarBeanDefinitionParser来解析
6 registerBeanDefinitionParser("car", new CarBeanDefinitionParser());
7
8 }
9
10 }

编写的NamespaceHandler 来帮助 Spring 解析 XML 中不同命名空间的各类元素。不同的命名空间需要不同的 NamespaceHandler 来处理。使用 CarNamespaceHandler 来解析 car 的命名空间。

1 public interface NamespaceHandler {
2 void init();
3 BeanDefinition parse(Element element, ParserContext parserContext);
4 BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
5 }

CarNamespaceHandler 继承 NamespaceHandlerSupport 抽象类,NamespaceHandlerSupport 抽象类实现了 NamespaceHandler 接口,并实现了parse()和decorate()方法,在CarNamespaceHandler 类中实现 NamespaceHandler 接口的init()方法,注册 BeanDefinitionParser 来完成解析节点以及注册 Bean 的工作。

3. 编写 CarBeanDefinitionParser 

BeanDefinitionParser 是最为关键的一环。每一个 BeanDefinitionParser 实现类都负责一个映射,将一个 XML 节点解析成 IOC 容器中的一个实体类。

 1 public class CarBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
2
3 @Override
4 protected Class<?> getBeanClass(Element element) {
5 //car元素对应Car对象类型
6 return Car.class;
7 }
8
9 @Override
10 protected void doParse(Element element, BeanDefinitionBuilder builder) {
11
12 String brand = element.getAttribute("brand");
13 String engine = element.getAttribute("engine");
14 String hp = element.getAttribute("horsePower");
15
16 //把对应的属性设置到bean中
17 if(StringUtils.hasText(brand))
18 builder.addPropertyValue("brand", brand);
19
20 if(StringUtils.hasText(engine))
21 builder.addPropertyValue("engine", engine);
22
23 if(StringUtils.hasText(hp))
24 builder.addPropertyValue("horsePower", hp);
25
26 }
27 }

parse() 方法会解析一个个 XML 中的元素,使用 RootBeanDefinition 组装成对象,并最终通过 parserContext 注册到 IOC 容器中。

至此,我们便完成了 XML 文件中定义的对象到 IOC 容器的映射。

4. 注册 schemahandler

最后一步还需要通知 Spring,告知其自定义 schema 的所在之处以及对应的处理器。

resources/META-INF/spring.handlers

1 http\://www.mycompany.com/schema/my1=spring.xml.ext.schema.CarNamespaceHandler

resources/META-INF/spring.schemas

1 http\://www.mycompany.com/schema/my1.xsd=META-INF/car.xsd

一个自定义的 XML schema 便扩展完成了,接下来验证一下自定义Schema扩展。

三、验证扩展

 定义好Spring的bean配置文件

 1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xmlns:my1="http://www.mycompany.com/schema/my1"
5 xsi:schemaLocation="
6 http://www.springframework.org/schema/beans
7 http://www.springframework.org/schema/beans/spring-beans.xsd
8 http://www.mycompany.com/schema/my1
9 http://www.mycompany.com/schema/my1.xsd">
10
11 <my1:car id="magic" brand="Magic" engine="4.5" horsePower="605" />
12
13 </beans>

编写测试方法

 1 @RunWith(SpringJUnit4ClassRunner.class)
2 @ContextConfiguration(locations = { "classpath:app.xml" })
3 public class SchemaTest {
4
5 @Autowired
6 @Qualifier("magic")
7 private Car car;
8
9 @Test
10 public void propertyTest() {
11 assertNotNull(car);
12
13 String brand = car.getBrand();
14 float engine = car.getEngine();
15 int horsePower = car.getHorsePower();
16
17 System.out.println("==============================");
18 assertEquals("Brand incorrect.Should be Magic.", "Magic", brand);
19 assertEquals("Engine incorrect.Should be 4.5L.", 4.5, engine, 0.000001);
20 assertEquals("HorsePower incorrect.Should be 605hp.", 605, horsePower);
21
22 }
23 }

控制台输出无异常,断言成功。

Spring中自定义Schema扩展机制的更多相关文章

  1. 聊聊 Spring 的 XML Schema 扩展机制的使用方式

    前言 在当前Java生态,Spring算的上是最核心的框架,所有的开发组件想要得到大范围更便捷的使用,都要和Spring进行整合,比如我们熟知的Mybatis.Dubbo等,以及内部封装的各类组件包括 ...

  2. 缘起 Dubbo ,讲讲 Spring XML Schema 扩展机制

    背景 在 Dubbo 中,可以使用 XML 配置相关信息,也可以用来引入服务或者导出服务.配置完成,启动工程,Spring 会读取配置文件,生成注入 相关 Bean.那 Dubbo 如何实现自定义 X ...

  3. 6.2 dubbo在spring中自定义xml标签源码解析

    在6.1 如何在spring中自定义xml标签中我们看到了在spring中自定义xml标签的方式.dubbo也是这样来实现的. 一 META_INF/dubbo.xsd 比较长,只列出<dubb ...

  4. 6.1 如何在spring中自定义xml标签

    dubbo自定义了很多xml标签,例如<dubbo:application>,那么这些自定义标签是怎么与spring结合起来的呢?我们先看一个简单的例子. 一 编写模型类 package ...

  5. Spring中ApplicationContext加载机制和配置初始化

    Spring中ApplicationContext加载机制.        加载器目前有两种选择:ContextLoaderListener和ContextLoaderServlet.        ...

  6. Spring Schema扩展机制

    1:概述 Spring2.0开始,Spring提供XML Schema可扩展机制,用户可以自定义XML Schema文件,并自定义 XML Bean解析器,集成到Spring IOC容器中. 2:步骤 ...

  7. day05 Spring中自定义注解的用处-之获取自定义的Servie

    PS: 在RPC远程调用中,想要获取自定义的service的方法,就得自定义标签遍历拿到方法 PS:在spring中,两个最核心的 概念是aop和ioc,aop其实就是动态代理. ioc 就是解决对象 ...

  8. spring中自定义Event事件的使用和浅析

    在我目前接触的项目中,用到了许多spring相关的技术,框架层面的spring.spring mvc就不说了,细节上的功能也用了不少,如schedule定时任务.Filter过滤器. intercep ...

  9. spring中的事务传播机制

    1.事务的实现思想 在spring中要想某个方法具有事务,只要在方法前加一个@Transactional注解.然后spring就会利用aop思想,在这个方法执行前开启事务, 在方法执行后选择提交事务或 ...

随机推荐

  1. 运行FreeSWITCH的命令行参数

    一般来说,FreeSWITCH 不需要任何命令行参数就可以启动,但在某些情况下,你需要以一些特殊的参数启动.在此,仅作简单介绍.如果你知道是什么意思,那么你就可以使用,如果不知道,多半你用不到. 使用 ...

  2. grep、cut、awk、sed的使用

    grep.cut.awk.sed 常常应用在查找日志.数据.输出结果等等,并对我们想要的数据进行提取.通常grep,sed命令是对行进行提取,cut跟awk是对列进行提取 处理海量数据之grep命令 ...

  3. FastAPI(7)- get 请求 - 详解 Path

    前言 上一篇讲了可以为查询参数添加额外的校验和元数据,Query 库:https://www.cnblogs.com/poloyy/p/15306809.html 这篇讲可以为路径查询添加额外的校验和 ...

  4. 物理机burp抓虚拟机包

    先设置网络连接为NAT模式. 这就相当于主机虚拟出一个网卡,虚拟机单独成为一个网段(相当于虚拟机为单独一台主机,物理机作为路由器网关使用),我将会在物理机,也就是这个"路由器"上设 ...

  5. 【多线程】Android多线程学习笔记——线程池

    Java线程池采用了享元设计模式,在系统中维持一定数量的线程,用于处理异步或并发需求,在平时处理异步或并发任务时被广泛使用.这里基于JDK1.8和Android28来整理一些关于线程池的知识点. 一. ...

  6. Lambda 表达式详解

    目录 前言 预备知识,理解委托的构成 引用实例方法的委托 引用静态方法的委托 Lambda 表达式的实际编译结果 CASE 1 没有捕获任何外部变量的Lambda 表达式 CASE 2 捕获了外部方法 ...

  7. php 开启报错

    // 开启报错提醒ini_set("display_errors", "On");error_reporting(E_ALL | E_STRICT); // 某 ...

  8. django中admin一些方法

    1.概述:内容发布,负责添加,修改,删除内容及公告访问2.配置admin应用在settings文件中INSTALLED_APPS添加:'django.contrib.admin', 默认是已经添加好的 ...

  9. hadoop生态之面试题篇

    一.hdfs的高可用 1.先说下自己的理解, 正常的hdfs有namenode,datanode,secondnamenode,但是second name node 不是真正意义上的namenode备 ...

  10. CF183D-T-shirtx【dp,贪心】

    正题 题目链接:https://www.luogu.com.cn/problem/CF183D 题目大意 \(n\)个人,\(m\)种衣服,给出每个人喜欢某件衣服的概率,你可以选择\(n\)件衣服带过 ...