parseBeanDefinitions.jpeg
根据命名空间判断,默认为:BEANS_NAMESPACE_URI = “http://www.springframework.org/schema/beans
如果是默认的命名空间的话,就会进入 DefaultBeanDefinitionDocumentReader # parseDefaultElement(ele, delegate);

  1. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
  2. if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
  3. // import 解析
  4. importBeanDefinitionResource(ele);
  5. } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
  6. // alias 解析
  7. processAliasRegistration(ele);
  8. } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
  9. // bean 解析
  10. processBeanDefinition(ele, delegate);
  11. } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
  12. // 如果是嵌套的beans标签,进行递归
  13. // recurse
  14. doRegisterBeanDefinitions(ele);
  15. }
  16. }

bean 标签解析及注册

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition

  1. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
  2. BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  3. if (bdHolder != null) {
  4. // 解析自定义标签
  5. bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
  6. try {
  7. // Register the final decorated instance.
  8. // 注册
  9. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  10. } catch (BeanDefinitionStoreException ex) {
  11. getReaderContext().error("Failed to register bean definition with name '" +
  12. bdHolder.getBeanName() + "'", ele, ex);
  13. }
  14. // Send registration event.
  15. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
  16. }
  17. }

bean 标签是最常用,也是最复杂的一个标签。了解该标签如何实现,那么其他的标签就迎刃而解了。

  1. 首先由 BeanDefinitionParserDelegate 解析得到 BeanDefinitionHolder 实例,其拿到了例如 className、 id、alias 等属性;
  2. 如果有自定义的属性,需要进一步解析下;
  3. 解析完毕后利用 BeanDefinitionReaderUtils 进行 bean 的注册;
  4. 最后发出通告,告诉相关的监听器,bean 已经加载完毕。

    解析 BeanDefinition

    首先从第一步:BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 解析BeanDefinition 开始。(parseBeanDefinitionElement(ele, null); 方法第二个参数为 null 表示该结点没有父节点。)

    1. @Nullable
    2. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
    3. return parseBeanDefinitionElement(ele, null);
    4. }
    1. @Nullable
    2. public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    3. // bean id
    4. String id = ele.getAttribute(ID_ATTRIBUTE);
    5. // bean 名称
    6. String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    7. // bean 的别名
    8. List<String> aliases = new ArrayList<>();
    9. // name可能有多个,通过,;或者空格隔开的
    10. if (StringUtils.hasLength(nameAttr)) {
    11. String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
    12. aliases.addAll(Arrays.asList(nameArr));
    13. }
    14. // 判断下没有ID就拿第一个名称作为ID
    15. String beanName = id;
    16. if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
    17. beanName = aliases.remove(0);
    18. if (logger.isTraceEnabled()) {
    19. logger.trace("No XML 'id' specified - using '" + beanName +
    20. "' as bean name and " + aliases + " as aliases");
    21. }
    22. }
    23. // 判断bean的名称和别名是否重复
    24. if (containingBean == null) {
    25. checkNameUniqueness(beanName, aliases, ele);
    26. }
    27. // 解析 bean 元素到 BeanDefinition
    28. AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    29. if (beanDefinition != null) {
    30. if (!StringUtils.hasText(beanName)) {
    31. try {
    32. if (containingBean != null) {
    33. // 根据Spring的命名规则给bean设置一个名字
    34. beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
    35. } else {
    36. beanName = this.readerContext.generateBeanName(beanDefinition);
    37. // Register an alias for the plain bean class name, if still possible,
    38. // if the generator returned the class name plus a suffix.
    39. // This is expected for Spring 1.2/2.0 backwards compatibility.
    40. String beanClassName = beanDefinition.getBeanClassName();
    41. if (beanClassName != null
    42. && beanName.startsWith(beanClassName)
    43. && beanName.length() > beanClassName.length()
    44. && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
    45. aliases.add(beanClassName);
    46. }
    47. }
    48. if (logger.isTraceEnabled()) {
    49. logger.trace("Neither XML 'id' nor 'name' specified - " +
    50. "using generated bean name [" + beanName + "]");
    51. }
    52. } catch (Exception ex) {
    53. error(ex.getMessage(), ele);
    54. return null;
    55. }
    56. }
    57. String[] aliasesArray = StringUtils.toStringArray(aliases);
    58. return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    59. }
    60. return null;
    61. }

整体处理过程:

  1. 依次得到结点的 id、name、alias;
  2. 进一步解析其他属性到 GenericBeanDefinition 中;
  3. 如果 bean 还没有 beanName 那么就根据默认规则生成一个名字;
  4. 最后将解析得到信息封装到 BeanDefinitionHolder 对象中。

