《Spring系列》第2章 解析XML获取Bean

一、基础代码

Spring加载bean实例的代码

public static void main(String[] args) throws IOException {
    // 1.获取资源
    Resource resource = new ClassPathResource("bean.xml");
    // 2.获取BeanFactory
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    // 3.获取资源的解析器
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
    // 4.装载资源
    reader.loadBeanDefinitions(resource);

    // 5.获取Bean实例
    Book book = factory.getBean(Book.class);
    System.out.println(book);
}

这段代码是 Spring 中编程式使用 IoC 容器,通过这四段简单的代码,我们可以初步判断 IoC 容器的使用过程。整个过程就分为三个步骤:

资源定位。我们一般用外部资源来描述 Bean 对象,所以在初始化 IoC 容器的第一步就是需要定位这个外部资源。

装载。装载就是 BeanDefinition 的载入。BeanDefinitionReader 读取、解析 Resource 资源,也就是将用户定义的 Bean 表示成 IoC 容器的内部数据结构:BeanDefinition

注册。向 IoC 容器注册在第二步解析好的 BeanDefinition,这个过程是通过BeanDefinitionRegistry接口来实现的。在 IoC 容器内部其实是将第二个过程解析得到的BeanDefinition注入到一个HashMap容器中,IoC 容器就是通过这个HashMap来维护这些BeanDefinition的。

​ 1)在这里需要注意的一点是这个过程并没有完成依赖注入(Bean 创建),Bean 创建是发生在应用第一次调用getBean()方法,向容器索要 Bean 时。
​ 2)当然我们可以通过设置预处理,即对某个 Bean 设置lazyinit = false 属性,那么这个 Bean 的依赖注入就会在容器初始化的时候完成。

简单点说,上面步骤的结果是:XML Resource -> XML Document -> Bean Definition

二、BeanDefinition

上面讲完了介绍到我们解析的Bean标签会封装成BeanDefinition,那么接下来看一下

1.基础介绍

org.springframework.beans.factory.config.BeanDefinition ,是一个接口,它描述了一个 Bean 实例的定义,包括属性值、构造方法值和继承自它的类的更多信息。代码如下:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
 	
    // 其中方法就不粘贴了,太多了
}

2.BeanDefinition 的父关系

BeanDefinition 继承 AttributeAccessorBeanMetadataElement 接口。两个接口定义如下:

org.springframework.cor.AttributeAccessor 接口,定义了与其它对象的(元数据)进行连接和访问的约定,即对属性的修改,包括获取、设置、删除。代码如下:

public interface AttributeAccessor {

   void setAttribute(String name, @Nullable Object value);

   @Nullable
   Object getAttribute(String name);

   @Nullable
   Object removeAttribute(String name);

   boolean hasAttribute(String name);

   String[] attributeNames();
}

org.springframework.beans.BeanMetadataElement 接口,Bean 元对象持有的配置元素可以通过 getSource() 方法来获取。代码如下:

public interface BeanMetadataElement {

   @Nullable
   default Object getSource() {
      return null;
   }
}

3.BeanDefinition 的子关系

在这里插入图片描述

我们常用的三个实现类有:

  • org.springframework.beans.factory.support.ChildBeanDefinition
  • org.springframework.beans.factory.support.RootBeanDefinition
  • org.springframework.beans.factory.support.GenericBeanDefinition
  1. ChildBeanDefinitionRootBeanDefinitionGenericBeanDefinition 三者都继承 AbstractBeanDefinition 抽象类,即 AbstractBeanDefinition 对三个子类的共同的类信息进行抽象。
  2. 如果配置文件中定义了父 和 子 ,则父 用 RootBeanDefinition 表示,子 用 ChildBeanDefinition 表示,而没有父 的就使用RootBeanDefinition 表示。
  3. GenericBeanDefinition 为一站式服务类。

三、调用链

在这里插入图片描述

1.loadBeanDefinitions() 加载

加载BeanDefinition

