Spring源码解析3——默认标签的解析(二)

1.3、AbstractBeanDefinition属性
1.3.1、AbstractBeanDefinition.java概要

  至此我们便完成了对XML 文档到GenericBeanDefinition.java 的转换, 也就是说到这里, XML中所有的配置都可以在GenericBeanDefinition.java 的实例类中找到对应的配置。
  GenericBeanDefinition.java 只是子类实现,而大部分的通用属性都保存在了AbstractBeanDefinition.java中。AbstractBeanDefinition.java部分代码如下:

public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccessor
      implements BeanDefinition, Cloneable {

   /**
    * Constant for the default scope name: {@code ""}, equivalent to singleton
    * status unless overridden from a parent bean definition (if applicable).
    */
   public static final String SCOPE_DEFAULT = "";

   /**
    * Constant that indicates no external autowiring at all.
    * @see #setAutowireMode
    */
   public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;

   /**
    * Constant that indicates autowiring bean properties by name.
    * @see #setAutowireMode
    */
   public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;

   /**
    * Constant that indicates autowiring bean properties by type.
    * @see #setAutowireMode
    */
   public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;

   /**
    * Constant that indicates autowiring a constructor.
    * @see #setAutowireMode
    */
   public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;

   /**
    * Constant that indicates determining an appropriate autowire strategy
    * through introspection of the bean class.
    * @see #setAutowireMode
    * @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
    * use annotation-based autowiring for clearer demarcation of autowiring needs.
    */
   @Deprecated
   public static final int AUTOWIRE_AUTODETECT = AutowireCapableBeanFactory.AUTOWIRE_AUTODETECT;

   /**
    * Constant that indicates no dependency check at all.
    * @see #setDependencyCheck
    */
   public static final int DEPENDENCY_CHECK_NONE = 0;

   /**
    * Constant that indicates dependency checking for object references.
    * @see #setDependencyCheck
    */
   public static final int DEPENDENCY_CHECK_OBJECTS = 1;

   /**
    * Constant that indicates dependency checking for "simple" properties.
    * @see #setDependencyCheck
    * @see org.springframework.beans.BeanUtils#isSimpleProperty
    */
   public static final int DEPENDENCY_CHECK_SIMPLE = 2;

   /**
    * Constant that indicates dependency checking for all properties
    * (object references as well as "simple" properties).
    * @see #setDependencyCheck
    */
   public static final int DEPENDENCY_CHECK_ALL = 3;

   /**
    * Constant that indicates the container should attempt to infer the
    * {@link #setDestroyMethodName destroy method name} for a bean as opposed to
    * explicit specification of a method name. The value {@value} is specifically
    * designed to include characters otherwise illegal in a method name, ensuring
    * no possibility of collisions with legitimately named methods having the same
    * name.
    * <p>Currently, the method names detected during destroy method inference
    * are "close" and "shutdown", if present on the specific bean class.
    */
   public static final String INFER_METHOD = "(inferred)";


   @Nullable
   private volatile Object beanClass;

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

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

   //是否延迟加载,对应bean属性lazy-init
   private boolean lazyInit = false;

   //自动注入模式,对应bean属性autowire
   private int autowireMode = AUTOWIRE_NO;
   //依赖检查
   private int dependencyCheck = DEPENDENCY_CHECK_NONE;
   //表示一个bean的实例化依靠另一个bean先实例化,对应bean属性dependOn
   @Nullable
   private String[] dependsOn;
   /*
   * autowireCandidate属性设置为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 ITestImpl implements ITest{}
   * class Main{
   *  Main(ITest i){}
   *  Main(ITestImpl i){}
   * }
   * 抛出异常,因为Spring无法准确定位哪个构造函数
   * 程序设置
   * */
   private boolean lenientConstructorResolution = true;
   /*
    *对应bean属性factory-bean
    * */
   @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属性destroy-method
    * **/
   @Nullable
   private String destroyMethodName;

   /*
   * 是否执行init-method
   * */
   private boolean enforceInitMethod = true;
   /*
   * 会否执行destroy-method
   * */
   private boolean enforceDestroyMethod = true;
   /*
   * 是否是用户定义的而不是应用程序本身定义的,创建AOP时候为true,程序设置
   * */
   private boolean synthetic = false;
   /*
   * 定义这个bean的应用,APPLICATION:用户INFRASTRUCTURE:完全内部使用,与用户无关,SUPPORT:某些复杂配置的一部分
   * */
   private int role = BeanDefinition.ROLE_APPLICATION;
   /*
   * bean的描述信息
   * */
   @Nullable
   private String description;
   /*
   * 这个bean定义的资源
   * */
   @Nullable
   private Resource resource;

}
1.4、解析默认标签中的自定义标签元素