那么接下来需要看第2步中,如何进一步解析的?

  1. public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {
  2. // 缓存bean
  3. this.parseState.push(new BeanEntry(beanName));
  4. // 解析class属性
  5. String className = null;
  6. if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
  7. className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
  8. }
  9. // 解析parent属性
  10. String parent = null;
  11. if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
  12. parent = ele.getAttribute(PARENT_ATTRIBUTE);
  13. }
  14. try {
  15. // 创建 GenericBeanDefinition 用来装载属性
  16. AbstractBeanDefinition bd = createBeanDefinition(className, parent);
  17. // 解析各种属性
  18. parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
  19. bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
  20. // 解析元数据
  21. parseMetaElements(ele, bd);
  22. // 解析 lookup-method 属性
  23. parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
  24. // 解析 replaced-method 属性
  25. parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
  26. // 解析构造函数
  27. parseConstructorArgElements(ele, bd);
  28. // 解析property子元素
  29. parsePropertyElements(ele, bd);
  30. // 解析qualifier子元素
  31. parseQualifierElements(ele, bd);
  32. bd.setResource(this.readerContext.getResource());
  33. bd.setSource(extractSource(ele));
  34. return bd;
  35. } catch (ClassNotFoundException ex) {
  36. error("Bean class [" + className + "] not found", ele, ex);
  37. } catch (NoClassDefFoundError err) {
  38. error("Class that bean class [" + className + "] depends on not found", ele, err);
  39. } catch (Throwable ex) {
  40. error("Unexpected failure during bean definition parsing", ele, ex);
  41. } finally {
  42. this.parseState.pop();
  43. }
  44. return null;
  45. }

可以看出,依次解析了class 属性、parent 属性、其他常用属性、元数据、构造器、property子元素、qualifier子元素,那么基本所有的标签都解析完毕了。那么接下来依次来看是如何解析的。

新建属性容器 BeanDefinition

解析的时候创 GenericBeanDefinition 来装载相关的属性,还有其他的实现类如下:
image.png
BeanDefinition 是一个接口,在 Spring 中存在三种实现:RootBeanDefinition、 ChildBeanDefinition 以及 GenericBeanDefinition。三种实现均继承了 AbstractBeanDefiniton,其中 BeanDefinition 是配置文件 元素标签在容器中的内部表示形式,他们的属性都是一一对应的(包含:class、scope、lazy-init 等属性,AbstractBeanDefiniton 中 beanClass、scope、lazyInit 与其对应)。
创建 GenericBeanDefinition 方法是委托 BeanDefinitionReaderUtils#createBeanDefinition 来实现。

  1. protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
  2. throws ClassNotFoundException {
  3. return BeanDefinitionReaderUtils.createBeanDefinition(parentName, className
  4. , this.readerContext.getBeanClassLoader());
  5. }
  1. public static AbstractBeanDefinition createBeanDefinition(
  2. @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader)
  3. throws ClassNotFoundException {
  4. GenericBeanDefinition bd = new GenericBeanDefinition();
  5. bd.setParentName(parentName);
  6. if (className != null) {
  7. if (classLoader != null) {
  8. bd.setBeanClass(ClassUtils.forName(className, classLoader));
  9. }
  10. else {
  11. bd.setBeanClassName(className);
  12. }
  13. }
  14. return bd;
  15. }

解析各种属性

org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseBeanDefinitionAttributes

  1. public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
  2. @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
  3. // 解析 scope 属性
  4. if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
  5. error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
  6. } else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
  7. bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
  8. } else if (containingBean != null) {
  9. // Take default from containing bean in case of an inner bean definition.
  10. bd.setScope(containingBean.getScope());
  11. }
  12. // 解析 abstract 属性
  13. if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
  14. bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
  15. }
  16. // 解析 lazy-init 属性
  17. String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
  18. if (isDefaultValue(lazyInit)) {
  19. lazyInit = this.defaults.getLazyInit();
  20. }
  21. bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
  22. // 解析 autowire 属性
  23. String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
  24. bd.setAutowireMode(getAutowireMode(autowire));
  25. // 解析 depends-on 属性
  26. if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
  27. String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
  28. bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
  29. }
  30. // 解析 autowire-candidate 属性
  31. String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
  32. if (isDefaultValue(autowireCandidate)) {
  33. String candidatePattern = this.defaults.getAutowireCandidates();
  34. if (candidatePattern != null) {
  35. String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
  36. bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
  37. }
  38. } else {
  39. bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
  40. }
  41. // 解析 primary 属性
  42. if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
  43. bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
  44. }
  45. // 解析 init-method 属性
  46. if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
  47. String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
  48. bd.setInitMethodName(initMethodName);
  49. } else if (this.defaults.getInitMethod() != null) {
  50. bd.setInitMethodName(this.defaults.getInitMethod());
  51. bd.setEnforceInitMethod(false);
  52. }
  53. // 解析 destroy-method 属性
  54. if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
  55. String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
  56. bd.setDestroyMethodName(destroyMethodName);
  57. } else if (this.defaults.getDestroyMethod() != null) {
  58. bd.setDestroyMethodName(this.defaults.getDestroyMethod());
  59. bd.setEnforceDestroyMethod(false);
  60. }
  61. // 解析 factory-method 属性
  62. if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
  63. bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
  64. }
  65. // 解析 factory-bean 属性
  66. if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
  67. bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
  68. }
  69. return bd;
  70. }