// XmlBeanDefinitionReader XML解析类
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
   // 将Resource资源封装成EncodedResource,封装的原因是为了进行编码,保证内容读取的正确性
   return loadBeanDefinitions(new EncodedResource(resource));
}
// XmlBeanDefinitionReader XML解析类
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
   Assert.notNull(encodedResource, "EncodedResource must not be null");
   if (logger.isTraceEnabled()) {
      logger.trace("Loading XML bean definitions from " + encodedResource);
   }

   // 获取已经加载过的资源
   Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

   // 将当前资源加入记录中,如果已存在,则抛出异常
   // 主要为了避免一个EncodedResource在加载时,还没加载完成,又加载自身,从而导致死循环
   if (!currentResources.add(encodedResource)) {
      throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
   }

   // 获取输入流
   try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
      InputSource inputSource = new InputSource(inputStream);
      if (encodedResource.getEncoding() != null) {
         // 设置编码
         inputSource.setEncoding(encodedResource.getEncoding());
      }
      // **核心逻辑部分,执行加载 BeanDefinition
      return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException(
            "IOException parsing XML document from " + encodedResource.getResource(), ex);
   }
   finally {
      // 缓存中剔除该资源,代表加载完了该文件
      currentResources.remove(encodedResource);
      if (currentResources.isEmpty()) {
         this.resourcesCurrentlyBeingLoaded.remove();
      }
   }
}

-> doLoadBeanDefinitions()

// XmlBeanDefinitionReader XML解析类
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {

   try {
      // 获取XML Document实例
      Document doc = doLoadDocument(inputSource, resource);
      // 根据 Document 实例,注册 Bean 信息
      int count = registerBeanDefinitions(doc, resource);
      if (logger.isDebugEnabled()) {
         logger.debug("Loaded " + count + " bean definitions from " + resource);
      }
      return count;
   }
   catch (BeanDefinitionStoreException ex) {
      throw ex;
   }
   catch (SAXParseException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
            "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
   }
   catch (SAXException ex) {
      throw new XmlBeanDefinitionStoreException(resource.getDescription(),
            "XML document from " + resource + " is invalid", ex);
   }
   catch (ParserConfigurationException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
            "Parser configuration exception parsing XML from " + resource, ex);
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
            "IOException parsing XML document from " + resource, ex);
   }
   catch (Throwable ex) {
      throw new BeanDefinitionStoreException(resource.getDescription(),
            "Unexpected exception parsing XML document from " + resource, ex);
   }
}

2.doLoadDocument()

XML文件解析成Docuemnt

private DocumentLoader documentLoader = new DefaultDocumentLoader();

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
   return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
         getValidationModeForResource(resource), isNamespaceAware());
}
// DefaultDocumentLoader 默认的文档加载器
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
      ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

   DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
   if (logger.isTraceEnabled()) {
      logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
   }
   DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
   return builder.parse(inputSource);
}

3.registerBeanDefinitions() 开始注册

注册BeanDefinition

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   // <1>创建对象BeanDefinitionDocumentReader
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   // <2>获取已注册的BeanDefinition数量
   int countBefore = getRegistry().getBeanDefinitionCount();
   // <3>创建XmlReaderContext
   // <4>注册新的Bean
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   // <5>两个相减,返回注册成功的数量
   return getRegistry().getBeanDefinitionCount() - countBefore;
}

-> registerBeanDefinitions()

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
   this.readerContext = readerContext;
   doRegisterBeanDefinitions(doc.getDocumentElement());
}

-> doRegisterBeanDefinitions()

该方法特殊处理profile属性,然后进行解析

protected void doRegisterBeanDefinitions(Element root) {
   // 记录老的 BeanDefinitionParserDelegate 对象
   BeanDefinitionParserDelegate parent = this.delegate;
   // 创建 BeanDefinitionParserDelegate 对象,并进行设置到 delegate
   // *** 它负责解析Document的各种对象
   this.delegate = createDelegate(getReaderContext(), root, parent);

   // 检查 <beans /> 根标签的命名空间是否为空,或者是 http://www.springframework.org/schema/beans
   if (this.delegate.isDefaultNamespace(root)) {
      // 处理profile属性
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      if (StringUtils.hasText(profileSpec)) {
         // 使用分隔符切分,可能有多个 profile 。
         String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
               profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
         // 如果所有 profile 都无效,则不进行注册
         // We cannot use Profiles.of(...) since profile expressions are not supported
         // in XML config. See SPR-12458 for details.
         if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
            if (logger.isDebugEnabled()) {
               logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                     "] not matching: " + getReaderContext().getResource());
            }
            return;
         }
      }
   }

   // 解析前处理, 空实现
   preProcessXml(root);
   // 解析
   parseBeanDefinitions(root, this.delegate);
   // 解析后处理, 空实现
   postProcessXml(root);

   this.delegate = parent;
}


