SpringBoot启动流程分析原理(二)
最后更新于:2022-08-12 11:35:45
本次开始查看run()方法之后做了哪些事情
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
// 计时器开始
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
// 配置Headless模式,是在缺少显示屏、键盘或者鼠标时的系统配置
// 默认为true
this.configureHeadlessProperty();
// 获取所有的监听器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 启动监听器
listeners.starting();<br />
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
// 配置忽略的bean
this.configureIgnoreBeanInfo(environment);
// 打印banner
Banner printedBanner = this.printBanner(environment);
// 创建容器
context = this.createApplicationContext();
//异常处理相关
exceptionReporters = this.getSpringFactoriesInstances(
SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class},
context);
// 准备应用上下文
this.prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 刷新容器
this.refreshContext(context);
// 刷新容器后的扩展接口
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass))
.logStarted(this.getApplicationLog(), stopWatch);
}
// 发布监听应用上下文启动完成
listeners.started(context);
// 执行runner
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
// 监听应用上下文运行中
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters,
(SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
一、获取所有的监听器
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = this.getClassLoader();
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = this.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()
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
启动的时候,又创建一个ApplicationStartingEvent对象,其实就是监听应用十七栋事件。
其中initialMulticaster是一个SimpleApplicationEventMulticaster
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Iterator var4 = this.getApplicationListeners(event, type).iterator();
while(var4.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var4.next();
Executor executor = this.getTaskExecutor();
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
this.invokeListener(listener, event);
}
}
}
二、准备环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
//这里因为加了web的依赖,所以是一个servert容器
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
//配置环境
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
// 环境准备完成
listeners.environmentPrepared((ConfigurableEnvironment)environment);
this.bindToSpringApplication((ConfigurableEnvironment)environment);
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
由于我们添加了web的依赖getOrCreateEnvironment()返回的是一个标准的servlet的环境。
2.1配置环境
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
//嵌入式的转换器
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService)conversionService);
}
//配置属性资源文件
this.configurePropertySources(environment, args);
// 配置文件
this.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.1环境准备完成
同上面的启动监听事件,这次的环境准备也是同样的代码
public void environmentPrepared(ConfigurableEnvironment environment) {
Iterator var2 = this.listeners.iterator();
while(var2.hasNext()) {
SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
listener.environmentPrepared(environment);
}
}
//创建一个应用环境准备事件对象
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
进去以后和代码和ApplicationStartingEvent事件对象一样。不过这里是7个监听对象
3.配置忽略的bean
this.configureIgnoreBeanInfo(environment);
4、打印banner
SpringBoot启动时的默认图标
Banner printedBanner = this.printBanner(environment);
这个是可以自定义的,也可以是图层或者文本文件中的图形
5、创建容器
紧接着,开始创建容器
context = this.createApplicationContext();
我们的环境是servelt,通过反射的方式创建对象,并且实例化AnnotationConfigServletWebServerApplicationContext对象
6、异常错误处理
exceptionReporters = this.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);
}
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//注册一个字是springApplicationArguments单例的bean
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
//注册一个字是springBootBanner单例的bean,
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
//覆盖同名的bean
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//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的源码
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
//准备刷新上下文
this.prepareRefresh();
// 通知子类刷新内部工厂
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
// 准备Bean工厂
this.prepareBeanFactory(beanFactory);
try {
// 运行在上下文子类中对bean工厂进行后处理
this.postProcessBeanFactory(beanFactory);
// 调用上下文中注册为bean的工厂处理器
this.invokeBeanFactoryPostProcessors(beanFactory);
// 注册后置处理器
this.registerBeanPostProcessors(beanFactory);
// 初始化信息源
this.initMessageSource();
// 初始化上下文事件发布器
this.initApplicationEventMulticaster();
// 初始化其他自定义bean
this.onRefresh();
// 注册其他自定义bean
this.registerListeners();
// 完成bean工厂初始化
this.finishBeanFactoryInitialization(beanFactory);
// 完成刷新,清缓存,初始化生命周期,事件发布
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
//销毁bean
this.destroyBeans();
// 重置active标志
this.cancelRefresh(var9);
throw var9;
} finally {
// 重置Spring核心中的常见内省缓存
this.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对象,并设置参数
@Override
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
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
initialize();
}
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
addInstanceIdToEngineName();
Context context = findContext();
context.addLifecycleListener((event) -> {
if (context.equals(event.getSource())
&& Lifecycle.START_EVENT.equals(event.getType())) {
// 删除服务连接器,以便在服务启动时不会发生协议绑定
removeServiceConnectors();
}
});
// 启动服务器以触发初始化侦听器
this.tomcat.start();
// 我们可以直接在主线程中重新抛出失败异常
rethrowDeferredStartupExceptions();
try {
ContextBindings.bindClassLoader(context, context.getNamingToken(),
getClass().getClassLoader());
}
catch (NamingException ex) {
// 未启用命名。继续
}
// 与Jetty不同,所有Tomcat线程都是守护进程线程。我们创建一个阻塞的非守护进程来停止立即关闭
startDaemonAwaitThread();
}
catch (Exception ex) {
stopSilently();
throw new WebServerException("Unable to start embedded Tomcat", ex);
}
}
}
9、刷新后处理
afterRefresh()是一个空实现,留着后期扩展
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
// TODO
}
10、发布监听应用启动事件
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(
new ApplicationStartedEvent(this.application, this.args, context));
}
这里是调用context.publishEvent()方法,发布应用启动事件ApplicationStartedEvent
11、执行runner
获取所有的ApplicationRunner和CommandLineRunner来初始化一些参数
callRunners()是一个回调函数
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);
Iterator var4 = (new LinkedHashSet(runners)).iterator();
while(var4.hasNext()) {
Object runner = var4.next();
if (runner instanceof ApplicationRunner) {
this.callRunner((ApplicationRunner)runner, args);
}
if (runner instanceof CommandLineRunner) {
this.callRunner((CommandLineRunner)runner, args);
}
}
}
12、发布上下文准备完成的事件
listeners.running(context);
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(
new ApplicationReadyEvent(this.application, this.args, context));
}
前面有很多类似的代码,不同的是这里上下文准备完成之后发布了一个ApplicationReadyEvent事件,声明一下应用上下文准备完成