Spring Boot 启动流程与重要的回调机制

说明: 以下使用的是 Spring Boot 2.0.4 版本

几个重要的事件回调机制

配置在META-INF/spring.factories
ApplicationContextInitializer
SpringApplicationRunListener
和放在ioc容器中
ApplicationRunner
CommandLineRunner

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

启动Spring Boot 项目

具体的启动流程可以从 SpringApplication.run()方法点进去,进一步查看源码

@SpringBootApplication
public class AdminCommonApplication {
    public static void main(String[] args) {
        SpringApplication.run(AdminCommonApplication.class, args);
    }
}

创建 SpringApplication

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// 根据是否能加载到Web环境中的几个类,来决定webApplicationType 的类型
		this.webApplicationType = deduceWebApplicationType();
		// 从META-INF/spring.factories 下获取 ApplicationContextInitializer类型的子类
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		// 从META-INF/spring.factories 下获取 ApplicationListener 类型的子类
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

SpringApplication运行run()方法

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		// 从META-INF/spring.factories 下获取 SpringApplicationRunListeners 类型的子类 
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 回调SpringApplicationRunListener的starting()方法
		listeners.starting();
		try {
			// 包装命令行参数
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			// 准备环境,此方法中,创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准
			//备完成
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			// 打印 banner 图标
			Banner printedBanner = printBanner(environment);
			// 创建ApplicationContext,决定创建web的ioc还是普通的ioc
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			// 准备IOC容器,将environment 设置到ApplicationContext中
			// 并回调所有ApplicationContextInitializer 的initialize()
			// 再回调SpringApplicationRunListener的contextPrepared()方法
			// 再回调SpringApplicationRunListener的contextLoaded()方法
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			//[spring 启动流程,参看另外几篇文章] 刷新容器ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat)
			// 扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
			refreshContext(context);
			// 这是一个空方法
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			// 再回调SpringApplicationRunListener的started()方法
			listeners.started(context);
			//从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
			//先回调ApplicationRunner.run()方法,再回调CommandLineRunner.run()方法
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

准备环境

包含读取项目中的配置文件

	private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		//配置环境,传入 environment 和命令行参数
		//项目中的配置文件也是在此读取!
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		// 回调SpringApplicationRunListener的environmentPrepared()方法
		listeners.environmentPrepared(environment);
		bindToSpringApplication(environment);
		if (this.webApplicationType == WebApplicationType.NONE) {
			environment = new EnvironmentConverter(getClassLoader())
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

准备IOC容器

	private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		//先将 environment 设置到 ApplicationContext 中
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		//回调所有ApplicationContextInitializer 的initialize()
		applyInitializers(context);
		// 回调SpringApplicationRunListener的contextPrepared()方法
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}

		// Add boot specific singleton beans
		context.getBeanFactory().registerSingleton("springApplicationArguments",
				applicationArguments);
		if (printedBanner != null) {
			context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
		}

		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		// 回调SpringApplicationRunListener的contextLoaded()方法
		listeners.contextLoaded(context);
	}

回调 ApplicationContextInitializer 的initialize()方法

利用此性质,可以用来,从远程的配置中心拉取配置,并覆盖本地配置

	protected void applyInitializers(ConfigurableApplicationContext context) {
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
					initializer.getClass(), ApplicationContextInitializer.class);
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			initializer.initialize(context);
		}
	}

回调ApplicationRunner 和 CommandLineRunner 的run()方法

	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);
			}
		}
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值