protected void preProcessXml(Element root) {
}

protected void postProcessXml(Element root) {
}

4.parseBeanDefinitions() 分类解析

下面代码中会区分2类,

  1. 默认命名空间,例如:<bean id="book" class="com.h3c.ss.Book"></bean>
  2. 自定义注解方式,例如:<tx:annotation-driven>
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   // <1>如果根节点使用默认命名空间,执行默认解析
   if (delegate.isDefaultNamespace(root)) {
      // 遍历子节点,也就是XML文件中的内容
      NodeList nl = root.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
         Node node = nl.item(i);
         if (node instanceof Element) {
            Element ele = (Element) node;
            // 如果该节点使用默认命名空间,执行默认解析
            if (delegate.isDefaultNamespace(ele)) {
               parseDefaultElement(ele, delegate);
            }
            else {
               // 否则执行自定义解析
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      //<2> 根节点非默认命名空间,执行自定义解析
      delegate.parseCustomElement(root);
   }
}

-> parseDefaultElement() 解析默认命名空间

public static final String BEAN_ELEMENT = "bean";
public static final String IMPORT_ELEMENT = "import";
public static final String ALIAS_ATTRIBUTE = "alias";
public static final String NESTED_BEANS_ELEMENT = "beans";


private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   // 解析import标签
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      importBeanDefinitionResource(ele);
   }
   // 解析alias
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      processAliasRegistration(ele);
   }
   // 解析bean
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      processBeanDefinition(ele, delegate);
   }
   // 解析嵌套的bean
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recurse
      doRegisterBeanDefinitions(ele);
   }
}

-> parseCustomElement() 自定义解析

5.importBeanDefinitionResource() 解析import标签

6.processBeanDefinition() 解析bean标签

解析工作分为三步:

  1. 解析默认标签。
  2. 解析默认标签后下得自定义标签。
  3. 注册解析后的 BeanDefinition 。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   // <1> 如果解析成功,则返回 BeanDefinitionHolder 对象。而 BeanDefinitionHolder 为 name 和 alias 的 BeanDefinition 对象
   // 如果解析失败,则返回 null 。
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
      // <2> 解析默认标签后下得自定义标签
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // <3> 进行 BeanDefinition 的注册
         // 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步:解析beanName

在这里插入图片描述

  1. 解析 idname 属性,确定 aliases 集合
  2. 检测 beanName 是否唯一
  3. 对属性进行解析并封装成 AbstractBeanDefinition 实例 beanDefinition
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
   return parseBeanDefinitionElement(ele, null);
}

