zoukankan      html  css  js  c++  java
  • 正确使用参数

    上一篇文章提到过Spring中的标签包括默认标签自定义标签,而两种标签的用法以及解析方式存在着很大的不同,先说说默认标签的解析。

    首先我们需要明确两个概念:属性子元素

    • 属性是指一个bean标签它自带的一些属性,例如idnameparent等,都存放于AbstractBeanDefinition的各项成员变量中。
    • 子元素是指bean标签下的一些子标签的内容,例如:
    <bean id="mybbTestBean" class="bean.MyTestBean">
    	<meta key="testStr" value="aaaa"/>
    </bean>
    

    其中的meta就是一个子元素。

    默认标签的解析是在parseDefaultElement函数中进行的,分别对importaliasbeanbeans做了不同的处理。

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }
    

    1 bean标签的解析和注册

    进入processBeanDefinition(ele, delegate)方法:

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }
    

    大致逻辑如下:

    1. 委托BeanDefinitionParserDelegateparseBeanDefinitionElement方法进行元素解析,返回bhHolder。这个bdHolder实例已经包含我们配置文件中配置的各种属性了,例如classnameidalias等。
    2. 当返回的bdHolder不为空的情况下,若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析。
    3. 解析完成后,需要对解析后的bdHolder进行注册,同样,注册操作委托给了BeanDefinitionReaderUtilsregisterBeanDefinition方法。
    4. 最后发出响应事件,通知相关的监听器,这个bean就加载完成了。

    1.1 解析BeanDefinition

    我们进入到BeanDefinitionDelegate类的parseBeanDefinitionElement方法。

    @Nullable
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
      return parseBeanDefinitionElement(ele, null);
    }
    
    /**
    	 * Parses the supplied {@code <bean>} element. May return {@code null}
    	 * if there were errors during parse. Errors are reported to the
    	 * {@link org.springframework.beans.factory.parsing.ProblemReporter}.
    	 */
    @Nullable
    public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
      // 解析id属性
      String id = ele.getAttribute(ID_ATTRIBUTE);
      // 解析name属性
      String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
    
      List<String> aliases = new ArrayList<>();
      if (StringUtils.hasLength(nameAttr)) {
        // name属性也是alias的一种
        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);
      }
    
      // 对标签其他属性进行解析,如class、parent等
      AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
      if (beanDefinition != null) {
        if (!StringUtils.hasText(beanName)) {
          try {
            // 如果不存在beanName那么根据Spring提供的命名规则为当前bean生成
            if (containingBean != null) {
              beanName = BeanDefinitionReaderUtils.generateBeanName(
                beanDefinition, this.readerContext.getRegistry(), true);
            }
            else {
              beanName = this.readerContext.generateBeanName(beanDefinition);
              // Register an alias for the plain bean class name, if still possible,
              // if the generator returned the class name plus a suffix.
              // This is expected for Spring 1.2/2.0 backwards compatibility.
              String beanClassName = beanDefinition.getBeanClassName();
              if (beanClassName != null &&
                  beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                  !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                aliases.add(beanClassName);
              }
            }
            if (logger.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);
        // 封装进BeanDefinitionHolder中
        return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
      }
    
      return null;
    }
    

    parseBeanDefinitionElement的主要工作包括如下内容:

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

    进一步查看步骤2中对标签其他属性的解析过程。

    @Nullable
    public AbstractBeanDefinition parseBeanDefinitionElement(
      Element ele, String beanName, @Nullable BeanDefinition containingBean) {
    
      this.parseState.push(new BeanEntry(beanName));
    
      String className = null;
      if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
        // 解析class属性
        className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
      }
      String parent = null;
      if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
        // 解析parent属性
        parent = ele.getAttribute(PARENT_ATTRIBUTE);
      }
    
      try {
        // 创建用于承载属性的AbstractBeanDefinition
        AbstractBeanDefinition bd = createBeanDefinition(className, parent);
    
        // 解析默认bean的各种属性
        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
    
        // 解析元数据
        parseMetaElements(ele, bd);
        // 解析lookup-method属性
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
        // 解析replaced-method属性
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
    
        // 解析构造函数参数
        parseConstructorArgElements(ele, bd);
        // 解析property子元素
        parsePropertyElements(ele, bd);
        // 解析qualifier子元素
        parseQualifierElements(ele, bd);
    
        bd.setResource(this.readerContext.getResource());
        bd.setSource(extractSource(ele));
    
        return bd;
      }
      catch (ClassNotFoundException ex) {
        error("Bean class [" + className + "] not found", ele, ex);
      }
      catch (NoClassDefFoundError err) {
        error("Class that bean class [" + className + "] depends on not found", ele, err);
      }
      catch (Throwable ex) {
        error("Unexpected failure during bean definition parsing", ele, ex);
      }
      finally {
        this.parseState.pop();
      }
    
      return null;
    }
    

    1.1.1 解析子元素constructor-arg

    举个Spring构造函数配置的例子:

    <bean id="helloBean" class="com.HelloBean">
      <constructor-arg index="0">
        <value>mutianjie</value>
      </constructor-arg>
      <constructor-arg index="1">
      	<value>你好</value>
      </constructor-arg>
    </bean>
    

    上面的配置实现的功能是对HelloBean自动寻找对应的构造函数,并在初始化的时候将设置的参数传入进去。对于constructor-arg子元素的解析,Spring是通过parseConstructorArgElements函数来实现的,具体代码如下:

    	/**
    	 * Parse constructor-arg sub-elements of the given bean element.
    	 */
    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);
        }
      }
    }
    

    在循环中,parseConstructorArgElements函数遍历了所有的<constructor-arg>标签,提取所有的子元素。具体的解析放在了另一个函数parseConstructorArgElement中,该函数的具体代码如下:

    /**
    	 * Parse a constructor-arg element.
    	 */
    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));
              // 解析ele对应的属性元素
              Object value = parsePropertyValue(ele, bd, null);
              // 封装解析出来的元素
              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);
          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();
        }
      }
    }
    

    根据以上代码总结出来的流程为:

    如果配置中指定了index属性,那么操作步骤如下:

    1. 使用parsePropertyValue方法解析constructor-arg的子元素
    2. 使用ConstructArgumentValues.ValueHolder类型来封装解析出来的元素
    3. typenameindex属性一并封装在ConstructArgumentValues.ValueHolder中,并添加至当前BeanDefinitionconstructorArgumentValuesindexedArgumentValue中。

    如果配置中未指定index属性,前两步操作与指定index属性的做法相同,但是第三步中,valueHolder会添加至当前BeanDefinitionconstructorArgumentValuesgenericArgumentValue中。

    在构造函数解析中,解析constructor-arg的子元素使用的是parsePropertyValue方法,它是用来解析某一个具体的构造函数参数的。这个方法的代码如下:

    @Nullable
    public Object parsePropertyValue(Element ele, BeanDefinition bd, @Nullable String propertyName) {
      String elementName = (propertyName != null ?
                            "<property> element for property '" + propertyName + "'" :
                            "<constructor-arg> element");
    
      // 一个属性只能对应一种类型,如 ref, value, list等
      NodeList nl = ele.getChildNodes();
      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)) {
          // Child element is what we're looking for.
          if (subElement != null) {
            error(elementName + " must not contain more than one sub-element", ele);
          }
          else {
            subElement = (Element) node;
          }
        }
      }
    
      // 解析constructor-arg的ref属性
      boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
      // 解析constructor-arg的value属性
      boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
      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) {
        // map、list等子元素的处理,如
        /**
          * <constructor-arg>
          * <map>
          * 	<entry key="key" value="value"/>
          * </map>
          * </constructor-arg>
          */
        return parsePropertySubElement(subElement, bd);
      }
      else {
        // Neither child element nor "ref" or "value" attribute found.
        error(elementName + " must specify a ref or value", ele);
        return null;
      }
    }
    

    值得注意的是,除了refvalue两种子元素之外,还有嵌套子元素如maplistarray等。这类嵌套子元素的处理在parsePropertySubElement方法中完成,对嵌套子元素进行分类处理。

    1.1.2 解析子元素property

    parsePropertyElements函数完成了对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>
    

    而具体的解析过程与前面的子元素一样,也是使用单数形式的方法parsePropertyElement来对所有的property子元素进行解析,代码如下:

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

    parsePropertyElement方法的实现如下:

    public void parsePropertyElement(Element ele, BeanDefinition bd) {
      String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
      if (!StringUtils.hasLength(propertyName)) {
        error("Tag 'property' must have a 'name' attribute", ele);
        return;
      }
      this.parseState.push(new PropertyEntry(propertyName));
      try {
        if (bd.getPropertyValues().contains(propertyName)) {
          error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
          return;
        }
        Object val = parsePropertyValue(ele, bd, propertyName);
        PropertyValue pv = new PropertyValue(propertyName, val);
        parseMetaElements(ele, pv);
        pv.setSource(extractSource(ele));
        bd.getPropertyValues().addPropertyValue(pv);
      }
      finally {
        this.parseState.pop();
      }
    }
    

    可以看到上面函数与构造函数注入方式不同的是将返回值使用PropertyValue封装,并记录在BeanDefinition中的propertyValues属性中。

    1.2 AbstractBeanDefinition属性

    至此我们便完成了对XML文档到GenericBeanDefinition的转换,也就是说,所有的配置都可以在GenericBeanDefinition中找到对应的配置。

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

    我们来回顾一下默认标签解析函数的起始函数:

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
      BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
      if (bdHolder != null) {
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
          // Register the final decorated instance.
          BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
        }
        catch (BeanDefinitionStoreException ex) {
          getReaderContext().error("Failed to register bean definition with name '" +
                                   bdHolder.getBeanName() + "'", ele, ex);
        }
        // Send registration event.
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
      }
    }
    

    我们已经用大量篇幅分析了BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele)这句代码。接下来,我们进入到下一行代码delegate.decorateBeanDefinitionIfRequired(ele, bdHolder)中。

    public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
       return decorateBeanDefinitionIfRequired(ele, originalDef, null);
    }
    

    这段代码的作用是对beanDefinition进行装饰,当Spring中的bean使用的是默认的标签配置,但是其中子元素使用了自定义的配置时,这句代码就会起作用,如:

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

    这并不是自定义标签类型的解析,因为它并不是以bean的形式而是以自定义属性的形式出现的。decorateBeanDefinitionIfRequired方法实现了寻找自定义标签并根据自定义标签寻找命名空间处理器,并进行进一步的解析。

    1.4 注册解析的BeanDefinition

    接下来还剩下的工作是对BeanDefinition的注册,也就是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);
        }
      }
    }
    

    可以看到,对BeanDefinition的注册分为两部分:通过beanName和别名进行注册。

    1.4.1 通过beanName注册BeanDefinition

    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
          throws BeanDefinitionStoreException {
    
       Assert.hasText(beanName, "Bean name must not be empty");
       Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    
       if (beanDefinition instanceof AbstractBeanDefinition) {
          try {
             /**
              * 注册前的最后一次校验,这里的校验不同于之前的XML文件校验
              * 主要是对与AbstractBeanDefinition属性中的methodOverrides校验
              * 校验methodOverrides是否与工厂方法并存或者methodOverrides对应的方法根本不存在
              */
             ((AbstractBeanDefinition) beanDefinition).validate();
          }
          catch (BeanDefinitionValidationException ex) {
             throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                   "Validation of bean definition failed", ex);
          }
       }
    
       // 这里使用ConcurrentHashMap进行并发访问全局变量beanDefinitionMap
       BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
       if (existingDefinition != null) {
          //  处理注册已经注册的beanName情况
          if (!isAllowBeanDefinitionOverriding()) {
             // 如果对应的beanName已经注册且在配置中配置了bean不允许被覆盖,则抛出异常
             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 {
          if (hasBeanCreationStarted()) {
             // Cannot modify startup-time collection elements anymore (for stable iteration)
             synchronized (this.beanDefinitionMap) {
                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
             this.beanDefinitionMap.put(beanName, beanDefinition);
             this.beanDefinitionNames.add(beanName);
             removeManualSingletonName(beanName);
          }
          this.frozenBeanDefinitionNames = null;
       }
    
       if (existingDefinition != null || containsSingleton(beanName)) {
          // 重置所有beanName对应的缓存
          resetBeanDefinition(beanName);
       }
       else if (isConfigurationFrozen()) {
          clearByTypeCache();
       }
    }
    

    一共分为4个步骤。

    1. 针对AbstractBeanDefinition的methodOverride属性进行校验,这里的methodOverride存放的实际上就是<look-up><replaced-method>两个子标签所解析的结果。
    2. bean已经注册过的情况处理。如果设置了不允许bean的覆盖,则需要抛出异常,否则直接覆盖。
    3. 加入beanDefinitionMap中缓存,使用ConcurrentHashMap进行并发访问。
    4. 清除解析之前留下的对应的bean的缓存,例如解决循环依赖时使用的缓存singletonObjectsearlySingletonObjects等。

    1.4.2 通过别名注册BeanDefinition

    通过别名注册BeanDefinitionregisterAlias方法是在SimpleAliasRegistry类中定义的。

    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)) {
             // alias与beanName相同,从aliasMap中移除该alias
             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)) {
                   // 该alias被使用并且指向的是当前beanName
                   return;
                }
                // 该alias被使用但是指向的是另外的bean
                if (!allowAliasOverriding()) {
                   // 若用户不允许alias覆盖则抛出异常
                   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 + "'");
                }
             }
             // 当A->B存在时,若再次出现A->C->B时会出现异常
             checkForAliasCircle(name, alias);
             this.aliasMap.put(alias, name);
             if (logger.isTraceEnabled()) {
                logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
             }
          }
       }
    }
    

    由以上代码可知,注册alias的步骤如下:

    1. aliasbeanName相同时,不需要处理并移除原有的alias
    2. alias已经使用并指向了另外的beanName,则根据用户是否允许alias覆盖进行处理。
    3. alias循环检查。
    4. 注册alias

    2 alias标签的解析

    在上一节中已经讲过了对于beanname元素的解析,那么现在再来看一下alias标签的解析过程。

    protected void processAliasRegistration(Element ele) {
       String name = ele.getAttribute(NAME_ATTRIBUTE);
       String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
       boolean valid = true;
       if (!StringUtils.hasText(name)) {
          getReaderContext().error("Name must not be empty", ele);
          valid = false;
       }
       if (!StringUtils.hasText(alias)) {
          getReaderContext().error("Alias must not be empty", ele);
          valid = false;
       }
       if (valid) {
          try {
             getReaderContext().getRegistry().registerAlias(name, alias);
          }
          catch (Exception ex) {
             getReaderContext().error("Failed to register alias '" + alias +
                   "' for bean with name '" + name + "'", ele, ex);
          }
          getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
       }
    }
    

    可以发现,跟之前讲过的bean中的alias解析大同小异,都是将别名与beanName组成键值对注册至registry中。至于为什么之前已经解析过bean中的alias这里又要再解析一遍,是因为之前的alias是可以定义在beanname属性中,如:

    <bean id="some" name="oneBean,twoBean,threeBean" class="src.com.Some"/>
    

    而现在这一节所说的alias是作为bean的子标签存在的,所以又得重新注册一遍。

  • 相关阅读:
    [BZOJ 1552] 排序机械臂
    [BZOJ 1124][POI 2008] 枪战 Maf
    [BZOJ 1647][USACO 2007 Open] Fliptile 翻格子游戏
    [BZOJ 1592] Making The Grade路面修整
    [BZOJ 3829][POI2014] FarmCraft
    [技术] 如何正确食用cnblogs的CSS定制
    [BZOJ 1458] 士兵占领
    今天写了一个Imageloader,,AndroidStudio报了Error:Execution failed for task ':app:mergeDebugResources'. > Error: Java.util.concurrent.ExecutionException: com.Android.ide.common.process.ProcessException: 这个错误
    Http响应码代表的含义
    获取WIFI列表,在旧手机上运行就没有问题,在新手机上就怎么也获取不到WIFI列表,长度一直为0,还不报异常,很疑惑。
  • 原文地址:https://www.cnblogs.com/muuu520/p/14503169.html
Copyright © 2011-2022 走看看