Spring 标签netty 加载bean原理的原理是怎样的

【Spring】核心源码学习(三):spring bean标签和自定义标签实现原理
我的图书馆
【Spring】核心源码学习(三):spring bean标签和自定义标签实现原理
本文将解析spring bean定义标签和自定义标签的解析实现原理。
这里说的标签仅限于以xml作为bean定义描述符的spring容器,继承AbstractXmlApplicationContext的一些子 容器,如XmlApplicationContext、ClassPathXmlApplicationContext、 FileSystemXmlApplicationContext等。同时也仅限于描述schema作为标签定义的情况。
Spring& xml ioc 容器常用标签和自定义标签
以Xml 资源定义的容器配置是我们最常见的一种方式。
Spring容器需要解析xml 的标签,并把xml里bean 的定义转化为内部的结构BeanDifinition。
Spring的标签有很多种,其支持的常见的标签有:
最常用的,定义一个普通bean。
Java代码 &
&bean&id="myBean"&&&&&&&&&&class="com.test.MyBean"&lazy-init="true"/&&&&
如&tx: advice&等,提供事务配置通用支持。
&tx:advice id="txAdvice" transaction-manager="transactionManager"&
&tx:attributes&
&tx:method name="save*"/&
&tx:method name="remove*"/&
&tx:method name="*" read-only="true"/&
&/tx:attributes&
&/tx:advice&
&aop:config&,&aop: aspectj-autoproxy& 等提供代理bean通用配置支持。
Java代码 &
&aop:config&proxy-target-class="true"&&&&&
&&&&&aop:advisor&pointcut="..."&advice-ref="txAdvice"/&&&&&
&&&&&aop:advisor&pointcut="..."&advice-ref="fooAdvice"/&&&&&
&/aop:config&&&&
提供在容器内配置一些JDK自带的工具类、集合类和常量的支持。
Java代码 &
&util:list&id="list"&list-class="java.util.ArrayList"&&&
&&&value&listValue1&/value&&&
&&&value&listValue2&/value&&&
&/util:list&&&
&util:map&id="map"&&&
&&&entry&key="key1"&&value="mapValue1"&&/entry&&&
&&&entry&key="key12"&value="mapValue2"&&/entry&&&
&/util:map&&&&
属性的简单访问
Java代码 &
&bean&id="loginAction"&class="com.test.LoginAction"&p:name="test"&&/bean&&&&
&lang:groovy&&lang:jruby&等,提供对动态脚本的支持。
Java代码 &
&lang:groovy&id="test"&&
&&&&&&&&&&&&&refresh-check-delay="5000"&&
&&&&&&&&&&&&&script-source="classpath:com/test/groovy/test.groovy"&&&
&/lang:groovy&&&&
&jee:jndi-lookup/&等,对一些javaEE规范的bean配置的简化,如jndi等。
Java代码 &
&jee:jndi-lookup&id="simple"&&&&
&&&&&&&&&&&&&jndi-name="jdbc/MyDataSource"&&&&
&&&&&&&&&&&&&cache="true"&&&&
&&&&&&&&&&&&&resource-ref="true"&&&&
&&&&&&&&&&&&&lookup-on-startup="false"&&&&
&&&&&&&&&&&&&expected-type="com.myapp.DefaultFoo"&&&&
&&&&&&&&&&&&&proxy-interface="com.myapp.Foo"/&&&&&&
基本上每一种标签都是用来定义一类bean的(P标签除外)。以上都是 spring自带的一些标签,当然spring 也支持自定义标签。其实&tx&&aop& 这些也可以认为是自定义标签,不过是由spring扩展的而已。
其实所有的bean定义都可以用bean标签来实现定义的。而衍生这种自定义标签来定义 bean有几个好处:
1.&&&&&& 见名知意。
2.&&&&&& 对于同一类的通用bean 。封装不必要的配置,只给外部暴露一个简单易用的标签和一些需要配置的属性。很多时候对于一个框架通用的bean ,我们不需要把bean 的所有配置都暴露出来,甚至像类名、默认值等我们都想直接封装,这个时候就可以使用自定义标签了,如: &services:property-placeholder /& 可能这个标签就默认代表配置了一个支持property placeholder的通用 bean,我们都不需要去知道配这样一个bean 的类路径是什么。
可以说自定义标签是spring的 xml容器的一个扩展点,本身spring 自己的很多标签也是基于这个设计上面来构造出来的。
Spring 对于自定义(声明式)bean标签解析如何设计
Bean的定义方式有千千万万种,无论是何种标签,无论是何种资源定义,无论是何种容器,最终的bean定义内部表示都将转换为内部的唯一结构:BeanDefinition。外部的各种定义说白了就是为了方便配置。
Spring提供对其支持的标签解析的天然支持。所以只要按照spring的规范编写xml配置文件。所有的配置,在启动时都会正常的被解析成BeanDefinition。但是如果我们要实现一个自定义标签,则需要提供对自定义标签的全套支持。
我们知道要去完成一个自定义标签,需要完成的事情有:
1.&&&&&& 编写自定义标签schema 定义文件,放在某个classpath下。
2.&&&&&& 在classpath的在 META-INF下面增加spring.schemas 配置文件,指定schema虚拟路径和实际xsd 的映射。我们在xml里的都是虚拟路径,如:
&version="1.0"&encoding="UTF-8"&&
&xmlns="http://www.springframework.org/schema/beans"&&
&&&&xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&xmlns:context="http://www.springframework.org/schema/context"&&
&&&&xmlns:p="http://www.springframework.org/schema/p"&&
&&&&xsi:schemaLocation="&&
&&&&&&&&&&&&http://www.springframework.org/schema/beans&http://www.springframework.org/schema/beans/spring-beans-2.5.xsd&&
&&&&&&&&&&&&http://www.springframework.org/schema/context&http://www.springframework.org/schema/context/spring-context-2.5.xsd&&
&&&&&&&&&&&&"&&
&&&&&&&&&id="otherBean"&&&&&&&&&&class="com.test.OtherBean"&scope="prototype"&&
&&&&&id="myBean"&&&&&&&&&&class="com.test.MyBean"&lazy-init="true"&&
&&&&&id="singletonBean"&&&&&&&&&&class="com.test.SingletonBean"&&
Java代码 &
&就是一个虚拟路径,其对应的真实路径在spring jar包里的META-INF/spring.schemas里面有映射到classpath定义:
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd&&&
3.&&&&&& 增加一个NamespaceHandler和BeanDefinitionParser ,用于解析自定义的标签,将自定义标签的bean 解析成一个BeanDefinition 返回。
4.&&&&&& 在classpath的在 META-INF下面增加spring.handlers 配置文件,指定标签命名空间和handlers的映射。
为什么要做以上几个事情?我们来看看设计:
Spring对标签解析的设计的过程如下:
Step 1: 将xml文件解析成Dom树。将 xml文件解析成dom树的时候,需要 xml标签定义schema来验证文件的语法结构。 Spring约定将所有的shema 的虚拟路径和真是文件路径映射定义在classpath的在 META-INF/spring.schemas下面。在容器启动时Spring 会扫描所有的META-INF/spring.schemas并将映射维护到一个 map里。
&&&&&如spring jar 包里会有自带的标签的schemas映射,可以看一下部分配置:
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/aop/spring-aop-2.5.xsd&&=&&org/springframework/aop/config/spring-aop-2.5.xsd&&
http\://www.springframework.org/schema/aop/spring-aop.xsd&&=&&org/springframework/aop/config/spring-aop-2.5.xsd&&
http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd&&=&&org/springframework/beans/factory/xml/spring-beans-2.0.xsd&&
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd&&=&&org/springframework/beans/factory/xml/spring-beans-2.5.xsd&&
http\://www.springframework.org/schema/beans/spring-beans.xsd&&=&&org/springframework/beans/factory/xml/spring-beans-2.5.xsd&&
http\://www.springframework.org/schema/context/spring-context-2.5.xsd&&=&&org/springframework/context/config/spring-context-2.5.xsd&&
http\://www.springframework.org/schema/context/spring-context.xsd&&=&&org/springframework/context/config/spring-context-2.5.xsd&&
http\://www.springframework.org/schema/jee/spring-jee-2.0.xsd&&=&&org/springframework/ejb/config/spring-jee-2.0.xsd&&
http\://www.springframework.org/schema/jee/spring-jee-2.5.xsd&&=&&org/springframework/ejb/config/spring-jee-2.5.xsd&&
等号左边是虚拟路径,右边是真是路径(classpath下的)。虚拟路径用在我们的bean定义配置文件里,如:
&xmlns="http://www.springframework.org/schema/beans"&&
&&&&xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&xmlns:context="http://www.springframework.org/schema/context"&&
&&&&xmlns:p="http://www.springframework.org/schema/p"&&
&&&&xsi:schemaLocation="&&
&&&&&&&&&&&&http://www.springframework.org/schema/beans&http://www.springframework.org/schema/beans/spring-beans-2.5.xsd&&
&&&&&&&&&&&&http://www.springframework.org/schema/context&http://www.springframework.org/schema/context/spring-context-2.5.xsd&&
beans里面的
http://www.springframework.org/schema/beans&http://www.springframework.org/schema/beans/spring-beans-2.5.xsd&&
就是个虚拟路径。
Step 2: 将dom树解析成BeanDifinition 。将定义bean的标签和xml 定义解析成BeanDefinition的过程。如果是默认的 bean标签,spring 会直接进行解析。而如果不是默认的bean标签,包括自定义和 spring扩展的&aop&、 &p&、&util& 等标签,则需要提供专门的xmlparser来处理。 paorser由自己定义和编写,并通过handler注册到容器。Spring约定了 META-INF/spring.handlers文件,在这里面定义了标签命名空间和 handler的映射。容器起来的时候会加载handler ,handler会向容器注册该命名空间下的标签和解析器。在解析的自定义标签的时候, spring会根据标签的命名空间和标签名找到一个解析器。由该解析器来完成对该标签内容的解析,并返回一个 BeanDefinition。
以下是spring jar 包自带的一些自定义标签扩展的spring.handlers文件,可以看到定义了 aop\p等其扩展标签的handlers。
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler&&
http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler&&
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler&&
http\://www.springframework.org/schema/jms=org.springframework.jms.config.JmsNamespaceHandler&&
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler&&
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler&&
http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler&&
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler&&&
META-INF/spring.handlers
等号左边是标签的命名空间,右边是标签对应命名空间的解析handler。
看看UtilNamespaceHandler的代码实现
Java代码 &
public&void&init()&{&&
&&&&registerBeanDefinitionParser("constant",&new&ConstantBeanDefinitionParser());&&
&&&&registerBeanDefinitionParser("property-path",&new&PropertyPathBeanDefinitionParser());&&
&&&&registerBeanDefinitionParser("list",&new&ListBeanDefinitionParser());&&
&&&&registerBeanDefinitionParser("set",&new&SetBeanDefinitionParser());&&
&&&&registerBeanDefinitionParser("map",&new&MapBeanDefinitionParser());&&
&&&&registerBeanDefinitionParser("properties",&new&PropertiesBeanDefinitionParser());&&
&实现了标签和对应parser的映射注册。
ListBeanDefinitionParser的实现如下:
Java代码 &
private&static&class&ListBeanDefinitionParser&extends&AbstractSingleBeanDefinitionParser&{&&
&&&&protected&Class&getBeanClass(Element&element)&{&&
&&&&&&&&return&ListFactoryBean.class;&&
&&&&protected&void&doParse(Element&element,&ParserContext&parserContext,&BeanDefinitionBuilder&builder)&{&&
&&&&&&&&String&listClass&=&element.getAttribute("list-class");&&
&&&&&&&&List&parsedList&=&parserContext.getDelegate().parseListElement(element,&builder.getRawBeanDefinition());&&
&&&&&&&&builder.addPropertyValue("sourceList",&parsedList);&&
&&&&&&&&if&(StringUtils.hasText(listClass))&{&&
&&&&&&&&&&&&builder.addPropertyValue("targetListClass",&listClass);&&
&&&&&&&&}&&
&&&&&&&&String&scope&=&element.getAttribute(SCOPE_ATTRIBUTE);&&
&&&&&&&&if&(StringUtils.hasLength(scope))&{&&
&&&&&&&&&&&&builder.setScope(scope);&&
&&&&&&&&}&&
这里父类代码不贴了,主要完成的是beanDifinition的生成。
Spring 对于自定义(声明式)bean标签源码实现大概的源码结构如下:
XmlBeanDefinitionReader是核心类,它接收 spring容器传给它的资源resource 文件,由它负责完成整个转换。它调用DefaultDocumentLoader 来完成将Resource到Dom 树的转换。调用DefaultBeanDefinitionDocumentReader 完成将Dom树到BeanDefinition 的转换。
具体的代码流程细节完全可以基于这个结构去阅读,下面就贴几个核心源码段:
源码段1:加载 spring.shemas,在PluggableSchemaResolver.java里实现:
Java代码 &
public&class&PluggableSchemaResolver&implements&EntityResolver&{&&
&&&&public&static&final&String&DEFAULT_SCHEMA_MAPPINGS_LOCATION&=&"META-INF/spring.schemas";&&
&&&&private&static&final&Log&logger&=&LogFactory.getLog(PluggableSchemaResolver.class);&&
&&&&private&final&ClassLoader&classL&&
&&&&private&final&String&schemaMappingsL&&
&&&&private&Properties&schemaM&&
&&&&public&PluggableSchemaResolver(ClassLoader&classLoader)&{&&
&&&&&&&&this.classLoader&=&classL&&
&&&&&&&&this.schemaMappingsLocation&=&DEFAULT_SCHEMA_MAPPINGS_LOCATION;&&
&&&&public&PluggableSchemaResolver(ClassLoader&classLoader,&String&schemaMappingsLocation)&{&&
&&&&&&&&Assert.hasText(schemaMappingsLocation,&"'schemaMappingsLocation'&must&not&be&empty");&&
&&&&&&&&this.classLoader&=&classL&&
&&&&&&&&this.schemaMappingsLocation&=&schemaMappingsL&&
&&&&protected&String&getSchemaMapping(String&systemId)&{&&
&&&&&&&&if&(this.schemaMappings&==&null)&{&&
&&&&&&&&&&&&if&(logger.isDebugEnabled())&{&&
&&&&&&&&&&&&&&&&logger.debug("Loading&schema&mappings&from&["&+&this.schemaMappingsLocation&+&"]");&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&try&{&&
&&&&&&&&&&&&&&&&this.schemaMappings&=&&
&&&&&&&&&&&&&&&&&&&&&&&&PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation,&this.classLoader);&&
&&&&&&&&&&&&&&&&if&(logger.isDebugEnabled())&{&&
&&&&&&&&&&&&&&&&&&&&logger.debug("Loaded&schema&mappings:&"&+&this.schemaMappings);&&
&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&catch&(IOException&ex)&{&&
&&&&&&&&&&&&&&&&throw&new&FatalBeanException(&&
&&&&&&&&&&&&&&&&&&&&&&&&"Unable&to&load&schema&mappings&from&location&["&+&this.schemaMappingsLocation&+&"]",&ex);&&
&&&&&&&&&&&&}&&
&&&&&&&&}&&
&&&&&&&&return&this.schemaMappings.getProperty(systemId);&&
源码段2: 加载spring.handlers,在DefaultNamespaceHandlerResolver里实现:
Java代码 &
public&class&DefaultNamespaceHandlerResolver&implements&NamespaceHandlerResolver&{&&
&&&&public&static&final&String&DEFAULT_HANDLER_MAPPINGS_LOCATION&=&"META-INF/spring.handlers";&&
&&&&protected&final&Log&logger&=&LogFactory.getLog(getClass());&&
&&&&private&final&ClassLoader&classL&&
&&&&private&final&String&handlerMappingsL&&
&&&&private&Map&handlerM&&
&&&&public&DefaultNamespaceHandlerResolver()&{&&
&&&&&&&&this(null,&DEFAULT_HANDLER_MAPPINGS_LOCATION);&&
&&&&public&DefaultNamespaceHandlerResolver(ClassLoader&classLoader)&{&&
&&&&&&&&this(classLoader,&DEFAULT_HANDLER_MAPPINGS_LOCATION);&&
&&&&public&DefaultNamespaceHandlerResolver(ClassLoader&classLoader,&String&handlerMappingsLocation)&{&&
&&&&&&&&Assert.notNull(handlerMappingsLocation,&"Handler&mappings&location&must&not&be&null");&&
&&&&&&&&this.classLoader&=&(classLoader&!=&null&?&classLoader&:&ClassUtils.getDefaultClassLoader());&&
&&&&&&&&this.handlerMappingsLocation&=&handlerMappingsL&&
&&&&private&Map&getHandlerMappings()&{&&
&&&&&&&&if&(this.handlerMappings&==&null)&{&&
&&&&&&&&&&&&try&{&&
&&&&&&&&&&&&&&&&Properties&mappings&=&&
&&&&&&&&&&&&&&&&&&&&&&&&PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation,&this.classLoader);&&
&&&&&&&&&&&&&&&&if&(logger.isDebugEnabled())&{&&
&&&&&&&&&&&&&&&&&&&&logger.debug("Loaded&mappings&["&+&mappings&+&"]");&&
&&&&&&&&&&&&&&&&}&&
&&&&&&&&&&&&&&&&this.handlerMappings&=&new&HashMap(mappings);&&
&&&&&&&&&&&&}&&
&&&&&&&&&&&&catch&(IOException&ex)&{&&
&&&&&&&&&&&&&&&&IllegalStateException&ise&=&new&IllegalStateException(&&
&&&&&&&&&&&&&&&&&&&&&&&&"Unable&to&load&NamespaceHandler&mappings&from&location&["&+&this.handlerMappingsLocation&+&"]");&&
&&&&&&&&&&&&&&&&ise.initCause(ex);&&
&&&&&&&&&&&&&&&&throw&&&
&&&&&&&&&&&&}&&
&&&&&&&&}&&
&&&&&&&&return&this.handlerM&&
源码段3: xml到dom树的解析。
在XmlBeanDefinitionReader.java的doLoadBeanDefinitions 方法里,调用DefaultDocumentLoader完成。
Java代码 &
protected&int&doLoadBeanDefinitions(InputSource&inputSource,&Resource&resource)&&
&&&&&&&&&&&&throws&BeanDefinitionStoreException&{&&
&&&&&&&&try&{&&
&&&&&&&&&&&&int&validationMode&=&getValidationModeForResource(resource);&&
&&&&&&&&&&&&Document&doc&=&this.documentLoader.loadDocument(&&
&&&&&&&&&&&&&&&&&&&&inputSource,&getEntityResolver(),&this.errorHandler,&validationMode,&isNamespaceAware());&&
&&&&&&&&&&&&return&registerBeanDefinitions(doc,&resource);&&
&&&&&&&&}&&
&&&&&&&&catch&(BeanDefinitionStoreException&ex)&{&&
&&&&&&&&&&&&throw&&&
&&&&&&&&}&&
&&&&&&&&catch&(SAXParseException&ex)&{&&
&&&&&&&&&&&&throw&new&XmlBeanDefinitionStoreException(resource.getDescription(),&&
&&&&&&&&&&&&&&&&&&&&"Line&"&+&ex.getLineNumber()&+&"&in&XML&document&from&"&+&resource&+&"&is&invalid",&ex);&&
&&&&&&&&}&&
&&&&&&&&catch&(SAXException&ex)&{&&
&&&&&&&&&&&&throw&new&XmlBeanDefinitionStoreException(resource.getDescription(),&&
&&&&&&&&&&&&&&&&&&&&"XML&document&from&"&+&resource&+&"&is&invalid",&ex);&&
&&&&&&&&}&&
&&&&&&&&catch&(ParserConfigurationException&ex)&{&&
&&&&&&&&&&&&throw&new&BeanDefinitionStoreException(resource.getDescription(),&&
&&&&&&&&&&&&&&&&&&&&"Parser&configuration&exception&parsing&XML&from&"&+&resource,&ex);&&
&&&&&&&&}&&
&&&&&&&&catch&(IOException&ex)&{&&
&&&&&&&&&&&&throw&new&BeanDefinitionStoreException(resource.getDescription(),&&
&&&&&&&&&&&&&&&&&&&&"IOException&parsing&XML&document&from&"&+&resource,&ex);&&
&&&&&&&&}&&
&&&&&&&&catch&(Throwable&ex)&{&&
&&&&&&&&&&&&throw&new&BeanDefinitionStoreException(resource.getDescription(),&&
&&&&&&&&&&&&&&&&&&&&"Unexpected&exception&parsing&XML&document&from&"&+&resource,&ex);&&
&&&&&&&&}&&
Java代码 &
getEntityResolver()&&
&会完成spring.schemas的装载,里面会间接调用源码段1。穿进去的entityResolver作为标签解析使用。
源码段4: dom树到Beandifinition:
在XmlBeanDefinitionReader.java的doLoadBeanDefinitions 方法里,调用BeanDefinitionDocumentReader完成。
Java代码 &
public&int&registerBeanDefinitions(Document&doc,&Resource&resource)&throws&BeanDefinitionStoreException&{&&
&&&&&&&&&&
&&&&&&&&if&(this.parserClass&!=&null)&{&&
&&&&&&&&&&&&XmlBeanDefinitionParser&parser&=&&
&&&&&&&&&&&&&&&&&&&&(XmlBeanDefinitionParser)&BeanUtils.instantiateClass(this.parserClass);&&
&&&&&&&&&&&&return&parser.registerBeanDefinitions(this,&doc,&resource);&&
&&&&&&&&}&&
&&&&&&&&&&
&&&&&&&&BeanDefinitionDocumentReader&documentReader&=&createBeanDefinitionDocumentReader();&&
&&&&&&&&int&countBefore&=&getRegistry().getBeanDefinitionCount();&&
&&&&&&&&documentReader.registerBeanDefinitions(doc,&createReaderContext(resource));&&
&&&&&&&&return&getRegistry().getBeanDefinitionCount()&-&countB&&
具体细节这里不在累述。
spring标签的扩展性做得还是不错的。在我们公司很多框架的一些通用配置都基于spring的声明式标签来实现。中间的一些约定和设计思想值得学习。 本文很多代码细节没办法累述,有兴趣可以去看看。文章中若有不对的地方请大家指出,一起探讨。
后续的spring源码学习可能会是代理的具体实现,欢迎一起探讨!
TA的最新馆藏[转]&[转]&[转]&[转]&
喜欢该文的人也喜欢博客分类:
spring中编写配置可以用两种方式:
普通的通过 &bean id="" class=""&&property name="" ref=""&&/bean& 这种默认标签配置方式
自定义Bean 配置方式,例如:
&?xml version="1.0" encoding="UTF-8"?&
&beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tsearcher="http://www.taobao.com/terminator/tsearcher"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.taobao.com/terminator/tsearcher http://www.taobao.com/terminator/tsearcher.xsd"&
&tsearcher:parent id="testparent"&
&tsearcher:child /&
&/tsearcher:parent&
这种自定义的配置方式,在spring的实现中已经有部门实现,分别在几个命令空间中详细说明请查看(http://static.springsource.org/spring/docs/2.0.x/reference/xsd-config.html)
下面通过一个例子来说明一下,如何实现spring 自定义bean的方式来配置对象:
首先,定义两个properties文件,
一个是 spring.handlers 这个文件的作用是将配置文件中的命名空间与处理命名空间的handler(NamespaceHandlerSupport)联系起来,
另外一个文件是spring.schemas 文件,这个文件的作用是定义xsd文件的虚拟路径
spring.handlers例子:
http\://www.taobao.com/terminator/tsearcher=com.taobao.terminator.tag.TermiantorTSearcherNamespaceHandler
spring.shcemas例子:
http\://www.taobao.com/terminator/tsearcher.xsd=com/taobao/terminator/xsd/tsearcher.xsd
接下来要写一个namespaceHandler 类,用来为这个名称空间下的每个标签定义解析器。
例如,上面提到的TermiantorTSearcherNamespaceHandler:
import org.springframework.beans.factory.xml.NamespaceHandlerS
public class TermiantorTSearcherNamespaceHandler extends
NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("parent", new ParentBeanParser());
registerBeanDefinitionParser("child", new ChildParser());
init方法中调用了两次registerBeanDefinitionParser,申明了parent,child 标签的解析器。
parent标签和child标签的关系是父子关系,spring配置文件如下:
&?xml version="1.0" encoding="UTF-8"?&
&beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tsearcher="http://www.taobao.com/terminator/tsearcher"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.taobao.com/terminator/tsearcher http://www.taobao.com/terminator/tsearcher.xsd"&
&tsearcher:parent id="testparent"&
&tsearcher:child /&
&/tsearcher:parent&
定义tsearcher.xsd的xml元素结构信息:
&?xml version="1.0" encoding="UTF-8"?&
&xsd:schema xmlns="http://www.taobao.com/terminator/tsearcher"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans"
targetNamespace="http://www.taobao.com/terminator/tsearcher"
elementFormDefault="qualified" attributeFormDefault="unqualified"&
&xsd:import namespace="http://www.springframework.org/schema/beans" /&
&xsd:element name="parent"&
&xsd:complexType&
&xsd:complexContent&
&xsd:extension base="beans:identifiedType"&
&xsd:sequence&
&xsd:element ref="child" /&
&/xsd:sequence&
&/xsd:extension&
&/xsd:complexContent&
&/xsd:complexType&
&/xsd:element&
&xsd:element name="child"&
&xsd:complexType&
&xsd:complexContent&
&xsd:extension
base="beans:identifiedType"&
&/xsd:extension&
&/xsd:complexContent&
&/xsd:complexType&
&/xsd:element&
&/xsd:schema&
这里最重要的是parent标签的解析器ParentBeanParser,在doParse方法中,还需要启动子标签child的解析流程,不然的话子标签child不会被解析:
import org.springframework.beans.factory.support.BeanDefinitionB
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionP
import org.springframework.beans.factory.xml.ParserC
import org.springframework.util.xml.DomU
import org.w3c.dom.E
* @author 百岁()
public class ParentBeanParser extends AbstractSimpleBeanDefinitionParser {
protected void doParse(Element element, ParserContext parserContext,
BeanDefinitionBuilder builder) {
super.doParse(element, parserContext, builder);
builder.addPropertyValue("child", parserContext.getDelegate()
.parseCustomElement(
DomUtils.getChildElementByTagName(element, "child"),
builder.getRawBeanDefinition()));
protected Class&Parent& getBeanClass(Element element) {
return Parent.
这里特别要说明的是,在调用parserContext.getDelegate()
.parseCustomElement(DomUtils.getChildElementByTagName(element, "child"), builder.getRawBeanDefinition())
方法的时候,方法parseCustomElement的第二个beanDefinition参数是必须的,不然的话框架会认为这个元素是根结点元素,必须要有一个id属性。
接下来又出现一个新的需求,如果parent和child是一对多关系,例如标签格式如下:
&tsearcher:parent id="testparent"&
&tsearcher:child name="1"/&
&tsearcher:child name="2"/&
&tsearcher:child name="3"/&
&/tsearcher:parent&
显然,用上面介绍的ParentBeanParser这个类中的解析标签的方式是不能满足需求的。
如果要知道如何解决一对多的关系请查阅下一篇博客()
浏览 11332
mozhenghua
浏览: 183138 次
来自: 杭州
query.setDistrib(false); 之后只是获取 ...
她的酒窝 写道你好,楼主,将数据库中的几张业务表打成宽表的方式 ...
大牛的思想才是最好的东西
你的 demo 似乎写错了,我理解了你的意思 ,spring
你好,楼主,将数据库中的几张业务表打成宽表的方式,这个打宽表的 ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'

我要回帖

更多关于 spring 加载bean 原理 的文章

 

随机推荐