2.8解析及注册BeanDefinitions
XmlBeanDefinitionReader public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
当文件转换为Document后,接下来提取和注册bean。doc是通过上一节的loadDocument加载转换出来的。BeanDefinitionDocumentReader是接口,而实例化的工作是在createBeanDefinitionDocumentReader中实现的,通过这个方法,beanDefinitionDocumentReader真正的类型其实是DefaultBeanDefinitionDocument-Reader。
DefaultBeanDefinitionDocumentReader public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; doRegisterBeanDefinitions(doc.getDocumentElement()); } DefaultBeanDefinitionDocumentReader @SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...) protected void doRegisterBeanDefinitions(Element root) { // Any nested <beans> elements will cause recursion in this method. In // order to propagate and preserve <beans> default-* attributes correctly, // keep track of the current (parent) delegate, which may be null. Create // the new (child) delegate with a reference to the parent for fallback purposes, // then ultimately reset this.delegate back to its original (parent) reference. // this behavior emulates a stack of delegates without actually necessitating one. //专门处理解析 BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { //处理Profile属性 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { //函数将给定的字符串按照给定的分隔符分隔成字符串数组 //这里把profile按照,;分隔 String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } //解析前处理,留给子类实现 preProcessXml(root); parseBeanDefinitions(root, this.delegate); //解析后处理,留给子类实现 postProcessXml(root); this.delegate = parent; }
上述代码首先对profile处理,然后进行解析。可是当我们跟进preProcessXml、post-ProcessXml发现代码是空的。这里使用了模版方法模式()。
2.8.1profile属性的使用
profile的使用:
<?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:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="..."> <beans profile="dev"> ... </beans> <beans profile="prod"> ... </beans> </beans>
web.xml <context-param> <param>spring.profiles.active</param> <param>prod</param> </context-param>
DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions() if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } }
profile的特性可以让我们在配置文件里配置多种环境,方便进行切换。上述代码首先获取beans节点是否定义了profile属性,如果定义了则会需要到环境变量中寻找。所以这里断言environment不能为空,因为profile是可以同时指定多个,如果encironment中没有定义使用哪个profile则不会浪费性能去解析。
2.8.2解析并注册BeanDefinition
处理profile后就可以进行XML的读取了。跟踪代码进入parseBeanDefinition
DefaultBeanDefinitionDocumentReader 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)) { //对bean的处理 parseDefaultElement(ele, delegate); } else { //对bean的处理 delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
因为Spring的XMl配置里面有两大类Bean声明,一个是默认的,比如<bean id="test" class="test.person" />。另一类就是自定义的,如:<tx:annotation-driver />。
两种方式的解析与读取差距是巨大的。对于默认的配置spring知道怎么做,但对于自定义的就需要用户实现一些接口及配置。
判断默认的还是自定义的命名空间就是使用node.getNamespaceURI()获取命名空间,并与Spring中固定的命名空间 http://www.springframework.org/schema/beans进行比较。
下一章进行默认标签、自定义标签解析。

浙公网安备 33010602011771号