小读spring ioc源码(五)——BeanDefinitionDocumentReader

来源:互联网 发布:手机js脚本 编辑:程序博客网 时间:2024/06/06 00:03
上一篇博客说到,BeanDefinition的解析,已经走到了DefaultBeanDefinitionDocumentReader里,这时候配置文件已经被加载,并解析成w3c的Document对象。

这篇博客就接着介绍,DefaultBeanDefinitionDocumentReader和BeanDefinitionParserDelegate类,是怎么协同完成bean的解析和注册的
BeanDefinitionParserDelegate parent = this.delegate;this.delegate = createHelper(readerContext, root, parent);preProcessXml(root);parseBeanDefinitions(root, this.delegate);postProcessXml(root);this.delegate = parent;

这段代码,创建了一个BeanDefinitionParserDelegate组件,然后就是preProcessXml()、parseBeanDefinitions()、postProcessXml()方法

其中preProcessXml()和postProcessXml()默认是空方法,接下来就看下parseBeanDefinitions()方法
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);}}

从这个方法开始,BeanDefinitionParserDelegate就开始发挥作用了,判断当前解析元素是否属于默认的命名空间,如果是的话,就调用parseDefaultElement()方法,否则调用delegate上的parseCustomElement()方法
public boolean isDefaultNamespace(String namespaceUri) {return (!StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri));}public boolean isDefaultNamespace(Node node) {return isDefaultNamespace(getNamespaceURI(node));}

只有http://www.springframework.org/schema/beans,会被认为是默认的命名空间。也就是说,<beans>、<bean>这些元素,会认为属于默认的命名空间,而像<task:scheduled>这些,就认为不属于默认命名空间

