又是美好的一天呀~ 个人博客地址: huanghong.top
本文预估阅读时长为30分钟左右~
核心功能
- 可独立运行的Spring项目:Spring Boot可以以jar包的形式独立运行。
- 内嵌的Servlet容器:Spring Boot可以选择内嵌Tomcat、Jetty或者Undertow,无须以war包形式部署项目。
- 简化的Maven配置:Spring提供推荐的基础 POM 文件来简化Maven 配置。
- 自动配置Spring:Spring Boot会根据项目依赖来自动配置Spring 框架,极大地减少项目要使用的配置。
- 提供生产就绪型功能:提供可以直接在生产环境中使用的功能,如性能指标、应用信息和应用健康检查。
- 无代码生成和xml配置:Spring Boot不生成代码。完全不需要任何xml配置即可实现Spring的所有配置。
启动过程源码分析
源码基于Springboot 2.6.6版本
运行 SpringApplication.run()
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @Time 2023-03-09 20:17
* Created by Huang
* className: Main
* Description:
*/
@SpringBootApplication
public class Main {
public static void main(String[] args) {
//执行run方法
SpringApplication.run(Main.class, args);
}
}
构建SpringApplication对象
//org.springframework.boot.SpringApplication#SpringApplication(org.springframework.core.io.ResourceLoader, java.lang.Class<?>...)
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//赋值null
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//设置主启动类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//设置应用程序类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置启动注册初始化器
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
//设置初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
确定应用程序类型
//org.springframework.boot.WebApplicationType#deduceFromClasspath
static WebApplicationType deduceFromClasspath() {
//如果org.springframework.web.reactive.DispatcherHandler存在且org.springframework.web.servlet.DispatcherServlet和、、、、
//org.glassfish.jersey.servlet.ServletContainer不存在,则设置为响应式类型
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
//javax.servlet.Servlet或org.springframework.web.context.ConfigurableWebApplicationContext不存在,则设置为NONE类型
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
//默认返回servlet-web类型
return WebApplicationType.SERVLET;
}
加载所有的初始化器
通过org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories读取META-INF/spring.factories配置文件
org\springframework\boot\spring-boot-autoconfigure\2.6.6\spring-boot-autoconfigure-2.6.6.jar!\META-INF\spring.factories
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
org\springframework\boot\spring-boot\2.6.6\spring-boot-2.6.6.jar!\META-INF\spring.factories
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
加载Springboot内置初始化器
自定义初始化器
-
定义初始化器
package com.huang.Initializer; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Component; /** * @Time 2023-03-09 21:11 * Created by Huang * className: DemoInitializer * Description: */ @Slf4j @Component public class DemoInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext applicationContext) { log.info("DemoInitializer is running"); log.info("applicationContext: {}",applicationContext.toString()); } } -
在src/main/resources目录下创建META-INF/spring.factories;
org.springframework.context.ApplicationContextInitializer=com.huang.Initializer.DemoInitializer -
启动项目后日志打印
com.huang.Initializer.DemoInitializer : DemoInitializer is running com.huang.Initializer.DemoInitializer : applicationContext: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@576f63f6, started on Thu Jan 01 08:00:00 CST 1970
加载所有的监听器
通过org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories读取META-INF/spring.factories配置文件
org\springframework\boot\spring-boot-autoconfigure\2.6.6\spring-boot-autoconfigure-2.6.6.jar!\META-INF\spring.factories
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
org\springframework\boot\spring-boot\2.6.6\spring-boot-2.6.6.jar!\META-INF\spring.factories
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener
加载Springboot内置初始化器
自定义监听器
-
定义监听器
package com.huang.listener; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * @Time 2023-03-09 21:34 * Created by Huang * className: DemoListener * Description: */ @Slf4j @Component public class DemoListener implements ApplicationListener<ApplicationReadyEvent> { @Override public void onApplicationEvent(ApplicationReadyEvent event) { log.info("DemoListener is running"); log.info("event: {}",event.toString()); } } -
在src/main/resources目录下创建META-INF/spring.factories;
org.springframework.context.ApplicationListener=com.huang.listener.DemoListener -
启动项目后日志打印
com.huang.listener.DemoListener : DemoInitializer is running com.huang.listener.DemoListener : applicationContext: org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent[source=org.springframework.boot.web.embedded.tomcat.TomcatWebServer@47b2e9e1]
设置程序运行的主启动类
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
//返回运行main方法的启动类
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
run方法
//org.springframework.boot.SpringApplication#run(java.lang.String...)
public ConfigurableApplicationContext run(String... args) {
//记录启动时间
long startTime = System.nanoTime();
//创建启动上下文
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
//将java.awt.headless设置为true,表示运行在服务器端,在没有显示器器和鼠标键盘的模式下照样可以工作,模拟输入输出设备功能
configureHeadlessProperty();
//创建所有Spring 运行监听器并发布应用启动事件并启用监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
//将执行run方法时传入的参数封装成一个对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备环境变量,包含系统属性和用户配置的属性
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
//将spring.beaninfo.ignore的默认值值设为true,跳过beanInfo的搜索
configureIgnoreBeanInfo(environment);
//打印Banner
Banner printedBanner = printBanner(environment);
//创建应用程序的上下文
context = createApplicationContext();
//为上下文设置applicationStartup,用于记录应用启动期间的metrics
context.setApplicationStartup(this.applicationStartup);
//准备上下文环境
//实例化单例的beanName生成器、执行初始化方法、将启动参数注册到容器中
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//刷新上下文(自动配置和tomcat容器启动)
refreshContext(context);
//后置处理,空实现
afterRefresh(context, applicationArguments);
//启动结束,输出启动时长
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
//发布上下文准备就绪事件
listeners.started(context, timeTakenToStartup);
//执行自定义扩展的run方法
//实现 ApplicationRunner、CommandLineRunner 接口
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//应用启动期间异常处理
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
//发布上下文构建完成事件
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
//应用启动期间异常处理
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
//返回上下文
return context;
}
初始化启动上下文对象
//org.springframework.boot.SpringApplication#createBootstrapContext
private DefaultBootstrapContext createBootstrapContext() {
//创建默认启动上下文对象
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
//执行所有org.springframework.boot.BootstrapRegistryInitializer初始化器中初始化方法
this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
//返回上下文实例
return bootstrapContext;
}
设置headless为true
//org.springframework.boot.SpringApplication#configureHeadlessProperty
private void configureHeadlessProperty() {
//将java.awt.headless设置为true,表示运行在服务器端,在没有显示器器和鼠标键盘的模式下照样可以工作,模拟输入输出设备功能
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
获取并启用监听器
//org.springframework.boot.SpringApplication#getRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
//获取META-INF/spring.factories中SpringApplicationRunListener配置监听器
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
this.applicationStartup);
}
//org.springframework.boot.SpringApplicationRunListeners#starting
//启动监听器
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
(step) -> {
if (mainApplicationClass != null) {
step.tag("mainApplicationClass", mainApplicationClass.getName());
}
});
}
设置应用程序参数
//org.springframework.boot.DefaultApplicationArguments#DefaultApplicationArguments
//将执行run方法时传入的参数(main方法参数)封装成一个对象
public DefaultApplicationArguments(String... args) {
Assert.notNull(args, "Args must not be null");
this.source = new Source(args);
this.args = args;
}
准备环境变量
准备环境变量,包含系统属性和用户配置的属性
//org.springframework.boot.SpringApplication#prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
//创建并配置环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
//发布环境准备就绪状态
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
//绑定至应用程序
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = convertEnvironment(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
忽略bean信息
将 spring.beaninfo.ignore 的默认值值设为true,跳过beanInfo的搜索
//org.springframework.boot.SpringApplication#configureIgnoreBeanInfo
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
//spring.beaninfo.ignore
if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
}
}
可以通过配置不跳过beanInfo搜索
spring.beaninfo.ignore=false
打印 banner 信息
- 在resources目录下添加一个自定义banner.txt
- 图片banner后缀(gif/jpg/png)
创建应用程序的上下文
实例化应用程序的上下文, 调用 createApplicationContext() 方法,这里就是用反射创建对象
//org.springframework.boot.SpringApplication#createApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
准备上下文环境
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//设置环境
context.setEnvironment(environment);
//实例化单例的beanName生成器
postProcessApplicationContext(context);
//执行初始化方法
applyInitializers(context);
listeners.contextPrepared(context);
bootstrapContext.close(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 AbstractAutowireCapableBeanFactory) {
//允许循环应用
((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
//允许beanDefinition重写
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
//如果是上下文是懒加载,则添加LazyInitializationBeanFactoryPostProcessor扩展点
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);
}
实例化单例beanName生成器
//org.springframework.boot.SpringApplication#postProcessApplicationContext
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
//注册org.springframework.context.annotation.internalConfigurationBeanNameGenerator
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
//注册资源加载器
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
//设置类型转换服务
if (this.addConversionService) {
context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService());
}
}
执行初始化方法
//org.springframework.boot.SpringApplication#applyInitializers
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);
}
}
刷新上下文
//org.springframework.boot.SpringApplication#refreshContext
//上下文刷新
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
shutdownHook.registerApplicationContext(context);
}
refresh(context);
}
org.springframework.context.support.AbstractApplicationContext#refresh完成上下文刷新
包含beanFactory初始化、beanFactory扩展、bean实例化、事件广播器初始化、监听器注册、Tomcat容器初始化
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh完成tomcat容器初始化
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
刷新上下文后置处理
应用启动后扩展使用
//org.springframework.boot.SpringApplication#afterRefresh
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
执行自定义的run方法
//org.springframework.boot.SpringApplication#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);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
自定义run方法
-
实现 ApplicationRunner 接口
package com.huang.run; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; /** * @Time 2023-03-09 22:37 * Created by Huang * className: DemoApplicationRunner * Description: */ @Slf4j @Component public class DemoApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { log.info("DemoApplicationRunner is running"); log.info("ApplicationArguments: {}",args); } } -
实现 CommandLineRunner 接口
package com.huang.run; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; /** * @Time 2023-03-09 22:38 * Created by Huang * className: DemoCommandLineRunner * Description: */ @Slf4j @Component public class DemoCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { log.info("DemoApplicationRunner is running"); log.info("ApplicationArguments: {}", String.join(",", args)); } }
日志输出
com.huang.run.DemoApplicationRunner : DemoApplicationRunner is running
com.huang.run.DemoApplicationRunner : ApplicationArguments: org.springframework.boot.DefaultApplicationArguments@f973499
com.huang.run.DemoCommandLineRunner : DemoApplicationRunner is running
com.huang.run.DemoCommandLineRunner : ApplicationArguments:
感谢阅读完本篇文章!!! 个人博客地址: huanghong.top