这里解析了bean上所有能出现的属性。

解析 meta 标签

  1. <bean id"aa" class="cn.xxx.Abean">
  2. <meta key="mykey" value= "hello" />
  3. </bean>

这是一个额外的声明,当需要使用里的信息的时候可以通过 BeanDefinition 的 getAttribute(key)方法进行获取。但它并不会注册进bean的属性容器中,只是在解析的时候可以使用它。
解析方法如下:

  1. public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
  2. NodeList nl = ele.getChildNodes();
  3. for (int i = 0; i < nl.getLength(); i++) {
  4. Node node = nl.item(i);
  5. if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {
  6. Element metaElement = (Element) node;
  7. String key = metaElement.getAttribute(KEY_ATTRIBUTE);
  8. String value = metaElement.getAttribute(VALUE_ATTRIBUTE);
  9. BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);
  10. attribute.setSource(extractSource(metaElement));
  11. attributeAccessor.addMetadataAttribute(attribute);
  12. }
  13. }
  14. }

解析 lookup-method

获取器注入,它并非常用。如下代码:

  1. public abstract class GetBeanTest
  2. public void showMe(){
  3. this.getBean().showMe ()
  4. }
  5. public abstract User getBean ()
  6. }
  1. <bean id="getBeanTest"class="test.lookup.app.GetBeanTest">
  2. <lookup-method name="getBean" bean="teacher"/>
  3. </bean>

它的作用:动态的将 teacher 所代表的 bean 作为 getBean 的返回值。
其解析方法:

  1. public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
  2. NodeList nl = beanEle.getChildNodes();
  3. for (int i = 0; i < nl.getLength(); i++) {
  4. Node node = nl.item(i);
  5. if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
  6. Element ele = (Element) node;
  7. String methodName = ele.getAttribute(NAME_ATTRIBUTE);
  8. String beanRef = ele.getAttribute(BEAN_ELEMENT);
  9. LookupOverride override = new LookupOverride(methodName, beanRef);
  10. override.setSource(extractSource(ele));
  11. overrides.addOverride(override);
  12. }
  13. }
  14. }

解析 replaced-method

方法替换:可以在运行时用新的方法替换现有的方法。与之前的 look-up 不同的是,replaced-method 不但可以动态地替换返回实体bean,而且还能动态地更改原有方法的逻辑。
首先测试使用
测试 bean,用于业务需要去替换OrgMethod的change方法。

  1. public class OrgMethod {
  2. public void change() {
  3. System.out.println("change!");
  4. }
  5. }
  6. public class OrgMethodReplace implements MethodReplacer {
  7. @Override
  8. public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
  9. System.out.println("change replaced!");
  10. return null;
  11. }
  12. }

xml 注册bean

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans
  3. xmlns="http://www.springframework.org/schema/beans"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
  6. <bean id="orgMethod" class="cn.lichenghao.replacedmethod.OrgMethod">
  7. <replaced-method name="change" replacer="orgMethodReplace"/>
  8. </bean>
  9. <bean id="orgMethodReplace" class="cn.lichenghao.replacedmethod.OrgMethodReplace"/>
  10. </beans>

测试

  1. @DisplayName("测试 replaced-method")
  2. @Test
  3. public void replaceMethodTest() {
  4. ClassPathXmlApplicationContext context
  5. = new ClassPathXmlApplicationContext("replacemethod.xml");
  6. OrgMethod orgMethod = context.getBean(OrgMethod.class);
  7. orgMethod.change();
  8. }
  9. // 结果
  10. change replaced!

看下解析的源码

  1. public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
  2. NodeList nl = beanEle.getChildNodes();
  3. for (int i = 0; i < nl.getLength(); i++) {
  4. Node node = nl.item(i);
  5. // 仅 bean 标签下的 replaced-method 有效
  6. if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
  7. Element replacedMethodEle = (Element) node;
  8. // 原始方法
  9. String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);
  10. // 替换的方法
  11. String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);
  12. // 记录到 ReplaceOverride 中
  13. ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
  14. // Look for arg-type match elements.
  15. // 记录参数
  16. List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
  17. for (Element argTypeEle : argTypeEles) {
  18. String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
  19. match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
  20. if (StringUtils.hasText(match)) {
  21. replaceOverride.addTypeIdentifier(match);
  22. }
  23. }
  24. replaceOverride.setSource(extractSource(replacedMethodEle));
  25. overrides.addOverride(replaceOverride);
  26. }
  27. }
  28. }

