Spring标签的解析及注册源码解析

介绍

  1. 本文仅做学习记录,若有不正确的地方,请多多指教
  2. Spring版本:5.2.x(代码可能略有不同)

配置文件的加载

测试代码

//替代xmlBeanFactory
//bean工厂
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
//资源管理器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
//资源 - 配置文件
ClassPathResource resource = new ClassPathResource("DefaultApplicationContext.xml");
//解析bean
reader.loadBeanDefinitions(resource);

准备工作

  1. DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

//跟踪代码会发现一共会实例化2个父类
//1. AbstractAutowireCapableBeanFactory
//2. AbstractBeanFactory
//期间,AbstractAutowireCapableBeanFactory的构造器如下
public AbstractAutowireCapableBeanFactory() {
   super();
   //将Class类型加入集合ignoredDependencyInterfaces中 即add操作
   ignoreDependencyInterface(BeanNameAware.class);
   ignoreDependencyInterface(BeanFactoryAware.class);
   ignoreDependencyInterface(BeanClassLoaderAware.class);
}

  1. XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
//在XmlBeanFactoryReader构造器中调用了super(registry)父类构造器方法
//以下是父类AbstractBeanFactoryReader构造方法
//ps:DefaultListableBeanFactory实现了BeanFactoryRegistry接口,所以它也是BeanDefinitionRegistry类型
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
   Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
   //将DefaultListableBeanFactory赋值给当前类属性
   this.registry = registry;

   // Determine ResourceLoader to use.
   //因为当前beanFactory并没有实现ResourceLoader接口 不能解析xml if进入失败
   if (this.registry instanceof ResourceLoader) {
      this.resourceLoader = (ResourceLoader) this.registry;
   }
   else {
       //PathMatchingResourcePatternResolver是作为资源路径的解析器 支持解析通配符和classpath:等
       //在实例化时,会new DefaultResourceLoader();给成员变量resourceLoader赋值
       //ps:PathMatchingResourcePatternResolver和DefaultResourceLoader都是ResourceLoader的子类
       //DefaultResourceLoader的getResources方法可以获取资源(这里可以当做配置文件)
       //而PathMatchingResourcePatternResolver加入了Ant解析资源路径 所以它的getResources功能相对更强大一些
      this.resourceLoader = new PathMatchingResourcePatternResolver();
   }

   // Inherit Environment if possible
   // DefaultListableBeanFactory也不是Environment子类 所以走else
   if (this.registry instanceof EnvironmentCapable) {
      this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
   }
   else {
       //StandardEnvironment:包含了环境配置数据 如spring.profiles.active
       //但是StandardEnvironment无参构造器并无任何操作 所以这里一切都是默认值 仅做了实例化操作
      this.environment = new StandardEnvironment();
   }
}
  1. ClassPathResource resource = new ClassPathResource(“DefaultApplicationContext.xml”);
//在ClassPathResource单参构造器中调用了以下构造器 其中classLoader默认为null
public ClassPathResource(String path, @Nullable ClassLoader classLoader) {
   Assert.notNull(path, "Path must not be null");
   //对资源路径做一些转换 如window的""转为"/"
   String pathToUse = StringUtils.cleanPath(path);
   //如果以“/”开头 则去除
   if (pathToUse.startsWith("/")) {
      pathToUse = pathToUse.substring(1);
   }
   //记录资源路径
   this.path = pathToUse;
   this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}

一切准备工作结束,就可以开始解析xml文件了。

XML文件解析(无关代码或步骤将略过)

1. 入口:reader.loadBeanDefinitions(resource)
2. 参数:
- reader:XmlBeanDefinitionReader
- resource:ClassPathResource
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
   //EncodedResource是记录资源编码字符的对象
   //这里实例化EncodedResource 在构造器中仅将resource赋值给了成员变量作以记录 编码字符均默认为null
   return loadBeanDefinitions(new EncodedResource(resource));
}


