二.Spring源码剖析-IOC启动流程

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 上一章节我们讲的是Spirng中的一些核心类,包括IOC容器工厂,和配置解析的一些类,这一章主要是跟一下IOC启动流程。这里我先贴一下IOC的启动部分流程图,在后面的源码分析就是在走这张图,为什么是部分流程图,因为我先分多篇文章来写IOC启动流程,太长了看起来费劲。我把IOC启动流程分为4个阶段:容器创建 -> 配置加载 -> Bean的解析 -> Bean的注册,如下:

前言

上一章节我们讲的是Spirng中的一些核心类,包括IOC容器工厂,和配置解析的一些类,这一章主要是跟一下IOC启动流程。这里我先贴一下IOC的启动部分流程图,在后面的源码分析就是在走这张图,为什么是部分流程图,因为我先分多篇文章来写IOC启动流程,太长了看起来费劲。我把IOC启动流程分为4个阶段:容器创建 -> 配置加载 -> Bean的解析 -> Bean的注册,如下:
在这里插入图片描述

ClasspathXmlApplicationContext 容器

源码分析入口从 ClasspathXmlApplicationContext 开始,通过它来加载一个配置

//加载Spring配置文件,拿到Spring容器
ApplicationContext context = new  ClassPathXmlApplicationContext("配置文件.xml")
//从容器中拿到对象实例
MyBean myBean = context.getBean(MyBean.class);

进入ClassPathXmlApplicationContext构造器可以看到,该构造器接收一个配置文件,构造器的注释是这一样描述的:创建一个新的 ClassPathXmlApplicationContext,从给定的 XML 文件加载定义并自动刷新上下文。