解析 constructor-arg 标签

构造函数是及其重要的,解析也是相对较复杂的。
比如:

  1. <bean id="user" class="cn.com.lichenghao.model.User">
  2. <constructor-arg index="0" value="张无忌"></constructor-arg>
  3. </bean>

对于构造函数的解析,交给了方法 parseConstructorArgElement((Element) node, bd);

  1. public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
  2. NodeList nl = beanEle.getChildNodes();
  3. for (int i = 0; i < nl.getLength(); i++) {
  4. Node node = nl.item(i);
  5. if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) {
  6. parseConstructorArgElement((Element) node, bd);
  7. }
  8. }
  9. }
  1. public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
  2. // 属性索引
  3. String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
  4. // 属性类型
  5. String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
  6. // 属性名称
  7. String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
  8. if (StringUtils.hasLength(indexAttr)) {
  9. try {
  10. int index = Integer.parseInt(indexAttr);
  11. if (index < 0) {
  12. error("'index' cannot be lower than 0", ele);
  13. } else {
  14. try {
  15. this.parseState.push(new ConstructorArgumentEntry(index));
  16. // 解析得到值
  17. Object value = parsePropertyValue(ele, bd, null);
  18. // 得到的值交给 valueHolder
  19. ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
  20. if (StringUtils.hasLength(typeAttr)) {
  21. valueHolder.setType(typeAttr);
  22. }
  23. if (StringUtils.hasLength(nameAttr)) {
  24. valueHolder.setName(nameAttr);
  25. }
  26. valueHolder.setSource(extractSource(ele));
  27. // 判断重复指定相同参数
  28. if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) {
  29. error("Ambiguous constructor-arg entries for index " + index, ele);
  30. } else {
  31. bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
  32. }
  33. } finally {
  34. this.parseState.pop();
  35. }
  36. }
  37. } catch (NumberFormatException ex) {
  38. error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele);
  39. }
  40. } else {
  41. // 如果没有 index
  42. try {
  43. this.parseState.push(new ConstructorArgumentEntry());
  44. Object value = parsePropertyValue(ele, bd, null);
  45. ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);
  46. if (StringUtils.hasLength(typeAttr)) {
  47. valueHolder.setType(typeAttr);
  48. }
  49. if (StringUtils.hasLength(nameAttr)) {
  50. valueHolder.setName(nameAttr);
  51. }
  52. valueHolder.setSource(extractSource(ele));
  53. bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
  54. } finally {
  55. this.parseState.pop();
  56. }
  57. }
  58. }

整体过程: 首先得到属性索引、类型、名称; 分支1:设置了 index 属性

  1. 解析 constructor-arg 子元素(比如:value);
  2. 结果放到 ConstructorArgumentValues.ValueHolder,并设置参数类型和名称;
  3. 判断是否重复设置了参数。
  4. ValueHolder 添加至当前 BeanDefinition 的 constructorArgumentValues 的 indexedArgumentValues 属性中。

分支2:没有设置 index 属性

  1. 解析 constructor-arg 子元素;
  2. 结果放到 ConstructorArgumentValues.ValueHolder,并设置参数类型和名称;
  3. ValueHolder 添加至当前 BeanDefinition 的 constructorArgumentValues 的 genericArgumentValues 属性中。

所以不管有无index操作都是一样的,但是最终存放的地方是不同的。