public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {

   //值得一提的是 resourcesCurrentlyBeingLoaded是ThreadLocal 保存的类型是Set<EncodingResource>类型 默认容量为4
   //这里获取了当前线程所加载过的资源文件
   Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();

   //如果add失败 则说明当前文件已加载过
   if (!currentResources.add(encodedResource)) {
      throw new BeanDefinitionStoreException(
            //检测到xx(某资源文件)循环加载
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
   }

   //获取当前文件的输入流
   try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
      //inputSource XML实体的单个输入源
      InputSource inputSource = new InputSource(inputStream);
      if (encodedResource.getEncoding() != null) {
         inputSource.setEncoding(encodedResource.getEncoding());
      }
      //加载xml核心逻辑开始 (查看下一代码片段
      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()) {
        //如果当前线程完成了所有资源的加载 则移除LocalThread当前线程数据
         this.resourcesCurrentlyBeingLoaded.remove();
      }
   }
}

/**
  * XML解析核心逻辑
  * param: inputSource 当前资源ClassPathResource输入流
  * param: resource 当前资源ClassPathResource对象
  */
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {

   try {
      //加载xml文件 获取document对象 用于解析xml
      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 (xxException ex) {
       //略
   }
}

/**
 * 根据资源获取Document对象
 */
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
    //this.documentLoader = new DefaultDocumentLoader(); 默认生成的无参构造器 无任何操作
    //其余参数方法的解析请查看下一代码块
   return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
         getValidationModeForResource(resource), isNamespaceAware());
}

//DefaultCocumentLoader类
/**
 * param: inputSource XML实体的单个输入源
 * param: entityResolver -> ResourceEntityResolver 用于解析DTD或XSD的XML文件
 * param:errorHandler 错误处理器
 * param:validationMode XML文件验证模式 这里是XSD(3)
 * param:namespaceAware 为false (true时表示开启解析命名空间)
 */
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
      ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
   //在factory的创建中 如果验证模式是XSD 则会设置namespaceAware为true 这里是开启了命名空间感知
   //实际类型是生成了com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
   DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
   if (logger.isTraceEnabled()) {
      logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
   }
   //使用factory创建一个 JAXP DocumentBuilder,将使用它来解析 XML 文档
   //com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl
   DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
   //生成com.sun.org.apache.xerces.internal.dom.DeferredDocumentImpl对象
   return builder.parse(inputSource);
}

生成的Document对象:
在这里插入图片描述

对于doLoadDocument方法中的参数方法的解释

  • getEntityResolver()
/**
* 
* 当解析xml时 需要寻找DTD定义 以便对文档进行验证 而默认的方式是通过声明DTD的uri地址去下载对应文件
* 但是这种方式可能会因为网络原因下载失败而导致报错
* 所以该方法是提供一个寻找DTD声明的方法 即读取本地DTD文件路径
*/
protected EntityResolver getEntityResolver() {
    //初次调用 entityResolver默认为null
   if (this.entityResolver == null) {
      //获取资源加载器 
      //在实例化XmlBeanDefinitionReader时就给resourceLoader赋值了 所以get出来的对象是PathMatchingResourcePatternResolver
      ResourceLoader resourceLoader = getResourceLoader();
      if (resourceLoader != null) {
          //详见下一代码片段
         this.entityResolver = new ResourceEntityResolver(resourceLoader);
      }
      else {
         this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
      }
   }
   return this.entityResolver;
}

public ResourceEntityResolver(ResourceLoader resourceLoader) {
    //调用父类DelegatingEntityResolver构造器
   super(resourceLoader.getClassLoader());
   //记录resourceLoader 即PathMatchingResourcePatternResolver
   this.resourceLoader = resourceLoader;
}

public DelegatingEntityResolver(@Nullable ClassLoader classLoader) {
    //初始化dtd文件的解析器
   this.dtdResolver = new BeansDtdResolver();
   //解析META-INF/spring.schemas文件(该路径是默认值)这里边包含了XML所有命名空间对应的xsd文件路径 直接本地获取 无需网络下载
   this.schemaResolver = new PluggableSchemaResolver(classLoader);
}
  • getValidationModeForResource(Resource resource)