/**
    创建一个新的 ClassPathXmlApplicationContext,从给定的 XML 文件加载定义并自动刷新上下文。
     * Create a new ClassPathXmlApplicationContext, loading the definitions
     * from the given XML file and automatically refreshing the context.
     * @param configLocation resource location
     * @throws BeansException if context creation failed
     */
    public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
   
   
        this(new String[] {
   
   configLocation}, true, null);
    }

    /**
     * Create a new ClassPathXmlApplicationContext with the given parent,
     * loading the definitions from the given XML files.
     * @param configLocations array of resource locations
     * @param refresh whether to automatically refresh the context,
     * loading all bean definitions and creating all singletons.
     * Alternatively, call refresh manually after further configuring the context.
     * @param parent the parent context
     * @throws BeansException if context creation failed
     * @see #refresh()
     */
    public ClassPathXmlApplicationContext(
            String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
            throws BeansException {
   
   
        //调用父类的构造器
        super(parent);
        //设置位置文件地址
        setConfigLocations(configLocations);
        if (refresh) {
   
   
            //刷新容器【重点】
            refresh();
        }
    }

在ClasspathXmlApplication的构造器中做了如下事情:

  • 调用了父容器的构造器方法,目的是加载设置Bean的资源加载器 ResourcePatternResolver
  • 然后通过setConfigLocations方法保存好配置文件地址,
  • 最后调用refresh()刷新容器

    ResourcePatternResolver 资源加载器

    ResourcePatternResolver是Bean的资源加载器 ,通过父容器 AbstractApplicationContext 中的构造方法创建:
    ```java
    public abstract class AbstractApplicationContext extends DefaultResourceLoader

      implements ConfigurableApplicationContext
    

    public AbstractApplicationContext(@Nullable ApplicationContext parent) {

      //加载 resourcePatternResolver 
      this();
      //
      setParent(parent);
    

    }

    /**

    • Create a new AbstractApplicationContext with no parent.
      */
      //创建一个AbstractApplicationContext容器工厂,并构建一个ResourcePatternResolver
      public AbstractApplicationContext() {
      this.resourcePatternResolver = getResourcePatternResolver();
      }
      //获取 PathMatchingResourcePatternResolver
      protected ResourcePatternResolver getResourcePatternResolver() {
      return new PathMatchingResourcePatternResolver(this);
      }

      public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
      Assert.notNull(resourceLoader, "ResourceLoader must not be null");
      //资源加载器
      this.resourceLoader = resourceLoader;
      }

父容器AbstractApplicationContext 继承了 DefaultResourceLoader ,拥有资源加载的能力,在构造器中中创建了ResourcePatternResolver,使用的是`PathMatchingResourcePatternResolver`作为实现,它能够将指定的资源位置路径解析为一个或多个匹配的资源。
![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/202106011009566.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTQ0OTQxNDg=,size_16,color_FFFFFF,t_70)
下面是ResourceLoader 源码:

```java
public interface ResourceLoader {
    //默认从classpath中加载资源文件
    /** Pseudo URL prefix for loading from the class path: "classpath:". */
    String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
    //把资源文件转换成Resource
    Resource getResource(String location);
    ClassLoader getClassLoader();
}

public interface ResourcePatternResolver extends ResourceLoader {

    //从classpath加载资源
    String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

    //把文件转换成Resource[] ,对ResourceLoader做了扩展
    Resource[] getResources(String locationPattern) throws IOException;
}

setConfigLocations 保存配置地址

然后就是保存配置地址 ,从源码可以看出,我们是可以传入多个配置文件给容器的。

public abstract class AbstractRefreshableConfigApplicationContext extends AbstractRefreshableApplicationContext
        implements BeanNameAware, InitializingBean {
   
   
    //地址保存到这里
    @Nullable
    private String[] configLocations;

    /**
     * Set the config locations for this application context.
     * <p>If not set, the implementation may use a default as appropriate.
     */
     //可以传入多个配置
    public void setConfigLocations(@Nullable String... locations) {
   
   
        if (locations != null) {
   
   
            Assert.noNullElements(locations, "Config locations must not be null");
            this.configLocations = new String[locations.length];

            for (int i = 0; i < locations.length; i++) {
   
   
                this.configLocations[i] = resolvePath(locations[i]).trim();
            }
        }
        else {
   
   
            this.configLocations = null;
        }
    }

Refresh() 刷新容器

ClasspathXmlApplication调用 AbstractApplicationContext#refresh 方法刷新容器,该方法中实现了IOC容器的整个初始化过程。

 @Override
    public void refresh() throws BeansException, IllegalStateException {
   
   
        synchronized (this.startupShutdownMonitor) {
   
   
            // Prepare this context for refreshing.
            //准备刷新工作 ,记录开始时间,初始化属性,校验配置文件,准备事件的存储Set
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            //告诉子类,刷新Bean工厂,销毁旧beanFactory,创建新beanFactory,默认DefaultListableBeanFactory
            //从子容器的refreshBeanFactory方法中载入Bean的资源文件
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            //准备工厂,配置工厂的下文特性, 例如上下文的 ClassLoader 和后处理器。Bean表达式解析器,
            //BeanPostProcessor和 Aware类的自动装配等
            prepareBeanFactory(beanFactory);

            try {
   
   
                // Allows post-processing of the bean factory in context subclasses.
                //BeanFactory初始化完成的后置工作,这是一个空方法,留给三方框架或者自己配置,作用是允许对beanFoctory进行扩展处理
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                //调用BeanFactory的后置处理器BeanFactoryPostProcessor,在 bean定义注册之后bean实例化之前调用
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                //注册Bean的后置处理器BeanPostProcessor,在Bean初始化前,后执行
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                //初始化信息源,国际化相关
                initMessageSource();

                // Initialize event multicaster for this context.
                //初始化容器事件传播器
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                //空方法,该方法子类实现,在容器刷新的时候可以自定义逻辑;如创建Tomcat,Jetty等WEB服务器
                onRefresh();

                // Check for listener beans and register them.
                //注册事件监听器,注册实现了ApplicationListener接口的监听器bean,
                //这些监听器是注册到ApplicationEventMulticaster中的
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                //实例化所有剩余的(非延迟初始化)单例的Bean
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                //完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,并且发布事件(ContextRefreshedEvent)
                finishRefresh();
            }

            catch (BeansException ex) {
   
   
                if (logger.isWarnEnabled()) {
   
   
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }
                // Destroy already created singletons to avoid dangling resources.
                //销毁已经创建的单例Bean。
                destroyBeans();
                // Reset 'active' flag.
                //取消容器刷新
                cancelRefresh(ex);
                // Propagate exception to caller.
                throw ex;
            }
            finally {
   
   
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                //重置缓存
                resetCommonCaches();
            }
        }
    }

refresh()方法主要是通过子类 refreshBeanFactory()方法加载Bean信息,然后就是一些列的容器生命周期事件。这里其实是用到了模板设计模式,在refresh()方法中指定容器刷新流程,很多的细节步骤由子类去实现。

工厂的创建:obtainFreshBeanFactory

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   
   
        //刷新工厂,有子类实现
        refreshBeanFactory();
        //通过子类返回工厂,默认 DefaultListableBeanFactory
        return getBeanFactory();
    }

这里只是定义了抽象方法,refreshBeanFactory由子类实现,见:AbstractRefreshableApplicationContext#refreshBeanFactory

AbstractRefreshableApplicationContext#refreshBeanFactory

@Override
    protected final void refreshBeanFactory() throws BeansException {
   
   
        if (hasBeanFactory()) {
   
   
            //如果已经有BeanFactory,销毁Bean,关闭容器
            destroyBeans();
            closeBeanFactory();
        }
        try {
   
   
            //创建IOC容器
            DefaultListableBeanFactory beanFactory = createBeanFactory();
            beanFactory.setSerializationId(getId());
            //定制BeanFactory,如设置启动参数,开启注解的自动装配等
            customizeBeanFactory(beanFactory);
            //载入Bean,由子类实现
            loadBeanDefinitions(beanFactory);
            this.beanFactory = beanFactory;
        }
        catch (IOException ex) {
   
   
            throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
        }
    }

该方法中先判断如果已经存在BeanFactory就销毁掉重新创建,默认使用的是DefaultListableBeanFactory作为BeanFactory,并loadBeanDefinitions方法加载Bean,方法由子类 AbstractXmlApplicationContext#loadBeanDefinitions实现。

加载Bean:AbstractXmlApplicationContext#loadBeanDefinitions

@Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   
   
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        //创建 XmlBeanDefinitionReader ,用来从XML中读取Bean
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

        // Configure the bean definition reader with this context's
        // resource loading environment.
        //把Environment 和 ResourceLoader 设置给beanDefinitionReader
        beanDefinitionReader.setEnvironment(this.getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        //设置Sax解析器
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        //初始化Bean的读取器,启用 Xml 的校验机制 , 允许子类自定义初始化读取器
        initBeanDefinitionReader(beanDefinitionReader);
        //加载Bean, XmlBeanDefinitionReader真正实现加载逻辑
        loadBeanDefinitions(beanDefinitionReader);
    }

loadBeanDefinitions方法是用来加载Bean的,创建了XmlBeanDefinitionReader 基于XML的Bean的读取器,最终会调用 XmlBeanDefinitionReader.(configLocations)从配置中加载Bean,见:AbstractXmlApplicationContext#loadBeanDefinitions(.XmlBeanDefinitionReader)

    protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   
   
        //获取配置的Resource
        Resource[] configResources = getConfigResources();
        //如果有Resrouce就调用 XmlBeanDefinitionReader.loadBeanDefinitions 加载Bean
        if (configResources != null) {
   
   
            //Xml Bean 读取器调用其父类 AbstractBeanDefinitionReader 读取定位的 Bean 配置资源
            reader.loadBeanDefinitions(configResources);
        }
        // 如果子类中获取的 Bean 配置资源Resource为空,
        // 则获取 ClassPathXmlApplicationContext  构造方法中 setConfigLocations 方法设置的资源
        String[] configLocations = getConfigLocations();
        if (configLocations != null) {
   
   
            //Xml Bean 读取器调用其父类 AbstractBeanDefinitionReader 读取定位的 Bean 配置资源
            reader.loadBeanDefinitions(configLocations);
        }
    }

