开篇
上一篇讲解了 Spring 中的标签包含自定义标签和默认标签,这两种方式存在较大不同,所以本文主要讲解默认标签的解析过程。
默认标签的解析是在 parseDefaultElement 方法中。
该方法分别对不同标签做不同处理。
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //对import标签处理 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } //对alias标签处理 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } //对bean标签处理 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } //对beans标签处理 else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { doRegisterBeanDefinitions(ele); } }
Bean 标签的解析及注册
这四种中,我们主要关注对 bean 标签的解析。bean 标签的解析是最复杂且重要的。我们进入 processBeanDefinition 方法。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
该段代码我们还是先看时序图。
该方法 processBeanDefinition 大致逻辑如下:
- 首先调用了
delegate.parseBeanDefinitionElement(ele)
方法进行元素解析。并返回 BeanDefinitionHolder 类型的 bdHolder,经过这个方法后,bdHolder 实例中已经包含了配置文件中的各种属性,比如 class,name,id,alias 等 - 当返回的 bdHolder 不为空的情况下,若存在默认标签的子节点下还有自定义属性,还要对自定义标签进行解析。
- 解析完成后,需要对解析后的 bdHolder 进行注册,注册操作委托给了
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
方法 - 最后发出响应事件,通知相关监听器,该 bean 已经加载完成
解析 BeanDefinition
接下来我们一点点分析,首先我们分析该方法delegate.parseBeanDefinitionElement(ele)
。
该方法在BeanDefinitionParserDelegate类中。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) { return parseBeanDefinitionElement(ele, null); }
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { //解析id属性 String id = ele.getAttribute(ID_ATTRIBUTE); //解析name属性 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); //分割name属性 List<String> aliases = new ArrayList<>(); 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.isTraceEnabled()) { logger.trace("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } // 代码(1) 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); String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isTraceEnabled()) { logger.trace("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; }
该方法就是对默认标签解析的全过程,我们现在可以看到对属性 id、name 的解析。
在当前方法主要完成的内容如下:
- 提取元素 id、name 属性
- 解析其他属性并封装到 GenericBeanDefinition 类型实例中
- 如果检测到 bean 没有指定 beanName,则使用默认规则生成一个 beanName
- 将获取到的信息封装到 BeanDefinitionHolder 实例中
我们看一下代码中标注的代码(1)调用的parseBeanDefinitionElement
方法是如何对其他标签进行解析的。
public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; //解析class属性 if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } String parent = null; //解析parent属性 if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } try { //代码(1)创建用于承载属性的AbstractBeanDefinition类型的GenericBeanDefinition AbstractBeanDefinition bd = createBeanDefinition(className, parent); //代码(2)解析默认bean的各种属性 parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); //提取 description bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); //代码(3)解析元数据 parseMetaElements(ele, bd); //解析lookup-medthod属性 (用的很少,这里就不深入介绍) parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); //解析replace-medthod属性(用的很少,这里就不深入介绍) parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); //代码(4)解析构造函数参数 parseConstructorArgElements(ele, bd); //代码(5)解析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; }
创建用于属性承载的 BeanDefinition
我们先看一下代码(1)调用的方法之前,我们先再了解一下 BeanDefinition。
BeanDefinition 是一个接口,在 Spring 中存在三种实现:RootBeanDefinition、ChildBeanDefinition、GenericBeanDefinition。它们均继承自AbstractBeanDefinition,其中 BeanDefinition 是配置文件<bean>
元素在容器内部的表现形式。该标签拥有 class、scope、lazy-init 等配置属性,BeanDefinition 也提供了对应的属性:beanClass、scope、lazyInit。
其中 RootBeanDefinition 是最常用的实现类,一般对应<bean>
元素标签,而 GenericBeanDefinition 是 2.5 版本后加入的 bean 文件配置属性定义类,提供一站式服务类。
在配置文件中我们可以父<bean>
和子<bean>
,父就用 RootBeanDefinition 表示,而子就使用 ChildBeanDefinition 表示。普通的<bean>
就使用 RootBeanDefinition 来表示, AbstractBeanDefinition 则对两者共同类的信息进行抽象。
Spring 通过 BeanDefinition 将配置文件的<bean>
转换为容器内部表示,并且将这些 BeanDefinition 注册到 BeanDefinitionRegistry 中。
Spring 容器的 BeanDefinitionRegistry 主要以 map 形式存储,后续操作可以直接从该类中获取配置信息。
但首先,我们解析属性之前就需要创建用于承载属性的实例,也就是创建了我们之前说的 GenericBeanDefinition 类型的实例。也就是代码(1)调用的createBeanDefinition(className, parent)
方法。
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName) throws ClassNotFoundException { return BeanDefinitionReaderUtils.createBeanDefinition( parentName, className, this.readerContext.getBeanClassLoader()); }
public static AbstractBeanDefinition createBeanDefinition( @Nullable String parentName, @Nullable String className, @Nullable 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; }
至此,我们就创建好了 GenericBeanDefinition 实例。
解析各种属性
当创建完用来承载 Bean 信息的 GenericBeanDefinition 实例后,就可以对 bean 信息的各种属性进行解析了。
首先我们进入代码(2)parseBeanDefinitionAttributes
方法,该方法对 element 所有元素属性进行解析。
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) { // 解析singleton属性 if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { // singleton属性已经不被支持,使用scope代替 error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele); } //解析scope属性 else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); } else if (containingBean != null) { //在嵌入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 (isDefaultValue(lazyInit)) { lazyInit = this.defaults.getLazyInit(); } bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); //解析autowire属性 String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); bd.setAutowireMode(getAutowireMode(autowire)); // 解析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 (isDefaultValue(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); 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); 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; }
该方法主要做的就是拿到各种属性对应的属性值放入 AbstractBeanDefinition 对应属性中。
解析子元素 meta
首先我们回顾一下如何使用 meta 属性。
<bean id="myTestBean" class="cn.jack.MyTestBean"> <meta key="jack" value="HelloWorld"/> </bean>
public class MyTestBean { private String testStr = "testStr"; public String getTestStr() { return testStr; } public void setTestStr(String testStr) { this.testStr = testStr; } }public class MyTestBean { private String testStr = "testStr"; public String getTestStr() { return testStr; } public void setTestStr(String testStr) { this.testStr = testStr; } }
这段代码并没有体现在 MyTestBean 中,只是一个声明,在使用的时候可以使用BeanDefinition类的getAttribute(key)
方法获取。
接下来我们看一下是如何解析的,进入代码(3)。
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) { //获取当前节点所有元素 NodeList nl = ele.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); //判断节点是否为meta if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) { Element metaElement = (Element) node; String key = metaElement.getAttribute(KEY_ATTRIBUTE); String value = metaElement.getAttribute(VALUE_ATTRIBUTE); //构造BeanMetadataAttribute实例 BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value); attribute.setSource(extractSource(metaElement)); //记录信息 attributeAccessor.addMetadataAttribute(attribute); } } }
解析子元素 constructor-arg
对构造函数的解析还是非常常见的,同时也是很复杂,举个例子:
<bean id="myTestBean" class="cn.jack.MyTestBean"> <constructor-arg index="0"> <value>Jack</value> </constructor-arg> <constructor-arg index="1"> <value>hello</value> </constructor-arg> </bean>
该代码就是 Spring 中最基础的配置,自动寻找对应的构造器并在初始化的时候将设置的参数传入进去,接下来看一下如何解析。
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) { //拿到bean所有子节点 NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) { //解析constructor-arg parseConstructorArgElement((Element) node, bd); } } }
进入parseConstructorArgElement
方法。
public void parseConstructorArgElement(Element ele, BeanDefinition bd) { //提取index属性 String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE); //提取type属性 String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE); //提取name属性 String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); if (StringUtils.hasLength(indexAttr)) { try { int index = Integer.parseInt(indexAttr); if (index < 0) { error("'index' cannot be lower than 0", ele); } else { try { this.parseState.push(new ConstructorArgumentEntry(index)); //代码(1)解析ele对应的属性元素 Object value = parsePropertyValue(ele, bd, null); //使用ConstructorArgumentValues.ValueHolder类型封装解析出来的元素 ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); //将name属性和type都封装到valueHolder中 if (StringUtils.hasLength(typeAttr)) { valueHolder.setType(typeAttr); } if (StringUtils.hasLength(nameAttr)) { valueHolder.setName(nameAttr); } valueHolder.setSource(extractSource(ele)); //不允许重复指定相同参数 if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) { error("Ambiguous constructor-arg entries for index " + index, ele); } else { //添加到BeanDefinition的ConstructorArgumentValues中,存入结构为Map bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder); } } finally { this.parseState.pop(); } } } catch (NumberFormatException ex) { error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele); } } else { //index为空的处理 try { this.parseState.push(new ConstructorArgumentEntry()); //解析ele节点对应的属性值 Object value = parsePropertyValue(ele, bd, null); //使用ConstructorArgumentValues.ValueHolder类型封装解析出来的元素 ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); //将name属性和type都封装到valueHolder中 if (StringUtils.hasLength(typeAttr)) { valueHolder.setType(typeAttr); } if (StringUtils.hasLength(nameAttr)) { valueHolder.setName(nameAttr); } valueHolder.setSource(extractSource(ele)); //添加到BeanDefinition的ConstructorArgumentValues中,因为没有index则存入结构为List bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder); } finally { this.parseState.pop(); } } }
该方法并不是特别复杂,首先提取 constructor-arg 上必要的属性(index、type、name)。
配置中指定了 index 的话操作步骤如下:
- 解析 constructor-arg 的子元素
- 使用 ConstructorArgumentValues.ValueHolder 类型封装解析后的元素
- 最后将 index、name、type 封装到 ValueHolder 类型中,并添加到 BeanDefinition 的 constructorArgumentValues 的 indexedArgumentValues 属性中。
配置中没有指定 index 的话操作步骤如下:
- 解析 constructor-arg 的子元素
- 使用 ConstructorArgumentValues.ValueHolder 类型封装解析后的元素
- 最后将 index、name、type 封装到 ValueHolder 类型中,并添加到 BeanDefinition 的 constructorArgumentValues 的 genericArgumentValues 属性中。
了解完流程之后,我们看一下具体是如何进行解析的,进入代码(1)parsePropertyValue
的方法中。
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) { String elementName = (propertyName != null ? "<property> element for property '" + propertyName + "'" : "<constructor-arg> element"); // 获取ele节点的子节点,一个属性只能对应一种类型:ref/value/list等 NodeList nl = ele.getChildNodes(); Element subElement = null; for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); //跳过meta节点或description节点 if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) && !nodeNameEquals(node, META_ELEMENT)) { //只能有一个子节点,否则异常 if (subElement != null) { error(elementName + " must not contain more than one sub-element", ele); } else { //把子节点赋值给subElement subElement = (Element) node; } } } //解析constructor-arg的ref属性 boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE); //解析constructor-arg的value属性 boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE); //在constructor-arg中不存在: 1.既有ref又有value属性 2.存在ref或者value属性并且有子元素 if ((hasRefAttribute && hasValueAttribute) || ((hasRefAttribute || hasValueAttribute) && subElement != null)) { error(elementName + " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele); } if (hasRefAttribute) { // ref属性处理,使用RuntimeBeanReference封装对应的ref名称 String refName = ele.getAttribute(REF_ATTRIBUTE); if (!StringUtils.hasText(refName)) { error(elementName + " contains empty 'ref' attribute", ele); } RuntimeBeanReference ref = new RuntimeBeanReference(refName); ref.setSource(extractSource(ele)); return ref; } else if (hasValueAttribute) { //value属性的处理,使用TypedStringValue封装 TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE)); valueHolder.setSource(extractSource(ele)); return valueHolder; } else if (subElement != null) { //解析子元素 return parsePropertySubElement(subElement, bd); } else { //如果没有ref和value,也没有子元素则抛出异常 // Neither child element nor "ref" or "value" attribute found. error(elementName + " must specify a ref or value", ele); return null; } }
该方法对构造函数中属性的元素解析,经过以下过程:
- 跳过 description 或者 meta
- 提取 constructor-arg 上的 ref 和 value 属性,随后进行校验
- ref 属性处理,使用 RuntimeBeanReference 封装对应的 ref 名称,比如:
<constructor-arg ref="a"></constructor-arg>
- value 属性的处理,使用 TypedStringValue 封装,比如:
<constructor-arg value="a"></constructor-arg>
- 子元素处理,比如:
<bean id="myTestBean" class="cn.jack.MyTestBean"> <constructor-arg> <map> <entry key="jack" value="nihao"></entry> </map> </constructor-arg> </bean>
对于子元素的处理,比如这里提到的加入了 map 元素,是如何处理的?具体在parsePropertySubElement
中实现了各种子元素的处理。
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) { return parsePropertySubElement(ele, bd, null); }
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) { //判断是否为默认命名空间,如果不是就进行解析自定义节点 if (!isDefaultNamespace(ele)) { return parseNestedCustomElement(ele, bd); } //解析是否为bean节点 else if (nodeNameEquals(ele, BEAN_ELEMENT)) { BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd); if (nestedBd != null) { nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd); } return nestedBd; } //解析ref标签 else if (nodeNameEquals(ele, REF_ELEMENT)) { // A generic reference to any name of any bean. String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE); boolean toParent = false; if (!StringUtils.hasLength(refName)) { // A reference to the id of another bean in a parent context. //解析parent refName = ele.getAttribute(PARENT_REF_ATTRIBUTE); toParent = true; if (!StringUtils.hasLength(refName)) { error("'bean' or 'parent' is required for <ref> element", ele); return null; } } if (!StringUtils.hasText(refName)) { error("<ref> element contains empty target attribute", ele); return null; } RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent); ref.setSource(extractSource(ele)); return ref; } //解析idref元素 else if (nodeNameEquals(ele, IDREF_ELEMENT)) { return parseIdRefElement(ele); } //解析value元素 else if (nodeNameEquals(ele, VALUE_ELEMENT)) { return parseValueElement(ele, defaultValueType); } //解析null元素 else if (nodeNameEquals(ele, NULL_ELEMENT)) { // It's a distinguished null value. Let's wrap it in a TypedStringValue // object in order to preserve the source location. TypedStringValue nullHolder = new TypedStringValue(null); nullHolder.setSource(extractSource(ele)); return nullHolder; } //解析array元素 else if (nodeNameEquals(ele, ARRAY_ELEMENT)) { return parseArrayElement(ele, bd); } //解析list元素 else if (nodeNameEquals(ele, LIST_ELEMENT)) { return parseListElement(ele, bd); } //解析set元素 else if (nodeNameEquals(ele, SET_ELEMENT)) { return parseSetElement(ele, bd); } //解析map元素 else if (nodeNameEquals(ele, MAP_ELEMENT)) { return parseMapElement(ele, bd); } //解析props元素 else if (nodeNameEquals(ele, PROPS_ELEMENT)) { return parsePropsElement(ele); } else { error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele); return null; } }
在该方法中实现了所有支持的类型的分类处理,到此就已经理清楚构造函数是如何解析了,这里就不深入研究如何解析 list、map 等元素了。
解析子元素 property
在分析完构造函数后,我们可以接着往下看,这里避免忘记我们再看一下目前到哪里了。
到这里我们先回顾一下如何使用 property 属性。当然,property 属性里也可以使用 list 等类型的元素。
<bean id="myTestBean" class="cn.jack.MyTestBean"> <property name="testStr" value="jack"/> </bean>
public class MyTestBean { private String testStr = "testStr"; public String getTestStr() { return testStr; } public void setTestStr(String testStr) { this.testStr = testStr; } }
接下来我们看一下是如何解析的。
public void parsePropertyElements(Element beanEle, BeanDefinition bd) { //获取到beanElement的所有子节点 NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) { //解析property节点 parsePropertyElement((Element) node, bd); } } }
public void parsePropertyElement(Element ele, BeanDefinition bd) { //获取配置元素的name值 String propertyName = ele.getAttribute(NAME_ATTRIBUTE); if (!StringUtils.hasLength(propertyName)) { //name为空则抛出异常 error("Tag 'property' must have a 'name' attribute", ele); return; } this.parseState.push(new PropertyEntry(propertyName)); try { //校验在相同bean节点下,是否存在同样的name属性,如果存在则抛出异常 if (bd.getPropertyValues().contains(propertyName)) { error("Multiple 'property' definitions for property '" + propertyName + "'", ele); return; } //解析属性值 Object val = parsePropertyValue(ele, bd, propertyName); //解析后的值和name属性封装为PropertyValue PropertyValue pv = new PropertyValue(propertyName, val); //解析meta节点 parseMetaElements(ele, pv); pv.setSource(extractSource(ele)); //解析完成后添加到BeanDefinition的propertyValues属性中 bd.getPropertyValues().addPropertyValue(pv); } finally { this.parseState.pop(); } }
和之前讲解的过程都差不多,都是先获取所有子标签然后进行遍历进行解析,获取对应的 name、value 值进行封装。
解析子元素 qualifier
该元素我们一般使用注解偏多,主要就是当接口存在多个实现类时候,在我们注入时指定某一个实现类,这样 Spring 容器就可以找到对应的 bean。因为在 Spring 中候选的 Bean 数目必须有且仅有一个。解析过程和之前都差不多,这里就不再赘述。
<bean id="myTestBean" class="cn.jack.MyTestBean"> <qualifier type="org.springframework.beans.factory.annotation.Qualifier" value="Bean的名称"/> </bean>
AbstractBeanDefinition 属性
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
至此,我们就完成了对 XML 文档到 GenericBeanDefinition 的转换,XML 中的配置都可以在 GenericBeanDefinition 中看到,但 GenericBeanDefinition 只是子类,大部分属性都在 AbstractBeanDefinition 中。我们回顾一下都有哪些配置。
public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor implements BeanDefinition, Cloneable { // 此处省略静态变量以及final变量 @Nullable private volatile Object beanClass; /** * bean的作用范围,对应bean属性scope */ @Nullable private String scope = SCOPE_DEFAULT; /** * 是否是抽象,对应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 */ @Nullable private String[] dependsOn; /** * autowire-candidate属性设置为false,这样容器在查找自动装配对象时, * 将不考虑该bean,即它不会被考虑作为其他bean自动装配的候选者, * 但是该bean本身还是可以使用自动装配来注入其他bean的 */ private boolean autowireCandidate = true; /** * 自动装配时出现多个bean候选者时,将作为首选者,对应bean属性primary */ private boolean primary = false; /** * 用于记录Qualifier,对应子元素qualifier */ private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>(0); @Nullable private Supplier<?> instanceSupplier; /** * 允许访问非公开的构造器和方法,程序设置 */ private boolean nonPublicAccessAllowed = true; /** * 是否以一种宽松的模式解析构造函数,默认为true, * 如果为false,则在以下情况 * interface ITest{} * class ITestImpl implements ITest{}; * class Main { * Main(ITest i) {} * Main(ITestImpl i) {} * } * 抛出异常,因为Spring无法准确定位哪个构造函数程序设置 */ private boolean lenientConstructorResolution = true; /** * 对应bean属性factory-bean,用法: * <bean id = "instanceFactoryBean" class = "example.chapter3.InstanceFactoryBean" /> * <bean id = "currentTime" factory-bean = "instanceFactoryBean" factory-method = "createTime" /> */ @Nullable private String factoryBeanName; /** * 对应bean属性factory-method */ @Nullable private String factoryMethodName; /** * 记录构造函数注入属性,对应bean属性constructor-arg */ @Nullable private ConstructorArgumentValues constructorArgumentValues; /** * 普通属性集合 */ @Nullable private MutablePropertyValues propertyValues; /** * 方法重写的持有者,记录lookup-method、replaced-method元素 */ @Nullable private MethodOverrides methodOverrides; /** * 初始化方法,对应bean属性init-method */ @Nullable private String initMethodName; /** * 销毁方法,对应bean属性destroy-method */ @Nullable private String destroyMethodName; /** * 是否执行init-method,程序设置 */ private boolean enforceInitMethod = true; /** * 是否执行destroy-method,程序设置 */ private boolean enforceDestroyMethod = true; /** * 是否是用户定义的而不是应用程序本身定义的,创建AOP时候为true,程序设置 */ private boolean synthetic = false; /** * 定义这个bean的应用,APPLICATION:用户,INFRASTRUCTURE:完全内部使用,与用户无关, * SUPPORT:某些复杂配置的一部分 * 程序设置 */ private int role = BeanDefinition.ROLE_APPLICATION; /** * bean的描述信息 */ @Nullable private String description; /** * 这个bean定义的资源 */ @Nullable private Resource resource; }
解析默认标签中的自定义标签元素
到目前为止,我们已经分析了BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
这行代码,接下来我们继续分析bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
这行代码。
我们先了解一下这行代码的大概作用,从语义上来分析:如果需要的话就对 BeanDefinition 进行装饰,类似于如下场景:
<bean id="myTestBean" class="cn.jack.MyTestBean"> <mybean:user username="jack"/> </bean>
当 Spring 中的 bean 使用的是默认标签配置,但是子元素却使用自定义配置的时候,这行代码就会执行。
但是为什么会在默认类型解析中单独添加一个自定义类型呢?首先这个自定义类型并不是以 bean 的形式出现的,在这里的自定义类型其实相当于是属性。
我们继续分析该方法代码。
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) { return decorateBeanDefinitionIfRequired(ele, originalDef, null); }
在调用decorateBeanDefinitionIfRequired
方法时,第三个参数传入为 null,该参数是父类 bean,当对某个嵌套配置分析时需要传递父类的 BeanDefinition,其实就是为了使用父类的 scope 属性,如果子类没有设置 scope 属性则使用父类的 scope 属性。这里是顶层配置,所以传递为 null。
public BeanDefinitionHolder decorateBeanDefinitionIfRequired( Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) { BeanDefinitionHolder finalDefinition = originalDef; // 遍历节点,查看是否存在适用于装饰的属性 // 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; }
最终都调用到了decorateIfRequired
方法,我们进入此方法查看。
public BeanDefinitionHolder decorateIfRequired( Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) { //获取自定义命名空间 String namespaceUri = getNamespaceURI(node); // 过滤默认命名空间 if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) { //根据命名空间找到相应的处理器 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler != null) { //进行装饰处理 BeanDefinitionHolder decorated = handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd)); if (decorated != null) { return decorated; } } else if (namespaceUri.startsWith("http://www.springframework.org/schema/")) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node); } else { // A custom namespace, not to be handled by Spring - maybe "xml:...". if (logger.isDebugEnabled()) { logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]"); } } } return originalDef; }
到这里已经很明确了,首先获取元素或者属性的命名空间,然后判断是否适用于自定义标签的解析条件,随后找到对应的 NamespaceHandler 进行下一步解析,该部分会在自定义标签解析中讲解。
总结:该方法的作用就是对自定义标签或者自定义属性进行处理,然后找到对应的命名空间处理器进行进一步的解析。
注册解析的 BeanDefinition
到这里,我们对配置文件的解析、装饰都已经完成,现在的 BeanDefinition 已经满足使用要求了,后续就剩下了注册工作。
也就是 processBeanDefinition 方法中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
这行代码。
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { //获取beanName做唯一标识注册 String beanName = definitionHolder.getBeanName(); registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); //如果有别名的话,注册所有别名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
该方法获取到 beanName 后,最终 BeanDefinition 都会注册到BeanDefinitionRegistry中,该方法分为两部分,一种为 beanName 注册方式和别名注册方式。
对于 BeanDefinition 的注册,不仅仅是将 BeanDefinition 放入 map 中,然后 beanName 作为 key。除此之外还做了别的事情。
进入 DefaultListableBeanFactory 类实现中。
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中的methodOverrides校验 //校验methodOverrides是否与工厂方法并存,或者methodOverrides对应的方法压根不存在 ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } //判断是否已经存在bean BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) { //如果对应的beanName已经注册过并且不允许覆盖,则抛出异常 if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } else if (existingDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (logger.isInfoEnabled()) { logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(existingDefinition)) { if (logger.isDebugEnabled()) { logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else { if (logger.isTraceEnabled()) { logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } //存入BeanDefinition this.beanDefinitionMap.put(beanName, beanDefinition); } else { //是否已经开始创建bean if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) // 因为beanDefinitionMap是全局变量,这里会存在并发访问的情况 synchronized (this.beanDefinitionMap) { //存入BeanDefinition this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; removeManualSingletonName(beanName); } } else { // Still in startup registration phase //存入BeanDefinition this.beanDefinitionMap.put(beanName, beanDefinition); //记录beanName this.beanDefinitionNames.add(beanName); //从factoryBeanCreatedCache中移除掉这个beanName removeManualSingletonName(beanName); } this.frozenBeanDefinitionNames = null; } if (existingDefinition != null || containsSingleton(beanName)) { // 重置所有beanName对应的缓存 resetBeanDefinition(beanName); } }
注册 bean 分为以下四步:
- 对 AbstractBeanDefinition 的 methodOverrides 属性校验
- 如果已经注册过并且不允许覆盖则抛出异常,否则直接覆盖
- 加入 map 缓存
- 清除解析前的 beanName 缓存
之后我们再看通过别名注册就简单多了。
public void registerAlias(String name, String alias) { Assert.hasText(name, "'name' must not be empty"); Assert.hasText(alias, "'alias' must not be empty"); synchronized (this.aliasMap) { //如果beanName与alias相同则不记录alias,并删除对应的alias if (alias.equals(name)) { this.aliasMap.remove(alias); if (logger.isDebugEnabled()) { logger.debug("Alias definition '" + alias + "' ignored since it points to same name"); } } else { String registeredName = this.aliasMap.get(alias); if (registeredName != null) { if (registeredName.equals(name)) { // An existing alias - no need to re-register return; } //不允许覆盖则抛出异常 if (!allowAliasOverriding()) { throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" + name + "': It is already registered for name '" + registeredName + "'."); } if (logger.isDebugEnabled()) { logger.debug("Overriding alias '" + alias + "' definition for registered name '" + registeredName + "' with new target name '" + name + "'"); } } //确保添加的没有name和alias值相反的数据且alias和name不相等 checkForAliasCircle(name, alias); //存入map中 this.aliasMap.put(alias, name); if (logger.isTraceEnabled()) { logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'"); } } } }
从该方法可知,注册 alias 步骤如下:
- alias 和 beanName 相同情况处理,如果相同则不需要处理并删除原有 alias
- 覆盖校验处理
- alias 循环检查
- 注册 alias
通知监听器解析及注册完成
在注册完成后,当开发人员需要对注册 BeanDefinition 事件进行监听时可以通过注册监听器方式将处理逻辑写入监听器中,在 Spring 中并没有对此事件做任何逻辑处理。
总结
到这里,Bean 的解析和注册过程已经全部 OK 了。
回顾一下,解析 BeanDefinition 的入口在 DefaultBeanDefinitionDocumentReader.parseBeanDefinitions()
。该方法会根据命令空间来判断标签是默认标签还是自定义标签,其中默认标签由 parseDefaultElement()
实现,自定义标签由 parseCustomElement()
实现。在默认标签解析中,会根据标签名称的不同进行 import 、alias 、bean 、beans 四大标签进行处理,其中 bean 标签的解析为核心,它由 processBeanDefinition()
方法实现。processBeanDefinition()
开始进入解析核心工作,分为三步:
- 解析默认标签:
BeanDefinitionParserDelegate.parseBeanDefinitionElement()
- 解析默认标签下的自定义标签:
BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired()
- 注册解析的 BeanDefinition:
BeanDefinitionReaderUtils.registerBeanDefinition()
在默认标签解析过程中,核心工作由 parseBeanDefinitionElement()
方法实现,该方法会依次解析 Bean 标签的属性、各个子元素,解析完成后返回一个 GenericBeanDefinition 实例对象。
最后通过registerBeanDefinition方法进行对BeanDefinition进行注册后就大功告成了。