/**
* 获取验证xml的方式 主要用于验证xml文件格式是否正确 文件是否有效
* 验证XML的方式有两种:
*  1. DTD:需要在XML文件头部声明
*  <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd">
*      意思就是使用spring-beans-2.0.dtd该文件来验证当前文件
*
*  2. XSD:XML Schema语言。该语言用于描述XML文件的结构 也是用于验证XML格式是否正确的
*  xmlns="http://www.springframework.org/schema/beans"
*  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
*  xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
*     http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"
*  xmlns=表示命名空间
*  xsi:schemaLocation=命名空间 + 该命名空间所标识的XML Schema文件位置
*
* 默认验证模式:VALIDATION_AUTO(其实最后返回的默认验证模式是XSD
*/
protected int getValidationModeForResource(Resource resource) {
   //可以通过 setValidationMode设定验证模式
   int validationModeToUse = getValidationMode();//成员变量validationMode 默认VALIDATION_AUTO
   if (validationModeToUse != VALIDATION_AUTO) {
      return validationModeToUse;
   }
   //如果没有【显式】的配置验证模式 则从inputStream中读取XML资源内容获取 VALIDATION_DTD or VALIDATION_XSD
   int detectedMode = detectValidationMode(resource);
   //如果发生了异常 则会返回VALIDATION_AUTO
   if (detectedMode != VALIDATION_AUTO) {
      return detectedMode;
   }
   
   return VALIDATION_XSD;
}
  • isNamespaceAware()
public boolean isNamespaceAware() {
/**
 * 该属性在关闭XML验证模式后会设置为true 即开启解析命名空间
 * 以使无验证模式下 仍能正确使用命名空间解析XML文件标签
 */
   return this.namespaceAware;
}

继续执行int count = registerBeanDefinitions(doc, resource);

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    //创建DefaultBeanDefinitionDocumentReader根据“spring-beans”DTD 和 XSD 格式(Spring 的默认 XML bean 定义格式)读取 bean 定义。
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   //registry就是XmlBeanFactory对象 这里是已注册的bean数量 
   //即DefaultListableBeanFactory的beanDefinitionMap属性的长度 默认256的容量 
   int countBefore = getRegistry().getBeanDefinitionCount();//map.size()
   //核心代码 注册bean
   createReaderContext方法返回XmlReaderContext对象 俗称文档阅读器
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   //返回注册数 即DefaultListableBeanRegistry的beanDefinitionMap的size - 注册前的size
   return getRegistry().getBeanDefinitionCount() - countBefore;
}

//DefaultBeanDefinitionDocumentReader类方法
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    //把文档阅读器赋值给了DefaultBeanDefinitionDocumentReader
   this.readerContext = readerContext;
   //这里的getDocumentElement 返回的是代表Beans标签的com.sun.org.apache.xerces.internal.dom.DeferredElementNSImpl对象(父类Element)
   //beans标签就是spring配置文件的根节点
   doRegisterBeanDefinitions(doc.getDocumentElement());
}

protected void doRegisterBeanDefinitions(Element root) {
   //BeanDefinitionParserDelegate用于解析 XML bean 定义的有状态委托类。供主解析器和任何扩展使用
   BeanDefinitionParserDelegate parent = this.delegate;//第一次为null
   
   //创建BeanDefinitionParserDelegate对象 它包含着一个属性DocumentDefaultsDefinition
   //这个成员属性中包含了当前标签上的定义 如lazy-init值 init-method值
   //并在其中初始化完成BeanDefinitionParserDelegate对象后 触发注册事件来做一些事情(默认为空 开发者自行实现)
   this.delegate = createDelegate(getReaderContext(), root, parent);
   /**
    * 1. 空
    * 2. http://www.springframework.org/schema/beans
    * 校验是否是spring的配置文件
    */
   if (this.delegate.isDefaultNamespace(root)) {//判断是不是beans标签 拿命名空间做比较
      //获取profile属性 配置分离
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      //以下是加载不同配置文件的逻辑 若无指定profile 直接跳过
      if (StringUtils.hasText(profileSpec)) {
         String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
               profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
         // 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 parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   //
   if (delegate.isDefaultNamespace(root)) {
      //获取子标签 其实是把xml转换成了一行一行的文本
      NodeList nl = root.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
         //顺序获取标签对象 这里不要误解 是获取xml一行一行的文本内容 然后判断当前行是不是标签 或者是空行 又或者是xml注释 
         //如果是标签 则会读取整个标签体内容 封装成标签对象了
         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 {
      delegate.parseCustomElement(root);
   }
}

默认标签的解析

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);
   }
   //对beans标签解析
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // recurse
      doRegisterBeanDefinitions(ele);
   }
}

