Spring中bean的定义可以通过编程,可以定义在properties文件,也可以定义在通过xml文件中,用的最多的是通过xml形式,由于xml格式具有很好的自说明便于编写及维护。对于xml的文档结构、数据定义及格式验证可以通过DTD和Schema, 在spring2.0之前采用的是DTD,在spring2.0之后采用Schema。使用Schema方式使得spring更加便于与第三方进行集成以及第三方可以提供更简单更便于使用的个性化配置方式。对于XmlSchema具体知识这里不做介绍,但是Schema中有个重要的概念命名空间(namespace)必须要提一下,spring就是利用它来做第三方自定配置格式的解析的,在spring中aop, transaction的就是给第三一个实现自己自定义配置很好实例。
//默认命名空间 xmlns=http://www.springframework.org/schema/beans //定义zop的命名空间 xmlns:aop=http://www.springframework.org/schema/aop //定义了事物的命名空间 xmlns:tx="http://www.springframework.org/schema/tx
各命名空间下的格式定义文件通过xsi:schemaLocation来指定。
spring的类DefaultBeanDefinitionDocumentReader会把spring的xml配置文件当做一个文档格式来读取
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
每读取一个元素节点都会判断下这个元素的命名空间,如果是默认命名空间(http://www.springframework.org/schema/beans)则按默认方式读取bean的定义, 如果不是则走解析自定义元素流程。根据命名空间去获取具体的处理器NamespaceHandler
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { String namespaceUri = getNamespaceURI(ele); NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
DefaultNamespaceHandlerResolver类传了key为namespaceUri,在类中有个Map存储类所有的自定义NamespaceHandler,这个Map中的值是通过工具类PropertiesLoaderUtils加载所有在”META-INF/spring.handlers”中的值。
在dbbo中自定义处理器DubboNamespaceHandler来把dubbo自定义的元素转换成的bean定义并注册到spring的容器中去的。
public class DubboNamespaceHandler extends NamespaceHandlerSupport { static { Version.checkDuplicate(DubboNamespaceHandler.class); } public void init() { registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true)); } }
利用自定义元素解析更加简洁,同时也可以屏蔽一些具体的实现类型,如你不需要知道com.alibaba.dubbo.config.RegistryConfig这个类,只需要知道注册registry这个元素就可以了,用户可以通过文档以及schema的在ide中的自动提示可以很方便的去配置。