2、Spring源码-默认标签的解析

·  阅读 21
2、Spring源码-默认标签的解析

更多文章请观看:www.shangsw.com

一 默认标签的解析

1、Spring容器的基本实现 中提到了Spring中的标签包括默认标签和自定义标签两种,而两种标签的用法以及解析方式存在很大的不同。

默认标签的解析是在parseDefaultElement() 函数中进行的,如下:

/**
* 解析默认的标签元素
* @param ele 元素
* @param delegate BeanDefinitionParserDelegate
*/
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)) {//解析beans标签(递归)

      doRegisterBeanDefinitions(ele);
   }
}
复制代码

代码很清晰,分别对4中不同标签(import、alias、bean和beans)做了不同的处理。

1.1 bean标签的解析及注册

我们先从bean标签的解析开始,因为bean标签是最复杂也是最重要的。进入函数processBeanDefinition(ele, delegate)

/**
* Bean定义的解析处理
* @param ele bean标签元素
* @param delegate Bean的解析代理
*/
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   //解析Bean标签的元素 Start   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   //解析Bean标签的元素 End

   if (bdHolder != null) {
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);//对beanDefinition进行装饰(自定义标签), 如果需要的话
      try {
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());//注册Bean实例
      } catch (BeanDefinitionStoreException ex) {
         getReaderContext().error("Failed to register bean definition with name '" +
               bdHolder.getBeanName() + "'", ele, ex);
      }

      //通知监听器解析及注册完成(Spring没有做任何逻辑, 可以通过程序扩展)
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}
复制代码

大致逻辑如下:

  1. 首先委托BeanDefinitionDelegate类的parseBeanDefinitionElement方法进行元素解析,返回BeanDefinitionHolder类型的实例bgHolder,经过这个方法后,bgHolder实例已经包含了配置文件中的各种属性了,如class、name、id、alias之类的属性
  2. 当返回gbHolder不为空的情况若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析
  3. 解析完成后,需要对解析后的gbHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法

1.1.1 解析BeanDefinition

首先进入delegate.parseBeanDefinitionElement(ele)方法:

/**
* 解析Bean元素
* @param ele            Bean元素
* @return BeanDefinitionHolder
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
   return parseBeanDefinitionElement(ele, null);
}
复制代码
/**
* 解析Bean元素
* @param ele            Bean元素
* @param containingBean Bean容器
* @return BeanDefinitionHolder
*/
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
   String id = ele.getAttribute(ID_ATTRIBUTE);//解析ID属性
   String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);//解析name属性

   //分隔name属性(以,;分隔) Start
   List<String> aliases = new ArrayList<>();
   if (StringUtils.hasLength(nameAttr)) {
      String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
      aliases.addAll(Arrays.asList(nameArr));
   }
   //分隔name属性(以,;分隔) End

   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);
   }

   //解析其他所有的属性并统一封装到GenericBeanDefinition(AbstractBeanDefinition子类)的实例中 Start
   AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
   //解析其他所有的属性并统一封装到GenericBeanDefinition(AbstractBeanDefinition子类)的实例中 End

   if (beanDefinition != null) {
      if (!StringUtils.hasText(beanName)) {//beanName不为空
         try {
            if (containingBean != null) {//如果不存在beanName那么根据Spring中提供的命名规则为当前bean生成对应的beanName
               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;
}
复制代码

完成的主要工作如下:

  1. 提取元素中的id以及name属性
  2. 进一步解析其他所有属性并统一封装至GenericBeanDefinition类型的实例中
  3. 如果检测到bean没有指定beanName,那么使用默认规则为此Bean生成beanName
  4. 将获取到的信息封装到BeanDefinitionHolder的实例中

我们进一步查看解析其他属性parseBeanDefinitionElement(ele, beanName, containingBean) 方法:

/**
* 解析bean的其他所有属性并封装成GenericBeanDefinition
* @param ele            bean标签的元素
* @param beanName       beanName
* @param containingBean BeanDefinition
* @return GenericBeanDefinition。当解析错误的时候可能为空
*/
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {
   this.parseState.push(new BeanEntry(beanName));//使用ArrayDeque存储beanName的状态

   //解析class属性 Start
   String className = null;//class属性的值
   if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
      className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
   }
   //解析class属性 End

   //解析parent属性 Start
   String parent = null;
   if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
      parent = ele.getAttribute(PARENT_ATTRIBUTE);
   }
   //解析parent属性 End

   try {
      //用于创建承载属性AbstractBeanDefinition类型的GenericBeanDefinition Start
      AbstractBeanDefinition bd = createBeanDefinition(className, parent);//只有parent和className或className对应的对象实例
      //用于创建承载属性AbstractBeanDefinition类型的GenericBeanDefinition End

      //硬编码解析bean的各种属性 Start
      parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
      //硬编码解析bean的各种属性 End

      //提取description Start
      bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
      //提取description End

      parseMetaElements(ele, bd);//解析元数据
      parseLookupOverrideSubElements(ele, bd.getMethodOverrides());//解析lookup-method属性
      parseReplacedMethodSubElements(ele, bd.getMethodOverrides());//解析replaced-method属性

      parseConstructorArgElements(ele, bd);//解析构造函数参数
      parsePropertyElements(ele, bd);//解析property子元素
      parseQualifierElements(ele, bd);//解析qulifier子元素
      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();//弹出beanName的状态
   }

   return null;
}
复制代码

1.1.1.1 创建用于属性承载的BeanDefinition

BeanDefinition是一个接口,其实现如下: Image.png

BeanDefinition在Spring中有三种实现:RootBeanDefinition、ChildBeanDefinition及GenericBeanDefinition。三种实现都继承了AbstractBeanDefinition,其中BeanDefinition是配置文件元素标签在容器中的内部表示形式。元素标签拥有class、scope、lazy-init等配置属性,BeanDefinition和中的属性是一一对应的。其中RootBeanDefinition是最常用的实现类,它对应一般性的元素标签,GenericBeanDefinition是自2.5版本以后新加入的bean文件配置属性定义类,是一站式服务类

在配置文件中可以定义父和子,父用RootBeanDefinition表示,而子用ChildBeanDefinition表示,而没有父的就使用RootBeanDefinition表示,AbstractBeanDefinition对两者共同的类信息进行抽象。

Spring通过BeanDefinition将配置文件中的配置信息转换为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中。Spring容器的BeanDefinitionRegistry就像是Spring配置信息的内存库,主要是以Map的形式保存,后续操作直接从BeanDefinitionRegistry中读取配置信息。

由此可知,要解析属性首先要创建用于承载属性的实例,也就是创建GenericBeanDefinition类型的实例,而代码createBeanDefinition(className, parent) 作用就是实现此功能:

/**
* 创建一个AbstractBeanDefinition, 使用其子类GenericBeanDefinition
* @param className  class名字
* @param parentName parent名字
* @return GenericBeanDefinition
* @throws ClassNotFoundException 类不存在异常
*/
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName)
      throws ClassNotFoundException {
   return BeanDefinitionReaderUtils.createBeanDefinition(
         parentName, className, this.readerContext.getBeanClassLoader());//创建
}
复制代码