clipboard

  1.2内容已经完成了默认标签的解析,如上图中篮色框标注的部分。接着要对红色框的部分进行分析。红色框适用场景:

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

1.2的节详细内容,请查看:Spring源码解析2——默认标签的解析(一)
  当Spring 中的bean 使用的是默认的标签配置,但是其中的子元素却使用了自定义的配置时。
  Spring中一种是默认类型的解析,另一种是自定义类型的解析,这不正是自定义类型的解析吗?为什么会在默认类型解析中单独添加一个方法处理呢?这个自定义类型并不是以Bean 的形式出现的呢?我们之前讲过的两种类型的不同处理只是针对Bean 的,这里我们看到,这个自定义类型其实是属性。
clipboard

  这里将函数中第三个参数设置为空,那么第三个参数是做什么用的呢?什么情况下不为空呢?其实这第三个参数是父类bean ,当对某个嵌套配置进行分析时,这里需要传递父类beanDefinition 。分析源码得知这里传递的参数其实是为了使用父类的scope 属性,以备子类若没有设置scope 时默认使用父类的属性,这里分析的是顶层配置, 所以传递null 。将第三个参数设置为空后进一步跟踪函数:
clipboard

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

①、首先获取属性或者元素的命名空间, 以此来判断该元素或者属性是否适用于自定义标签的解析条件。
②、对于程序默认的标签的处理其实是直接略悔过的,因为默认的标签到这里已经被处理完了, 这里只对自定义的标签或者说对bean 的自定义属性感兴趣。
③、找出自定义类型所对应的NamespaceHandler 并进行进一步解析。

1.5、注册解析BeanDefinition
1.5.1、通过beanName注册BeanDefinition

clipboard

clipboard

@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");

   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);
      }
   }
   //在spring容器中查找是否已经存在该beanName的Bean
   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
   if (existingDefinition != null) {
      //如果已经存在同名的Bean,则抛出异常。
      if (!isAllowBeanDefinitionOverriding()) {
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
               "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
               "': There is already [" + existingDefinition + "] bound.");
      }
      else if (existingDefinition.getRole() < beanDefinition.getRole()) {
         // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
         if (logger.isWarnEnabled()) {
            logger.warn("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.isInfoEnabled()) {
            logger.info("Overriding bean definition for bean '" + beanName +
                  "' with a different definition: replacing [" + existingDefinition +
                  "] with [" + beanDefinition + "]");
         }
      }
      else {
         if (logger.isDebugEnabled()) {
            logger.debug("Overriding bean definition for bean '" + beanName +
                  "' with an equivalent definition: replacing [" + existingDefinition +
                  "] with [" + beanDefinition + "]");
         }
      }
      this.beanDefinitionMap.put(beanName, beanDefinition);
   }
   else {
      if (hasBeanCreationStarted()) {
         // Cannot modify startup-time collection elements anymore (for stable iteration)
         //处理并发访问
         synchronized (this.beanDefinitionMap) {
            //注册beanDefinition
            this.beanDefinitionMap.put(beanName, beanDefinition);
            List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
            updatedDefinitions.addAll(this.beanDefinitionNames);
            //记录beanName
            updatedDefinitions.add(beanName);
            this.beanDefinitionNames = updatedDefinitions;
            if (this.manualSingletonNames.contains(beanName)) {
               Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
               updatedSingletons.remove(beanName);
               this.manualSingletonNames = updatedSingletons;
            }
         }
      }
      else {
         // Still in startup registration phase
         this.beanDefinitionMap.put(beanName, beanDefinition);
         this.beanDefinitionNames.add(beanName);
         this.manualSingletonNames.remove(beanName);
      }
      this.frozenBeanDefinitionNames = null;
   }

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

