`

Mule 与 Spring2.0's extensible XML configuration mechanism

 
阅读更多

mule2.2.1已经采用从spring2.0开始出现的namespace配置方式了,这样的好处不多说了,坏处是源码更难看了,还增加了一坨BDP。以下是spring官方手册的附录B及spring源码的一些摘录。

 

  Spring2.0's extensible XML configuration mechanism基础是xml的schema(xsd),总体来说首先需要NamespaceHandler空间处理器,然后空间处理器来根据xml配置的元素名注册BeanDefinitionParser定义解析器(有两种类型:BDPBDD。org.springframework.beans.factory.xml.NamespaceHandler的说明是:被DefaultBeanDefinitionDocumentReader所使用的空间处理器基本接口,是专用于处理自定义空间的。实现类应返回以下两种策略接口的实现(这两种实现也就是定义解析器的两种类型):

  • BeanDefinitionParser接口实现实例 —— BDP、解析顶级标签
  • BeanDefinitionDecorator接口实现实例 —— BDD、解析嵌套标签(包括属性和嵌套元素)

  spring的xml配置解析器遇到beans直接下属的自定义标签(顶级)应调用parse方法、遇到bean下属的自定义标签应调用decorate方法。要编写自定义空间解析不要直接实现本接口、应该继承NamespaceHandlerSupport类。NamespaceHandler接口定义了3个方法:

  1. void init()——DefaultBeanDefinitionDocumentReader在解析任何自定义元素之前调用该方法
  2. BeanDefinition parse(Element element, ParserContext parserContext)——解析特定元素(org.w3c.dom.Element)并把解析结果BeanDefinition注册到ParserContext . BeanDefinitionRegistry中,实现类如果想在嵌套解析中继续使用(如解析property属性)则应返回该BeanDefinition,如果不想在嵌套中再使用可以返回null
  3. BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);——解析特定的节点(org.w3c.dom.Node)、进一步补充BeanDefinitionHolder并返回,Node可以是org.w3c.dom.Attr或org.w3c.dom.Element(即:可以是属性或嵌套子元素),实现类可以返回一个全新的definition来替代BeanFactory中旧的definition(即:配置了某属性或子元素可以完全改变父bean的类型)、传入的ParserContext 参数可以让你注册更多额外的bean(比如读取了一个配置项如一个子元素却需要额外注册多个bean来辅助完成某功能),严格来说不允许返回null、但是如果返回null则spring认为返回了原始也就是传入的bean definition

  NamespaceHandler定义了这3个方法,不管你怎么去实现(接口都是甩手掌柜),spring的NamespaceHandlerSupport类提供了一种简单直观的实现,那就是应用自己负责先批量地把一堆Element本地名与相应的解析器(也是应用自己的实现)实例关联映射起来,然后在调用parse或decorate的时候再去拿出相应解析器来解析。这个关联映射在哪里做?只能是在init方法中,所以说NamespaceHandler这个接口很可能是后期抽象出来的,完全按照实际的解析需要抽象出来必要的3个方法,所以说接口往往是根据下面的实现后期抽象出来的,实现早于接口而存在,既然实现都有了还抽象接口干嘛用呢?可能在后期也有用——这就是重构干的事儿。

  NamespaceHandlerSupport:实现自定义空间处理器的帮助类。它的三对Map成员和注册方法共同构成了解析器注册的基础功能:

  1. parsers(存储BDP实现、以元素本地名为键)和registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser)方法,这个方法是提供给子类调用的protected final方法、注意它的传参elementName是本地名(不带空间限定的元素名)
  2. decorators用来存储BDD实现和registerBeanDefinitionDecorator方法
  3. attributeDecorators用来存储专门解析属性的BDD实现和registerBeanDefinitionDecoratorForAttribute方法

  根据上述、继承NamespaceHandlerSupport的空间处理器的自定义编写非常简单,它只留给你一个需要实现的方法:init()。你只要实现init方法注册一堆你的解析器即可。

  如果是解析顶级的自定义标记相对比较简单,实现个NamespaceHandlerSupport、再实现个AbstractSingleBeanDefinitionParser即可。但是对于非顶级的嵌套自定义标签以及自定义属性等等就没那么简单了。

一、例如对于这样一个非顶级的嵌套自定义标签配置:

Xml代码  收藏代码
  1. <foo:component id="bionic-family" name="Bionic-1">  
  2.       <foo:component name="Sport-1"/>  
  3.       <foo:component name="Rock-1"/>  
  4. </foo:component>  

 对应类是:

Java代码  收藏代码
  1. public class Component {  
  2.    private String name;  
  3.    private List components = new ArrayList();  
  4.   
  5.    // mmm, there is no setter method for the 'components'  
  6.    public void addComponent(Component component) {  
  7.       this.components.add(component);  
  8.    }  
  9.   
  10.    public List getComponents() {  
  11.       return components;  
  12.    }  
  13.   
  14.    public String getName() {  
  15.       return name;  
  16.    }  
  17.   
  18.    public void setName(String name) {  
  19.       this.name = name;  
  20.    }  
  21. }  

 注意对于成员components并没有提供一个setter方法供你注入!对这种问题的解决方式是创建一个自定义工厂bean来暴露一个setter方法:

Java代码  收藏代码
  1. import org.springframework.beans.factory.FactoryBean;  
  2.   
  3. import java.util.Iterator;  
  4. import java.util.List;  
  5.   
  6. public class ComponentFactoryBean implements FactoryBean {  
  7.   
  8.    private Component parent;  
  9.    private List children;  
  10.   
  11.    public void setParent(Component parent) {  
  12.       this.parent = parent;  
  13.    }  
  14.   
  15.    public void setChildren(List children) {  
  16.       this.children = children;  
  17.    }  
  18.   
  19.    public Object getObject() throws Exception {  
  20.       if (this.children != null && this.children.size() > 0) {  
  21.          for (Iterator it = children.iterator(); it.hasNext();) {  
  22.             Component childComponent = (Component) it.next();  
  23.             this.parent.addComponent(childComponent);  
  24.          }  
  25.       }  
  26.       return this.parent;  
  27.    }  
  28.   
  29.    public Class getObjectType() {  
  30.       return Component.class;  
  31.    }  
  32.   
  33.    public boolean isSingleton() {  
  34.       return true;  
  35.    }  
  36. }  

 除了一点上述工厂bean工作良好:暴露了过多的spring内部装配细节,我们用自定义标签解析器来做这件事,按步骤来:

  1.  先写schema:
    Xml代码  收藏代码
    1. <?xml version="1.0" encoding="UTF-8" standalone="no"?>  
    2.   
    3. <xsd:schema xmlns="http://www.foo.com/schema/component"  
    4.          xmlns:xsd="http://www.w3.org/2001/XMLSchema"  
    5.          targetNamespace="http://www.foo.com/schema/component"  
    6.          elementFormDefault="qualified"  
    7.          attributeFormDefault="unqualified">  
    8.   
    9.    <xsd:element name="component">  
    10.       <xsd:complexType>  
    11.          <xsd:choice minOccurs="0" maxOccurs="unbounded">  
    12.             <xsd:element ref="component"/>  
    13.          </xsd:choice>  
    14.          <xsd:attribute name="id" type="xsd:ID"/>  
    15.          <xsd:attribute name="name" use="required" type="xsd:string"/>  
    16.       </xsd:complexType>  
    17.    </xsd:element>  
    18.   
    19. </xsd:schema>  
     
  2. 创建自定义空间处理器:
    Java代码  收藏代码
    1. import org.springframework.beans.factory.xml.NamespaceHandlerSupport;  
    2.   
    3. public class ComponentNamespaceHandler extends NamespaceHandlerSupport {  
    4.   
    5.    public void init() {  
    6.       registerBeanDefinitionParser("component"new ComponentBeanDefinitionParser());  
    7.    }  
    8. }  
     
  3. 创建自定义定义解析器(解析出ComponentFactoryBean的bean定义的哦):
    Java代码  收藏代码
    1. import org.springframework.beans.factory.support.AbstractBeanDefinition;  
    2. import org.springframework.beans.factory.support.BeanDefinitionBuilder;  
    3. import org.springframework.beans.factory.support.ManagedList;  
    4. import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;  
    5. import org.springframework.beans.factory.xml.ParserContext;  
    6. import org.springframework.util.xml.DomUtils;  
    7. import org.w3c.dom.Element;  
    8.   
    9. import java.util.List;  
    10.   
    11. public class ComponentBeanDefinitionParser extends AbstractBeanDefinitionParser {  
    12.   
    13.    protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {  
    14.       BeanDefinitionBuilder factory = BeanDefinitionBuilder.rootBeanDefinition(ComponentFactoryBean.class);  
    15.       BeanDefinitionBuilder parent = parseComponent(element);  
    16.       factory.addPropertyValue("parent", parent.getBeanDefinition());  
    17.   
    18.       List childElements = DomUtils.getChildElementsByTagName(element, "component");  
    19.       if (childElements != null && childElements.size() > 0) {  
    20.          parseChildComponents(childElements, factory);  
    21.       }  
    22.       return factory.getBeanDefinition();  
    23.    }  
    24.   
    25.    private static BeanDefinitionBuilder parseComponent(Element element) {  
    26.       BeanDefinitionBuilder component = BeanDefinitionBuilder.rootBeanDefinition(Component.class);  
    27.       component.addPropertyValue("name", element.getAttribute("name"));  
    28.       return component;  
    29.    }  
    30.   
    31.    private static void parseChildComponents(List childElements, BeanDefinitionBuilder factory) {  
    32.       ManagedList children = new ManagedList(childElements.size());  
    33.       for (int i = 0; i < childElements.size(); ++i) {  
    34.          Element childElement = (Element) childElements.get(i);  
    35.          BeanDefinitionBuilder child = parseComponent(childElement);  
    36.          children.add(child.getBeanDefinition());  
    37.       }  
    38.       factory.addPropertyValue("children", children);  
    39.    }  
    40. }  
     
  4. 最后在spring配置文件中注册空间处理器和定义解析器:
# in 'META-INF/spring.handlers'
http\://www.foo.com/schema/component=com.foo.ComponentNamespaceHandler
# in 'META-INF/spring.schemas'
http\://www.foo.com/schema/component/component.xsd=com/foo/component.xsd

  o了。

 

  二、“一般”元素(非自定义空间配置元素)的自定义属性的解析

  写定义解析器和注册并不难,但有时你需要向已存在的bean定义添加元数据,比如带空间限定的自定义属性。这种自定义属性的情况在mule中尚未见到就先不译了,原文拷贝:

By way of another example, let's say that the service class that you are defining a bean definition for a service object that will (unknown to it) be accessing a clustered JCache, and you want to ensure that the named JCache instance is eagerly started within the surrounding cluster:

<bean id="checkingAccountService" class="com.foo.DefaultCheckingAccountService"
      jcache:cache-name="checking.account">
   <!-- other dependencies here... -->
</bean>

What we are going to do here is create another BeanDefinition when the 'jcache:cache-name' attribute is parsed; this BeanDefinition will then initialize the named JCache for us. We will also modify the existingBeanDefinition for the 'checkingAccountService' so that it will have a dependency on this new JCache-initializing BeanDefinition.

package com.foo;

public class JCacheInitializer {

   private String name;

   public JCacheInitializer(String name) {
      this.name = name;
   }

   public void initialize() {
      // lots of JCache API calls to initialize the named cache...
   }
}

Now onto the custom extension. Firstly, the authoring of the XSD schema describing the custom attribute (quite easy in this case).

<?xml version="1.0" encoding="UTF-8" standalone="no"?>

<xsd:schema xmlns="http://www.foo.com/schema/jcache"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.foo.com/schema/jcache"
            elementFormDefault="qualified">

   <xsd:attribute name="cache-name" type="xsd:string"/>

</xsd:schema>

Next, the associated NamespaceHandler.

package com.foo;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class JCacheNamespaceHandler extends NamespaceHandlerSupport {

   public void init() {
      super.registerBeanDefinitionDecoratorForAttribute("cache-name",
            new JCacheInitializingBeanDefinitionDecorator());
   }
}

Next, the parser. Note that in this case, because we are going to be parsing an XML attribute, we write aBeanDefinitionDecorator rather than a BeanDefinitionParser.

package com.foo;

import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Attr;
import org.w3c.dom.Node;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class JCacheInitializingBeanDefinitionDecorator implements BeanDefinitionDecorator {
   
   private static final String[] EMPTY_STRING_ARRAY = new String[0];

   public BeanDefinitionHolder decorate(
         Node source, BeanDefinitionHolder holder, ParserContext ctx) {
      String initializerBeanName = registerJCacheInitializer(source, ctx);
      createDependencyOnJCacheInitializer(holder, initializerBeanName);
      return holder;
   }

   private void createDependencyOnJCacheInitializer(BeanDefinitionHolder holder, String initializerBeanName) {
      AbstractBeanDefinition definition = ((AbstractBeanDefinition) holder.getBeanDefinition());
      String[] dependsOn = definition.getDependsOn();
      if (dependsOn == null) {
         dependsOn = new String[]{initializerBeanName};
      } else {
         List dependencies = new ArrayList(Arrays.asList(dependsOn));
         dependencies.add(initializerBeanName);
         dependsOn = (String[]) dependencies.toArray(EMPTY_STRING_ARRAY);
      }
      definition.setDependsOn(dependsOn);
   }

   private String registerJCacheInitializer(Node source, ParserContext ctx) {
      String cacheName = ((Attr) source).getValue();
      String beanName = cacheName + "-initializer";
      if (!ctx.getRegistry().containsBeanDefinition(beanName)) {
         BeanDefinitionBuilder initializer = BeanDefinitionBuilder.rootBeanDefinition(JCacheInitializer.class);
         initializer.addConstructorArg(cacheName);
         ctx.getRegistry().registerBeanDefinition(beanName, initializer.getBeanDefinition());
      }
      return beanName;
   }
}

Lastly, the various artifacts need to be registered with the Spring XML infrastructure.

# in 'META-INF/spring.handlers'
http\://www.foo.com/schema/jcache=com.foo.JCacheNamespaceHandler
# in 'META-INF/spring.schemas'
http\://www.foo.com/schema/jcache/jcache.xsd=com/foo/jcache.xsd

分享到:
评论

相关推荐

    mule 2.0 users-guide.pdf

    根据提供的文档信息,本文将对"Mule 2.0用户指南"进行深入解析,并提炼出与标题、描述及部分文档内容相关的IT知识点。 ### Mule 2.0配置指南概览 #### 一、Mule 2.0简介 Mule 2.0是一款高度可扩展的企业服务总线...

    mule2.0 getting-started

    - **Spring XML Configuration**:支持使用Spring的XML配置来定义Mule应用程序,简化了配置过程。 - **Configuration Builder + Mule Context Factory**:新增的配置构建器和Mule上下文工厂简化了Mule应用程序的初始...

    Mule-CNC-2.0:Mule CNC 2.0 - 3D 打印 CNC 铣床

    Mule CNC 2.0 自述 Mule CNC 2.0 是一款使用 3D 打印组件构建的 CNC 铣床。 ! [Mule CNC 2.0 CAD] (/doc/Mule CNC 2.0.png) ###主要功能 3D 打印部件。包括主轴在内的主要部件均由 PLA 3D 打印。 它可以使用 3D ...

    mule-spring-boot-starter

    该项目将允许开发人员以与其他Spring Boot应用程序几乎相同的方式构建Mule应用程序。 Maven依赖关系: 要开始使用,只需将依赖项包含在pom文件中: &lt;groupId&gt;net.taptech&lt;/groupId&gt; &lt;artifactId&gt;mule-spring-...

    mule -esb 源码

    `mule-spring-configuration.dtd`和`mule-configuration.dtd`是Mule ESB的XML配置文件的DTD(文档类型定义),它们规定了XML配置文件的结构和元素。Spring是Mule ESB的核心组件之一,负责管理对象的生命周期和依赖...

    mule-springboot-module:Mule 4模块可嵌入Spring Boot应用程序

    Spring Boot模块扩展添加描述... ... ... 将此依赖项添加到您的应用程序pom.xml &lt;groupId&gt;...

    Mule与MQ集成

    【Mule与MQ集成】是关于整合Mule ESB(企业服务总线)与消息中间件,如Apache ActiveMQ的开发文档。Mule是一个开源的ESB,它提供了一个平台来连接各种应用系统,实现数据交换和服务集成。而ActiveMQ则是Apache软件...

    mule(java)开发简介

    - 这种非入侵式的方式使得现有应用程序可以无缝地与 Mule 集成,而无需对现有的代码进行大量修改。 14. **强大的应用集成框架和完整的可扩展的开发模式:** - 这些特点使得 Mule 成为了一个非常灵活且强大的工具...

    esb-mule系统设计

    MULE ESB可以与Spring框架无缝集成,利用Spring的DI特性管理服务的生命周期,同时Spring的安全、事务管理等特性也可以应用于MULE中的服务。这种集成使得开发者可以利用Spring的丰富功能,同时享受到MULE在ESB领域的...

    mule_spring_hibernate_demo:Spring和Hibernate的Mule持久性示例

    【标题】"mule_spring_hibernate_demo"是一个基于Mule ESB(企业服务总线)、Spring框架和Hibernate ORM(对象关系映射)的实战项目,展示了如何在Mule应用程序中实现数据持久化。这个例子旨在帮助开发者理解如何将...

    Mule 2 A Developer's Guide

    **完整的 Mule 2.0 配置文件示例 (Complete Mule2.0 Configuration File)** 这一部分提供了一个实际的 Mule 配置文件示例,展示了如何组合上述所有元素来构建一个完整的 Mule 应用程序。 #### 第二章:路由器及其...

    Mule V.S ServiceMix

    尽管Mule并不依赖Java Business Integration (JBI)标准,但它通过JBI绑定支持与JBI容器(如ServiceMix)的交互,这意味着Mule组件可以与其他遵循JBI标准的组件进行通信。然而,Mule的内部API并未基于JBI标准,而是...

    mule文档详解 mule

    Mule ESB使用XML或图形化的Mule Studio进行配置。开发者可以创建数据流,定义消息如何在各个服务之间流动,包括数据转换、错误处理和流控制。Mule的工作流程通常包括消息的接收、转换、路由和发送。 **5. 安全性** ...

    mule in action 说明+文档介绍

    Mule的核心组件是UMO(Universal Message Objects,从Mule2.0开始UMO这一概念已经被组件Componse所代替),UMO实现整合逻辑。UMO可以是POJO,JavaBean等等。它支持30多种传输协议(file,FTP,UDP,TCP,email,HTTP,SOAP,JMS...

    Mule stdio 安装过程

    Mule ESB支持多种传输协议,如文件、FTP、UDP、TCP、SOAP、电子邮件、JMS等,并能够与Spring、ActiveMQ、CXF、Axis、Drools等流行开源项目无缝集成。此外,尽管Mule ESB并非基于JBI(Java Business Integration)...

    MuleSmooksEDItoXML:使用 smooks 和 MULE ESB 将 EDI 转换为 XML

    #Convertiong EDI 到 XML 使用 smooks 和 MULE ESB Mule Demo 使用 - EDI-to-XML 项目 使用 Smooks 和 MULE ESB 将 EDI 消息转换为 XML 的演示 这个项目 持续检查应用程序的心跳 从 zip 文件夹中读取文件 解压文件 ...

    mule-spring-run-1

    mule-starter-app-入门级Spring Boot Mule嵌入式应用程序内容博客请查看。用法通过Gradle git clone https://github.com/glawson6/mule-starter-app.gitcd mule-starter-app ...使用./gradlew进行./gradlew ... ./...

    MuleStudio用户手册.doc

    - **核心特点**:Mule 是一款以 Java 为核心的基础框架及整合平台,它支持超过30种传输协议,如 FTP、HTTP、SOAP 等,并且能够与 Spring、ActiveMQ 等流行开源项目进行无缝集成。 - **设计理念**:Mule 的设计基于...

    mule开发环境搭建和部署

    在config目录下新增一个sayHello-mule-config.xml配置文件,该文件用于定义Mule项目的配置信息。该文件的内容包括Mule项目的命名空间、SchemaLocation等信息。 四、Mule项目的配置和部署 在Mule项目中,需要配置...

Global site tag (gtag.js) - Google Analytics