BeanDefinitionReaderUtils#createBeanDefinition()代码如下:

/**
* 从给定的parentName和className创建一个新的GenericBeanDefinition
* 如果类加载器存在, 则从指定的类加载器创建。否则只是记录beanName
* @param parentName  parentName
* @param className   className
* @param classLoader 类加载器
* @return GenericBeanDefinition
* @throws ClassNotFoundException 类不存在异常
*/
public static AbstractBeanDefinition createBeanDefinition(
      @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {

   GenericBeanDefinition bd = new GenericBeanDefinition();
   bd.setParentName(parentName);
   if (className != null) {
      if (classLoader != null) {
         bd.setBeanClass(ClassUtils.forName(className, classLoader));
      } else {
         bd.setBeanClassName(className);
      }
   }
   return bd;
}
复制代码

1.1.1.2 解析各种属性

创建完bean信息的承载实例后,便可以进行beanxinxi的各种属性解析了,进入parseBeanDefinitionAttributes(ele, beanName, containingBean, db)方法:

/**
* 解析Bean的各种属性
* @param ele            给定的bean元素
* @param beanName       beanName
* @param containingBean bean容器
* @param bd             AbstractBeanDefinition
* @return AbstractBeanDefinition
*/
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {

   //解析scope属性 Start
   if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {//解析singleton属性, 该属性只是在老版本的1.x中使用, 在后续的版本中已经废弃, 错误日志
      error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
   } else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {//解析scope属性
      bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
   } else if (containingBean != null) {//解析scope属性
      //在嵌入BeanDefinition情况下且没有单独指定scope属性则使用父类默认的属性
      bd.setScope(containingBean.getScope());
   }
   //解析scope属性 End

   //解析abstract属性 Start
   if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {      bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
   }
   //解析abstract属性 End

   //解析lazy-init属性 Start
   String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
   if (isDefaultValue(lazyInit)) {
      lazyInit = this.defaults.getLazyInit();
   }
   bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
   //解析lazy-init属性 End

   //解析autowire属性 Start
   String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
   bd.setAutowireMode(getAutowireMode(autowire));
   //解析autowire属性 End

   //解析depends-on属性 Start
   if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
      String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
      bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
   }
   //解析depends-on属性 End

   //解析autowire-candidate属性 Start
   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));
   }
   //解析autowire-candidate属性 End

   //解析primary属性 Start
   if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
	bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
   }
   //解析primary属性 End

   //解析init-method属性 Start
   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);
   }
   //解析init-method属性 End

   //解析 destroy-method 属性 Start
   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);
   }
   //解析 destroy-method 属性 End

   //解析 factory-method 属性 Start
   if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
      bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
   }
   //解析 factory-method 属性 End

   //解析 factory-bean 属性 Start
   if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
      bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
   }
   //解析 factory-bean 属性 End

   return bd;
}
复制代码