//bean标签的解析
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   //解析bean标签 见下一代码片段
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
      /**
       * 用于处理自定义属性 适用以下场景
       * <bean id="xx" class="xx">
       *     <custom-element></custom-element>
       * </bean>
       * 再次遍历所有标签 通过命名空间来解析自定义属性
       */
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // Register the final decorated instance. 注册最终的装饰实例 ★
         //这里就是bean要注册进ioc容器的入口了(请查看下一代码块
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
         getReaderContext().error("Failed to register bean definition with name '" +
               bdHolder.getBeanName() + "'", ele, ex);
      }
      // Send registration event.发送注册事件。
      // 对注册BeanDefinition事件进行监听 这个方法并未实现 继承EmptyReaderEventListener实现
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}

/**
 * 参数:
 * ele: bean标签
 * containingBean:null
 */
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<>();
   //如果当前元素有name属性 则使用“,;”拆分
   if (StringUtils.hasLength(nameAttr)) {
      String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
      aliases.addAll(Arrays.asList(nameArr));
   }

   //默认把id为bean注册的name
   String beanName = id;
    //如果当前bean没有指定id作为注册时使用的name 则用name属性定义的第一个名称作为beanName
    if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
       beanName = aliases.remove(0);
    }

   if (containingBean == null) {
   //官方解释:验证指定的 bean 名称和别名尚未在当前级别的 beans 元素嵌套中使用
      //校验bean名称是否重复 如果不重复 则将beanName及其定义的别名通通加入this.usedName 防止其他bean重名
      checkNameUniqueness(beanName, aliases, ele);
   }

   //bean的所有属性都封装在子类 GenericBeanDefinition中 见下一代码片段
   AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
   if (beanDefinition != null) {
      if (!StringUtils.hasText(beanName)) {
         try {
            if (containingBean != null) {
            //内部bean名称生成
               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;
         }
      }
      //这里的别名是通过name属性指定的 不是alias
      String[] aliasesArray = StringUtils.toStringArray(aliases);
      //返回beanDefinitionHolder对象 这个对象封装了对于bean维度的信息 即GenericBeanDefinition,bean名称,别名
      return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
   }

   return null;
}

