springboot启动流程源码分析 (springboot源码简单介绍一下)

Spring Boot 启动过程可以分为三个主要步骤:准备阶段、应用上下文创建阶段和刷新阶段。Spring Boot 启动过程的源码主要位于 spring-boot-autoconfigure、spring-boot-starter、spring-boot 等模块中。下面是一个简要的启动过程源码分析:

本次Spring Boot是基于2.1.0 分析。Spring boot 每个版本有些变化,读者尽量和我保持一致,以防源码有些出入。

一.启动过程

  1. 准备阶段

在准备阶段,Spring Boot 会读取应用程序的配置信息并加载到 Spring 环境中。这部分的源码主要位于 spring-boot-autoconfigure 模块中的 AutoConfigurationImportSelector 类中。在这个类中,首先会加载默认的配置文件,如 application.properties,然后会读取用户自定义的配置文件,如 application.yml。这些配置信息会被封装成一个 Environment 对象,并且会注册到 Spring 环境中。同时,这个类还会扫描所有的 Spring Boot 自动配置类,将这些自动配置类注册到 Spring 环境中。

  1. 应用上下文创建阶段

在应用上下文创建阶段,Spring Boot 会根据读取到的配置信息创建应用程序上下文。这部分的源码主要位于 spring-boot 模块中的 SpringApplication 类中。在这个类中,首先会创建一个 ApplicationContextInitializer 对象,并将其注册到应用程序上下文中。然后,这个类会根据读取到的配置信息创建一个 ApplicationContext 对象,并将其设置为主应用程序上下文。接着,这个类会检测是否有 Web 应用程序上下文,并根据需要创建出相应的 Web 应用程序上下文。这个过程中,Spring Boot 会扫描所有的 Bean 定义,并根据这些 Bean 定义创建出相应的 Bean 实例。

  1. 刷新阶段

在刷新阶段,Spring Boot 会将应用程序上下文中的所有 Bean 进行初始化和依赖注入。同时,Spring Boot 还会执行各种初始化操作,如加载数据库驱动、创建数据库连接池等。这部分的源码主要位于 spring-context、spring-core 等模块中。在初始化完成后,Spring Boot 会启动内嵌的 Tomcat 服务器或其他 Web 服务器,将 Web 应用程序发布到服务器上。当应用程序启动完成后,Spring Boot 会将控制权交给应用程序,等待处理请求。

二.源码分析

创建SpringApplication

启动类入口SpringApplication

springboot底层原理与源码分析,初窥springboot源码之框架初始化

springboot底层原理与源码分析,初窥springboot源码之框架初始化

springboot底层原理与源码分析,初窥springboot源码之框架初始化

1.设置应用类型

这个过程非常重要,直接决定了项目的类型,应用类型分为三种,都在WebApplicationType这个枚举类中,如下:

  1. NONE:顾名思义,什么都没有,正常流程走,不额外的启动web容器,比如Tomcat。
  2. SERVLET:基于servlet的web程序,需要启动内嵌的servletweb容器,比如Tomcat。
  3. REACTIVE:基于reactive的web程序,需要启动内嵌reactiveweb容器

2.设置初始化器(Initializer)

springboot底层原理与源码分析,初窥springboot源码之框架初始化

初始化器ApplicationContextInitializer 第一步获取初始化器的名称,详细源码在loadFactoryNames()方法中了,跟着源码进入,最终调用的是#SpringFactoriesLoader.loadSpringFactories()方法。loadSpringFactories()方法就不再详细解释了,其实就是从类路径META-INF/spring.factories中加载ApplicationContextInitializer的值。在spring-boot-autoconfigure的spring.factories文件中的值如下图:

springboot底层原理与源码分析,初窥springboot源码之框架初始化

springboot底层原理与源码分析,初窥springboot源码之框架初始化

3.设置监听器(Listener)

监听器(ApplicationListener)主要用于监听特定的事件(ApplicationEvent),比如IOC容器刷新、容器关闭等。Spring Boot扩展了ApplicationEvent构建了SpringApplicationEvent这个抽象类,主要用于Spring Boot启动过程中触发的事件,比如程序启动中、程序启动完成等。