这个方法里面没有业务逻辑,主要是解析bean的属性,然后存放到GenericBeanDefinition中。

1.1.1.3 解析子元素meta

在开始解析元数据之前,meta的属性如下:

<bean id="myTestBean" class="bean.MyTestBean">
    <meta key="testKey" value="testValue">
</bean>
复制代码

这段代码不会体现在MyTestBean的属性中,而是一个额外的声明,当需要使用里面的信息的时候可以通过BeanDefinition的getAttribute(key) 方法获取:

/**
* 解析子元素meta标签
* @param ele               Element
* @param attributeAccessor BeanMetadataAttributeAccessor
*/
public void parseMetaElements(Element ele, BeanMetadataAttributeAccessor attributeAccessor) {
   NodeList nl = ele.getChildNodes();
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      if (isCandidateElement(node) && nodeNameEquals(node, META_ELEMENT)) {//meta标签
         Element metaElement = (Element) node;
         String key = metaElement.getAttribute(KEY_ATTRIBUTE);//key
         String value = metaElement.getAttribute(VALUE_ATTRIBUTE);//value
         BeanMetadataAttribute attribute = new BeanMetadataAttribute(key, value);//使用key-value构造BeanMetadataAttribute
         attribute.setSource(extractSource(metaElement));
         attributeAccessor.addMetadataAttribute(attribute);//存储信息
      }
   }
}
复制代码

1.1.1.4 解析子元素lookup-method

子元素lookup-method似乎并不是很常用,通常我们称之为获取器注入。获取器注入是一种特殊的方法注入,它是把一个方法声明为返回某种类型的bean,但实际要返回的bean是在配置文件里面配置的。此方法可用在设计有些可插拔的功能上,我们看一个具体实例:

(1) 首先创建一个父类

public class User {
    public void showMe() {
        System.out.println("I am a user");
    }
}
复制代码

(2) 创建其子类并覆盖showMe方法

public class Teacher extends User {
    public void showMe() {
        System.out.println("I am a teacher");
    }
}
复制代码

(3) 创建调用方法

public abstract class GetBeanTest {
    public void showMe() {
        this.getBean().showMe();
    }
    public abstract User getBean();
}
复制代码

(3) 配置文件如下

<bean id="getBeanTest" class="test.GetBeanTest">
    <lookup-method name="getBean" bean="teacher"/>
</bean>
<bean id="teacher" class="test.Teacher"/>
复制代码

(4)测试方法如下

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("lookupTest.xml");
        GetBeanTest test = context.getBean(GetBeanTest.class);
        test.showMe();
    }
}
复制代码

最后运行的效果如下:

I am a teacher
复制代码

此时如果我想换一个实现,改变lookup-method的配置即可。

如下,是解析lookup-method的源码:

/**
* 解析lookup-method子元素
* @param beanEle   Element
* @param overrides 子元素
*/
public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) {
   NodeList nl = beanEle.getChildNodes();
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) {
         Element ele = (Element) node;
         String methodName = ele.getAttribute(NAME_ATTRIBUTE);//方法名
         String beanRef = ele.getAttribute(BEAN_ELEMENT);//bean名称
         LookupOverride override = new LookupOverride(methodName, beanRef);//构造成LookupOverride
         override.setSource(extractSource(ele));
         overrides.addOverride(override);//存储
      }
   }
}
复制代码

代码与parseMetaElements大同小异,最后通过使用LookupOverride类型的实体来进行数据承载并记录在AbstractBeanDefinition中的methodOverrides属性中

1.1.1.5 解析子元素replacedmethod

这个方法是针对bean中的replaced-method子元素的提取,功能是:可以在运行时用心的方法替换现有的方法。我们先看一个实例:

(1) 在changeMe中完成某个业务逻辑

public class TestChangeMethod {
    public void changeMe() {
        System.out.println("changeMe");
    }
}
复制代码

(2) 一段时间后需要改变原有逻辑

public class TestMethodReplacer implements MethodReplacer {
    @Override
    public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {
        System.out.println("我替换了原有的方法");
    }
}
复制代码

(3) 配置文件

<bean id="testChangeMethod" class="test.TestChangeMethod">
    <replaced-method name="changeMe" replacer="replacer"/>
</bean>

<bean id="replacer" class="test.TestMethodReplacer"/>
复制代码

(4) 测试如下

public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext(replaceMethodTest.xml);
    TestChangeMethod test = context.getBean(TestChangeMethod.class);
    test.changeMe();
}
复制代码

测试结果如下:

我替换了原有的方法
复制代码

知道这个标签的用途之后,我们再看其源码:

/**
* 解析replaced-method子元素的标签
* @param beanEle   Element
* @param overrides MethodOverrides
*/
public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) {
   NodeList nl = beanEle.getChildNodes();
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) {
         Element replacedMethodEle = (Element) node;
         String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE);//要替换的旧方法
         String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE);//对应的新的替换方法
         ReplaceOverride replaceOverride = new ReplaceOverride(name, callback);
         List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
         for (Element argTypeEle : argTypeEles) {
            //记录参数
            String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
            match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
            if (StringUtils.hasText(match)) {
               replaceOverride.addTypeIdentifier(match);
            }
         }
         replaceOverride.setSource(extractSource(replacedMethodEle));
         overrides.addOverride(replaceOverride);
      }
   }
}
复制代码

我们可以看到无论是look-up还是replaced-method都是构造了一个MethodOverride,并最终记录在AbstractBeanDefinition中的methodOverrides属性中

1.1.1.6 解析子元素constructor-arg

对构造函数的解析是非常常用的,同时也是非常复杂的。首先,我们看一个简单的例子:

<beans>
    <bean id="helloBean" class="test.HelloBean">
        <constructor-arg index="0">
            <value>张三</value>
        </constructor-arg>
        <constructor-arg index="1">
            <value>你好</value>
        </constructor-arg>
    </bean>
</beans>
复制代码

上面实现的功能是对HelloBean自动寻找对应的构造函数,并在初始化的时候将设置的参数传入进去。

对于constructor-arg子元素的解析,Spring是通过parseConstructorArgElements 函数来实现的,代码如下:

/**
* 解析constructor-arg子标签
* @param beanEle Element
* @param bd      BeanDefinition(GenericBeanDefinition)
*/
public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) {
   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)) {
         parseConstructorArgElement((Element) node, bd);//解析constructor-arg
      }
   }
}
复制代码

遍历所有子元素,提取constructor-arg,然后进行解析,解析的函数是parseConstructorArgElement中,代码如下:

/**
* 解析constructor-arg元素
* @param ele Element
* @param bd  BeanDefinition(GenericBeanDefinition)
*/
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
   String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);//index属性

   String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);//type属性

   String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);//name属性

   if (StringUtils.hasLength(indexAttr)) {//有index属性
      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));//保存状态
               Object value = parsePropertyValue(ele, bd, null);//解析ele对应的属性元素
               ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);//封装解析的属性元素
               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 {
		  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());//保存状态
         Object value = parsePropertyValue(ele, bd, null);//解析construct-arg的子元素
         ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value);//封装子元素
         if (StringUtils.hasLength(typeAttr)) {
            valueHolder.setType(typeAttr);
         }

         if (StringUtils.hasLength(nameAttr)) {
            valueHolder.setName(nameAttr);
         }
         valueHolder.setSource(extractSource(ele));

       bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
      } finally {
         this.parseState.pop();//弹出状态
      }
   }
}
复制代码

上面代码很多,但是逻辑并不复杂,首先是提取constructor-arg上必要的属性(index、type、name)。

  • 如果配置中指定了index属性,那么操作步骤如下:
    • 解析constructor-arg的子元素
    • 使用ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素
    • 将type、name和index属性一并封装在ConstructorArgumentValues.ValueHolder类型中并添加至当前beanDefinition的constructorArgumentValues的indexedArgumentValues属性中
  • 如果没有指定index属性,那么操作步骤如下:
  • 解析constructor-arg的子元素
  • 使用ConstructorArgumentValues.ValueHolder类型来封装解析出来的元素
  • 将type、name和index属性一并封装在ConstructorArgumentValues.ValueHolder类型中并添加至当前BeanDefinition的constructorArgumentValues的genericArgumentValues属性中

可以看到,对于是否制定index属性来说,Spring的处理流程是不同的,关键在于属性信息被保存的位置。

整个流程了解后,我们进入解析构造函数配置中子元素的过程,进入parsePropertyValue()函数:

/**
* 解析construct-arg标签的元素。并封装成相关的类
* @param ele          construct-arg
* @param bd           BeanDefinition(GenericBeanDefinition)
* @param propertyName 属性名(null)
* @return 如果是ref属性则返回RuntimeBeanRefrence; 如果是value属性则返回TypedStringValue; 解析错误的情况下返回null
*/
@Nullable
public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
   String elementName = (propertyName != null ?
         "<property> element for property '" + propertyName + "'" :
         "<constructor-arg> element");

   NodeList nl = ele.getChildNodes();//一个属性只能对应一种类型: ref, value, list等
   Element subElement = null;
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) && !nodeNameEquals(node, META_ELEMENT)) {//对应description或者meta不处理

         if (subElement != null) {
            error(elementName + " must not contain more than one sub-element", ele);
         } else {
            subElement = (Element) node;
         }
      }
   }

   boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);//解析construct-arg上的ref属性
   boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);//解析construct-arg上的value属性
   if ((hasRefAttribute && hasValueAttribute) ||
         ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
      /*
       * 在construct-arg上不存在以下两种情况
       *     1、同时既有ref属性又有value属性
       *     2、存在ref属性或value属性且又有子元素
       */
      error(elementName +
            " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
   }

   if (hasRefAttribute) {//ref属性的处理
      String refName = ele.getAttribute(REF_ATTRIBUTE);//获取ref属性
      if (!StringUtils.hasText(refName)) {
         error(elementName + " contains empty 'ref' attribute", ele);
      }

      RuntimeBeanReference ref = new RuntimeBeanReference(refName);//封装到RuntimeBeanRefrence中
      ref.setSource(extractSource(ele));
      return ref;
   } else if (hasValueAttribute) {//value属性的处理
      TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));//获取value属性并封装到TypedStringValue中
      valueHolder.setSource(extractSource(ele));
      return valueHolder;
   } else if (subElement != null) {//子元素处理
      return parsePropertySubElement(subElement, bd);
   } else {//错误的形式
      //没有ref、value和子元素, Spring记录错误日志
      error(elementName + " must specify a ref or value", ele);
      return null;
   }
}
复制代码