@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
   // 获取Id 和 name
   String id = ele.getAttribute(ID_ATTRIBUTE);
   String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

   // 解析name
   List<String> aliases = new ArrayList<>();
   if (StringUtils.hasLength(nameAttr)) {
      // name属性可以存在多个,可以以不同的分隔符间隔,这里就是用于拆分的工具类
      String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
      aliases.addAll(Arrays.asList(nameArr));
   }

   // bean名称优先使用id
   String beanName = id;
   if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
      // 没有id,则默认使用别名的第1个
      beanName = aliases.remove(0);
      if (logger.isTraceEnabled()) {
         logger.trace("No XML 'id' specified - using '" + beanName +
               "' as bean name and " + aliases + " as aliases");
      }
   }

   // 检查bean是否存在重名现象
   if (containingBean == null) {
      checkNameUniqueness(beanName, aliases, ele);
   }

   // 解析属性,构造 AbstractBeanDefinition 对象
   AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
   if (beanDefinition != null) {
      // beanName如果不存在,则需要系统生成
      if (!StringUtils.hasText(beanName)) {
         try {
            // containingBean传过来的就是Null
            if (containingBean != null) {
               beanName = BeanDefinitionReaderUtils.generateBeanName(
                     beanDefinition, this.readerContext.getRegistry(), true);
            }
            else {
               // 生成唯一的 beanName
               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);
      // 返回
      return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
   }

   return null;
}

-> 第2步:解析内部标签

第2步的入口来自第1步
在这里插入图片描述

@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
      Element ele, String beanName, @Nullable BeanDefinition containingBean) {

   this.parseState.push(new BeanEntry(beanName));

   // 解析 class 属性
   String className = null;
   if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
      className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
   }
   // 解析 parent 属性
   String parent = null;
   if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
      parent = ele.getAttribute(PARENT_ATTRIBUTE);
   }

   try {
      // 创建用于承载属性的 AbstractBeanDefinition 实例
      // 创建的类型为: GenericBeanDefinition
      AbstractBeanDefinition bd = createBeanDefinition(className, parent);
    
      // 解析默认 bean 的各种属性
      parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
      // 提取 description
      bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

      // tips:
      // 下面的一堆是解析 <bean>......</bean> 内部的子元素,
      // 解析出来以后的信息都放到 bd 的属性中
      // 解析元数据 <meta />
      parseMetaElements(ele, bd);
      // 解析 lookup-method 属性 <lookup-method />
      parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
      // 解析 replaced-method 属性 <replaced-method />
      parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

      // 解析构造函数参数 <constructor-arg />
      parseConstructorArgElements(ele, bd);
      // 解析 property 子元素 <property />
      parsePropertyElements(ele, bd);
      // 解析 qualifier 子元素 <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;
}

-> 第3步:解析bean属性

第3步的入口来自第2步
在这里插入图片描述

public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
      @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {

   if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
      error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
   }
   // 解析scope属性
   else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
      bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
   }
   else if (containingBean != null) {
      // Take default from containing bean in case of an inner bean definition.
      bd.setScope(containingBean.getScope());
   }

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

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

   String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
   bd.setAutowireMode(getAutowireMode(autowire));

   if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
      String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
      bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
   }

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

   if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
      bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
   }

   // 解析init-method属性
   if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
      String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
      bd.setInitMethodName(initMethodName);
   }
   else if (this.defaults.getInitMethod() != null) {
      bd.setInitMethodName(this.defaults.getInitMethod());
      bd.setEnforceInitMethod(false);
   }

   // 解析destroy-method属性
   if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
      String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
      bd.setDestroyMethodName(destroyMethodName);
   }
   else if (this.defaults.getDestroyMethod() != null) {
      bd.setDestroyMethodName(this.defaults.getDestroyMethod());
      bd.setEnforceDestroyMethod(false);
   }

   if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
      bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
   }
   if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
      bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
   }

   return bd;
}

7.registerBeanDefinition() 注册

从第6步开始,解析完bean标签的各个属性,然后就是要注册到容器中了

// BeanDefinitionReaderUtils#registerBeanDefinition()  该方式是1个工具类,一个中转而已

public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

   // 获取beanName
   // Register bean definition under primary name.
   String beanName = definitionHolder.getBeanName();
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   // 存在别名则注册
   // Register aliases for bean name, if any.
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         registry.registerAlias(beanName, alias);
      }
   }
}

-> registerBeanDefinition() 注册bean

