SpringBoot启动流程详解(二)

执行run(args)方法

源码


	public ConfigurableApplicationContext run(String... args) {
		//创建任务计时器,并开始计时
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        //设置Headless为true,该模式是在缺少显示屏、键盘或者鼠标是的系统配置
		configureHeadlessProperty();
		//获取运行监听器集合,并启动监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			//创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
			//并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			//Banner打印
			Banner printedBanner = printBanner(environment);
			//根据WebApplicationType的值来决定创建何种类型的ApplicationContext对象
			context = createApplicationContext();
			//根据SpringBootExceptionReporter类名,去找到所有META-INF/spring.factories的jar包,获取该类(根据当前类名,去找到所有META-INF/spring.factories的jar包,
			// 获取该类名为key值的value值,然后返回根据@order注解排序后的实例化集合。
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
            //准备上下文,并刷新上下文
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			//关闭任务执行时间监听器
			stopWatch.stop();
			//如果开启日志,则打印执行是时间
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			//调用所有的SpringApplicationRunListener的started()方法,广播SpringBoot已经完成了ApplicationContext初始化的全部过程。
			listeners.started(context);
			//遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。
			//我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}
		try {
			//调用所有的SpringApplicationRunListener的running()方法,广播SpringBoot已经可以处理服务请求了。
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

以上源码已经有注释,我挑选核心步骤深入源码

获取运行监听器集合,并启动监听器

源码

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

根据当前类名SpringApplicationRunListener,去找到所有META-INF/spring.factories的jar包,获取该类名为key值的value值,然后返回根据@order注解排序后的实例化集合。详细代码解析参考
SpringBoot启动流程分析(一)

创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile)

//创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
			//并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);

源码分析
获取环境、应用绑定环境、监听器触发相应的事件

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		//根据webApplicationType创建不同的环境对象
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		//获取指定激活的配置文件或默认配置文件
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		//将ConfigurationPropertySource支持附加到指定的环境
		ConfigurationPropertySources.attach(environment);
        //监听器去触发响应的事件
		listeners.environmentPrepared(environment);
		//将环境绑定到应用
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		//将ConfigurationPropertySource支持附加到指定的环境
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

准备上下文,并刷新上下文

//准备上下文,并刷新上下文
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		//设置环境
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		applyInitializers(context);
		//监听器出发相应事件
		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);
		}
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
		// 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);
	}

遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。

//遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。
			//我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。
			callRunners(context, applicationArguments);

源码

private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<>();
		//从上下文获取ApplicationRunner、CommandLineRunner相关Bean,加入到集合里面
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		//根据@Order进行排序
		AnnotationAwareOrderComparator.sort(runners);
		//对集合遍历,并分别执行其run方法
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

这就是为什么我们想要SpringBoot启动时执行的代码,需要实现ApplicationRunner或者CommandLineRunner接口。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值