Spring IOC 之加载解析BeanDefinition及注册进IOC容器

 2023-01-12
原文作者:程序员蛋蛋 原文地址:https://juejin.cn/post/7036363215067955231

IOC容器初始化流程

    ClassPathResource resource = new ClassPathResource("bean.xml"); // <1>
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); // <2>
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); // <3>
    reader.loadBeanDefinitions(resource); // <4>

初始化流程大致三个步骤,资源定位,资源装载,以及资源注册

  • 资源定位:因为一般都是通过外部资源来描述Bean对象,所以初始化IOC容器第一步就是需要定位这个外部资源
  • 装载:装载就是BeanDefinition载入。通过BeanDefinitionReader读取、解析Resource资源,将用户定义的Bean表示成IOC的内部数据结构:BeanDefinition.
  • 注册: 向IOC容器注册在第二步解析好的BeanDefinition。IOC容器维护着一个HashMap,注册就是将BeanDefinition注入到Map中。

loadBeanDefinitions (容器初始化核心方法)

通过XmlBeanDefinitionReader的loadBeanDefinitions方法完成解析和注册

    
    // XmlBeanDefinitionReader.java@Overridepublic int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {      
    return loadBeanDefinitions(new EncodedResource(resource));}

传入定义好的资源,然后对其进行封装,封装是为了对Resource编码
接着看内部调用的
loadBeanDefinitions(new EncodedResource(resource));

     public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
         
            //  获取已经加载过的资源
            Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
            
              // 将当前资源加入记录中,如果已经存在就抛出异常
            if (!((Set)currentResources).add(encodedResource)) {
                throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
            } else {
                int var5;
                try {
                // 从EncodedResource获取封装的Resource,然后从中获取InputStream
                    InputStream inputStream = encodedResource.getResource().getInputStream();
    
                    try {
                        InputSource inputSource = new InputSource(inputStream);
                        if (encodedResource.getEncoding() != null) {
                            inputSource.setEncoding(encodedResource.getEncoding());
                        }
                          // 核心逻辑,执行加载和注册BeanDefinition
                        var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
                    } finally {
                        inputStream.close();
                    }
                } catch (IOException var15) {
                    throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
                } finally {
                // 从缓存中剔除资源
                    ((Set)currentResources).remove(encodedResource);
                    if (((Set)currentResources).isEmpty()) {
                        this.resourcesCurrentlyBeingLoaded.remove();
                    }
    
                }
    
                return var5;
            }
        }
  • 首先会去获取已经加载过的资源,然后将encodeedResource加入其中,如果已经存在该资源就抛出异常,为啥要这样处理,是为了避免EncodedResource在加载时,还没加载完成,又加载自身,从而导致死循环
  • 获取Resource资源,将资源流当中参数传给
    doLoadBeanDefinitions(InputSource inputSource, Resource resource),这个方法才是加载和注册
    Bean Definition的真正逻辑。

doLoadBeanDefinitions

     protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
        // 获取XML Document实例
                Document doc = this.doLoadDocument(inputSource, resource);
                
                // 根据Document实例,注册Bean信息
                return this.registerBeanDefinitions(doc, resource);
        }
  • 首先就是根据xml文件来解析这个xml文件,获得Document实例
  • 然后根据句获取的Document实例,注册Bean信息
    具体如何解析xml,这里不再展开,我们来看看如何注册Bean信息的

注册Bean信息

    
    // XmlBeanDefinitionReader.java
        public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        // 创建BeanDefinitionDocumentReader对象
            BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
            // 获取已注册的BeanDefinition对象数量
            int countBefore = this.getRegistry().getBeanDefinitionCount();
            // 创建xmlReaderContext对象,注册BeanDefinition
            documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
            // 计算新注册的BeanDefinition数量
            return this.getRegistry().getBeanDefinitionCount() - countBefore;
        }
  • createBeanDefinitionDocumentReader()
    创建BeanDefinitionDocumentReader对象,这个对象是用来定义读取Document并注册BeanDefinition功能。
  • registerBeanDefinitions
    这个方法用来读取XML元素,注册BeanDefinition们

BeanDefinitionDocumentReader#registerBeanDefinitions(Document doc, XmlReaderContext readerContext) 方法,注册 BeanDefinition ,在接口 BeanDefinitionDocumentReader 中定义

    public interface BeanDefinitionDocumentReader {
        void registerBeanDefinitions(Document var1, XmlReaderContext var2) throws BeanDefinitionStoreException;
    }
DefaultBeanDefinitionDocumentReader

具体的registerBeanDefinitions方法实现是通过这个类来完成,这个是BeanDefinitionDocumentReader的默认实现类

        public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
            this.readerContext = readerContext;
            this.logger.debug("Loading bean definitions");
            // 获得 XML Document Element
            Element root = doc.getDocumentElement();
            //  执行注册 BeanDefinition
            this.doRegisterBeanDefinitions(root);
        }