根节点<beans>的一个子节点<bean>,是属于默认命名空间的,所以会进入parseDefaultElement()方法
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {importBeanDefinitionResource(ele);}else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {processAliasRegistration(ele);}else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {processBeanDefinition(ele, delegate);}else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {// recursedoRegisterBeanDefinitions(ele);}}

这里可能会有4种情况,<import>、<alias>、<bean>、<beans>,分别有一个方法与之对应,这里解析的是<bean>元素,所以会进入processBeanDefinition()方法
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// Register the final decorated instance.BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());}catch (BeanDefinitionStoreException ex) {getReaderContext().error("Failed to register bean definition with name '" +bdHolder.getBeanName() + "'", ele, ex);}// Send registration event.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}}

这里主要有3个步骤,先是委托delegate对bean进行解析,然后委托delegate对bean进行装饰,最后由一个工具类来完成BeanDefinition的注册。

可以看出来,DefaultBeanDefinitionDocumentReader不负责任何具体的bean解析,它面向的是xml Document对象,根据其元素的命名空间和名称,起一个类似路由的作用(不过,命名空间的判断,也是委托给delegate来做的)。所以这个类的命名,是比较贴切的,突出了其面向Document的特性。具体的工作,是由BeanDefinitionParserDelegate来完成的

下面就看下parseBeanDefinitionElement()方法
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {String id = ele.getAttribute(ID_ATTRIBUTE);String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);List<String> aliases = new ArrayList<String>();if (StringUtils.hasLength(nameAttr)) {String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);aliases.addAll(Arrays.asList(nameArr));}String beanName = id;if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {beanName = aliases.remove(0);if (logger.isDebugEnabled()) {logger.debug("No XML 'id' specified - using '" + beanName +"' as bean name and " + aliases + " as aliases");}}if (containingBean == null) {checkNameUniqueness(beanName, aliases, ele);}AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);if (beanDefinition != null) {if (!StringUtils.hasText(beanName)) {try {if (containingBean != null) {beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);}else {beanName = this.readerContext.generateBeanName(beanDefinition);// Register an alias for the plain bean class name, if still possible,// if the generator returned the class name plus a suffix.// This is expected for Spring 1.2/2.0 backwards compatibility.String beanClassName = beanDefinition.getBeanClassName();if (beanClassName != null &&beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {aliases.add(beanClassName);}}if (logger.isDebugEnabled()) {logger.debug("Neither XML 'id' nor 'name' specified - " +"using generated bean name [" + beanName + "]");}}catch (Exception ex) {error(ex.getMessage(), ele);return null;}}String[] aliasesArray = StringUtils.toStringArray(aliases);return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);}return null;}

这个方法很长,可以分成三段来看
String id = ele.getAttribute(ID_ATTRIBUTE);String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);List<String> aliases = new ArrayList<String>();if (StringUtils.hasLength(nameAttr)) {String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);aliases.addAll(Arrays.asList(nameArr));}String beanName = id;if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {beanName = aliases.remove(0);if (logger.isDebugEnabled()) {logger.debug("No XML 'id' specified - using '" + beanName +"' as bean name and " + aliases + " as aliases");}}if (containingBean == null) {checkNameUniqueness(beanName, aliases, ele);}

这一段,主要是处理一些跟alias,id等标识相关的东西
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

这一行是核心,进行实际的解析
if (beanDefinition != null) {if (!StringUtils.hasText(beanName)) {try {if (containingBean != null) {beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);}else {beanName = this.readerContext.generateBeanName(beanDefinition);// Register an alias for the plain bean class name, if still possible,// if the generator returned the class name plus a suffix.// This is expected for Spring 1.2/2.0 backwards compatibility.String beanClassName = beanDefinition.getBeanClassName();if (beanClassName != null &&beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {aliases.add(beanClassName);}}if (logger.isDebugEnabled()) {logger.debug("Neither XML 'id' nor 'name' specified - " +"using generated bean name [" + beanName + "]");}}catch (Exception ex) {error(ex.getMessage(), ele);return null;}}String[] aliasesArray = StringUtils.toStringArray(aliases);return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);}

这段是后置处理,对beanName进行处理

前置处理和后置处理,不是核心,就不细看了,重点看下核心的那一行调用
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {this.parseState.push(new BeanEntry(beanName));String className = null;if (ele.hasAttribute(CLASS_ATTRIBUTE)) {className = ele.getAttribute(CLASS_ATTRIBUTE).trim();}try {String parent = null;if (ele.hasAttribute(PARENT_ATTRIBUTE)) {parent = ele.getAttribute(PARENT_ATTRIBUTE);}AbstractBeanDefinition bd = createBeanDefinition(className, parent);parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));parseMetaElements(ele, bd);parseLookupOverrideSubElements(ele, bd.getMethodOverrides());parseReplacedMethodSubElements(ele, bd.getMethodOverrides());parseConstructorArgElements(ele, bd);parsePropertyElements(ele, bd);parseQualifierElements(ele, bd);bd.setResource(this.readerContext.getResource());bd.setSource(extractSource(ele));return bd;}catch (ClassNotFoundException ex) {error("Bean class [" + className + "] not found", ele, ex);}catch (NoClassDefFoundError err) {error("Class that bean class [" + className + "] depends on not found", ele, err);}catch (Throwable ex) {error("Unexpected failure during bean definition parsing", ele, ex);}finally {this.parseState.pop();}return null;}

这个方法也挺长的,拆开看看
this.parseState.push(new BeanEntry(beanName));String className = null;if (ele.hasAttribute(CLASS_ATTRIBUTE)) {className = ele.getAttribute(CLASS_ATTRIBUTE).trim();}

这段是从配置中抽取出类名。接下来的长长一段,把异常处理先抛开,看看实际的业务
String parent = null;if (ele.hasAttribute(PARENT_ATTRIBUTE)) {parent = ele.getAttribute(PARENT_ATTRIBUTE);}AbstractBeanDefinition bd = createBeanDefinition(className, parent);parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));parseMetaElements(ele, bd);parseLookupOverrideSubElements(ele, bd.getMethodOverrides());parseReplacedMethodSubElements(ele, bd.getMethodOverrides());parseConstructorArgElements(ele, bd);parsePropertyElements(ele, bd);parseQualifierElements(ele, bd);bd.setResource(this.readerContext.getResource());bd.setSource(extractSource(ele));return bd;