那么再看下是如何解析子属性的?

  1. @Nullable
  2. public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
  3. String elementName = (propertyName != null ?
  4. "<property> element for property '" + propertyName + "'" :
  5. "<constructor-arg> element");
  6. // Should only have one child element: ref, value, list, etc.
  7. // 每个标签只能有一个子标签类型
  8. NodeList nl = ele.getChildNodes();
  9. Element subElement = null;
  10. for (int i = 0; i < nl.getLength(); i++) {
  11. Node node = nl.item(i);
  12. // description 和 meta 不处理
  13. if (node instanceof Element
  14. && !nodeNameEquals(node, DESCRIPTION_ELEMENT)
  15. && !nodeNameEquals(node, META_ELEMENT)) {
  16. // Child element is what we're looking for.
  17. if (subElement != null) {
  18. error(elementName + " must not contain more than one sub-element", ele);
  19. } else {
  20. subElement = (Element) node;
  21. }
  22. }
  23. }
  24. // 不能同时用 ref 和 value 属性
  25. boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
  26. boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
  27. if ((hasRefAttribute && hasValueAttribute) ||
  28. ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
  29. error(elementName +
  30. " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
  31. }
  32. if (hasRefAttribute) {
  33. // ref属性的处理,使用RuntimeBeanReference封装对应的ref名称
  34. String refName = ele.getAttribute(REF_ATTRIBUTE);
  35. if (!StringUtils.hasText(refName)) {
  36. error(elementName + " contains empty 'ref' attribute", ele);
  37. }
  38. RuntimeBeanReference ref = new RuntimeBeanReference(refName);
  39. ref.setSource(extractSource(ele));
  40. return ref;
  41. } else if (hasValueAttribute) {
  42. // value属性的处理,使用TypedstringValue封装
  43. TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
  44. valueHolder.setSource(extractSource(ele));
  45. return valueHolder;
  46. } else if (subElement != null) {
  47. // 解析子元素
  48. return parsePropertySubElement(subElement, bd);
  49. } else {
  50. // Neither child element nor "ref" or "value" attribute found.
  51. // 既没有ref也没有value也没有子元素
  52. error(elementName + " must specify a ref or value", ele);
  53. return null;
  54. }
  55. }
  1. 首先忽略子标签 description、meta;
  2. 提取 constructor-arg 上的 ref 和 value 属性,为了保证以下情况不可以发生:
    1. 既有 ref 属性又有 value 属性。只能有其中一个!
    2. 有 ref 或者 value ,还有子元素。已经指定了,不需要子元素指定了!
  3. ref 属性使用 RuntimeBeanReference 封装。
  4. value 属性通过 TypedStringValue 处理;
  5. 解析子元素:
    1. <constructor-arg>
    2. <map>
    3. <entry key="key"value="value"/>
    4. </map>
    5. </constructor-arg>

对于子元素的解析,交给方法 parsePropertySubElement

  1. @Nullable
  2. public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
  3. return parsePropertySubElement(ele, bd, null);
  4. }
  1. @Nullable
  2. public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
  3. if (!isDefaultNamespace(ele)) {
  4. return parseNestedCustomElement(ele, bd);
  5. } else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
  6. BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
  7. if (nestedBd != null) {
  8. nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
  9. }
  10. return nestedBd;
  11. } else if (nodeNameEquals(ele, REF_ELEMENT)) {
  12. // A generic reference to any name of any bean.
  13. String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
  14. boolean toParent = false;
  15. if (!StringUtils.hasLength(refName)) {
  16. // A reference to the id of another bean in a parent context.
  17. refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
  18. toParent = true;
  19. if (!StringUtils.hasLength(refName)) {
  20. error("'bean' or 'parent' is required for <ref> element", ele);
  21. return null;
  22. }
  23. }
  24. if (!StringUtils.hasText(refName)) {
  25. error("<ref> element contains empty target attribute", ele);
  26. return null;
  27. }
  28. RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
  29. ref.setSource(extractSource(ele));
  30. return ref;
  31. } else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
  32. return parseIdRefElement(ele);
  33. } else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
  34. return parseValueElement(ele, defaultValueType);
  35. } else if (nodeNameEquals(ele, NULL_ELEMENT)) {
  36. // It's a distinguished null value. Let's wrap it in a TypedStringValue
  37. // object in order to preserve the source location.
  38. TypedStringValue nullHolder = new TypedStringValue(null);
  39. nullHolder.setSource(extractSource(ele));
  40. return nullHolder;
  41. } else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
  42. return parseArrayElement(ele, bd);
  43. } else if (nodeNameEquals(ele, LIST_ELEMENT)) {
  44. return parseListElement(ele, bd);
  45. } else if (nodeNameEquals(ele, SET_ELEMENT)) {
  46. return parseSetElement(ele, bd);
  47. } else if (nodeNameEquals(ele, MAP_ELEMENT)) {
  48. return parseMapElement(ele, bd);
  49. } else if (nodeNameEquals(ele, PROPS_ELEMENT)) {
  50. return parsePropsElement(ele);
  51. } else {
  52. error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
  53. return null;
  54. }
  55. }

解析 property 标签

完成对 property 属性的解析

  1. <bean id="user" class="cn.com.lichenghao.model.User">
  2. <property name="userName" value="张无忌"></property>
  3. </bean>
  4. 或者
  5. <bean id="user" class="cn.com.lichenghao.model.User">
  6. <property name="strings">
  7. <array>
  8. <value>1</value>
  9. <value>2</value>
  10. </array>
  11. </property>
  12. </bean>

同样是找到所有的 property 标签,交给 parsePropertyElement 方法去解析

  1. public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
  2. NodeList nl = beanEle.getChildNodes();
  3. for (int i = 0; i < nl.getLength(); i++) {
  4. Node node = nl.item(i);
  5. if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
  6. parsePropertyElement((Element) node, bd);
  7. }
  8. }
  9. }
  1. public void parsePropertyElement(Element ele, BeanDefinition bd) {
  2. String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
  3. if (!StringUtils.hasLength(propertyName)) {
  4. error("Tag 'property' must have a 'name' attribute", ele);
  5. return;
  6. }
  7. this.parseState.push(new PropertyEntry(propertyName));
  8. try {
  9. if (bd.getPropertyValues().contains(propertyName)) {
  10. error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
  11. return;
  12. }
  13. Object val = parsePropertyValue(ele, bd, propertyName);
  14. PropertyValue pv = new PropertyValue(propertyName, val);
  15. parseMetaElements(ele, pv);
  16. pv.setSource(extractSource(ele));
  17. bd.getPropertyValues().addPropertyValue(pv);
  18. } finally {
  19. this.parseState.pop();
  20. }
  21. }

