• 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 }

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


     

  • 相关阅读:
    c_str()函数
    Linux创建用户
    Linux vim替换命令
    计算器表达式求值源码
    结构体、共用体。面试必用
    Ubuntu打开中文输入法
    Unable to establish SSL connection
    变量的命名和使用
    C++ ARX二次开发-MFC 非模态对话框modaless dialog
    C++ ARX二次开发-创建三维实体
  • 原文地址:https://www.cnblogs.com/magic-sea/p/11285153.html
Copyright © 2020-2023  润新知