SpringBoot(2)—启动原理之run方法的运行

SpringBoot版本为:2.1.1.RELEASE

在上一篇文章中,我们讲到了SpringApplication对象的创建,接下来将会分析SpringApplication创建之后run()方法的执行。

通过debug的方式,我们可以进入run方法,源代码如下:

	/**
	 * Run the Spring application, creating and refreshing a new
	 * {@link ApplicationContext}.
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return a running {@link ApplicationContext}
	 */
	public ConfigurableApplicationContext run(String... args) {
	<1>:
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();

    <2>:
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {

    <3>:
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
					
    <4>:
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
					
			configureIgnoreBeanInfo(environment);

    <5>:
			Banner printedBanner = printBanner(environment);
 
    <6>:
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);

    <7>:
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);

    <8>:
			refreshContext(context);

    <9>:
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}

    <10>:
			listeners.started(context);

    <11>:
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {

    <12>:
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
    <13>:
		return context;
	}

下面我们逐一分析:
<1> : StopWatch stopWatch = new StopWatch();
stopWatch.start();

这段代码功能很简单,创建一个StopWatch对象,开始记录run()启动过程时长;

<2>: 先来看这个方法:SpringApplicationRunListeners listeners = getRunListeners(args);

private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
				SpringApplicationRunListener.class, types, this, args));
	}

在这段代码里,我们又看见了熟悉的 getSpringFactoriesInstances(),原理还是一样,就是 getSpringFactoriesInstances()方法会从类路径下的 META-INF/spring.factories文件中找 对应SpringApplicationRunListener的全路径数组,并通过createSpringFactoriesInstances()方法实例化成对象返回;
再看 listeners.starting() 方法:

public void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

所以这个方法就是回调之前获得的所有SpringApplicationRunListener对象的starting()方法,启动监听。我们可以继续再深入看一下这个监听对象的其他方法:

在这里插入图片描述
SpringApplicationRunListener 接口中共有上面几个方法,这几个方法将会贯穿run()方法的运行。

<3>:这个方法的作用也很简单,即使封装命令行参数。

<4>: ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments)

其实这是环境准备阶段,我们可以看一下它的实现过程:

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

这个方法表示创建环境,并且environment 的属性都会加载进来,包括 application.properties 和外部的属性配置,具体实现有兴趣的同学可以研究一下。其中listeners.environmentPrepared(environment)方法表示环境准备完成

<5>:功能为打印Banner,也可以自定义启动logo,比如在resources路径下创建一个banner.txt文件,将你想打印的图标放入其中

<6>:创建ApplicationContext容器,根据类型决定是创建普通WEB容器还是REACTIVE容器还是普通Annotation的ioc 容器

<7>:prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
这个方法的具体实现:

private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
    7.1:
		applyInitializers(context);
    7.2:
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		listeners.contextLoaded(context);
	}

7.1: 从initializers集合中遍历所有的ApplicationContextInitializer,并通过initializer.initialize( )方法初始化

7,2:回调SpringApplicationRunListener对象的contextPrepared()方法,表示容器已准备

<8>:refreshContext(context)
刷新容器,初始化ioc容器,向容器中加入配置类、组件,并且可以出发自动配置功能,具体原理可以参考SpringBoot的自动配置原理和Spring注解版容器的加载

<9>: afterRefresh(context, applicationArguments);
执行Spring容器初始化的后置处理,默认为空

protected void afterRefresh(ConfigurableApplicationContext context,
			ApplicationArguments args) {
	}

<10>: listeners.started(context);
回调所有的SpringApplicationRunListener对象的started()方法

<11>: callRunners(context, applicationArguments)

private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<>();
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

调用ApplicationRunner或者CommandLineRunner 的运行方法,其中ApplicationRunner的优先级要比CommandLineRunner要高。
在我们日常的项目里,经常需要初始化一些资源,比如线程池或者数据库数据等,我们就可以实现这两个接口,在实现方法里写具体的处理逻辑,也可以在实现类上加上@Order(value) 注解来指定优先级(ps:该实现类要加上@Component)

<12>:listeners.running(context);
回调所有SpringApplicationRunListener对象的running()方法

<13>:return context
返回容器

至此,SpringBoot的启动过程已全部完毕,下一篇文章将会讲解SpringBoot的事件监听机制!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值