从代码上看,对构造函数中属性元素的解析,流程如下:

  • 略过description或者meta
  • 提取constructor-arg上的ref和value属性,以便于根据规则验证正确性,其规则为在constructor-arg上不存在一下情况:
    • 同时既有ref属性又有value属性
    • 存在ref属性或者value属性且又有子元素
  • ref属性的处理,使用RuntimeBeanReference封装对应的ref名称
  • value属性的处理。使用TypedStringValue封装
  • 子元素处理。如
<constructor-arg>
    <map>
        <entry key="key" value="value"/>
    </map>
</constructor-arg>
复制代码

而对于子元素的处理,使用函数parsePropertySubElement()处理,如下:

/**
* 解析construct-arg中的value、ref或者子construct-arg属性或者construct-arg元素
* @param ele 子元素
* @param bd  BeanDefinition
*/
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd) {
   return parsePropertySubElement(ele, bd, null);
}
复制代码
/**
* 解析value、ref或集合子元素或construct-arg元素。bean启动来说只存在construct-arg的子元素
* @param ele              子元素属性
* @param bd               当前bean的定义
* @param defaultValueType 默认的类型
*/
@Nullable
public Object parsePropertySubElement(Element ele, @Nullable BeanDefinition bd, @Nullable String defaultValueType) {
   if (!isDefaultNamespace(ele)) {
      return parseNestedCustomElement(ele, bd);
   } else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
      BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
      if (nestedBd != null) {
         nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
      }
      return nestedBd;
   } else if (nodeNameEquals(ele, REF_ELEMENT)) {//对ref元素的解析
      String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);//获取ref的bean元素的属性
      boolean toParent = false;
      if (!StringUtils.hasLength(refName)) {
         //解析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;
   } else if (nodeNameEquals(ele, IDREF_ELEMENT)) {//对idref子元素解析
      return parseIdRefElement(ele);
   } else if (nodeNameEquals(ele, VALUE_ELEMENT)) {//对value子元素解析
      return parseValueElement(ele, defaultValueType);
   } else if (nodeNameEquals(ele, NULL_ELEMENT)) {//对null子元素解析
      TypedStringValue nullHolder = new TypedStringValue(null);
      nullHolder.setSource(extractSource(ele));
      return nullHolder;
   } else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {//解析array子元素
      return parseArrayElement(ele, bd);
   } else if (nodeNameEquals(ele, LIST_ELEMENT)) {//解析list子元素
      return parseListElement(ele, bd);
   } else if (nodeNameEquals(ele, SET_ELEMENT)) {//解析set子元素
      return parseSetElement(ele, bd);
   } else if (nodeNameEquals(ele, MAP_ELEMENT)) {//解析map子元素
      return parseMapElement(ele, bd);
   } else if (nodeNameEquals(ele, PROPS_ELEMENT)) {//解析props子元素
      return parsePropsElement(ele);
   } else {
      error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
      return null;
   }
}
复制代码

在上面的函数中实现了所有可支持的子类的分配处理。具体的解析处理有兴趣的可以自行探索。

1.1.1.7 解析子元素property

parsePropertyElement()函数完成了对property属性的提取,property使用方式如下:

<bean id="test" class="test.TestClass">
    <property name="testStr" value="aaa"/>
</bean>
复制代码

或者

<bean id="a">
    <property name="p">
        <list>
            <value>aa</value>
            <value>bb</value>
        </list>
    </property>
</bean>
复制代码

具体解析过程如下:

/**
* 解析property子元素
* @param beanEle property子元素
* @param bd      BeanDefinition(GenericBeanDefinition)
*/
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
   NodeList nl = beanEle.getChildNodes();
   for (int i = 0; i < nl.getLength(); i++) {
      Node node = nl.item(i);
      if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
         parsePropertyElement((Element) node, bd);
      }
   }
}
复制代码

提取property子元素,然后调用parsePropertyElement() 函数处理:

/**
* 解析property子元素
* 正常解析没有问题后, 将name和value封装成PropertyValue对象, 并存放到BeanDefinition的MutablePropertyValues中
* @param ele Element
* @param bd  BeanDefinition
*/
public void parsePropertyElement(Element ele, BeanDefinition bd) {
   String propertyName = ele.getAttribute(NAME_ATTRIBUTE);//解析name属性
   if (!StringUtils.hasLength(propertyName)) {
      error("Tag 'property' must have a 'name' attribute", ele);
      return;
   }

   this.parseState.push(new PropertyEntry(propertyName));//push状态

   try {
      if (bd.getPropertyValues().contains(propertyName)) {//不允许多次对同一属性配置
         error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
         return;
      }

      Object val = parsePropertyValue(ele, bd, propertyName);//解析value
      PropertyValue pv = new PropertyValue(propertyName, val);//封装成PropertyValue
      parseMetaElements(ele, pv);
      pv.setSource(extractSource(ele));
      bd.getPropertyValues().addPropertyValue(pv);
   } finally {
      this.parseState.pop();//弹出状态
   }
}
复制代码

使用PropertyValue封装,记录在BeanDefinition中的propertyValues属性中。

1.1.1.8 解析子元素qualifier

对于qualifier元素的提取,我们接触更多的是注解形式,在使用Spring框架自动注入时,Spring容器中匹配的候选Bean数目必须有且仅有一个,当找不到一个匹配的Bean时,Spring容器将抛出BeanCreationException异常。解析过程与上述大同小异,不在重复叙述。

1.1.2 AbstractBeanDefinition属性

GenericBeanDefinition是AbstractBeanDefinition子类实现,而大部分的通用属性都保存在AbstractBeanDefinition中,如下,是AbstractDefinition的部分属性:

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
      implements BeanDefinition, Cloneable {
   /**
    * bean的实体对象
    */
   @Nullable
   private volatile Object beanClass;

   /**
    * bean的作用范围, 对应bean属性的scope
    */
   @Nullable
   private String scope = SCOPE_DEFAULT;

   /**
    * 是否抽象, 对应bean属性的abstract
    */
   private boolean abstractFlag = false;

   /**
    * 是否延迟加载, 对应bean属性的lazy-init
    */
   @Nullable
   private Boolean lazyInit;

   /**
    * 自动注入模式, 对应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<>();

   /**
    *
    */
   @Nullable
   private Supplier<?> instanceSupplier;

   /**
    * 允许访问非公开的构造器和方法, 程序设置
    */
   private boolean nonPublicAccessAllowed = true;

   /**
    * 是否以一种宽松的模式解析构造函数, 默认为true。
    * 如果为false, 则情况如下:
    * interface ITest{};
    * class TestImpl implements ITest {};
    * class Main {
    * Main(ITest test){};
    * Main(TestImpl testImpl){};
    * }
    * 抛出异常, 因为Spring无法准确定位哪个构造函数
    * 程序设置
    */
   private boolean lenientConstructorResolution = true;


   /**
    * 对应bean属性的factory-bean, 用法:
    * <bean id = "instanceFactoryBean" class="com.shangsw.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元素
    */
   private MethodOverrides methodOverrides = new MethodOverrides();

   /**
    * 初始化方法, 对应bean属性的init-method
    */
   @Nullable
   private String initMethodName;


   /**
    * 销毁方法, 对应bean属性的destory-method
    */
   @Nullable
   private String destroyMethodName;

   /**
    * 是否执行init-method, 程序设置
    */
   private boolean enforceInitMethod = true;

   /**
    * 是否执行destory-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定义的XML资源
    */
   @Nullable
   private Resource resource;
}
复制代码

1.1.3 解析默认标签中的自定义标签元素

此时我们已经解析完bean标签了,我们再回到org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#processBeanDefinition方法,下面,我们要进行delegate.decorateBeanDefinitionIfRequired(ele, bdHolder) 代码的分析,

这段代码主要适用于这样的场景,如:

<bean id="test" class="test.MyClass">
    <mybean::user username="aaa">
</bean>
复制代码

当Spring中的bean使用的是默认的标签配置,但是其中的子元素却使用了自定义的配置时,这段代码就会起作用。而这里的自定义标签解析与上面的自定义标签解析不同,上面针对的是bean的自定义标签解析。

代码如下:

/**
* 装饰指定的BeanDefinition, 如果有需要的话
* @param ele         bean标签元素
* @param originalDef 当前的bean definition
* @return BeanDefinitionHolder
*/
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
   return decorateBeanDefinitionIfRequired(ele, originalDef, null);
}
复制代码

第三个参数是父类的bean,当对某个嵌套配置进行分析时,这里需要传递父类beanDefinition。这里分析的是顶层配置,所以传递null:

/**
* 装饰指定的BeanDefinition, 如果有需要的话
* @param ele          bean标签元素
* @param originalDef  当前的bean definition
* @param containingBd 这个参数其实是父类的BeanDefinition, 主要是当子类的bean没有指定scope时使用父类的scope
* @return BeanDefinitionHolder
*/
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
   BeanDefinitionHolder finalDefinition = originalDef;//解析过的BeanDefinition

   //遍历所有的属性, 看看是否有适用于装饰的属性 Start
   NamedNodeMap attributes = ele.getAttributes();
   for (int i = 0; i < attributes.getLength(); i++) {
      Node node = attributes.item(i);
      finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
   }
   //遍历所有的属性, 看看是否有适用于装饰的属性 End

   //遍历所有的子元素, 看看是否有适用于装饰的子元素 Start
   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);
      }
   }
   //遍历所有的子元素, 看看是否有适用于装饰的子元素 End
   return finalDefinition;
}
复制代码

上面的代码,我们看到函数分别对元素的所有属性以及子节点进行了decorateIfRequired()函数的调用,如下:

/**
* 如果有装饰的属性, 则处理装饰的属性
* @param node 属性节点
* @param originalDef 解析过的BeanDefinition
* @param containingBd BeanDefinition
* @return BeanDefinitionHolder
*/
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 {
         if (logger.isDebugEnabled()) {
            logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
         }
      }
   }
   return originalDef;
}
复制代码

首先获取属性或者元素的命名空间,以此来判断该元素或者属性是否适用于自定义标签的解析条件,找出自定义类型所对应的NamespaceHandler并进行进一步解析。

1.1.4 注册解析的BeanDefinition

对于配置文件,解析完了,装饰也完成了,对于得到的beanDefinition已经可以满足后续的使用要求了,唯一剩下的工作就是注册了,也就是processBeanDefinition函数中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()) 代码的解析了。

/**
* 通过给定的BeanDefinitionHolder和BeanFactory注册Bean的实例
* @param definitionHolder bean定义, 包含beanName及其别名
* @param registry         要注册的beanFactory
* @throws BeanDefinitionStoreException Bean定义存储异常
*/
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
   //通过beanName注册BeanDefinition Start
   String beanName = definitionHolder.getBeanName();
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
   //通过beanName注册BeanDefinition End

   //注册所有的别名 Start
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         registry.registerAlias(beanName, alias);
      }
   }
   //注册所有的别名 End
}
复制代码

解析的beanDefinition都会被注册到BeanDefinitionRegistry类型的实例registry中,而对于beanDefinition的注册分成了两部分:通过beanName的注册以及通过别名的注册

1.1.4.1 通过beanName注册BeanDefinition

registerBeanDefinition(beanName, beanDefinition) 函数代码如下:

/**
* 注册beanDefinition
* @param beanName       bean的名称
* @param beanDefinition BeanDefinition
* @throws BeanDefinitionStoreException BeanDefinition存储异常
*/
@Override
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");

   //注册前的最后一次校验 Start
   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);
      }
   }
   //注册前的最后一次校验 End

   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
   if (existingDefinition != null) {//已经在beanDefinitionMap中存在
      if (!isAllowBeanDefinitionOverriding()) {//不允许覆盖, 直接抛出异常
         throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
      } else if (existingDefinition.getRole() < beanDefinition.getRole()) {
    
         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 + "]");
         }
      }
      this.beanDefinitionMap.put(beanName, beanDefinition);//允许覆盖
   } else {//在beanDefinitionMap中不存在
      if (hasBeanCreationStarted()) {//如果这个beanDefinition已经存在了
         //由于beanDefinitionMap是全局变量, 这里会有并发访问的情况, 所以这里加锁
         synchronized (this.beanDefinitionMap) {
            this.beanDefinitionMap.put(beanName, beanDefinition);//将bean实例存放到beanDefinitionMap中

            //更新存放beanName的集合 Start
            List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
            updatedDefinitions.addAll(this.beanDefinitionNames);
            updatedDefinitions.add(beanName);
            this.beanDefinitionNames = updatedDefinitions;
            //更新存放beanName的集合 End

            removeManualSingletonName(beanName);//删除单例
         }
      } else {//这个bean没有创建
         this.beanDefinitionMap.put(beanName, beanDefinition);//存放到缓存中
         this.beanDefinitionNames.add(beanName);
         removeManualSingletonName(beanName);
      }
      this.frozenBeanDefinitionNames = null;
   }

   if (existingDefinition != null || containsSingleton(beanName)) {
      resetBeanDefinition(beanName);//重置所有beanName对应的缓存
   } else if (isConfigurationFrozen()) {
      clearByTypeCache();
   }
}
复制代码

对bean的注册处理,主要有几个步骤:

  1. 对AbstractBeanDefinition的校验。在解析XML文件时是对XML格式的校验,而此时的校验是针对AbstractBeanDefinition的methodOverrides属性的
  2. 对beanName已经注册的情况处理,如果设置了不允许bean的覆盖,则抛出异常,否则直接覆盖
  3. 加入map缓存
  4. 清除解析之前留下的对应beanName的缓存

1.1.4.2 通过别名注册BeanDefinition