处理过程如下:

  • <1> 对 BeanDefinition 进行校验,该校验也是注册过程中的最后一次校验了,主要是对 AbstractBeanDefinitionmethodOverrides 属性进行校验。
  • <2> 根据 beanName 从缓存中获取 BeanDefinition 对象。
  • <3> 如果缓存中存在,则根据 allowBeanDefinitionOverriding 标志来判断是否允许覆盖。如果允许则直接覆盖。否则,抛出 BeanDefinitionStoreException 异常。
  • <4>若缓存中没有指定 beanNameBeanDefinition,则判断当前阶段是否已经开始了 Bean 的创建阶段?如果是,则需要对 beanDefinitionMap 进行加锁控制并发问题,否则直接设置即可。
  • <5> 若缓存中存在该 beanName 或者单例 bean 集合中存在该 beanName ,则调用 #resetBeanDefinition(String beanName) 方法,重置 BeanDefinition 缓存。
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
      throws BeanDefinitionStoreException {

   // <1> 校验 BeanDefinition 。
   // 校验 beanName 与 beanDefinition 非空
   Assert.hasText(beanName, "Bean name must not be empty");
   Assert.notNull(beanDefinition, "BeanDefinition must not be null");

   // 这是注册前的最后一次校验了,主要是对属性 methodOverrides 进行校验。
   if (beanDefinition instanceof AbstractBeanDefinition) {
      try {
         ((AbstractBeanDefinition) beanDefinition).validate();
      }
      catch (BeanDefinitionValidationException ex) {
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
               "Validation of bean definition failed", ex);
      }
   }

   // <2> 从缓存中获取指定 beanName 的 BeanDefinition
   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
   // <3> 如果已经存在
   // 获取到则代表已经存在
   if (existingDefinition != null) {
      // 如果配置了不允许覆盖,则抛出异常
      if (!isAllowBeanDefinitionOverriding()) {
         throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
      }
      // 覆盖 beanDefinition 大于 被覆盖的 beanDefinition 的 ROLE ,打印 info 日志
      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 + "]");
         }
      }
      // 覆盖 beanDefinition 与 被覆盖的 beanDefinition 不相同,打印 debug 日志
      else if (!beanDefinition.equals(existingDefinition)) {
         if (logger.isDebugEnabled()) {
            logger.debug("Overriding bean definition for bean '" + beanName +
                  "' with a different definition: replacing [" + existingDefinition +
                  "] with [" + beanDefinition + "]");
         }
      }
      // 其它,打印 debug 日志
      else {
         if (logger.isTraceEnabled()) {
            logger.trace("Overriding bean definition for bean '" + beanName +
                  "' with an equivalent definition: replacing [" + existingDefinition +
                  "] with [" + beanDefinition + "]");
         }
      }
      // *************允许覆盖,直接覆盖原有的 BeanDefinition 到 beanDefinitionMap 中*************
      this.beanDefinitionMap.put(beanName, beanDefinition);
   }
   else {
      // <4> 如果未存在
      // 检测创建 Bean 阶段是否已经开启,如果开启了则需要对 beanDefinitionMap 进行并发控制
      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 {
         // 注册到容器中
         this.beanDefinitionMap.put(beanName, beanDefinition);
         // 注册beanName
         this.beanDefinitionNames.add(beanName);
         removeManualSingletonName(beanName);
      }
      this.frozenBeanDefinitionNames = null;
   }

   if (existingDefinition != null || containsSingleton(beanName)) {
      // <5> 重新设置 beanName 对应的缓存
      resetBeanDefinition(beanName);
   }
   else if (isConfigurationFrozen()) {
      clearByTypeCache();
   }
}

-> registerAlias() 注册别名

把别名和beanName的映射关系添加到1个Map里面,key=别名,value=beanName

// SimpleAliasRegistry.java

@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) {
      // name == alias 则去掉alias
      if (alias.equals(name)) {
         this.aliasMap.remove(alias);
         if (logger.isDebugEnabled()) {
            logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
         }
      }
      else {
         // 获取 alias 已注册的 beanName
         String registeredName = this.aliasMap.get(alias);
         // 当注册的beanName存在
         if (registeredName != null) {
            // 相同,则 return ,无需重复注册
            if (registeredName.equals(name)) {
               // An existing alias - no need to re-register
               return;
            }
            // 不允许覆盖,则抛出 IllegalStateException 异常
            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);
         // 注册 alias
         this.aliasMap.put(alias, name);
         if (logger.isTraceEnabled()) {
            logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
         }
      }
   }
}

允许覆盖,则抛出 IllegalStateException 异常
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);
// 注册 alias
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace(“Alias definition '” + alias + “’ registered for name '” + name + “'”);
}
}
}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/5073.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Airtest自动化测试工具实战演练