springboot底层原理与源码分析,初窥springboot源码之框架初始化

从源码中知道其实和初始化器(ApplicationContextInitializer)执行的是同一个方法,同样是从META-INF/spring.factories文件中获取。在spring-boot-autoconfigure的spring.factories

springboot底层原理与源码分析,初窥springboot源码之框架初始化

总结:

SpringApplication的构建都是为了run()方法启动做铺垫,最重要的部分就是设置应用类型、设置初始化器、设置监听器。( 「注意」 :初始化器和这里的监听器都要放置在 spring.factories 文件中才能在这一步骤加载,否则不会生效,因此此时 IOC容器 还未创建,即使将其注入到 IOC容器 中也是不会生效的)。

springboot底层原理与源码分析,初窥springboot源码之框架初始化

执行run()方法

springboot底层原理与源码分析,初窥springboot源码之框架初始化

获取、启动运行过程监听器

//从spring.factories中获取监听器
SpringApplicationRunListeners listeners = getRunListeners(args);

getRunListeners()方法,其实还是调用了loadFactoryNames()方法从spring.factories文件中获取值,如下:

org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

最终注入的是EventPublishingRunListener这个实现类,创建实例过程肯定是通过反射了,因此我们看看它的构造方法

springboot底层原理与源码分析,初窥springboot源码之框架初始化

springboot底层原理与源码分析,初窥springboot源码之框架初始化

//执行starting()方法
listeners.starting(bootstrapContext, this.mainApplicationClass);

执行SpringApplicationRunListeners的starting()方法,执行了multicastEvent()方法,广播了ApplicationStartingEvent事件。遍历ApplicationListener的实现类,找到监听ApplicationStartingEvent这个事件的监听器,执行onApplicationEvent()方法。

2.环境构建

加载了系统环境配置、用户自定义配置; 广播ApplicationEnvironmentPreparedEvent事件,触发监听器。

//要用于加载系统配置以及用户的自定义配置(application.properties)
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

springboot底层原理与源码分析,初窥springboot源码之框架初始化

3.创建IOC容器

context = createApplicationContext();
=================================
  
	/**
	 * Strategy method used to create the {@link ApplicationContext}. By default this
	 * method will respect any explicitly set application context or application context
	 * class before falling back to a suitable default.
	 * @return the application context (not yet refreshed)
	 * @see #setApplicationContextClass(Class)
	 */
	protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
          //webApplicationType决定创建的类型
          //这里的是servlet,因此创建的是AnnotationConfigServletWebServerApplicationContext  
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, "
								+ "please specify an ApplicationContextClass",
						ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

4.IOC容器的前置处理

prepareContext(context, environment, listeners, applicationArguments,printedBanner);

springboot底层原理与源码分析,初窥springboot源码之框架初始化

调用初始化器