registerAlias(name, alias) 是别名的注册:

/**
* 注册beanName和alias的对应关系
* @param name  beanName
* @param alias 别名
*/
@Override
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) {
      if (alias.equals(name)) {//如果beanName和alias是一样的
         this.aliasMap.remove(alias);//删除alias
         if (logger.isDebugEnabled()) {
            logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
         }
      } else {//beanName和alias是不一样
         String registeredName = this.aliasMap.get(alias);//获取是否存在这个alias
         if (registeredName != null) {//存在alias
            if (registeredName.equals(name)) {//如果两者相同则不作处理
               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 + "'");
            }
         }

         checkForAliasCircle(name, alias);
         this.aliasMap.put(alias, name);//存放alias和beanName的对应关系
         if (logger.isTraceEnabled()) {
            logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
         }
      }
   }
}
复制代码

注册alias的步骤如下:

  1. alias与beanName相同情况处理,若alias与beanName名称相同则不需要处理并删除掉原有alias
  2. alias覆盖处理。若aliasName已经使用并已经指向了另一beanName则需要用户设置进行处理
  3. alias循环检查,当A->B存在是,若再次出现A->C->B时候则会抛出异常
  4. 注册alias

1.1.5 通知监听器解析及注册完成

通过你代码getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)) 完成此工作,这里的实现只为扩展,当程序开发人员需要对注册BeanDefinition事件进行监听时可以通过注册监听器的方式并将处理逻辑写入监听器中,目前在Spring中并没有对此事件做任何逻辑处理。

1.2 alias标签的解析

在对bean进行定义时,除了使用id属性来指定名称之外,为了提供多个名称,可以使用alias标签来指定。而所有的这些名称都指向同一个bean,在某些情况下提供别名非常有用,比如为了让应用的每一个组件都更容器对公共组件进行引用。

alias标签的结果如下:

/**
* 解析并注册alias标签
* @param ele alias标签
*/
protected void processAliasRegistration(Element ele) {
   String name = ele.getAttribute(NAME_ATTRIBUTE);//获取beanName
   String alias = ele.getAttribute(ALIAS_ATTRIBUTE);//获取alias

   //数据为空校验 Start
   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;
   }
   //数据为空校验 End

   if (valid) {//校验没有问题
      try {
         getReaderContext().getRegistry().registerAlias(name, alias);//注册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中的alias解析大同小异,都是将别名与beanName组成一对注册至registry中。这里不仔赘述。

1.3 import标签的解析

对于Spring配置文件的编写,有时候有很多配置文件,而最后可以使用import进行引入,这样可以对配置文件进行模块化,易于管理。

如下,是Spring解析import配置文件:

/**
* import标签解析
* @param ele import标签
*/
protected void importBeanDefinitionResource(Element ele) {
   //获取resource属性 Start
   String location = ele.getAttribute(RESOURCE_ATTRIBUTE);//获取resource属性
   if (!StringUtils.hasText(location)) {//不存在则不做处理
      getReaderContext().error("Resource location must not be empty", ele);
      return;
   }
   //获取resource属性 End

   //解析系统属性, 格式如: ${user.dir}
   location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

   Set<Resource> actualResources = new LinkedHashSet<>(4);

   //判断location是绝对URI还是相对URI Start
   boolean absoluteLocation = false;//判断localtion是绝对URI还是相对URI
   try {
      absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();//判断
   } catch (URISyntaxException ex) {
   }
   //判断location是绝对URI还是相对URI End

   if (absoluteLocation) {//如果是绝对URI则直接根据地址加载对应的配置文件
      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 {//相对URI, 根据相对URI计算出绝对的URI
      try {
         int importCount;
         /*
          * 根据相对UIR创建资源, Resource存在多个子类, 如VfsResource、FileSystemResource等,
          * 而每个子类的createRelative方式实现都不一样, 所以这里先使用子类的方法尝试解析
          */
         Resource relativeResource = getReaderContext().getResource().createRelative(location);
         if (relativeResource.exists()) {//解析成功
            importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);//加载beanDefinitions
            actualResources.add(relativeResource);
         } else {//解析不成功
            //使用默认解析器ResourcePatternResolve进行解析
            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));
}
复制代码

代码流程也很清晰,Spring进行解析的步骤大致如下:

  1. 获取resource属性所表示的路径
  2. 解析路径中的系统属性,格式如“${user.dir}”
  3. 判定location是绝对路径还是相对路径
  4. 如果是绝对路径则递归调用bean的解析过程,进行另一次的解析
  5. 如果是相对路径则计算出绝对路径并进行解析
  6. 通知监听器,解析完成

1.4 嵌入式beans标签的解析

对于嵌入式的beans标签,其实没有太多可讲的,与单独的配置文件并没有太大差别,无非就是递归调用beans的解析过程。可自行研究。

而对于自定义标签的解析,我这里就不做过多的介绍了,有兴趣的可以自行阅读

分类:
后端
标签:
分类:
后端
标签: