Spring中自定义Schema扩展机制

一、前言

 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 }

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


 

原文地址:https://www.cnblogs.com/magic-sea/p/11285153.html