这里每个方法的命名,就说明了是要干什么,可以一个个跟进去看,本文就不细说了。总之,经过这里的解析,就得到了一个完整的BeanDefinitionHolder。只是说明一下,如果在配置文件里,没有对一些属性进行设置,比如autowire-candidate等,那么这个解析生成的BeanDefinition,都会得到一个默认值

然后,对这个Bean做一些必要的装饰
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder, BeanDefinition containingBd) {BeanDefinitionHolder finalDefinition = definitionHolder;// Decorate based on custom attributes first.NamedNodeMap attributes = ele.getAttributes();for (int i = 0; i < attributes.getLength(); i++) {Node node = attributes.item(i);finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);}// Decorate based on custom nested elements.NodeList children = ele.getChildNodes();for (int i = 0; i < children.getLength(); i++) {Node node = children.item(i);if (node.getNodeType() == Node.ELEMENT_NODE) {finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);}}return finalDefinition;}

完成了BeanDefinition的解析和装饰之后(装饰也是解析的一部分),就将BeanDefinition在BeanFactory里进行注册。

这个方法,是在BeanRegistry接口里定义的,前面提到过,DefaultListableBeanFactory实现了这个接口,所以这部分工作,也就是在DefaultListableBeanFactory里完成的
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {// Register bean definition under primary name.String beanName = definitionHolder.getBeanName();registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// Register aliases for bean name, if any.String[] aliases = definitionHolder.getAliases();if (aliases != null) {for (String aliase : aliases) {registry.registerAlias(beanName, aliase);}}}

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {Assert.hasText(beanName, "Bean name must not be empty");Assert.notNull(beanDefinition, "BeanDefinition must not be null");if (beanDefinition instanceof AbstractBeanDefinition) {try {((AbstractBeanDefinition) beanDefinition).validate();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);}}synchronized (this.beanDefinitionMap) {Object oldBeanDefinition = this.beanDefinitionMap.get(beanName);if (oldBeanDefinition != null) {if (!this.allowBeanDefinitionOverriding) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +"': There is already [" + oldBeanDefinition + "] bound.");}else {if (this.logger.isInfoEnabled()) {this.logger.info("Overriding bean definition for bean '" + beanName +"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");}}}else {this.beanDefinitionNames.add(beanName);this.frozenBeanDefinitionNames = null;}this.beanDefinitionMap.put(beanName, beanDefinition);resetBeanDefinition(beanName);}}

这个方法里,最关键的是以下2行
this.beanDefinitionNames.add(beanName);

this.beanDefinitionMap.put(beanName, beanDefinition);

前者是把beanName放到队列里,后者是把BeanDefinition放到map中,到此注册就完成了。在后面实例化的时候,就是从beanDefinitionMap中的BeanDefinition取出来,逐一实例化

BeanFactory准备完毕之后,代码又回到了XmlWebApplicationContext里
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}catch (BeansException ex) {// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}}}

也就是obtainFreshBeanFactory()方法执行之后,再进行下面的步骤。每个步骤的作用,在前面的博客里已经介绍过了

总结来说,ApplicationContext将解析配置文件的工作委托给BeanDefinitionReader,然后BeanDefinitionReader将配置文件读取为xml的Document文档之后,又委托给BeanDefinitionDocumentReader

BeanDefinitionDocumentReader这个组件是根据xml元素的命名空间和元素名,起到一个路由的作用,实际的解析工作,是委托给BeanDefinitionParserDelegate来完成的

BeanDefinitionParserDelegate的解析工作完成以后,会返回BeanDefinitionHolder给BeanDefinitionDocumentReader,在这里,会委托给DefaultListableBeanFactory完成bean的注册

XmlBeanDefinitionReader(计数、解析XML文档)-->BeanDefinitionDocumentReader(依赖xml文档,进行解析和注册)-->BeanDefinitionParserDelegate(实际的解析工作)。可以看出,在解析bean的过程中,这3个组件的分工是比较清晰的,各司其职,这种设计思想值得学习

到此为止,<bean>的解析过程就基本分析结束了。下一篇博客,会针对另外一些非Default Namespace的元素的处理过程,再更深入地分析一下spring的加载过程
原创粉丝点击