在前文《Spring Schema》中讲述了如何使用Spring Ext功能,但并未具体讲其内如原理。本篇进行一些补充。
要研究spring ext的实现原理就需要研究下解析xml文件生成相应的BeanDefination的过程了。下面的类图是与load BeanDefinition相关的主要的类:
其中与Spring EXT相关的主要是NamespaceHandlerResolver和ResourceEntityResolver两个类。
ResourceEntityResolver类由BeansDtdResolver和PluggableSchemaResolver组成,用以在classpath下搜寻schema和DTD文件。而其中的PluggableSchemaResolver有一个schemaMappingsLocation属性其值为写死的META-INF/spring.schemas,这就找到了我们上面第四步里写的spring.schemas文件了。PluggableSchemaResolver把扫描到的schema存储在属性Map
{http://www.springframework.org/schema/lang/spring-lang-3.0.xsd=org/springframework/scripting/config/spring-lang-3.0.xsd, http://www.example.org/mydateformat/mydateformat.xsd=com/ronry/springtest/schema/mydateformat.xsd, http://www.springframework.org/schema/lang/spring-lang-2.5.xsd=org/springframework/scripting/config/spring-lang-2.5.xsd, http://www.springframework.org/schema/aop/spring-aop-2.0.xsd=org/springframework/aop/config/spring-aop-2.0.xsd, http://www.springframework.org/schema/lang/spring-lang-2.0.xsd=org/springframework/scripting/config/spring-lang-2.0.xsd, http://www.springframework.org/schema/aop/spring-aop-2.5.xsd=org/springframework/aop/config/spring-aop-2.5.xsd, http://www.springframework.org/schema/task/spring-task.xsd=org/springframework/scheduling/config/spring-task-3.0.xsd, http://www.springframework.org/schema/jee/spring-jee.xsd=org/springframework/ejb/config/spring-jee-3.0.xsd}
(省略大部)
类似,DefaultNamespaceHandlerResolver中也有一个属性handlerMappingsLocation其默认值为META-INF/spring.handlers,这也是我们第四步骤定义的文件。也同样有一个属性handlerMappings存储了namespace对应的handler,例如:
{http://www.example.org/mydateformat=com.ronry.springtest.schema.MyNamespaceHandler, http://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler, http://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler, http://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler, http://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler, http://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler, http://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler, http://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler}
这其中也已经包括了自定义的handler和spring默认的一些handler。
有了这些信息后,在解析bean定义的xml文件时,当遇到非默认命名空间(beans)的元素时,就按如下过程进行解析:
- 从元素中解析出nameSpaceUri;
- 根据nameSpaceUri,从DefaultNamespaceHandlerResolver中获取其对应的Handler加载Handler并 调用其init()方法;我们实现的MyNamespaceHandler在其inti()方法中注册了dataFormat标签的parser;
- 调用handler的parser方法解析元素,具体又分为以下几步:
1) 查找元素名对应的parser;
2)调用找到的parser的parse()方法;
详细的解析过程如下图:
另外,还有一点想说的是BeanDefinitionParser的继承体系。自定的BeanDefinitionParser一般继承自AbstractSingleBeanDefinitionParser,其又继承自AbstractBeanDefinitionParser,如图:
这个继承体系充分使用了模板方法的设计模式。调用时,会调用AbstractBeanDefinitionParser的parse()方法,这也是BeanDefinitionParser接口定义的方法。parse()方法中调用parseInternal()方法,而该方法在AbstractBeanDefinitionParser只是个抽象方法,具体定义只有在AbstractSingleBeanDefinitionParser中才有。AbstractSingleBeanDefinitionParser的parseInternal方法又会调用getBeanClass()和doParse()方法,这两个在AbstractSingleBeanDefinitionParser也都是抽象方法(或者是return null的方法),需要在定义的Parser中覆盖。