/**
 * Apply any {@link ApplicationContextInitializer}s to the context before it is
 * refreshed.
 * @param context the configured ApplicationContext (not refreshed yet)
 * @see ConfigurableApplicationContext#refresh()
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) {
  //在SpringApplication构建过程中设置的初始化器,从spring.factories取值的遍历执行
  //将自定义的ApplicationContextInitializer放在META-INF/spring.factories中,在此时也是会被调用
   for (ApplicationContextInitializer initializer : getInitializers()) {
      Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
            initializer.getClass(), ApplicationContextInitializer.class);
      Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
      initializer.initialize(context);
   }
}

加载启动类,注入容器

// Load the sources
//在SpringApplication构建过程中将主启动类放置在primarySources这个集合中,
// 此时的getAllSources()即是从其中取值
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//取出主启动类,当然你的项目中可能不止一个,将其加载到IOC容器中
//将主启动类加载到beanDefinitionMap,后续该启动类将作为开启自动配置化的入口
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);

两次广播事件

两次事件广播,分别是ApplicationContextInitializedEvent和ApplicationPreparedEvent

listeners.contextPrepared(context);

5.刷新容器

刷新容器,比如初始化资源,初始化上下文广播器等。

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    //调用创建的容器applicationContext中的refresh()方法
    ((AbstractApplicationContext)applicationContext).refresh();
}
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        /**
         * 刷新上下文环境
         */
        prepareRefresh();
      /** 
         * 初始化BeanFactory,解析XML,相当于之前的XmlBeanFactory的操作,
         */
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        /**
         * 为上下文准备BeanFactory,即对BeanFactory的各种功能进行填充,如常用的注解@Autowired @Qualifier等
         * 添加ApplicationContextAwareProcessor处理器
         * 在依赖注入忽略实现*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等
         * 注册依赖,如一个bean的属性中含有ApplicationEventPublisher(beanFactory),则会将beanFactory的实例注入进去
         */
        prepareBeanFactory(beanFactory);
        try {
          /**
             * 提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess
             */
            postProcessBeanFactory(beanFactory);
            /**
             * 激活各种BeanFactory处理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
             * 执行对应的postProcessBeanDefinitionRegistry方法 和  postProcessBeanFactory方法
             */
            invokeBeanFactoryPostProcessors(beanFactory);
            /**
             * 注册拦截Bean创建的Bean处理器,即注册BeanPostProcessor,不是BeanFactoryPostProcessor,注意两者的区别
             * 注意,这里仅仅是注册,并不会执行对应的方法,将在bean的实例化时执行对应的方法
             */
            registerBeanPostProcessors(beanFactory);
            /**
             * 初始化上下文中的资源文件,如国际化文件的处理等
             */
            initMessageSource();
            /**
             * 初始化上下文事件广播器,并放入applicatioEventMulticaster,如ApplicationEventPublisher
             */
            initApplicationEventMulticaster();
            /**
             * 给子类扩展初始化其他Bean
             */
            onRefresh();
            /**
             * 在所有bean中查找listener bean,然后注册到广播器中
             */
            registerListeners();
            /**
             * 设置转换器
             * 注册一个默认的属性值解析器
             * 冻结所有的bean定义,说明注册的bean定义将不能被修改或进一步的处理
             * 初始化剩余的非惰性的bean,即初始化非延迟加载的bean
             */
            finishBeanFactoryInitialization(beanFactory);
            /**
             * 通过spring的事件发布机制发布ContextRefreshedEvent事件,以保证对应的监听器做进一步的处理
             * 即对那种在spring启动后需要处理的一些类,这些类实现了ApplicationListener<ContextRefreshedEvent>,
             * 这里就是要触发这些类的执行(执行onApplicationEvent方法)
             * 另外,spring的内置Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent
             * 完成初始化,通知生命周期处理器lifeCycleProcessor刷新过程,同时发出ContextRefreshEvent通知其他人
             */
            finishRefresh();
        }
        finally {
            resetCommonCaches();
        }
    }
}

6.IOC容器的后置处理

afterRefresh(context, applicationArguments);

7.发出结束执行的事件

listeners.started(context);

同样是EventPublishingRunListener这个监听器,广播ApplicationStartedEvent事件。但是这里广播事件和前几次不同,并不是广播给SpringApplication中的监听器(在构建过程中从spring.factories文件获取的监听器)。因此在IOC容器中注入的监听器(使用@Component等方式注入的)也能够生效。前面几个事件只有在spring.factories文件中设置的监听器才会生效。这里并没有用事件广播器SimpleApplicationEventMulticaster广播事件,而是使用ConfigurableApplicationContext直接在IOC容器中发布事件。

8.执行Runners

callRunners(context, applicationArguments);

callRunners(context, applicationArguments),它用于调用 Runner。Runner 是 Spring Boot 中的一个重要概念,它是一个用于执行特定任务的组件。在应用程序启动时,Spring Boot 会自动扫描并调用所有实现了 CommandLineRunner 和 ApplicationRunner 接口的 Bean,执行它们的 run 方法。这些 Bean 可以用来初始化数据、创建测试数据等操作。