处理的套路和上边其他标签处理方式类似:

  1. 判断名称是否为空;
  2. 判断属性重复;
  3. 解析结果交给 PropertyValue;
  4. 设置到 BeanDefinition中的 propertyValues属性中。

    解析 qualifier 标签

    完成对 qulifier 标签的解析,这个标签的作用和使用注解的作用一样,通常我们更多使用的是注解。

    1. <bean id="myTestBean"class="bean.MyTestBean">
    2. <qualifier type="org.Springframework.beans.factory.annotation.Qualifier" value="qf"/>
    3. </bean>

    找到这个标签,挨个处理。

    1. public void parseQualifierElements(Element beanEle, AbstractBeanDefinition bd) {
    2. NodeList nl = beanEle.getChildNodes();
    3. for (int i = 0; i < nl.getLength(); i++) {
    4. Node node = nl.item(i);
    5. if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ELEMENT)) {
    6. parseQualifierElement((Element) node, bd);
    7. }
    8. }
    9. }
    1. public void parseQualifierElement(Element ele, AbstractBeanDefinition bd) {
    2. String typeName = ele.getAttribute(TYPE_ATTRIBUTE);
    3. if (!StringUtils.hasLength(typeName)) {
    4. error("Tag 'qualifier' must have a 'type' attribute", ele);
    5. return;
    6. }
    7. this.parseState.push(new QualifierEntry(typeName));
    8. try {
    9. AutowireCandidateQualifier qualifier = new AutowireCandidateQualifier(typeName);
    10. qualifier.setSource(extractSource(ele));
    11. String value = ele.getAttribute(VALUE_ATTRIBUTE);
    12. if (StringUtils.hasLength(value)) {
    13. qualifier.setAttribute(AutowireCandidateQualifier.VALUE_KEY, value);
    14. }
    15. NodeList nl = ele.getChildNodes();
    16. for (int i = 0; i < nl.getLength(); i++) {
    17. Node node = nl.item(i);
    18. if (isCandidateElement(node) && nodeNameEquals(node, QUALIFIER_ATTRIBUTE_ELEMENT)) {
    19. Element attributeEle = (Element) node;
    20. String attributeName = attributeEle.getAttribute(KEY_ATTRIBUTE);
    21. String attributeValue = attributeEle.getAttribute(VALUE_ATTRIBUTE);
    22. if (StringUtils.hasLength(attributeName) && StringUtils.hasLength(attributeValue)) {
    23. BeanMetadataAttribute attribute = new BeanMetadataAttribute(attributeName, attributeValue);
    24. attribute.setSource(extractSource(attributeEle));
    25. qualifier.addMetadataAttribute(attribute);
    26. } else {
    27. error("Qualifier 'attribute' tag must have a 'name' and 'value'", attributeEle);
    28. return;
    29. }
    30. }
    31. }
    32. bd.addQualifier(qualifier);
    33. } finally {
    34. this.parseState.pop();
    35. }
    36. }

    解析结果

    完成了上面各种标签的解析,也就把 xml 配置的 bean 转变成了 GenericBeanDefinition。
    它是 AbstractBeanDefinition 的子类,大部分属性保存在这里。
    到这里完成了 bean 标签解析及注册的第1步:

  5. 首先由 BeanDefinitionParserDelegate 解析得到 BeanDefinitionHolder 实例,其拿到了例如 className、 id、alias 等属性;

  6. 如果有自定义的属性,需要进一步解析下;
  7. 解析完毕后利用 BeanDefinitionReaderUtils 进行 bean 的注册;
  8. 最后发出通告,告诉相关的监听器,bean 已经加载完毕。

    解析默认标签中的自定义标签

    完成了上一步后,接下来看如何解析默认标签中的自定义标签?
    首先来看下,使用自定义标签的配置是什么样的,如下所示:
    1. <bean id="test" class="test.Myclass">
    2. <mybean:user username="aaa"/>
    3. </bean>

