SpringBoot 执行原理

前言

每个SpringBoot项目都有一个主程序启动类,在主程序启动类中有一个启动项目的main方法,在该方法中通过执行SpringApplication.run()即可启动整个SpringBoot项目

那么SpringApplication.run方法是如何启动整个SpringBoot项目的呢?
让我们看一下run方法的内部代码

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }

通过上面的代码可以看出,SpringBootApplication.run内部进行了两个操作,SpringBootApplication实例化初始化创建和调用run方法启动项目,下面对这两个操作进行分析

SpringApplication实例化创建

实例化源码

@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {

		this.sources = new LinkedHashSet();
		this.bannerMode = Mode.CONSOLE;
		this.logStartupInfo = true;
		this.addCommandLineProperties = true;
		this.addConversionService = true;
		this.headless = true;
		this.registerShutdownHook = true;
		this.additionalProfiles = new HashSet();
		this.isCustomEnvironment = false;
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");

		//项目启动类 SpringbootDemoApplication.class设置为属性存储起来
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

		//设置应用类型是SERVLET应用(Spring 5之前的传统MVC应用)还是REACTIVE应用(Spring 5开始出现的WebFlux交互式应用)
		this.webApplicationType = WebApplicationType.deduceFromClasspath();

		// 设置初始化器(Initializer),最后会调用这些初始化器
		//所谓的初始化器就是org.springframework.context.ApplicationContextInitializer的实现类,在Spring上下文被刷新之前进行初始化的操作
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

		// 设置监听器(Listener)
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

		// 初始化 mainApplicationClass 属性:用于推断并设置项目main()方法启动的主程序启动类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

从上面可以看出SpringApplication初始化,主要分为四个部分,分别是:

1、this.webApplicationType = WebApplicationType.deduceFromClasspath();
用于判断当前WebApplicationType的应用类型,deduceFromClasspath方法用于查看当前classpath路径下,是否存在某个特征类,从而判断当前webApplicationType是servlet应用还是reactive应用。

2、 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
用于SpringApplication应用的初始化器设置,在初始化器设置的过程中,会使用Spring类加载器SpringFactoriesLoader从META-INF/spring.factories类路径下,获取所有可用的初始化器类ApplicationContextInitializer

3、setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
过程与上面的初始化类相同,不同在于,获取到的是所有可用的监听器类ApplicationListener

4、this.mainApplicationClass = deduceMainApplicationClass();
用于判断并设置项目main方法启动的主程序启动类

项目的初始化启动

在看完前面的SpringApplication初始化以后,现在看一下run方法

public ConfigurableApplicationContext run(String... args) {
	    // 创建 StopWatch 对象,并启动。StopWatch 主要用于简单统计 run 启动过程的时长。
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		// 初始化应用上下文和异常报告集合
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 配置 headless 属性
		configureHeadlessProperty();


		//   (1)获取并启动监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
		    // 创建  ApplicationArguments 对象 初始化默认应用参数类
			// args是启动Spring应用的命令行参数,该参数可以在Spring应用中被访问。如:--server.port=9000
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

			//(2)项目运行环境Environment的预配置
			// 创建并配置当前SpringBoot应用将要使用的Environment
			// 并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

			configureIgnoreBeanInfo(environment);
			// 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
			Banner printedBanner = printBanner(environment);

			// (3)创建Spring容器
			context = createApplicationContext();
			// 获得异常报告器 SpringBootExceptionReporter 数组
			//这一步的逻辑和实例化初始化器和监听器的一样,
			// 都是通过调用 getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类。
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);


			// (4)Spring容器前置处理
			//这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);

			// (5):刷新容器
			refreshContext(context);

			// (6):Spring容器后置处理
			//扩展接口,设计模式中的模板方法,默认为空实现。
			// 如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理
			afterRefresh(context, applicationArguments);
			// 停止 StopWatch 统计时长
			stopWatch.stop();
			// 打印 Spring Boot 启动的时长日志。
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			// (7)发出结束执行的事件通知
			listeners.started(context);

			// (8):执行Runners
			//用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序
			//Runner 运行器用于在服务启动时进行一些业务初始化操作,这些操作只在服务启动后执行一次。
			//Spring Boot提供了ApplicationRunner和CommandLineRunner两种服务接口
			callRunners(context, applicationArguments);
		} catch (Throwable ex) {
		    // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

        //   (9)发布应用上下文就绪事件
		//表示在前面一切初始化启动都没有问题的情况下,使用运行监听器SpringApplicationRunListener持续运行配置好的应用上下文ApplicationContext,
		// 这样整个Spring Boot项目就正式启动完成了。
		try {
			listeners.running(context);
		} catch (Throwable ex) {
            // 如果发生异常,则进行处理,并抛出 IllegalStateException 异常
            handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		 //返回容器
		return context;
	}

同样我们进行逐步分析
1、获取并启动监听器
this.getRunListeners(args)和listeners.starting()方法主要用于获取SpringApplication实例初始化过程中初始化的springApplicationRunListener监听器并运行

2、更具SpringApplicationRunLisenter以及其参数,准备环境
this.prepareEnvironment(listeners, applicationArguments)方法主要用于对项目运行环境进行预设置,同时通过this.configureIgnoreBeanInfo(environment)方法排除一些不需要的运行环境

3、创建Spring容器
根据webApplicationType进行判断, 确定容器类型,如果该类型为SERVLET类型,会通过反射装载对应的字节码,也就是AnnotationConfigServletWebServerApplicationContext,接着使用之前
初始化设置的context(应用上下文环境)、environment(项目运行环境)、listeners(运行监听器)、applicationArguments(项目参数)和printedBanner(项目图标信息)进行应用上下文的组装配置,并刷新配置

4、Spring容器的前置设置
这一步主要是在容器刷新之前的准备动作。设置容器环境,包括各种变量等等,其中包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础

5、刷新容器
开启刷新spring容器,通过refresh方法对整个IOC容器的初始化(包括bean资源的定位,解析,注册等等),同时向JVM运行时注册一个关机钩子,在JVM关机时会关闭这个上下文,除非当时它已经关闭

6、Spring容器后置处理
扩展接口,设计模式中的模板方法,默认为空实现。如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理。

7、发出结束执行的事件
获取EventPublishingRunListener监听器,并执行其started方法,并且将创建的Spring容器传进去了,创建一个ApplicationStartedEvent事件,并执行ConfigurableApplicationContext 的publishEvent方法,也就是说这里是在Spring容器中发布事件,并不是在SpringApplication中发布事件,和前面的starting是不同的,前面的starting是直接向SpringApplication中的监听器发布启动事件。

8、执行Runner
用于调用项目中自定义的执行器XxxRunner类,使得在项目启动完成后立即执行一些特定程序。其中,Spring Boot提供的执行器接口有ApplicationRunner 和CommandLineRunner两种,在使用时只需要自定义一个执行器类实现其中一个接口并重写对应的run()方法接口,然后Spring Boot项目启动后会立即执行这些特定程序

执行流程图

在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值