一开始知道Airtest大概是在年初的时候&#xff0c;当时&#xff0c;看了一下官方的文档&#xff0c;大概是类似Sikuli的一个工具&#xff0c;主要用来做游戏自动化的&#xff0c;通过截图的方式用来解决游戏自动化测试的难题。最近&#xff0c;移动端测试的同事尝试用它的poco库…

iwebsec靶场-命令执行漏洞

漏洞简介 命令执行漏洞&#xff08;Command Injection&#xff09;是一种常见的安全漏洞&#xff0c;也被称为代码注入漏洞。它允许攻击者将恶意代码注入到受攻击的应用程序中&#xff0c;从而可以在应用程序的上下文中执行任意命令。 命令执行漏洞通常出现在Web应用程序中&…

好的表单设计应该遵循什么规则?

在数字化时代&#xff0c;表单已经成为了人们生活中不可或缺的一部分。它们可能是网站注册表格、调查问卷、订单表格或者其他类型的表格。无论表单的类型是什么&#xff0c;都必须经过精心设计才能提供良好的用户体验。在本文中&#xff0c;我们将探讨如何设计一份用户体验好的…

Redis缓存双写一致性

目录双写一致性Redis与Mysql双写一致性canal配置流程代码案例双写一致性理解缓存操作细分缓存一致性多种更新策略挂牌报错,凌晨升级先更新数据库,在更新缓存先删除缓存,在更新数据库先更新数据库,在删除缓存延迟双删策略总结双写一致性 Redis与Mysql双写一致性 canal 主要是…

低代码开发公司:用科技强力开启产业分工新时代!

实现办公自动化&#xff0c;是不少企业的共同追求。低代码开发公司会遵循时代发展规律&#xff0c;注入强劲的科技新生力量&#xff0c;在低代码开发市场厚积爆发、努力奋斗&#xff0c;推动企业数字化转型升级&#xff0c;为每一个企业的办公自动化升级创新贡献应有的力量。 一…

【数据结构与算法】堆的实现(附源码)

目录 一.堆的概念及结构 二.接口实现 A.初始化 Heapinit 销毁 Heapdestroy B.插入 Heappush 向上调整 AdjustUp 1.Heappush 2.AdjustUp C.删除 Heappop 向下调整 AdjustDown D.堆的判空 Heapempty 堆顶数据 Heaptop 堆的大小 Heapsize 三.源码 Heap.h He…

【模板】带权并查集

文章目录1. 奇偶游戏2. 银河英雄传说1. 奇偶游戏 239. 奇偶游戏 题意&#xff1a; 依次给出多个区间的含 111 的个数的奇偶性&#xff0c;找出第一个不符合的答案的回答。 思路&#xff1a; 已知区间[a,b][a,b][a,b][b,c][b,c][b,c]的奇偶性&#xff0c;那么具有传递性&…

分享一个国内可用的免费ChatGPT网站(自己写的)

背景 ChatGPT作为一种基于人工智能技术的自然语言处理工具&#xff0c;近期的热度直接沸腾&#x1f30b;。 作为一个程序员&#xff0c;我也忍不住做了一个基于ChatGPT的网站&#xff0c;免费&#xff01;免登陆&#xff01;&#xff01;国内可直接对话ChatGPT&#xff0c;也…

10.线性表代码实战

10.1 与408关联解析及本节内容介绍 链表比顺序表出现的顺序更加的频繁。 10.2线性表地顺序表示原理解析 线性表的特点&#xff1a; &#xff08;1&#xff09;表中的元素的个数是有限的 &#xff08;2&#xff09;表中元素的数据类型相同。意味着每一个元素占用相同大小的空…

使用Dism++和360安全卫士搞定Windows10离线升级

Windows10有很多版本&#xff0c;常见的由1903、1909、20H1、21H2等&#xff0c;在离线状态下&#xff0c;很难下载到匹配的升级补丁。期间尝试多种方法均失败&#xff0c;最后用Dism和360安全卫士组合拳搞定。 1、使用下载补丁&#xff0c;升级失败 比如这里介绍了常见补丁&a…

【SL101】 传感器接入chirpstack平台