这里完成了 bean 标签解析及注册的前2步:

  1. 首先由 BeanDefinitionParserDelegate 解析得到 BeanDefinitionHolder 实例,其拿到了例如 className、 id、alias 等属性;
  2. 如果有自定义的属性,需要进一步解析下;
  3. 解析完毕后利用 BeanDefinitionReaderUtils 进行 bean 的注册;
  4. 最后发出通告,告诉相关的监听器,bean 已经加载完毕。

    注册 BeanDefinition

    解析完毕后,开始注册 BeanDefinition。可以看出来,分别通过名称、别名来注册。

    1. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

    org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition

    1. public static void registerBeanDefinition(
    2. BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
    3. throws BeanDefinitionStoreException {
    4. // Register bean definition under primary name.
    5. String beanName = definitionHolder.getBeanName();
    6. registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    7. // Register aliases for bean name, if any.
    8. String[] aliases = definitionHolder.getAliases();
    9. if (aliases != null) {
    10. for (String alias : aliases) {
    11. registry.registerAlias(beanName, alias);
    12. }
    13. }
    14. }

    通过 beanName 注册

    首先获得 beanName ,然后开始注册

    1. @Override
    2. public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    3. throws BeanDefinitionStoreException {
    4. // 非空校验
    5. Assert.hasText(beanName, "Bean name must not be empty");
    6. Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    7. // BeanDefinition 有效性校验
    8. if (beanDefinition instanceof AbstractBeanDefinition) {
    9. try {
    10. ((AbstractBeanDefinition) beanDefinition).validate();
    11. } catch (BeanDefinitionValidationException ex) {
    12. throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    13. "Validation of bean definition failed", ex);
    14. }
    15. }
    16. // 根据bean名称从缓存中获取bean定义
    17. BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    18. if (existingDefinition != null) {
    19. // 如果缓存中已经有改bean,并且定义不允许覆盖,那么直接抛异常
    20. if (!isAllowBeanDefinitionOverriding()) {
    21. throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
    22. } else if (existingDefinition.getRole() < beanDefinition.getRole()) {
    23. // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
    24. if (logger.isInfoEnabled()) {
    25. logger.info("Overriding user-defined bean definition for bean '" + beanName +
    26. "' with a framework-generated bean definition: replacing [" +
    27. existingDefinition + "] with [" + beanDefinition + "]");
    28. }
    29. } else if (!beanDefinition.equals(existingDefinition)) {
    30. if (logger.isDebugEnabled()) {
    31. logger.debug("Overriding bean definition for bean '" + beanName +
    32. "' with a different definition: replacing [" + existingDefinition +
    33. "] with [" + beanDefinition + "]");
    34. }
    35. } else {
    36. if (logger.isTraceEnabled()) {
    37. logger.trace("Overriding bean definition for bean '" + beanName +
    38. "' with an equivalent definition: replacing [" + existingDefinition +
    39. "] with [" + beanDefinition + "]");
    40. }
    41. }
    42. // 覆盖老的bean定义
    43. this.beanDefinitionMap.put(beanName, beanDefinition);
    44. } else {
    45. if (hasBeanCreationStarted()) {
    46. // 已有bean被标记为创建中
    47. // Cannot modify startup-time collection elements anymore (for stable iteration)
    48. synchronized (this.beanDefinitionMap) {
    49. this.beanDefinitionMap.put(beanName, beanDefinition);
    50. List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
    51. updatedDefinitions.addAll(this.beanDefinitionNames);
    52. updatedDefinitions.add(beanName);
    53. this.beanDefinitionNames = updatedDefinitions;
    54. removeManualSingletonName(beanName);
    55. }
    56. } else {
    57. // 还都在解析阶段,没有创建bean,直接加到缓存中
    58. // Still in startup registration phase
    59. this.beanDefinitionMap.put(beanName, beanDefinition);
    60. this.beanDefinitionNames.add(beanName);
    61. removeManualSingletonName(beanName);
    62. }
    63. this.frozenBeanDefinitionNames = null;
    64. }
    65. if (existingDefinition != null || containsSingleton(beanName)) {
    66. resetBeanDefinition(beanName);
    67. } else if (isConfigurationFrozen()) {
    68. clearByTypeCache();
    69. }
    70. }

