在上一节我们分析了SpringBoot启动流程中实例化SpringApplication的过程。
return new SpringApplication(primarySources).run(args)
这篇文章咱么说下run()方法开始之后都做了那些事情
继续往下跟着源码进入到run()这个是比较核心的一个方法了
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 {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 配置忽略的bean
configureIgnoreBeanInfo(environment);
// 打印banner
Banner printedBanner = printBanner(environment);
// 创建容器
context = createApplicationContext();
// 异常处理相关
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);
}
// 发布监听应用上下文启动完成
listeners.started(context);
// 执行runner
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;
}
接下来就对上面的关键步骤一一解释
1、获取所有的监听器
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
这段代码我们比较熟悉了,上一篇咱么详细介绍过,它的主要作用就是去 META-INFO/spring.properties 中加载配置 SpringApplicationRunListener 的监听器如下:
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=
org.springframework.boot.context.event.EventPublishingRunListener
显然只有一个事件发布监听器类,拿到了EventPublishingRunListener启动事件发布监听器,下一步就是开始启动了listeners.starting(); 我们往下跟源码看
@Override
public voidstarting() {
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}
启动的时候实际上是又创建了一个 ApplicationStartingEvent 对象,其实就是监听应用启动事件。
其中 initialMulticaster 是一个 SimpleApplicationEventMulticaster
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener :
// 根据ApplicationStartingEvent事件类型找到对应的监 听器
getApplicationListeners(event, type)) {
// 获取线程池,为每个监听事件创建一个线程
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
2、准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
继续往下跟看到
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
// 这里我们加了web的依赖所以是一个servlet容器
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;
}
由于我们是添加了web的依赖 getOrCreateEnvironment() 返回的是一个 StandardServletEnvironment 标准的servlet环境,
2.1 配置环境
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
if (this.addConversionService) {
// 嵌入式的转换器
ConversionService conversionService = ApplicationConversionService
.getSharedInstance();
environment.setConversionService(
(ConfigurableConversionService) conversionService);
}
// 配置属性资源文件
configurePropertySources(environment, args);
//配置文件
configureProfiles(environment, args);
}
应用嵌入的转换器ApplicationConversionService
public static void configure(FormatterRegistry registry) {
DefaultConversionService.addDefaultConverters(registry);
DefaultFormattingConversionService.addDefaultFormatters(registry);
addApplicationFormatters(registry);
addApplicationConverters(registry);
}
===================格式转换=============================
public static void addApplicationFormatters(FormatterRegistry registry) {
registry.addFormatter(new CharArrayFormatter());
registry.addFormatter(new InetAddressFormatter());
registry.addFormatter(new IsoOffsetFormatter());
}
====================类型转换============================
public static void addApplicationConverters(ConverterRegistry registry) {
addDelimitedStringConverters(registry);
registry.addConverter(new StringToDurationConverter());
registry.addConverter(new DurationToStringConverter());
registry.addConverter(new NumberToDurationConverter());
registry.addConverter(new DurationToNumberConverter());
registry.addConverter(new StringToDataSizeConverter());
registry.addConverter(new NumberToDataSizeConverter());
registry.addConverterFactory(new StringToEnumIgnoringCaseConverterFactory());
}
2.2 环境准备完成
同上面启动监听事件,这次的环境准备也是同样的代码
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(
// 创建了一个应用环境准备事件对象
new ApplicationEnvironmentPreparedEvent(
this.application, this.args, environment));
}
debug进去之后代码跟ApplicationStartingEvent事件对象是一样的。不再赘述。
不过这里是7个监听器对象
3、配置忽略的bean
configureIgnoreBeanInfo(environment);
4、打印banner
这是SpringBoot默认的启动时的图标
Banner printedBanner = printBanner(environment);
这个是可以自定义的,也可以是图篇或是文本文件中的图形。
5、创建容器
紧接着上一篇,接下来就是创建容器
context = createApplicationContext();
我们的环境是servlet,DEFAULT_SERVLET_WEB_CONTEXT_CLASS其实servlet通过反射的方式创建对象
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
并且实例化AnnotationConfigServletWebServerApplicationContext对象
6、异常错误处理
代码如下:
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
其实还是去 META-INFO/spring.factories 配置文件中加载 SpringBootExceptionReporter 类
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=
org.springframework.boot.diagnostics.FailureAnalyzers
7、准备应用上下文
这里就会根据之前创建的上下文、准备的环境、以及监听等准备应用上下文
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//设置环境参数
context.setEnvironment(environment);
//设置后处理应用上下文
this.postProcessApplicationContext(context);
//把从spring.properties中加载的org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,进行初始化操作
this.applyInitializers(context);
//EventPublishingRunLIstener发布应用上下文事件
listeners.contextPrepared(context);
//打印启动日志
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
//注册一个字是springApplicationArguments单例的bean,
context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
//注册一个字是springBootBanner单例的bean,
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
//Load the sources 获取所有资源
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//创建BeanDefinitionLoader加载器加载注册所有的资源
this.load(context, sources.toArray(new Object[0]));
//同之前,发布应用上下文加载事件
listeners.contextLoaded(context);
}
8、刷新应用上下文
刷新应用上下文就进入了spring的源码了
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//准备刷新上下文
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
// 通知子类涮新内部工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 准备Bean工厂
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
// 允许在上下文子类中对bean工厂进行后处理。
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//调用上下文中注册为bean的工厂处理器 invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//注册后置处理器。
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 初始化信息源
initMessageSource();
// Initialize event multicaster for this context.
// 初始化上下文事件发布器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 初始化其他自定义bean
onRefresh();
// Check for listener beans and register them.
// 注册监听器
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 完成bean工厂初始化 finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
// 完成刷新,清缓存,初始化生命周期,事件发布等
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context init ialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
// 销毁bean
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
刷新的代码有点深,也是在这时创建了Tomcat对象,这也是SpringBoot一键启动web工程的关键
@Override
protected void onRefresh() {
super.onRefresh();
try {
// 创建web服务
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
// 获取到Tomcat
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}
创建了Tomcat对象,并设置参数
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory
: createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
// 返回TomcatWebServer服务
return getTomcatWebServer(tomcat);
}
启动Tomcat服务器
9、刷新后处理
afterRefresh()是个一空实现,留着后期扩展
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
// TODO
}
10、发布监听应用启动事件
public void started(ConfigurableApplicationContext context) {
context.publishEvent(
new ApplicationStartedEvent(this.application, this.args, context));
}
这里是调用context.publishEvent()方法,发布应用启动事件ApplicationStartedEvent.
11、执行Runner
获取所有的ApplicationRunner和CommandLineRunner来初始化一些参数
callRunner()是一个回调函数
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);
}
}
}
12、发布上下文准备完成的事件
listeners.running(context);
public void running(ConfigurableApplicationContext context) {
context.publishEvent(
new ApplicationReadyEvent(this.application, this.args, context));
}
这段代码看上去似成相识,前面有很多类似的代码,不同的是这里上下文准备完成之后发布了一个ApplicationReadyEvent事件,声明一下应用上下文准备完成
小结
这篇主要是介绍了SpringBoot启动过程中run()的这个过程。从中我们也可以发现一些非常好的编码习惯,大家可以在日常的工作中从模仿到内化,慢慢变成自己的东西。