【SL101】 传感器接入chirpstack平台使用硬件SL100工程师答疑chirpstack 中 net-server 使能 80-87 频段网关开启80-87 频段设备传感器端配置频点连接成功测试结果---chirpstackSL100系列温湿度传感器产品&#xff08;墨水屏版&#xff09;接入chirpstack 平台笔记记录 使用硬件…

mysql学习之数据系统概述

☀️马上要成为打工人&#xff0c;这几天把前面的知识都捡了捡&#xff0c;发现自己对关系数据库这块的学习还有所缺失&#xff0c;于是本章开始学习mysql 这里写目录标题1. 数据库系统的发展1.1 人工管理阶段1.2 文件系统阶段1.3 数据库阶段1.4 大数据阶段2 数据库系统的组成2…

了解这7个Node.js库,让你的开发效率提升不止一点点

Node.js是一个流行的JavaScript运行时环境&#xff0c;拥有庞大的生态系统和丰富的库&#xff0c;使得在Node.js上构建高效、可靠的应用程序变得非常容易。在这篇文章中&#xff0c;我们将分享七个有用的Node.js库&#xff0c;它们可以提高您的工作效率&#xff0c;让您更轻松地…

android:手搓一个即时消息聊天框(包含消息记录)

先看一下效果 1.后端 要实现这个&#xff0c;先说一下后端要实现的接口 1.创建会话id 传入“发送id”和“接收id”给服务端&#xff0c;服务端去创建“会话id” 比如 get请求&#xff1a;http://xxxx:8110/picasso/createSession?fromUserId1&toUserId2 返回seesionId…

【SSconv:全色锐化:显式频谱-空间卷积】

SSconv: Explicit Spectral-to-Spatial Convolution for Pansharpening &#xff08;SSconv&#xff1a;用于全色锐化的显式频谱-空间卷积&#xff09; 全色锐化的目的是融合高空间分辨率的全色&#xff08;PAN&#xff09;图像和低分辨率的多光谱&#xff08;LR-MS&#xff…

HTML5 Web 存储

HTML5 Web 存储 在HTML5之前&#xff0c;主要是使用cookies存储&#xff0c;cookies的缺点有&#xff1a;需要在请求头上带着数据&#xff0c;存储大小不过&#xff0c;在4k之内。本节&#xff0c; HTML5 web 存储&#xff0c;一个比cookie更好的本地存储方式。 什么是 HTML5 …

Redis技术详解

Redis技术详解 Redis是一种支持key-value等多种数据结构的存储系统。可用于缓存&#xff0c;事件发布或订阅&#xff0c;高速队列等场景。支持网络&#xff0c;提供字符串&#xff0c;哈希&#xff0c;列表&#xff0c;队列&#xff0c;集合结构直接存取&#xff0c;基于内存&…

Proxmox VE 超融合集群虚拟的NFS服务性能很差的问题解决

作者&#xff1a;田逸&#xff08;formyz&#xff09; 场景描述 五节点Proxmox VE集群&#xff0c;万兆网络,数据网络与存储网络独立&#xff0c;接口两两bond&#xff0c;交换机堆叠。 单机配置两颗AMD 宵龙CPU&#xff0c;核心数48&#xff0c;单台线程数192&#xff0c;单台…

服务器版RstudioServer安装与配置详细教程

Docker部署Rstudio server 背景&#xff1a;如果您想在服务器上运行RstudioServer&#xff0c;可以按照如下方法进行操作&#xff0c;笔者测试时使用腾讯云服务器&#xff08;系统centos7&#xff09;&#xff0c;需要在管理员权限下运行 Rstudio 官方提供了使用不同 R 版本的 …

Baumer工业相机中偏振相机如何使用Baumer堡盟GAPI SDK来进行偏振数据的计算转换输出(C++)

项目场景 Baumer工业相机堡盟相机是一种高性能、高质量的工业相机&#xff0c;可用于各种应用场景&#xff0c;如物体检测、计数和识别、运动分析和图像处理。 Baumer的万兆网相机拥有出色的图像处理性能&#xff0c;可以实时传输高分辨率图像。此外&#xff0c;该相机还具…
最新文章