Spring的IoC容器启动过程之源码级分析

2023年 9月 12日 77.8k 0

一、概述

Spring的IoC容器的启动过程,核心流程是将bean的配置项从不同渠道,包括XML、注解或者配置文件中读取和解析后,生成BeanDefinition的过程,在这过程中IoC容器会进行refresh操作,这个过程可以设置一些BeanPostProcesser的前置或后置操作,在执行完这些操作后,BeanDefinition就会被注册到BeanDefinitionRegistry容器中。

整体IoC容器的启动过程分为3个阶段:定位—>加载–>注册

  • 定位: 通过ResourceLoader来完成资源的定位,后续的Bean配置项透视通过Resource资源文件来读取和解析的。
  • 加载:ApplicationContext容器调用refresh()方法,解析Bean配置信息将其解析成BeanDefinition,然后调用一系列容器、Bean级别的前置后置处理器,包括调用BeanFactoryPostProcessor 的前置操作。
  • 注册:BeanDefinition实例注册,将生成的BeanDefinition放到容器的缓存池BeanDefinitionRegistry中。
  • 二、IoC容器介绍

    首先必须要明确一点的就是BeanFactory作为所有IoC容器的顶级类,其包括哪些功能和层次结构,其中getBean(String name)是常见方法,通过该方法可以根据beanName获取Bean对象。

    在这里插入图片描述

    BeanFactory和其他IoC容器的层级关系如下。其中常见的是高级IoC容器ApplicationContext,该容器和BeanFactory相比,具有(1)能够响应时间机制;(2)具有国际化机制;

    三、加载过程中常见类介绍

    1.BeanDefinition

    IoC容器管理的是Bean实例和其关系,在实现层面是通过BeanDefinition来进行描述的。

    在这里插入图片描述

    2.BeanDefinitionReader

    BeanDefinitionReader是加载器和解析器,主要功能是从Resource资源类型中解析Bean实例出来,组装成BeanDefinition。

    在这里插入图片描述

    3.BeanDefinitionRegistry

    BeanDefinition的注册表,实例化后的BeanDefinition会注册到这里。

    四、源码层面分析IoC容器加载流程

    IoC的的高级容器ApplicationContext有多种实现,现在以从XML中加载Bean的配置项的实例ClassPathXmlApplicationContext来具体说明IoC容器的加载流程。

    1.定位阶段

    通过XML配置文件启动一个ApplicationContext容器的方法。

    	public static void main(String[] args) {
    		// 创建一个Spring容器
    		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    		User user1 = (User) applicationContext.getBean("user1");
    		user1.sayHello();
        }
    

    2.加载阶段

    通过ClassPathXmlApplicationContext类中的 refresh()进行刷新操作。

    //ClassPathXmlApplicationContext.java
    ...
    public ClassPathXmlApplicationContext(
          String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
          throws BeansException {
    
       super(parent);
       setConfigLocations(configLocations);
       if (refresh) {
          refresh();//my-刷新容器,生成BeanDefinition
       }
    }
    ...
    

    实现refresh()方法,其中核心是执行:obtainFreshBeanFactory() 方法,会创建一个BeanFactory的实例。同时在这个方法内也会进行一系列的初始化前或后的操作,比如执行方法:postProcessBeanFactory(beanFactory);

    //AbstractApplicationContext.java
    
    public void refresh() throws BeansException, IllegalStateException {
       synchronized (this.startupShutdownMonitor) {
          StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
    
          // Prepare this context for refreshing.
          prepareRefresh();
    
          // Tell the subclass to refresh the internal bean factory.
          // 这里会判断能否刷新,并且返回一个BeanFactory, 刷新不代表完全情况,主要是先执行Bean的销毁,然后重新生成一个BeanFactory,再在接下来的步骤中重新去扫描等等
          ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
          // Prepare the bean factory for use in this context.
          // 准备BeanFactory
          // 1. 设置BeanFactory的类加载器、SpringEL表达式解析器、类型转化注册器
          // 2. 添加三个BeanPostProcessor,注意是具体的BeanPostProcessor实例对象
          // 3. 记录ignoreDependencyInterface
          // 4. 记录ResolvableDependency
          // 5. 添加三个单例Bean
          prepareBeanFactory(beanFactory);
    
          try {
             // Allows post-processing of the bean factory in context subclasses.
             // 子类来设置一下BeanFactory
             postProcessBeanFactory(beanFactory);
    
             StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
    
             // Invoke factory processors registered as beans in the context.
             // BeanFactory准备好了之后,执行BeanFactoryPostProcessor,开始对BeanFactory进行处理
             // 默认情况下:
             // 此时beanFactory的beanDefinitionMap中有6个BeanDefinition,5个基础BeanDefinition+AppConfig的BeanDefinition
             // 而这6个中只有一个BeanFactoryPostProcessor:ConfigurationClassPostProcessor
             // 这里会执行ConfigurationClassPostProcessor进行@Component的扫描,扫描得到BeanDefinition,并注册到beanFactory中
             // 注意:扫描的过程中可能又会扫描出其他的BeanFactoryPostProcessor,那么这些BeanFactoryPostProcessor也得在这一步执行
             invokeBeanFactoryPostProcessors(beanFactory);  // scanner.scan()
    
             // Register bean processors that intercept bean creation.
             // 将扫描到的BeanPostProcessors实例化并排序,并添加到BeanFactory的beanPostProcessors属性中去
             registerBeanPostProcessors(beanFactory);
    
             beanPostProcess.end();
    
             // Initialize message source for this context.
             // 设置ApplicationContext的MessageSource,要么是用户设置的,要么是DelegatingMessageSource
             initMessageSource();
    
             // Initialize event multicaster for this context.
             // 设置ApplicationContext的applicationEventMulticaster,要么是用户设置的,要么是SimpleApplicationEventMulticaster
             initApplicationEventMulticaster();
    
             // Initialize other special beans in specific context subclasses.
             // 给子类的模板方法
             onRefresh();
    
             // Check for listener beans and register them.
             // 把定义的ApplicationListener的Bean对象,设置到ApplicationContext中去,并执行在此之前所发布的事件
             registerListeners();
    
             // Instantiate all remaining (non-lazy-init) singletons.
             finishBeanFactoryInitialization(beanFactory);
    
             // Last step: publish corresponding event.
             finishRefresh();
          }
    

    具体执行加载BeanDefinition实例的方法。

    //AbstractApplicationContext.java
    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
       refreshBeanFactory();
       return getBeanFactory();
    }
    
    ...
    //AbstractRefreshableApplicationContext.java
    protected final void refreshBeanFactory() throws BeansException {
    		if (hasBeanFactory()) {
    			destroyBeans();
    			closeBeanFactory();
    		}
    		try {
    			DefaultListableBeanFactory beanFactory = createBeanFactory();
    			beanFactory.setSerializationId(getId());
    			customizeBeanFactory(beanFactory);
    			loadBeanDefinitions(beanFactory);//my-加载BeanDefinition类
    			this.beanFactory = beanFactory;
    		}
    		catch (IOException ex) {
    			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    		}
        }
    

    需要从资源文件中读取和解析Bean的配置信息,主要通过XmlBeanDefinitionReader来实现。

    //AbstractXmlApplicationContext.java
    
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
       // Create a new XmlBeanDefinitionReader for the given BeanFactory.
       XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
       // Configure the bean definition reader with this context's
       // resource loading environment.
       beanDefinitionReader.setEnvironment(this.getEnvironment());
       beanDefinitionReader.setResourceLoader(this);
       beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    
       // Allow a subclass to provide custom initialization of the reader,
       // then proceed with actually loading the bean definitions.
    		initBeanDefinitionReader(beanDefinitionReader);//my-初始化XmlBeanDefinitionReader来
    		loadBeanDefinitions(beanDefinitionReader);//my-通过XmlBeanDefinitionReader来读取BeanDefinition
    }
    ...
      
    //AbstractXmlApplicationContext.java
    
    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
       Resource[] configResources = getConfigResources();
       if (configResources != null) {
          reader.loadBeanDefinitions(configResources);
       }
       String[] configLocations = getConfigLocations();
       if (configLocations != null) {
          reader.loadBeanDefinitions(configLocations);
       }
    }
    

    实际加载BeanDefinitions的方法。

    //XmlBeanDefinitionReader.java
    
    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 currentResources = this.resourcesCurrentlyBeingLoaded.get();
    
       if (!currentResources.add(encodedResource)) {
          throw new BeanDefinitionStoreException(
                "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
       }
    
       //my-从当前目录中加载bean的配置项
       try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
          InputSource inputSource = new InputSource(inputStream);
          if (encodedResource.getEncoding() != null) {
             inputSource.setEncoding(encodedResource.getEncoding());
          }
          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();
          }
       }
    }
    
    ...
    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 currentResources = this.resourcesCurrentlyBeingLoaded.get();
    
    		if (!currentResources.add(encodedResource)) {
    			throw new BeanDefinitionStoreException(
    					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    		}
    
    		//my-从当前目录中加载bean的配置项
    		try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
    			InputSource inputSource = new InputSource(inputStream);
    			if (encodedResource.getEncoding() != null) {
    				inputSource.setEncoding(encodedResource.getEncoding());
    			}
    			return doLoadBeanDefinitions(inputSource, encodedResource.getResource());//my-实际从XML中解析Bean
    		}
    		catch (IOException ex) {
    			throw new BeanDefinitionStoreException(
    					"IOException parsing XML document from " + encodedResource.getResource(), ex);
    		}
    		finally {
    			currentResources.remove(encodedResource);
    			if (currentResources.isEmpty()) {
    				this.resourcesCurrentlyBeingLoaded.remove();
    			}
    		}
    }
    

    3.注册阶段

    开始进行注册。

    //XmlBeanDefinitionReader.java
    
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
          throws BeanDefinitionStoreException {
    
       try {
          // spring.xml对应的Document对象
          Document doc = doLoadDocument(inputSource, resource);
          // 解析spring.xml
          int count = registerBeanDefinitions(doc, resource);
          if (logger.isDebugEnabled()) {
             logger.debug("Loaded " + count + " bean definitions from " + resource);
          }
          return count;
       }
    

    通过建立委托类BeanDefinitionParserDelegate来进行注册。

    doRegisterBeanDefinitions(Element root) {
       // Any nested  elements will cause recursion in this method. In
       // order to propagate and preserve  default-* attributes correctly,
       // keep track of the current (parent) delegate, which may be null. Create
       // the new (child) delegate with a reference to the parent for fallback purposes,
       // then ultimately reset this.delegate back to its original (parent) reference.
       // this behavior emulates a stack of delegates without actually necessitating one.
       BeanDefinitionParserDelegate parent = this.delegate;
       this.delegate = createDelegate(getReaderContext(), root, parent);
    
       if (this.delegate.isDefaultNamespace(root)) {
          String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
          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);//my-进行注册的核心方法
       postProcessXml(root);
    

    将BeanDefinition注册到IoC容器,其底层的数据结构还是HashMap。

    //SimpleBeanDefinitionRegistry.java
    
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
          throws BeanDefinitionStoreException {
    
       Assert.hasText(beanName, "'beanName' must not be empty");
       Assert.notNull(beanDefinition, "BeanDefinition must not be null");
       this.beanDefinitionMap.put(beanName, beanDefinition);
    }
    

    4.各阶段时序图

    创建ClassPathXmlApplicationContext的IoC容器类的时序图。

    在这里插入图片描述

    Spring的IoC容器启动过程之源码级分析:www.processon.com/diagraming/…

    五、总结

    最后总结一下,Spring IoC启动的过程就是将Bean组装成BeanDefinition保存在HashMap中的过程。

    TODO

    参考资料

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论