doRegisterBeanDefinitions
      protected void doRegisterBeanDefinitions(Element root) {
      // 记录老的BeanDefinitionParserDelegate对象
            BeanDefinitionParserDelegate parent = this.delegate;
            // 创建 BeanDefinitionParserDelegate 对象,并进行设置到 delegate
            this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
            if (this.delegate.isDefaultNamespace(root)) {
                String profileSpec = root.getAttribute("profile");
                if (StringUtils.hasText(profileSpec)) {
                    String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
                    if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                        if (this.logger.isInfoEnabled()) {
                            this.logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
                        }
    
                        return;
                    }
                }
            }
             // 解析前处理
            this.preProcessXml(root);
            // 解析
            this.parseBeanDefinitions(root, this.delegate);
             //解析后处理
            this.postProcessXml(root);
            this.delegate = parent;
        }
  • 创建BeanDefinitionParserDelegate 对象,这个对象是用来解析BeanDefinition
  • 解析前处理和解析后处理这两个方法都是空实现,交由子类来实现
  • parseBeanDefinitions
       protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
            // 根结点使用默认命名空间,执行默认解析
          if (delegate.isDefaultNamespace(root)) {
          
                // 遍历子节点
                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)) {
                            this.parseDefaultElement(ele, delegate);
                        } else {
                        // 根结点使用非默认命名空间,执行自定义解析
                            delegate.parseCustomElement(ele);
                        }
                    }
                }
            } else {
                delegate.parseCustomElement(root);
            }
    
        }

在Spring中有两种Bean声明方式

  • 配置文件式声明: 。对应默认命名空间
  • 自定义注解方式:tx:annotation-driven 。对应非默认命名空间
parseDefaultElement方法
       private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
            if (delegate.nodeNameEquals(ele, "import")) {
                this.importBeanDefinitionResource(ele);
            } else if (delegate.nodeNameEquals(ele, "alias")) {
                this.processAliasRegistration(ele);
            } else if (delegate.nodeNameEquals(ele, "bean")) {
                this.processBeanDefinition(ele, delegate);
            } else if (delegate.nodeNameEquals(ele, "beans")) {
                this.doRegisterBeanDefinitions(ele);
            }
    
        }

这里主要看processBeanDefinition方法,这个方法完成了Bean标签解析的核心工作

    // DefaultBeanDefinitionDocumentReader.java
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
         // 进行Bean元素解析
         //BeanDefinitionHolder为 name 和 alias 的 BeanDefinition 对象
         BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
            if (bdHolder != null) {
            // 自定义标签处理
                bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
    
                try {
                //  进行BeanDefinition注册
                    BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
                } catch (BeanDefinitionStoreException var5) {
                    this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
                }
           // 发出响应事件,通知相关的监听器,已完成Bean标签的解析
                this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
            }
    
        }

解析工作分为三步

  1. 解析默认标签
  2. 解析默认标签下的自定义标签
  3. 注册解析后的BeanDefinition

对BeanDefinition进行注册

注册BeanDefinition是由BeanDefinitionReaderUtils.registerBeanDefinition() 完成。

        public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException {
        // 注册 beanName
        
            String beanName = definitionHolder.getBeanName();
            registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
            // 注册 alias
            String[] aliases = definitionHolder.getAliases();
            if (aliases != null) {
                String[] var4 = aliases;
                int var5 = aliases.length;
    
                for(int var6 = 0; var6 < var5; ++var6) {
                    String alias = var4[var6];
                    registry.registerAlias(beanName, alias);
                }
            }
    
        }
  • 首先通过beanName注册BeanDefinition
  • 然后,再通过注册别名 alias 和 beanName 的映射。

BeanDefinitionRegistry

BeanDefinition的注册,由接口BeanDefinitionRegistry定义

通过beanName注册
    // DefaultListableBeanFactory.java
        public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
        // 校验beanName 与 beanDefinition非空
            Assert.hasText(beanName, "Bean name must not be empty");
            Assert.notNull(beanDefinition, "BeanDefinition must not be null");
            // 校验BeanDefinition
            if (beanDefinition instanceof AbstractBeanDefinition) {
                try {
                    ((AbstractBeanDefinition)beanDefinition).validate();
                } catch (BeanDefinitionValidationException var9) {
                    throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", var9);
                }
            }
            // 从缓存获取指定beanName的BeanDefinition
              BeanDefinition existingDefinition = (BeanDefinition)this.beanDefinitionMap.get(beanName);
      
              // 允许覆盖,直接覆盖原有的BeanDefinition到beanDefinitionMap中
                this.beanDefinitionMap.put(beanName, beanDefinition);
           
                         // 添加BeanDefinition到beanDefinitionMap中
                        this.beanDefinitionMap.put(beanName, beanDefinition);
                        
                          // 添加 beanName 到 beanDefinitionNames 中
                        List<String> updatedDefinitions = new ArrayList(this.beanDefinitionNames.size() + 1);

这段代码的核心就是beanDefinitionMap.put(beanName, beanDefinition);
将BeanDefinition存入到Map中。

总结

202301012053196281.png