标签:spring bean ioc bean定义的解析与加载
在上一篇Spring IOC容器启动简介中在ClassPathXmlApplicationContext的基础粗略的分析了IOC容器的启动过程,对一些比较复杂的步骤没有详细的说明,从本篇开始对其中的一些比较复杂的步骤进行分析。本篇对基于ClassPathXmlApplicationContext的IOC容器的bean定义的解析与加载过程进行分析。bean定义解析加载的简单时序图如下:
bean定义的解析通过XmlBeanDefinitionReader来完成,在解析前先做一些准备工作:1、设置环境变量(Environment)用来匹配在bean配置文件可能出现的一些占位符;2、设置资源定位器用来定位bean的xml定义文件;3、设置xml实体处理器辅助xml的解析工作。
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));XmlBeanDefinitionReader通过DocumentLoader把bean的定义文件解析成一颗DOM树,然后对DOM中的各个结点就行解析。
XmlBeanDefinitionReader的doLoadBeanDefinitions方法
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 ex; } 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); } }接下来是对DOM树根节点中的各个结点进行解析:
首先解析检查是否存在profile属性,如果定义了profile属性,判断该profile和设置到坏境中的profile是否匹配,如果不匹配说明这颗子树定义的bean不适用于当前的运行坏境,跳过当前整个颗子树的解析,见DefaultBeanDefinitionDocumentReader类的doRegisterBeanDefinitions方法中的代码片段
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { Assert.state(this.environment != null, "Environment must be set for evaluating profiles"); String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!this.environment.acceptsProfiles(specifiedProfiles)) { return; } }循环各个子树,检查节点是默认的名称空间还是扩展的名称空间,比如<bean>、<property>是默认的名称空间,而<aop:xxx>、<context:xxx>这种标签是扩展的标签需要额外的标签处理器,默认标签的一级标签时<beans>先看一下默认名称空间。
默认名称空间有几个二级标签import、alias、bean、beans。
import标签,读取resource属性,如果未设置解析报错,判断resource对应的路径是绝对路径还是相对路径,如果是相对路径需要把路径装换成完整的路径,然后递归调用loadBeanDefinitions
beans标签,直接递归调用doRegisterBeanDefinitions
alias标签,读取name和alias属性,把该别名映射注册到容器中
代码在DefaultBeanDefinitionDocumentReader的processAliasRegistration方法
protected void processAliasRegistration(Element ele) { String name = ele.getAttribute(NAME_ATTRIBUTE); String alias = ele.getAttribute(ALIAS_ATTRIBUTE); boolean valid = true; if (!StringUtils.hasText(name)) { getReaderContext().error("Name must not be empty", ele); valid = false; } if (!StringUtils.hasText(alias)) { getReaderContext().error("Alias must not be empty", ele); valid = false; } if (valid) { try { getReaderContext().getRegistry().registerAlias(name, alias); } catch (Exception ex) { getReaderContext().error("Failed to register alias '" + alias + "' for bean with name '" + name + "'", ele, ex); } getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); } }bean标签,解析bean标签下的子树是整个默认名称空间标签解析最核心的动作,import和beans标签最后也会走到这一步。bean标签的解析逻辑被委托给BeanDefinitionParserDelegate:
首先读取bean的id属性和name属性,分别代码bean的id和别名,name如果以逗号或分号分隔,分裂成别名数组。如果id没有设置,取第一个别名当做id,并且把这个别名从别名数组中删除。
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"); } } ... }如果bean不是内部bean,检查id和别名是否已经被其它的bean使用,如果已经被占用,解析报错。否则把id和别名都放到一个usedNames集合属性中,声明这些id和别名已经被占用。之所以内部bean不用检查时因为内部bean的id是容器生成的,容器会保证不会发生重复。parseBeanDefinitionElement等方法中有一个containingBean参数,这个参数用来区分当前解析的bean是否是内部bean,如果是非空值说明是内部bean,内部bean和非内部bean解析时只做了两件不同的事情:1、id生成策略;2、内部bean需要继承其外部bean的scope。
protected void checkNameUniqueness(String beanName, List<String> aliases, Element beanElement) { String foundName = null; if (StringUtils.hasText(beanName) && this.usedNames.contains(beanName)) { foundName = beanName; } if (foundName == null) { foundName = (String) CollectionUtils.findFirstMatch(this.usedNames, aliases); } if (foundName != null) { error("Bean name '" + foundName + "' is already used in this <beans> element", beanElement); } this.usedNames.add(beanName); this.usedNames.addAll(aliases); }
如果既没有设置id也没有设置别名,那么容器会给bean自动生成一个id,生成规则如下:
1、在定义bean时是否指定了class,如果指定了取类名,如果未指定,判断是否指定了parent,如果指定了取parent名+$child,如果未指定判断是否指定了factory bean,如果指定了取factory bean名+$created,如果都未指定说明配置有错误
2、如果是内部bean,取步骤一生成的名称+#+随机数
3、如果不是内部bean,如果步骤一生成的名称在容器中不存在,取步骤一生成的名称,否则取步骤一生成的名称+#计数,如果计数已经被使用再次计数直到计数未被使用为止
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition 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; } } ... } return null; } public static String generateBeanName( BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean) throws BeanDefinitionStoreException { String generatedBeanName = definition.getBeanClassName(); if (generatedBeanName == null) { if (definition.getParentName() != null) { generatedBeanName = definition.getParentName() + "$child"; } else if (definition.getFactoryBeanName() != null) { generatedBeanName = definition.getFactoryBeanName() + "$created"; } } if (!StringUtils.hasText(generatedBeanName)) { throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " + "'class' nor 'parent' nor 'factory-bean' - can't generate bean name"); } String id = generatedBeanName; if (isInnerBean) { // Inner bean: generate identity hashcode suffix. id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition); } else { // Top-level bean: use plain class name. // Increase counter until the id is unique. int counter = -1; while (counter == -1 || registry.containsBeanDefinition(id)) { counter++; id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter; } } return id; }
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; }最后如果定义了修饰器BeanDefinitionDecorator,对bean定义进行修改修饰,AOP标签定义了修饰器,这个在后面的文章介绍,然后把解析出来的bean定义和别名都注册到容器中。
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)); } } 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); } } }
首先需要查找一个名称空间处理器,名称空间和名称空间处理器的映射关系定义在jar包的META-INF/spring.handlers文件中,比如spring-context-xxxx.jar的spring.handlers的内容如下:
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/lang=org.springframework.scripting.config.LangNamespaceHandler http\://www.springframework.org/schema/task=org.springframework.scheduling.config.TaskNamespaceHandler http\://www.springframework.org/schema/cache=org.springframework.cache.config.CacheNamespaceHandler我们可以看到context的名称空间对应的处理是ContextNamespaceHandler,jee名称空间的处理器是JeeNamespaceHandler等。找到处理器之后调用处理器的parse方法。
public BeanDefinition parse(Element element, ParserContext parserContext) { return findParserForElement(element, parserContext).parse(element, parserContext); } private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) { String localName = parserContext.getDelegate().getLocalName(element); BeanDefinitionParser parser = this.parsers.get(localName); if (parser == null) { parserContext.getReaderContext().fatal( "Cannot locate BeanDefinitionParser for element [" + localName + "]", element); } return parser; }从上面代码可以看出首先定位出待解析的标签对应的解析器,以ContextNamespaceHandler为例,从ContextNamespaceHandler的代码可以看出context名称空间下所有标签的解析器。
public class ContextNamespaceHandler extends NamespaceHandlerSupport { public void init() { registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); } }找到解析器之后再调用解析器的parse方法解析具体的标签,比如annotation-config标签的解析器是AnnotationConfigBeanDefinitionParser,代码如下,在解析时遇到annotation-config标签时会注册ConfigurationClassPostProcessor、AutowiredAnnotationBeanPostProcessor等BPP到容器中。
public class AnnotationConfigBeanDefinitionParser implements BeanDefinitionParser { public BeanDefinition parse(Element element, ParserContext parserContext) { Object source = parserContext.extractSource(element); // Obtain bean definitions for all relevant BeanPostProcessors. Set<BeanDefinitionHolder> processorDefinitions = AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source); // Register component for the surrounding <context:annotation-config> element. CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source); parserContext.pushContainingComponent(compDefinition); // Nest the concrete beans in the surrounding component. for (BeanDefinitionHolder processorDefinition : processorDefinitions) { parserContext.registerComponent(new BeanComponentDefinition(processorDefinition)); } // Finally register the composite component. parserContext.popAndRegisterContainingComponent(); return null; } }关于扩展名称空间下的标签就不一个个分析,知道如何找相关的代码就好。
标签:spring bean ioc bean定义的解析与加载
原文地址:http://blog.csdn.net/pentiumchen/article/details/43926975