Mule 与 Spring2.0's extensible XML configuration mechanism—续1
一、空间处理器?
Mule的xml配置空间处理器层次结构(以CXF为例)是这样的:
?????????org.springframework.beans.factory.xml.NamespaceHandlerSupport
????????????????????????????????????????????????????????? |
?????????? org.mule.config.spring.handlers.AbstractMuleNamespaceHandler
???????????|???????????????????????????????????????????????????????????????????????????????????????????????? ?|...
org.mule.config.spring.handlers.MuleNamespaceHandler???org.mule.transport.cxf.config.CxfNamespaceHandler
?
? AbstractMuleNamespaceHandler抽象空间处理器的作用是允许子类忽略一些特定元素如:mule、description,是靠IgnoredDefinitionParser私有内部类和registerIgnoredElement方法联合实现的。
??另外因为connector都是顶级元素、类型比较多但功用结构都相似所以AbstractMuleNamespaceHandler对connector提供了单独的注册方法,采用了统一的顶级元素定义解析器:MuleOrphanDefinitionParser。所谓的“顶级”是特指运行时场景,在配置文件中connector的父元素是<mule>、但是解析出来的connector实例不会被注入到任何其他的bean实例中去、这就是所谓的“孤儿”元素。connector是比较重要的,而且它的的解析注册随时可能发生,即使你没有显式配置<cxf:connector...但如果在service端点使用了cxf:inbound、cxf:outbound或全局端点endpoint,则CXF的Connector也会被默认创建并注册,所以对connector提供了单独的便利注册方法:registerConnectorDefinitionParser,方法采用了统一的注册名:"connector"——可能会默认按照这个名字查找connector。Connector是transport传输的统管,传输是抽象的、Connector是具体的,Connector管理receiver、dispatcher。
? AbstractMuleNamespaceHandler还提供了一些便利方法以及一个私有内部类RegisteredMdps——已注册mule定义解析器。传输端点注册方法:registerStandardTransportEndpoints 和 registerMetaTransportEndpoints用到,也就是说传输端点的定义解析器除了正常注册到空间处理器以外还会被RegisteredMdps所缓存起来,缓存的目的从它一系列方法看来是为了能够对所有缓存的端点定义解析器增加注入一些东西如预处理器PreProcessor、后处理器PostProcessor,不过CxfNamespaceHandler虽然调用了registerMetaTransportEndpoints但是没有接收保留返回的RegisteredMdps对象,这说明CXF模块没有用到RegisteredMdps提供的功能。
? 以CXF的空间处理器为例:org.mule.transport.cxf.config.CxfNamespaceHandler,在其init()方法中所调用的头两个方法比较重要:
? 先看registerConnectorDefinitionParser方法、它相当于注册了这样一个定义解析器:
registerBeanDefinitionParser("connector", new MuleOrphanDefinitionParser(CxfConnector.class, true))?空间处理器说白了都是些辅助类、算是个解耦层吧,简单直观、不涉及xml配置元素的解析。仅仅用来注册定义解析器。
二、定义解析器BDP? MuleOrphanDefinitionParser我们说了是顶级元素的统一定义解析器,在构造它的时候传入解析后需要得到的bean的类型。先来看CxfNamespaceHandler的registerConnectorDefinitionParser方法、在这里就是上面的CxfConnector.class,如果你显式配置<cxf:connector...则启用这个BDP来做定义解析。这个BDP的层次结构是这样的:
?
??org.springframework.beans.factory.xml.BeanDefinitionParser
???????????????????????????????????????????|
??org.springframework.beans.factory.xml.AbstractBeanDefinitionParser
???????????????????????????????????????????| ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? org.mule.config.spring.parsers.AbstractMuleBeanDefinitionParser
???????????????????????????????????????????|????????????????????????????????????????????????????????????????????????????????????????
?? org.mule.config.spring.parsers.generic.OrphanDefinitionParser
???????????????????????????????????????????|
org.mule.config.spring.parsers.generic.MuleOrphanDefinitionParser
?
??AbstractMuleBeanDefinitionParser作为spring与mule之间的分界线,在mule层次中从下往上看:MuleOrphanDefinitionParser只是做了对OrphanDefinitionParser的唯一增强:对parseInternal方法增加了校验:检查element是否是顶级元素,不是则抛异常。OrphanDefinitionParser覆写了getBeanClass方法在这里是返回CxfConnector.class
? 在spring层次中从上往下看:BeanDefinitionParser 接口定义方法:BeanDefinition parse(Element element, ParserContext parserContext);—方法解释:解析特定元素并把解析结果BeanDefinition注册到ParserContext内部的BeanDefinitionRegistry(bean定义注册库,通过ParserContext.getRegistry()得到)中去——不愧是接口,简单明了,领导讲话都是很有高度的、恩、我们更希望不只是讲话有高度更要眼光做事有高度。Implementations must return the primary BeanDefinition that results from the parse if they will ever be used in a nested fashion (for example as an inner tag in a <property/> tag). Implementations may return null ?if they will not be used in a nested fashion.
? AbstractBeanDefinitionParser 抽象BDP实现,首先把parse实现为final的抽象方法、实现了BeanDefinition的注册动作、BeanDefinition的解析则延迟到parseInternal模板方法。另外还提供了一些便利方法不赘述了。子类在parseInternal方法中实现实际的解析逻辑。当你需要解析一些复合xml为一个或多个BeanDefinition时继承实现此类。如果只是解析为一个BeanDefinition可以采用AbstractSingleBeanDefinitionParser单一BDP(或其子类AbstractSimpleBeanDefinitionParser简单BDP)。也就是说AbstractBeanDefinitionParser是个复合BDP。
??遵从一个模板抽象类的典型做法、AbstractBeanDefinitionParser实现了parse方法的大体流程:
- 调用parseInternal模板方法校验或生成id(如果需要)注册bean定义:registerBeanDefinition(holder, parserContext.getRegistry());
? 在它的parseInternal模板方法中传入了ParserContext,所以你可以将解析出的多个BeanDefinition自行注册进去。至于parseInternal模板方法返回的AbstractBeanDefinition称为首要(primary)bean定义,也就是所解析的element产生的主要BeanDefinition,这个主要BD由AbstractBeanDefinitionParser“亲手”注册。
?
??回到中间的AbstractMuleBeanDefinitionParser这里、AbstractMuleBeanDefinitionParser是绝大多数mule BDP的公共基类以便在mule内部提供一致的解析行为,是mule对spring BDP的自定义。提供一些强化:
- 以"-ref"结尾的属性名将被认为是对其他bean的引用。registerBeanReference()方法可以显式地把一个属性注册为bean引用。例如:<bpm:connector bpms-ref="testBpms"/>将自动对connector设置一个属性"bpms"去引用另一个bean:testBpms。可以注册属性映射来将一个xml属性名对映到一个已创建的bean的名字上。如:addAlias("poolExhaustedAction", "poolExhaustedActionString")可以把'poolExhaustedAction' 映射到正在被创建的bean的名为'poolExhaustedActionString'属性上。Value Mappings can be used to map key value pairs from selection lists in the XML schema to property values on the bean being created. These are a comma-separated list of key=value pairs. For example:addMapping("action","NONE=0,ALWAYS_BEGIN=1,BEGIN_OR_JOIN=2,JOIN_IF_POSSIBLE=3");The first argument is the bean name to set, the second argument is the set of possible key=value pairsProvides an automatic way of setting the 'init-method' and 'destroy-method' for this object. This will then automatically wire the bean into the lifecycle of the Application context.The 'singleton' property provides a fixed way to make sure the bean is always a singleton or not.Collections will be automatically created and extended if the setter matches "property+s".
? Note that this class is not multi-thread safe.? The internal state is reset before each "use" by preProcess(org.w3c.dom.Element) which assumes sequential access.
?
? 都是些自定义辅助逻辑。首要还是完成了parseInternal方法、增加了getClassInternal => getBeanClass在这里还是得到CxfConnector.class。其中还涉及到Spring 2.0 增加的 BeanDefinitionBuilder?,通过 BeanDefinitionBuilder 可以编程式地创建BeanDefinition而不必需使用XML如:??
GenericApplicationContext ctx = new GenericApplicationContext();BeanDefinitionBuilder builderA = BeanDefinitionBuilder .rootBeanDefinition(BeanA.class) .addPropertyValue("name", "Joe");ctx.registerBeanDefinition("bean-a", builderA.getBeanDefinition()); BeanDefinitionBuilder builderB = BeanDefinitionBuilder.rootBeanDefinition(BeanB.class) .addPropertyReference("refA", "bean-a");ctx.registerBeanDefinition("bean-b", builderB.getBeanDefinition()); ctx.getBean("bean-b");上面的例子动态创建了两个 bean 定义,第二个Bean 引用了第一个Bean。其实BeanDefinitionBuilder的出现就是为了支持使用spring的各路框架如mule的动态特性。
? 备个忘:builder设计模式:http://www.jdon.com/designpatterns/builder.htm
??通过BeanDefinitionBuilder(BDB)、mule将自己的Initialisable接口所定义的initialise(也是PHASE_NAME名)方法“翻译”为了spring范畴的bean初始化方法,也就是说如果当前bean实现了Initialisable接口则将"initialise"作为bean初始化方法设置到BeanDefinition中,同理设置了Disposable接口的dispose方法为析构方法。最后是个doParse方法将所有属性还是通过BDB设置到BeanDefinition中。这样就完成了parseInternal方法的实现。
?
??还是看CxfNamespaceHandler.init() ->?registerConnectorDefinitionParser(CxfConnector.class);以"connector"为名字注册了BDP:MuleOrphanDefinitionParser(CxfConnector.class,true),通过上面嗦一大堆我们知道了这个过程就是实例化org.mule.transport.cxf.CxfConnector(作为了单件)的过程,在这个过程之中由于CxfConnector实现了Initialisable接口以及Disposable接口,所以在实例化过程中spring会调用CxfConnector.initialise方法;Disposable接口的dispose方法也是同样的。
?
??现在进入CxfConnector连接器的分析:
?
?
?
?
?
?
?
?
?
---------------------------------------------------------
? 再看registerMetaTransportEndpoints方法:回到CxfNamespaceHandler,registerMetaTransportEndpoints方法创建RegisteredMdps,RegisteredMdps的构造函数直接注册3个BDP:endpoint、inbound-endpoint、outbound-endpoint:
private RegisteredMdps(String protocol, boolean isMeta, String[] requiredAttributes) { registerBeanDefinitionParser("endpoint", add(new TransportGlobalEndpointDefinitionParser(protocol, isMeta, AbstractMuleNamespaceHandler.this.getGlobalEndpointBuilderBeanClass(), requiredAttributes, new String[]{}))); registerBeanDefinitionParser("inbound-endpoint", add(new TransportEndpointDefinitionParser(protocol, isMeta, AbstractMuleNamespaceHandler.this.getInboundEndpointFactoryBeanClass(), requiredAttributes, new String[]{}))); registerBeanDefinitionParser("outbound-endpoint", add(new TransportEndpointDefinitionParser(protocol, isMeta, AbstractMuleNamespaceHandler.this.getOutboundEndpointFactoryBeanClass(), requiredAttributes, new String[]{}))); }?
也就是说只要你的配置文件出现“cxf:”则直接生成CXF的端点出来。从TransportGlobalEndpointDefinitionParser开始追溯到spring的AbstractBeanDefinitionParser牵出来一长串中间类:
?
? AbstractBeanDefinitionParser????????MuleDefinitionParser
????????????????????? |??????????????????????????????????? |
??????? AbstractDelegatingDefinitionParser
????????????????????????????????? |
????? AbstractSerialDelegatingDefinitionParser
????????????????????????????????? |
????? AbstractFirstResultSerialDefinitionParser
????????????????????????????????? |
??? AbstractSingleParentFamilyDefinitionParser
????????????????????????????????? |
??????? ?AddressedEndpointDefinitionParser
????????????????????????????????? |
????? TransportGlobalEndpointDefinitionParser