上述代码的主要逻辑如下:
①、对AbstractBeanDefinition 的校验。在解析XML 文件的时候我们提过校验,但是此校验非彼校验,之前的校验时针对于XML 格式的校验,而此时的校验时针是对于AbstractBeanDefinition的methodOverrides 属性的。
②、对beanName 已经注册的情况的处理。如果设置了不允许bean 的覆盖,则需要抛出异常,否则直接覆盖。
③、加入map 缓存。
④、清除解析之前留下的对应beanName的缓存。

1.5.2、通过别名注册BeanDefinition
@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相同的话,不记录alias,并删除对应的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)) {
               // An existing alias - no need to re-register
               return;
            }
            //如果alias不允许被覆盖则抛出异常
            if (!allowAliasOverriding()) {
               throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
                     name + "': It is already registered for name '" + registeredName + "'.");
            }
            if (logger.isInfoEnabled()) {
               logger.info("Overriding alias '" + alias + "' definition for registered name '" +
                     registeredName + "' with new target name '" + name + "'");
            }
         }
         //当A->B存在时,若再次出现A->B->C时,则会抛出异常
         checkForAliasCircle(name, alias);
         this.aliasMap.put(alias, name);
         if (logger.isDebugEnabled()) {
            logger.debug("Alias definition '" + alias + "' registered for name '" + name + "'");
         }
      }
   }
}

上述代码的主要逻辑如下:
①、 alias 与beanName 相同情况处理。若alias 与beanName 的名称相同则不需要处理并删除掉原有alias 。
②、 alias 覆盖处理,若aliasName 已经使用并已经指向了另一个beanName 则需要用户的设置进行处理。
③、 alias 循环检查。当 A-> B 存在时,若再次出现A->C -> B 时候则会抛出异常。
④、 注册alias。

2、alias标签的解析

  在对bean 进行定义时,除了使用id 属性来指定名称之外,为了提供多个名称,可以使用alias 标签来指定。而所有的这些名称都指向同一个bean ,在某些情况下提供别名非常有用,比如为了让应用的每一个组件能更容易地对公共组件进行引用。
  然而,在定义bean 时就指定所有的别名并不是总是恰当的。有时我们期望能在当前位置为那些在别处定义的bean 引入别名。在XML配置文件中,可用单独的<alias>元素来完成bean 别名的定义。如配置文件中定义了一个JavaBean:

<bean id="testBean" class="com.test" />

  要给这个JavaBean 增加别名,以方便不同对象来调用。我们就可以直接使用bean 标签中的name 属性:

<bean id="testBean" name="testBean1,testBean2" class="com.test" />

  同样, Spring 还有另外一种声明别名的方式:

<bean id="testBean" class="com.test" />
<alias name="testBean" alias="testBean1,testBean2"/>

考虑一个更为具体的例子:
组件A 在XML 配置文件中定义了一个DataSource.java 类型的componentA ,但组件B 却想在其XML 文件中以componentB 命名来引用此DataSource.java类型的bean。
而且在主程序MyApp 的XML配置文件中,希望以myApp 的名字来引用此DataSource.java类型的bean。最后容器加载3 个XML 文件来生成最终的ApplicationContext 。在此情况下,可通过在配置文件中添加下列alias 元素来实现:

<alias name="componentA" alias="componentB"/>
<alias name="componentA" alias="myApp"/>

  下面是Spring源码对于<alias>标签的解析:

/**
 * Process the given alias element, registering the alias with the registry.
 */
protected void processAliasRegistration(Element ele) {
   //获取name属性,name属性代表beanName
   String name = ele.getAttribute(NAME_ATTRIBUTE);
   //获取alias属性
   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 {
         //注册alias
         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));
   }
}
posted @ 2025-12-26 12:31  Carey_ccl  阅读(2)  评论(0)    收藏  举报