过程:

  1. 首先是参数有效性,不能为空或者空字符串;
  2. BeanDefinition 的最后一次校验,主要为了 methodoverrides 属性,校验 methodoverrides 是否与工厂方法并存或者methodoverrides 对应的方法有没有;
  3. 对 bean 已经注册的处理,如果不能覆盖的话,抛异常,否则直接覆盖;
  4. 对于bean 没有被注册过的处理:
    1. 如果有其他 bean 在创建中了,那么加锁处理缓存map,涉及到其他线程来访问该 map;
    2. 如果没有 bean 在创建中,表明还在解析阶段,直接加入 map 中,因为还涉及不到并发访问;
  5. 加入 map 缓存;
  6. 重置,清除与之相关的缓存;

    通过 aliases 注册

    有了根据名称注册后,那么根据别名注册就容易理解了。最终添加到 aliasMap 缓存中。
    1. @Override
    2. public void registerAlias(String name, String alias) {
    3. Assert.hasText(name, "'name' must not be empty");
    4. Assert.hasText(alias, "'alias' must not be empty");
    5. synchronized (this.aliasMap) {
    6. if (alias.equals(name)) {
    7. this.aliasMap.remove(alias);
    8. if (logger.isDebugEnabled()) {
    9. logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
    10. }
    11. }
    12. else {
    13. String registeredName = this.aliasMap.get(alias);
    14. if (registeredName != null) {
    15. if (registeredName.equals(name)) {
    16. // An existing alias - no need to re-register
    17. return;
    18. }
    19. if (!allowAliasOverriding()) {
    20. throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
    21. name + "': It is already registered for name '" + registeredName + "'.");
    22. }
    23. if (logger.isDebugEnabled()) {
    24. logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
    25. registeredName + "' with new target name '" + name + "'");
    26. }
    27. }
    28. checkForAliasCircle(name, alias);
    29. this.aliasMap.put(alias, name);
    30. if (logger.isTraceEnabled()) {
    31. logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
    32. }
    33. }
    34. }
    35. }

注册完毕

将解析好的bean定义注册完毕后,也就完成了 bean 标签解析及注册的前3步:

  1. 首先由 BeanDefinitionParserDelegate 解析得到 BeanDefinitionHolder 实例,其拿到了例如 className、 id、alias 等属性;
  2. 如果有自定义的属性,需要进一步解析下;
  3. 解析完毕后利用 BeanDefinitionReaderUtils 进行 bean 的注册;
  4. 最后发出通告,告诉相关的监听器,bean 已经加载完毕。

    通知监听器注册完成

    1. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    这里并没有做什么特殊的处理,开发人员可以注册监听器实现自定义的处理。
    到此为止,bean 的解析及注册就完成了!

    alias 标签的解析

    首先来看 alias 标签的使用方式。 ```xml
学习了bean标签的解析,再看这个 alias 标签的解析就容易的多了。 1. 验证标签属性空值; 2. 注册别名; 3. 发送通知;java 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)); } } <a name="aagpF"></a> ## import 标签的解析 如果想要分模块的话,可以采用 import 标签来导入配置xml 解析过程也很容易理解:java protected void importBeanDefinitionResource(Element ele) { // 配置文件地址 String location = ele.getAttribute(RESOURCE_ATTRIBUTE); if (!StringUtils.hasText(location)) { getReaderContext().error(“Resource location must not be empty”, ele); return; } // 解析系统环境变量 // Resolve system properties: e.g. “${user.dir}” location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location); Set actualResources = new LinkedHashSet<>(4); // 判断是绝对路径还是相对路径 // Discover whether the location is an absolute or relative URI boolean absoluteLocation = false; try { absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute(); } catch (URISyntaxException ex) { // cannot convert to an URI, considering the location relative // unless it is the well-known Spring prefix “classpath*:” } // Absolute or relative? if (absoluteLocation) { try { // 绝对路径的话,直接去解析 int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources); if (logger.isTraceEnabled()) { logger.trace(“Imported “ + importCount + “ bean definitions from URL location [“ + location + “]”); } } catch (BeanDefinitionStoreException ex) { getReaderContext().error( “Failed to import bean definitions from URL location [“ + location + “]”, ele, ex); } } else { // No URL -> considering resource location as relative to the current file. // 相对路径的话,计算出绝对路径 try { int importCount; Resource relativeResource = getReaderContext().getResource().createRelative(location); if (relativeResource.exists()) { importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource); actualResources.add(relativeResource); } else { String baseLocation = getReaderContext().getResource().getURL().toString(); importCount = getReaderContext().getReader().loadBeanDefinitions( StringUtils.applyRelativePath(baseLocation, location), actualResources); } if (logger.isTraceEnabled()) { logger.trace(“Imported “ + importCount + “ bean definitions from relative location [“ + location + “]”); } } catch (IOException ex) { getReaderContext().error(“Failed to resolve current resource location”, ele, ex); } catch (BeanDefinitionStoreException ex) { getReaderContext().error( “Failed to import bean definitions from relative location [“ + location + “]”, ele, ex); } } Resource[] actResArray = actualResources.toArray(new Resource[0]); getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele)); } 解析过程: 1. 首先得到导入资源的地址; 2. 然后解析地址中的系统环境变量,比如:${user.dir} ; 3. 判断是绝对路径还是相对路径; 4. 如果是绝对路径,那么就调用解析方法去递归解析; 5. 如果是相对路径,那么把路径转换成绝对路径去解析 ; 6. 解析完毕后,发送通知。 <a name="WHJBR"></a> ## beans 标签的解析 很明显,beans 标签的解析,就是递归解析 bean 标签🏷。xml <?xml version=”1.0” encoding=”UTF-8”?>

```