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

简介: 上一章节我们讲的是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啦~

相关文章
|
2月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
6月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
414 70
|
5月前
|
XML Java 数据格式
Spring IoC容器的设计与实现
Spring 是一个功能强大且模块化的 Java 开发框架,其核心架构围绕 IoC 容器、AOP、数据访问与集成、Web 层支持等展开。其中,`BeanFactory` 和 `ApplicationContext` 是 Spring 容器的核心组件,分别定位为基础容器和高级容器,前者提供轻量级的 Bean 管理,后者扩展了事件发布、国际化等功能。
|
7月前
|
Java 容器 Spring
什么是Spring IOC 和DI ?
IOC : 控制翻转 , 它把传统上由程序代码直接操控的对象的调用权交给容 器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转 移,从程序代码本身转移到了外部容器。 DI : 依赖注入,在我们创建对象的过程中,把对象依赖的属性注入到我们的类中。
|
7月前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
139 0
|
3月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
783 0
|
11天前
|
JavaScript Java Maven
【SpringBoot(二)】带你认识Yaml配置文件类型、SpringMVC的资源访问路径 和 静态资源配置的原理!
SpringBoot专栏第二章,从本章开始正式进入SpringBoot的WEB阶段开发,本章先带你认识yaml配置文件和资源的路径配置原理,以方便在后面的文章中打下基础
130 3
|
11天前
|
Java 测试技术 数据库连接
【SpringBoot(四)】还不懂文件上传?JUnit使用?本文带你了解SpringBoot的文件上传、异常处理、组件注入等知识!并且带你领悟JUnit单元测试的使用!
Spring专栏第四章,本文带你上手 SpringBoot 的文件上传、异常处理、组件注入等功能 并且为你演示Junit5的基础上手体验
296 2
|
4月前
|
人工智能 Java 测试技术
Spring Boot 集成 JUnit 单元测试
本文介绍了在Spring Boot中使用JUnit 5进行单元测试的常用方法与技巧,包括添加依赖、编写测试类、使用@SpringBootTest参数、自动装配测试模块(如JSON、MVC、WebFlux、JDBC等),以及@MockBean和@SpyBean的应用。内容实用,适合Java开发者参考学习。
482 0