这里先尝试获取配置资源Resource,如果为空就通过指定配置reader.loadBeanDefinitions(configLocations);加载Bean,最终调用父类:AbstractBeanDefinitionReader#loadBeanDefinitions(java.lang.String)方法

AbstractBeanDefinitionReader#loadBeanDefinitions(

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
   
   
        //资源加载器,在初始化IOC容器的时候创建的
        ResourceLoader resourceLoader = getResourceLoader();
        if (resourceLoader == null) {
   
   
            throw new BeanDefinitionStoreException(
                    "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
        }

        if (resourceLoader instanceof ResourcePatternResolver) {
   
   
            // Resource pattern matching available.
            try {
   
   
                //把指定位置的配置文件解析成Resource,加载多个
                Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
                //委派 XmlBeanDefinitionReader加载Bean
                int count = loadBeanDefinitions(resources);
                if (actualResources != null) {
   
   
                    Collections.addAll(actualResources, resources);
                }
                if (logger.isTraceEnabled()) {
   
   
                    logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
                }
                return count;
            }
            catch (IOException ex) {
   
   
                throw new BeanDefinitionStoreException(
                        "Could not resolve bean definition resource pattern [" + location + "]", ex);
            }
        }
        else {
   
   
            // Can only load single resources by absolute URL.
            //把指定位置的配置文件解析成Resource,加载单个
            Resource resource = resourceLoader.getResource(location);
            //委派 XmlBeanDefinitionReader加载Bean
            int count = loadBeanDefinitions(resource);
            if (actualResources != null) {
   
   
                actualResources.add(resource);
            }
            if (logger.isTraceEnabled()) {
   
   
                logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
            }
            return count;
        }
    }

在该方法中显示得到初始化容器时创建的ResourceLoader,通过ResourceLoader.getResource(location)得到Resource资源对象后,调用loadBeanDefinitions(resources);方法,其实是委派XmlBeanDefinitionReader去加载Bean。程序最终来到XmlBeanDefinitionReader#loadBeanDefinitions(org.springframework.core.io.support.EncodedResource)方法

解析Bean:XmlBeanDefinitionReader#loadBeanDefinitions

//对 Resource进行了编码
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
   
   
        return loadBeanDefinitions(new EncodedResource(resource));
    }


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();
        if (currentResources == null) {
   
   
            currentResources = new HashSet<>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        if (!currentResources.add(encodedResource)) {
   
   
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
   
   
            //从Resource中得到输入流
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
   
   
                //从输入流中得到XML配置文件源
                InputSource inputSource = new InputSource(inputStream);
                if (encodedResource.getEncoding() != null) {
   
   
                    inputSource.setEncoding(encodedResource.getEncoding());
                }
                //加载BeanDefinitions , 加载Bean的核心方法
                return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
            }
            finally {
   
   
                //输入流关闭
                inputStream.close();
            }
        }
        catch (IOException ex) {
   
   
            throw new BeanDefinitionStoreException(
                    "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
   
   
            //删除Resource
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
   
   
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }

XmlBeanDefinitionReader#loadBeanDefinitions 对配置文件做了编码处理后,从Resource中得到输入流,然后包装成 InputSource(XML数据源),调用 doLoadBeanDefinitions方法去加载Bean,见:XmlBeanDefinitionReader#doLoadBeanDefinitions

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
   
   

        try {
   
   
            //将 XML 文件转换为 Document 对象,通过 documentLoader来解析
            Document doc = doLoadDocument(inputSource, resource);
            //【重要】解析和注册Bean的消息流程
            int count = registerBeanDefinitions(doc, resource);
            if (logger.isDebugEnabled()) {
   
   
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;
        }
        ...省略...

XmlBeanDefinitionReader 通过 DefaultDocumentLoader#loadDocument 把InputResource转成Document对象,然后委派 BeanDefinitionParserDelegate 去解析Document然后注册Bean。

文章就先到这里结束把,下一章接上Bean的解析和Bean的注册,如果喜欢就给个好评吧,你的肯定是我最大的动力~ 文章数量突破100啦~

相关文章
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
71 2
|
16天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
38 2
|
1月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
63 9
|
1月前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
1月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
39 0
|
Java 数据库连接 应用服务中间件
Spring源码剖析8:Spring事务概述
原文出处: 张开涛 9.1  数据库事务概述 事务首先是一系列操作组成的工作单元,该工作单元内的操作是不可分割的,即要么所有操作都做,要么所有操作都不做,这就是事务。
|
2月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
223 2
|
3天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
35 14
|
25天前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
42 1
SpringBoot入门(7)- 配置热部署devtools工具
|
1月前
|
缓存 IDE Java
SpringBoot入门(7)- 配置热部署devtools工具
SpringBoot入门(7)- 配置热部署devtools工具
43 2
 SpringBoot入门(7)- 配置热部署devtools工具