Spring源码阅读笔记04:默认xml标签解析
上文我们主要学习了Spring是如何获取xml配置文件并且将其转换成Document,我们知道xml文件是由各种标签组成,Spring需要将其解析成对应的配置信息。之前提到过Spring中的标签包括默认标签和自定义标签两种,而两种标签的用法以及解析方式存在着很大的不同,本文详细分析默认标签的解析过程。
默认标签的解析是在parseDefaultElement函数中进行的,函数中的功能逻辑一目了然,分别对4种不同标签(import、alias、bean和 beans)做了不同的处理。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // 对import标签的处理 importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { // 对alias标签的处理 processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { // 对bean标签的处理 processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse // 对beans标签的处理 doRegisterBeanDefinitions(ele); } }
在4种标签的解析中,对bean标签的解析最为复杂也最为重要,这是本文重点分析对象,如果能理解此标签的解析过程,其他标签的解析自然会迎刃而解。首先我们进入函数processBeanDefinition(ele, delegate):
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)); } }
大致的逻辑总结如下:
- 首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的实例bdHolder,经过这个方法后,bdHolder实例已经包含我们配置文件中配置的各种属性了,例如class、name、id、alias之类的属性;
- 当返回的bdHolder不为空的情况下若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析;
- 解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法;
- 最后发出响应事件,通知相关的监听器,这个bean已经加载完成了;
下面会针对各个操作做进一步分析。
1. 解析BeanDefinition
首先我们从元素解析及信息提取开始,也就是BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele),进入BeanDefinitionDelegate类的parseBeanDefinitionElement方法:
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { return parseBeanDefinitionElement(ele, null); } public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) { // 解析id属性 String id = ele.getAttribute(ID_ATTRIBUTE); // 解析name属性 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); // 分割name属性 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 { // 如果不存在beanName那么根据Spring中提供的命名规则为当前bean生成对应的beanName 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; }
以上便是对默认标签解析的全过程了。在开始对属性展开全面解析前,Spring又做了一些功能划分,主要如下:
- 提取元素中的id以及name属性;
- 进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中;
- 如果检测到bean没有指定beanName,那么使用默认规则为此Bean生成beanName;
- 将获取到的信息封装到BeanDefinitionHolder的实例中;
我们进一步地查看步骤2中对标签其他属性的解析过程:
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; // 解析class属性 if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } try { String parent = null; // 解析parent属性 if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } // 创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition AbstractBeanDefinition bd = createBeanDefinition(className, parent); // 硬编码解析默认bean的各种属性 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); // 提取description bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); // 解析元数据 parseMetaElements(ele, bd); // 解析lookup-method属性 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); // 解析replaced-method属性 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); // 解析构造函数参数 parseConstructorArgElements(ele, bd); // 解析property子元素 parsePropertyElements(ele, bd); // 解析qualifier子元素 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; }
到这里,bean标签的所有属性解析,不论常用的还是不常用的我们都看到了,尽管有些复杂的属性还需要进一步的解析。我们着重看一些复杂标签属性的解析。
1.1 创建用于属性承载的BeanDefinition
BeanDefinition是一个接口,在Spring中有三个实现类:RootBeanDefinition、ChildBeanDefinition以及GenericBeanDefinition,均继承自AbstractBeanDefiniton,其中BeanDefinition是配置文件<bean>元素标签在容器中的内部表示形式。<bean>元素标签拥有class、scope、lazy-init等配置属性,BeanDefinition则提供了相应的beanClass、scope、lazyInit属性,BeanDefinition和<bean>中的属性是一一对应的。其中RootBeanDefinition是最常用的实现类,它对应一般性的<bean>元素标签, GenericBeanDefinition是自2.5版本以后新加入的bean文件配置属性定义类,是一站式服务类。
在配置文件中可以定义父<bean>和子<bean>,父<bean>用RootBeanDefinition表示,而子<bean>用ChildBeanDefiniton表示,而没有父<bean>的<bean>就使用RootBeanDefinition表示。
Spring通过BeanDefinition将配置文件中的<bean>配置信息转换为容器的内部表示,并将这些BeanDefiniton注册到BeanDefinitonRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring配置信息的内存数据库,主要是以map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。
由此可知,要解析属性首先要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例。而代码createBeanDefinition(className,parent)的作用就是实现此功能:
protected AbstractBeanDefinition createBeanDefinition(String className, String parentName) throws ClassNotFoundException { return BeanDefinitionReaderUtils.createBeanDefinition( parentName, className, this.readerContext.getBeanClassLoader()); } public static AbstractBeanDefinition createBeanDefinition( String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException { GenericBeanDefinition bd = new GenericBeanDefinition(); // parentName可能为空 bd.setParentName(parentName); if (className != null) { // 如果classLoader不为空,则使用已传入的classLoader加载类对象,否则只是记录className if (classLoader != null) { bd.setBeanClass(ClassUtils.forName(className, classLoader)); } else { bd.setBeanClassName(className); } } return bd; }
1.2 解析各种属性
当我们创建了bean信息的承载实例后,便可以开始各种属性解析了,这部分的主要逻辑在parseBeanDefinitionAttributes方法中:
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, BeanDefinition containingBean, AbstractBeanDefinition bd) { // 解析scope属性 if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { // Spring 2.x "scope" attribute bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { // scope与singleton两个属性只能指定其中之一,不可以同时出现,否则Spring将会报出异常 error("Specify either 'scope' or 'singleton', not both", ele); } } // 解析singleton属性 else if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { // Spring 1.x "singleton" attribute bd.setScope(TRUE_VALUE.equals(ele.getAttribute(SINGLETON_ATTRIBUTE)) ? BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE); } else if (containingBean != null) { // Take default from containing bean in case of an inner bean definition. // 在嵌入beanDefinition情况下且没有单独指定scope属性则使用父类默认的属性 bd.setScope(containingBean.getScope()); } // 解析abstract属性 if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); } // 解析lazy-init属性 String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); if (DEFAULT_VALUE.equals(lazyInit)) { lazyInit = this.defaults.getLazyInit(); } // 若没有设置或设置成其他字符都会被设置为false bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); // 解析autowire属性 String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); bd.setAutowireMode(getAutowireMode(autowire)); // 解析dependency-check属性 String dependencyCheck = ele.getAttribute(DEPENDENCY_CHECK_ATTRIBUTE); bd.setDependencyCheck(getDependencyCheck(dependencyCheck)); // 解析depends-on属性 if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE); bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS)); } // 解析autowire-candidate属性 String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE); if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) { String candidatePattern = this.defaults.getAutowireCandidates(); if (candidatePattern != null) { String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern); bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName)); } } else { bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate)); } // 解析primary属性 if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) { bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE))); } // 解析init-method属性 if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) { String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE); if (!"".equals(initMethodName)) { bd.setInitMethodName(initMethodName); } } else { if (this.defaults.getInitMethod() != null) { bd.setInitMethodName(this.defaults.getInitMethod()); bd.setEnforceInitMethod(false); } } // 解析destroy-method属性 if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) { String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE); if (!"".equals(destroyMethodName)) { bd.setDestroyMethodName(destroyMethodName); } } else { if (this.defaults.getDestroyMethod() != null) { bd.setDestroyMethodName(this.defaults.getDestroyMethod()); bd.setEnforceDestroyMethod(false); } } // 解析factory-method属性 if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); } // 解析factory-bean属性 if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); } return bd; }
我们可以清楚地看到Spring完成了对所有bean属性的解析,这些属性中有很多是我们经常使用的,也有些是我们不熟悉的,有兴趣可以查阅相关资料进一步了解。当然除了这部分属性解析,后面还有一些其他的属性解析(比如constructor-arg、property等),在此就不全部列出了。
2. AbstractBeanDefinition
至此便完成了对XML文档到GenericBeanDefinition的转换,也就是说到这里,XML中所有的配置都可以在GenericBeanDefinition的实例类中找到对应的属性。
GenericBeanDefinition只是子类实现,大部分的通用属性都保存在了AbstractBeanDefinition中,所以这里再次通过AbstractBeanDefinition的属性来回顾一下都解析了哪些对应的配置,以加深理解:
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor implements BeanDefinition, Cloneable { // 此处省略静态变量以及final常量 /** bean的作用范围,对应bean属性scope */ private String scope = SCOPE_DEFAULT; /** 是否是单例,来自bean属性scope */ private boolean singleton = true; /** 是否是原型,来自bean属性scope */ private boolean prototype = false; /** 是否是抽象,对应bean属性abstract */ private boolean abstractFlag = false; /** 是否延迟加载,对应bean属性lazy-init */ private boolean lazyInit = false; /** 自动注入模式,对应bean属性autowire */ private int autowireMode = AUTOWIRE_NO; /** 依赖检查,Spring 3.0后弃用这个属性 */ private int dependencyCheck = DEPENDENCY_CHECK_NONE; /** 用来表示一个bean的实例化依靠另一个bean先实例化,对应bean属性depend-on */ private String[] dependsOn; /** autowire-candidate属性设置为false,这样容器在查找自动装配对象时, *将不考虑该bean,即它不会被考虑作为其他bean自动装配的候选 *者,但是该bean本身还是可以使用自动装配来注入其他bean的 *对应bean属性autowire-candidate */ private boolean autowireCandidate = true; /** 自动装配时当出现多个bean候选着时,将作为首选着,对应bean属性primary */ private boolean primary = false; /** */ private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<String, AutowireCandidateQualifier>(0); /** */ private boolean nonPublicAccessAllowed = true; /** */ private boolean lenientConstructorResolution = true; /** */ private ConstructorArgumentValues constructorArgumentValues; /** */ private MutablePropertyValues propertyValues; /** */ private MethodOverrides methodOverrides = new MethodOverrides(); /** */ private String factoryBeanName; /** */ private String factoryMethodName; /** */ private String initMethodName; /** */ private String destroyMethodName; /** */ private boolean enforceInitMethod = true; /** */ private boolean enforceDestroyMethod = true; /** */ private boolean synthetic = false; /** */ private int role = BeanDefinition.ROLE_APPLICATION; /** */ private String description; /** 这个bean定义的资源 */ private Resource resource; }
3. 注册解析的BeanDefinition
解析完成之后得到的beanDinition已经可以满足后续的使用要求了,唯一还剩下的工作就是注册,也就是processBeanDefinition函数中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext(). get())所完成的工作:
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); } } }
从上面的代码可以看出,解析好的beanDefinition都会被注册到BeanDefinitionRegistry类型的实例registry中,beanDefinition的注册分成了两部分:通过beanName的注册以及通过别名的注册。
3.1 通过beanName注册BeanDefinition
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 { // 注册前的最后一次校验,这里的校验不同于之前的XML文件校验 // 主要是对于AbstractBeanDefinition属性中的methodOverrides校验 // 校验methodOverrides是否与工厂方法并存或者methodOverrides对应的方法根本不存在 ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } BeanDefinition oldBeanDefinition; // 因为beanDefinitionMap是全局变量,这里肯定会存在并发访问的情况 synchronized (this.beanDefinitionMap) { oldBeanDefinition = this.beanDefinitionMap.get(beanName); // 处理注册已经注册的beanName情况 if (oldBeanDefinition != null) { // 如果对应的BeanName已经注册并且在配置中配置了bean不允许被覆盖,则抛出异常 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 { // 记录beanName this.beanDefinitionNames.add(beanName); this.frozenBeanDefinitionNames = null; } // 注册beanDefinition this.beanDefinitionMap.put(beanName, beanDefinition); } // 重置所有beanName对应的缓存 if (oldBeanDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }
如上,在对于bean的注册处理方式上,主要进行了如下几个步骤:
- 对AbstractBeanDefinition的校验。在解析XML文件的时候我们提过校验,但是此校验非彼校验,之前的校验是针对于XML格式的校验,而此时的校验则是对于AbstractBeanDefinition的methodOverrides属性的;
- 对beanName已经注册的情况的处理。如果设置了不允许bean的覆盖,会抛出异常,否则直接覆盖;
- 加入map缓存;
- 清除解析之前留下的对应 beanName的缓存;
3.2 通过别名注册BeanDefinition
public void registerAlias(String name, String alias) { Assert.hasText(name, "'name' must not be empty"); Assert.hasText(alias, "'alias' must not be empty"); // 如果beanName与alias相同的话不记录alias,并删除对应的alias if (alias.equals(name)) { this.aliasMap.remove(alias); } else { // 如果alias不允许被覆盖则抛出异常 if (!allowAliasOverriding()) { String registeredName = this.aliasMap.get(alias); if (registeredName != null && !registeredName.equals(name)) { throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'."); } } // 当A->B存在时,若再次出现A->C->B时候则会抛出异常 checkForAliasCircle(name, alias); this.aliasMap.put(alias, name); } }
由以上代码中可以得知注册alias的步骤如下:
- alias与beanName相同情况处理。若alias与beanName名称相同则不需要处理并删除掉原有alias;
- alias覆盖处理。若aliasName已经使用并已经指向了另一beanName则需要根据用户的设置进行处理;
- alias循环检查。当A->B存在时,若再次出现A->C->B时候则会抛出异常;
- 注册alias;
4. 总结
本文主要集中在分析从如何将默认xml标签解析成BeanDefinition到将其注册到容器中这一过程,至此Spring对配置的转化工作就完成了,后面就要开始Bean的获取这部分的逻辑的分析了。