public AbstractBeanDefinition parseBeanDefinitionElement(
      Element ele, String beanName, @Nullable BeanDefinition containingBean) {
   //this.parseState是ArrayDeque类型 双队列,BeanEntry对象没什么特别的 就是一个包含了beanName的对象
   //用于存储正在解析的标签 如bean/property等 解析完毕后会弹出 先进先出 这里当作栈来使用
   this.parseState.push(new BeanEntry(beanName));
   
   //记录当前类的class路径
   String className = null;
   if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
      className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
   }

   //bean元素可以指定parent属性 将其作为当前bean的父类 具有继承意义
   String parent = null;
   if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
      parent = ele.getAttribute(PARENT_ATTRIBUTE);
   }

   //以下仅仅是解析工作 并没有真正设置到真实的对象当中
   try {
      //用于创建 承载解析标签得到的所有内容的AbstractBeanDefinition子类的GenericBeanDefinition
      //指定父类名称 如果存在classLoader 则立马加载当前bean(this.readerContext.getBeanClassLoader
      //在xml中可以使用parent属性指定父类引用
      AbstractBeanDefinition bd = createBeanDefinition(className, parent);//这里仅设置了上面获取的parent属性值到GenericBeanDefinition对象中

      //解析bean的各种属性到GenericBeanDefinition 如abstract/auto-wire/depend-on/primary等
      parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
      bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

      //会遍历当前bean的所有子节点 解析当前bean的元数据<meta>标签内容并并封装到GenericBeanDefinition(父类AttributeAccessorSupport的成员属性attributes[map]中
      parseMetaElements(ele, bd);
      
      //解析lookup-method属性 同上
      parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
      
      //解析replaced-method属性 同上
      /**
       * replaced-method
       * A类实现了MethodReplacer类后就可以注册到容器中 当B类中的某个方法xx需要被替换
       *     则在XML中写入
       *        <bean class="B">
       *           <replaced-method name="xx" replacer="A"></replaced-method>
       *         </bean>
       * 这样 方法xx就可以被替换 所以返回值也可能会变化
       */
      parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

      //解析constructor-arg属性
      parseConstructorArgElements(ele, bd);
      
      //解析property属性 把解析出来的属性放在MutablePropertyValues类的propertyValueList[List]中
      //仅解析bean本身 不包括父类
      parsePropertyElements(ele, bd);//如果遇上子元素为bean标签 则进入方法parsePropertySubElement后,重新递归parseBeanDefinitionElement方法进行解析
      
      //解析qualifier属性
      //存储在GenericBeanDefinition的父类AbstractBeanDefinition的qualifiers[map]属性中
      parseQualifierElements(ele, bd);

      bd.setResource(this.readerContext.getResource());
      //设置来源
      bd.setSource(extractSource(ele));

      return bd;
   }
   catch (ClassNotFoundException ex) {
      //略
   }
   finally {
      this.parseState.pop();
   }

   return null;
}

Bean的注册

在上面的方法:processBeanDefinition中,有这段代码:

try {
         // Register the final decorated instance. 注册最终的装饰实例 ★
         //这里就是bean要注册进ioc容器的入口了(请查看下一代码块
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
         //略
      }

跟踪代码查看实现:

/**
 * 参数:
 * definitionHolder: 当前bean的BeanDefinitionHolder对象
 * registry: defaultListableBeanFactory
 */
public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

   // 当前bean名称
   String beanName = definitionHolder.getBeanName();
   /**
    * 使用beanName将BeanDefinition注册进beanDefinitionMap
    */
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   // 如果有别名 则要注册别名
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         /**
          * registerAlias:SimpleAliasRegistry的方法 也是DefaultListableBeanFactory的父类
          * 见下面的代码片段
          */
         registry.registerAlias(beanName, alias);
      }
   }
}

/**
 * 参数:
 * beanName 对象注册bean名称
 * beanDefinition 就是上面用于封装bean所有属性及子标签的GenericBeanDefinition对象
 */
@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");
   //是GenericBeanDefinition对象(AbstractBeanDefinition子类 这里需要对该对象做一次校验
   if (beanDefinition instanceof AbstractBeanDefinition) {
      try {
         /**
          * 这里校验不能同时指定<lookup-method/><replaced-method/>和<factory-method/>二者
          * 并且如果指定了<lookup-method/><replaced-method/> 
          * 则会校验方法是否存在 并给他们的MethodOverride对象上的属性overLoading(是否重载)设为false
          */
         ((AbstractBeanDefinition) beanDefinition).validate();
      }
      catch (BeanDefinitionValidationException ex) {
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
               "Validation of bean definition failed", ex);
      }
   }

//从已注册的bean集合中获取当前beanName的对象
   BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
   //不为null 则说明当前bean已经注册过了
   if (existingDefinition != null) {
      //是否允许当前bean覆盖第一个bean(同名情况下) 默认true
      if (!isAllowBeanDefinitionOverriding()) {
         throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
      }
      //这里开始就无论如何都会覆盖当前已存在的bean 部分日志输出代码省略
     
      //覆盖
      this.beanDefinitionMap.put(beanName, beanDefinition);
   }
   //不存在同名的bean 即当前bean还未被注册
   else {
      //这里去判断在此期间是否有任何 bean 被标记为已创建, 存在则返回true 不存在返回false
      if (hasBeanCreationStarted()) {
          //加锁保证线程安全
         synchronized (this.beanDefinitionMap) {
             //将当前bean注册进beanDefinitionMap
            this.beanDefinitionMap.put(beanName, beanDefinition);
            /**
             * beanDefinitionNames是实例注册的名称排序集合List 按注册先后顺序排列
             * 以下操作就是把当前beanName加入beanDefinitionNames集合中
             */
            List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
            updatedDefinitions.addAll(this.beanDefinitionNames);
            updatedDefinitions.add(beanName);
            //将当前实例放入map 注册完成
            this.beanDefinitionNames = updatedDefinitions;
            removeManualSingletonName(beanName);
         }
      }
      //还没有实例注册 则当前容器内是空的
      else {
         // Still in startup registration phase
         this.beanDefinitionMap.put(beanName, beanDefinition);
         this.beanDefinitionNames.add(beanName);
         removeManualSingletonName(beanName);
      }
      //缓存beanName的数组清空
      this.frozenBeanDefinitionNames = null;
   }

   /**
    * existingDefinition:若存在同名bean时,该对象是当前对象注册前就已注册的“可能”被覆盖的bean
    * containsSingleton()会去DefaultSingletonBeanRegistry的singletonObjects[map]中查找是否存在
    * 当前bean的单例实例缓存。注意:【singletonObjects是缓存单例实例bean的map集合】
    */
   if (existingDefinition != null || containsSingleton(beanName)) {
      //重置所有beanName对应的缓存
      resetBeanDefinition(beanName);
   }
   else if (isConfigurationFrozen()) {
      //删除关于按类型映射的任何假设
      clearByTypeCache();
   }
}

//别名的注册
public void registerAlias(String name, String alias) {
   //aliasMap: Map<String, String> aliasMap = new ConcurrentHashMap<>(16);
   synchronized (this.aliasMap) {
      if (alias.equals(name)) {
         //如果别名和bean注册名一致 则不保存 其实也是覆盖的一种方式
         this.aliasMap.remove(alias);
         
      }
      else {
         //获取别名注册的beanName
         String registeredName = this.aliasMap.get(alias);
         // 如果当前别名已经被注册
         if (registeredName != null) {
            //如果当前beanName和已经注册的别名的bean名称一致 则返回
            if (registeredName.equals(name)) {
               // An existing alias - no need to re-register 翻译:已存在别名与当前beanName相同 不需要重复注册
               return;
            }
            //如果允许覆盖 则直接覆盖 默认为true
            if (!allowAliasOverriding()) {
               throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
                     name + "': It is already registered for name '" + registeredName + "'.");
            }
            
         }
         /**
          * <bean name="B" class="MDJBean.Car"/>
          * <bean name="C" class="MDJBean.Car"/>
          * <alias name="B" alias="A"/>   // B类的名字有两个 一个是B 一个是A 即大名叫B 小名叫A 只要是这两个名字 都可以指向B
          * <alias name="C" alias="B"/>   // C类的名称也有两个 一个是C 一个是B
          * //单单以上是不会报错的 因为别名不同 不出现闭环 getBean("B")返回的是C对象 详见参考获取对象方法(getBean("A")也会返回C对象
          * //假设现在添加一个别名 A可以叫C (同时需要定义一个A的bean 否则这个别名是没用的 也就不存在循环了)
          * <alias name="A" alias="C"/> //那么通过C别名可以找到A名称的类(这时候spring会再次调用getBean("A") 但getBean又会来aliasMap中找 找到A对应的名称是B 以此产生循环
          * 这样就出现了闭环 循环引用
          * 以下方法就是校验防止出现以上情况
          */
         checkForAliasCircle(name, alias);
         //注册别名
         this.aliasMap.put(alias, name);
         if (logger.isTraceEnabled()) {
            logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
         }
      }
   }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值