本文最后更新于 2025年7月15日 上午
Spring Boot 核心思想
基于Spring框架派生的脚手架
SpringBoot 提供了各种技术组件的一站式启动器,开发者只要定义好对应技术组件的配置参数,SpringBoot 就会进行自动配置
SpringBoot 框架的核心思想是:约定大于配置,即按照约定进行编程
1.独立运行
Spring Boot通过内置servlet容器 可以直接完成 web应用的部署
2.简化配置
通过注解和SPI的方法简化了配置过程,并且允许用户自定义自动配置启动的部署过程。
3.自动配置
提供了自动配置功能,通过自动配置注解完成 对应对应依赖的查询,实例化以及装载过程,可以快速的开箱即用
配置类定义 1 使用SpringBootConfiguration注解 定义SpringBoot 配置类
SpringBootConfiguration 只是Cofiguration 注解的别名,用于区分SpringBoot与原生Spring应用
1 2 3 4 5 6 7 8 9 @Taget({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration @Indexed public @interface SpringBootConfiguration{ @AliasFor(annotation = Configuration.class) boolean proxyBeanMethods () default true ; }
配置 application application (注册.properties 文件到spring项目中,从文件中读取字符串信息用于对对象进行赋值)
springboot 通过查询 配置文件信息加载值到对应的bean变量声明中
1 2 static final String CONFIG_NAME_PROPERTY = "spring.config.name" ;static final String[] DEFAULT_CONFIG_NAMES = { "application" };
配置格式
.properties :
1 2 server.port = 8080 server.servlet.contextPath = /javastack
.yml
1 2 3 4 server : port : 8080 servlet : contextPath : /javastack
bootstrap bootstrap 文件用于在加载阶段,为Spring Cloud 项目提供环境配置信息
向Bean对象中进行注入 使用@Value注解 和 Spel 表达式完成 值对属性的注入
1 2 3 4 5 6 7 8 @Value("${user.username}") private String username ;@Value("${user.age}") private int age ;@Value("${user.sex}") private int sex ;
使用@PropertySource 执行需要注入的properties文件(默认application.properties)
1 2 3 4 5 6 7 8 9 @Data @Component @PropertySource(value = {"config/db-config.properties"}) public class DbProperties { @Value("${db.username}") private String username ; @Value("{db.password}") private String password ; }
使用 @ConfigurationProperties 完成注入
1 2 3 4 5 6 7 8 9 10 11 12 @Data @ConfigurationProperties( prefix ="javastack") public class JavastackProperties { private boolean enabled ; private String name ; private String site ; private String author ; private List<String> users ; private Map<String , String> params ; private Security security ; }
在启动类中添加@EnableConfigurationProperties 注解,用于从目标类中创建/配置Bean ,如果有需要将该Bean注入到 被注解类中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @SpringBootApplication @RequiredArgsConstructor @EnableConfigurationProperties(value = {JavastackProperties.class}) @Slf4j public class Application { private final JavastackProperties javastatckProperties ; @Bean public CommandLineRunner commandLineRunner () { return ( args ) -> { log.info("javastack properties:{}" , javastackProperties); }; } }
构造器绑定 定义配置参数
1 2 3 4 5 member : name : Tom sex : 1 age : 18 birthday : 2000 -12 -12 12 :00 :00
创建一个MemberProperties参数类进行绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Data @NoArgsConstructor @ConfigurationProperties(prefix = "member") public class MemberProperties { private String name ; private int sex ; private int age ; private String country ; private Date birthday ; public MemberProperties (String name , int sex , int age) { this .name = name ; this .sex = sex ; this .age = age ; } @ConstructorBinding public MemberProperties (String name , int sex , int age , @DefaultValue("Chiane") String country , @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date birthday) { this .name = name ; this .sex = sex ; this .age = age ; this .country = country ; this .birthday = birthday ; } }
在@Bean 中使用 @ConfigurationProperties 完成 属性配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Data @NoArgsConstructor public class OtherMember { private String name ; private int sex ; private int age ; }@SpringBootConfiguration public class MainConfig { @Bean @ConfigurationProperties(prefix = "member") public OtherMember otherMember () { return new OtherMember (); } }
在应用类中进行 相关调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @SpringBootApplication @RequiredArgsConstructor @EnableConfigurationProperties(value = {JavastackProperties.class, MemberProperties.class}) @Slf4j public class Application { private final JavastackProperties javastackProperties ; private final MemberProperties memberProperties ; private final OtherMember otherMember ; @Bean public CommandLineRunner commandLineRunner () { return (args) -> { log.info("javastack properties : {}" , javastackProperties); log.info("member properties : {}" , memberProperties); log.info("other member: {}" , otherMember); } } }
参数类扫描 1 2 3 4 5 6 7 @SpringBootApplication @RequiredArgsConstructor @ConfigurationPropertiesScan @Slf4j public class Application { }
Profile 使用profile指定环境配置信息
1 2 3 spring : profiles : default : dev
1 2 3 spring : profiles : active : dev, test
加载对应的Profile信息
1 2 3 4 5 6 @Profile("main") @SpringBootConfiguration @Import({Configuration1.class , Configuration2.class}) public class MainConfig { }
自动装配 Spring Boot的自动装配过程 SpringBoot 通过 @EnableAutoConfiguration完成自动装配过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackeage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration{ String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration" ; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
实现的ImportSelector 接口描述
1 2 3 4 5 6 7 8 9 public interface ImportSelector { String[] selectImports(AnnotationMetadata importingClassMetadata); @Nullable default Predicate<String> getExclusionFilter () { return null ; } }
方法实现
1 2 3 4 5 6 7 8 @Override public String[] selectImports(AnnotationMetadata annotationMetadata){ if (!isEnabled(annotationMetadata)){ return NO_IMPORTS; } AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
在2.7中的实现:
1 2 3 4 5 6 7 8 9 10 protected List<String> getCandidateConfigurations (AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = new ArrayList <>( SpringFactories.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader())); ImportCanditates.load(AutoConfiguration.class , getBeanClassLoader()).forEach(configurations::add); Assert.notEmpty(configurations,"No auto configuration classes found ..." ); return configurations; }
3.0 :
1 2 3 4 5 6 7 protected List<String> getCandidateConfigurations (AnnotationMedata metadata, AnnotationAttributes attributes) { List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()) .getCandidates(); Assert.notEmpty(configurations,"No auto configuration classes found ..." ); return configurations; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public static List<String> LoadFactoryNames (Class<?> factoryType , @Nullable ClassLoader classLoader) { return forDefaultResourceLoader(classLoader).loadFactory(factoryType); }public static SpringFactoriesLoader forDefaultResourceLocation (@Nullable ClassLoader classLoader) { return forResourceLocation(classLoader, FACTORIES_RESOURCE_LOCATION); }public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" ;public static SpringFactoriesLoader forResourceLocation (@Nullable ClassLoader classLoader, String resrouceLocation) { Assert.hasText(resourceLocation, "'resourceLocation' must not be empty" ); ClassLoader resourceClassLoader = (classLoader != null ) ? classLoader : SpringFactoriesLoader.class.getClassLoader(); Map<String , SpringFactoriesLoader> loaders = SpringFactoriesLoader.cache.get(resourceClassloader); if (loaders == null ){ loaders = new ConcurrentReferenceHashMap <>(); SpringFactoriesLoader.cache.put(resourceClassLoader,loaders); } SpringFactoriesLoader lodaer = loaders.get(resourceLocation); if (loader == null ){ Map<String, List<String>> factories = loadFactoriesResource(resourceClassLoader, resourceLocation); loader = new SpringFactoriesLoader (classLoader, factories); loaders.put(resourceLocation,loader); } return loader; }static Map<String , List<String>> loadFactoriesResource (ClassLoader classLoader , String resourceLocation) { Map<String, List<String>> result = new LinkedHashMap <>(); try { Enumeration<URL> urls = classLoader.getResources(resourceLocation); while (urls.hasMoreElements()){ UrlResource resource = new UrlResource (urls.nexElement()); Properties properties = PropertiesLoaderUtils.loadProperties(resource); properties.forEach((name, value) -> { List<String> implementations = result.computeIfAbsent(((String) name),trim(), key -> new ArrayList <>()); Arrays.stream(StringUtils.commaDelimitedListToStringArray((String)vale)) .map(Strintg::trim).forEach(implementations::add); }); } result.replaceAll(SpringFactoriesLoader::toDistinctUnmodifiableList); } catch (IOException ex){ throw new IllegalArgumentException ("Unable to load factories from location ..." ); } return Collections.unmodifiableMap(result); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 private static final String LOCATION = "META-INF/spring/%s.imports" ;public static ImportCandidates load (Class<?> annotation, ClassLoader classLoader) { Assert.notNull(annotation, "ananotation must not be null" ); ClassLoader classLoaderToUse = decideClassloader(classLoader); String location = String.format(LOCATION,annotation.getName()); Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse , location); List<String> importCandidates = new ArrayList <>(); while (urls.hasMoreElements()){ URL url = urls.nextElement(); importCandidates.addAll(readACandidateConfigurations(url)); } return new ImportCandidates (importCandidates); }
从 加载位置加载自动配置类进行注册
以DataSrouce 为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @AutoConfiguration(before = SqlInitializationAutoConfiguration.class) @ConditionalOnClass({DataSrouce.class , EmbeddedDatabaseType.class}) @ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory") @EnableConfigurationProperties(DataSourceProperties.class) @Import(DataSourcePoolMetadataProvidersConfiguration.class) public class DataSourceAutoConfiguration { @Configuration(proxyBeanMethods = false) @Conditional(EmbeddedDatabaseCondition.class) @ConditionalOnMissingBean({DataSource.class,XADAataSource.class}) @Import(EmbeddedDataSourceConfiguration.class) protected static class EmebddedDatabaseConfiguration { } @Configuration(proxyBeanMethods = false) @Conditional(PooledDataSourceCondition.class) @ConditionalOnMissingBean({DataSource.class, XADataSource.class}) @Import({DataSourceConfiguration.Hikari.class , DataSourceConfiguration.Tomcat.class , DataSourceConfiguration.Dbcp2.class , DataSourceConfiguration.OracleUcp.class , DataSourceConfiguration.Generic.class , DataSourceJmxConfiguration.class } ) protected static class PooledDataSourceConfiguration { } ... }
@ConditionalOn 注解 当满足条件时 触发配置类的自动配置
1 2 3 4 5 6 7 8 9 @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Conditional(OnClassCondition.class) public @interface ConditionalOnClass { Class <?>[] value() default {}; String[] name() default {}; }
exclude 排除部分类的加载
1 2 3 4 5 @EnableAutoConfiguration(exclude = {}) public class Applicaiton { } ...
Spring Boot 启动过程 1 初始化SpringApplication 从spring.factories 读取listener ApplicationContextInitializer
2 运行run方法
3 读取环境信息 配置信息
4 创建SpringApplication上下文
5 预初始化上下文: 读取启动类
6 调用refresh加载IOC容器 加载所有的自动配置类 创建servlet 容器
7 在这个过程中springboot会调用很多监听器对外进行扩展
创建的Spring Boot 应用 通过main方法启动
1 2 3 4 5 6 @SpringBootApplication public class Application { public static void main (String[] args) { SpringApplication.run(Application.class, args); } }
1 2 3 4 5 6 7 8 9 10 @Target(ElementType.TYPE) @Retetion(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM , classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigrationExcludeFilter.class}) public @interface SpringBootApplication{ }
@SpringBootConfiguration alias为@Configuration
@EnableAutoConfiguration 启动自动配置功能
@ComponentScan 自动扫描Spring组件
Springboot 会通过一个run方法 来启动程序
1 2 3 4 public static ConfigurableApplicationContext run (Class<?>[] primarySources , String[] args) { return new SpringApplication (primarySources).run(args); }
创建Spring Application 应用 ,进行相关的资源加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public SpringApplication (ResourceLoader resourceLoader, Class<?>... primarySources) { this .addCommandLineProperties = true ; this .addConversionService = true ; this .headless = true ; this .additionalProfiles = Collections.emptySet(); this .applicationContextFactory = ApplicationContextFactory.DEFAULT; this .applicationStartup = ApplicationStartup.DEFAULT; this .properties = new ApplicationProperties (); this .resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null" ); this .primarySources = new LinkedHashSet (Arrays.asList(primarySources)); this .properties.setWebApplicationType(WebApplicationType.deduceFromClasspath()); this .bootstrapRegistryInitializers = new ArrayList (this .getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); this .setInitializers(this .getSpringFactoriesInstances(ApplicationContextInitializer.class)); this .setListeners(this .getSpringFactoriesInstances(ApplicationListener.class)); this .mainApplicationClass = this .deduceMainApplicationClass(); }
加载类资源后,进行应用类型推断
1 2 3 4 5 6 7 8 9 10 11 12 static WebApplicationType deduceFromClasspath () { if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null ) && !ClassUtils.isPresent && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null )){ return WebApplicationType.REACTIVE; } for (String className : SERVLET_INDICATOR_CLASSES){ if (!ClassUtils.isPresent(className, null )){ return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; }
最终在实例化过程中组织了 SpringApplication 对象。 执行run方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public ConfigurableApplicationContext run (String... args) { Startup startup = SpringApplication.Startup.create(); if (this .properties.isRegisterShutdownHook()) { shutdownHook.enableShutdownHookAddition(); } DefaultBootstrapContext bootstrapContext = this .createBootstrapContext(); ConfigurableApplicationContext context = null ; this .configureHeadlessProperty(); SpringApplicationRunListeners listeners = this .getRunListeners(args); listeners.starting(bootstrapContext, this .mainApplicationClass); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments (args); ConfigurableEnvironment environment = this .prepareEnvironment(listeners, bootstrapContext, applicationArguments); Banner printedBanner = this .printBanner(environment); context = this .createApplicationContext();' // 设置应用启动监控器,用于记录启动过程中的各种指标。 context.setApplicationStartup(this.applicationStartup); // 环境准备 设置环境变量,应用初始化器,注册监听器,加载著配置类 this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); // 加载/解析配置,创建和初始化Bean,解决依赖关系,启动内嵌服务器 this.refreshContext(context); // 执行刷新后的回调处理,标记启动阶段完成。 this.afterRefresh(context, applicationArguments); startup.started(); if (this.properties.isLogStartupInfo()) { (new StartupInfoLogger(this.mainApplicationClass, environment)).logStarted(this.getApplicationLog(), startup); } listeners.started(context, startup.timeTakenToStarted()); this.callRunners(context, applicationArguments); } catch (Throwable ex) { throw this.handleRunFailure(context, ex, listeners); } try { if (context.isRunning()) { listeners.ready(context, startup.ready()); } return context; } catch (Throwable ex) { throw this.handleRunFailure(context, ex, (SpringApplicationRunListeners)null); } }
prepareEnvironment
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private ConfigurableEnvironment prepareEnvironment (SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { ConfigurableEnvironment environment = this .getOrCreateEnvironment(); this .configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(bootstrapContext, environment); ApplicationInfoPropertySource.moveToEnd(environment); DefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix" ), "Environment prefix cannot be set via properties." ); this .bindToSpringApplication(environment); if (!this .isCustomEnvironment) { EnvironmentConverter environmentConverter = new EnvironmentConverter (this .getClassLoader()); environment = environmentConverter.convertEnvironmentIfNecessary(environment, this .deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
prepareContext
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 private void prepareContext (DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); this .postProcessApplicationContext(context); this .addAotGeneratedInitializerIfNecessary(this .initializers); this .applyInitializers(context); listeners.contextPrepared(context); bootstrapContext.close(context); if (this .properties.isLogStartupInfo()) { this .logStartupInfo(context.getParent() == null ); this .logStartupInfo(context); this .logStartupProfileInfo(context); } ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments" , applicationArguments); if (printedBanner != null ) { beanFactory.registerSingleton("springBootBanner" , printedBanner); } if (beanFactory instanceof AbstractAutowireCapableBeanFactory autowireCapableBeanFactory) { autowireCapableBeanFactory.setAllowCircularReferences(this .properties.isAllowCircularReferences()); if (beanFactory instanceof DefaultListableBeanFactory listableBeanFactory) { listableBeanFactory.setAllowBeanDefinitionOverriding(this .properties.isAllowBeanDefinitionOverriding()); } } if (this .properties.isLazyInitialization()) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor ()); } if (this .properties.isKeepAlive()) { context.addApplicationListener(new KeepAlive ()); } context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor (context)); if (!AotDetector.useGeneratedArtifacts()) { Set<Object> sources = this .getAllSources(); Assert.notEmpty(sources, "Sources must not be empty" ); this .load(context, sources.toArray(new Object [0 ])); } listeners.contextLoaded(context); }
事件与监听过程 在Spring Boot 中通过构建监听器,在执行过程中在不同的阶段触发对应的监听器,来完成数据和资源的准备工作
观察者模式 观察者模式 提供了一种 注册-通知机制。在流程开始时,将所有关注被观察对象的对象注册到观察者容器中,当被观察对象状态发生变化时,遍历观察者容器中的对象,执行其中的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 interface Subject { void addObserver (Observer observer) ; void removeObserver (Observer observer) ; void notifyObservers () ; }interface Observer { void update (String message) ; }class NewsAgency implements Subject { private List<Observer> observers = new ArrayList <>(); private String news; @Override public void addObserver (Observer observer) { observers.add(observer); } @Override public void notifyObservers () { for (Observer observer : observers) { observer.update(news); } } public void setNews (String news) { this .news = news; notifyObservers(); } }class NewsChannel implements Observer { private String name; @Override public void update (String news) { System.out.println(name + " 收到新闻: " + news); } }NewsAgency agency = new NewsAgency (); agency.addObserver(new NewsChannel ("CNN" )); agency.addObserver(new NewsChannel ("BBC" )); agency.setNews("重大新闻!" );
事件与监听 Spring boot 中 实现了更加复杂的观察者模式
定义了一个事件对象
1 2 3 4 5 public class MyCustomEvent extends ApplicationEvent { public MyCustomEvent (Object source) { super (source); } }
监听器通过实现ApplicationListener 接口,重写onApplicationEvent 方法来定义事件发生时所需要调用的方法
1 2 3 4 5 6 7 8 @Component public class MyEventListener implements ApplicationListener <MyCustomEvent> { @Override public void onApplicationEvent (MyCustomEvent event) { System.out.println("收到事件:" + event.getSource()); } }
通过ApplicationEventPublisher 对象 发布事件
1 2 3 4 5 6 7 @Autowired private ApplicationEventPublisher publisher;public void doSomething () { publisher.publishEvent(new MyCustomEvent ("自定义事件" )); }
在SpringBoot 启动过程中,
1 在创建SpringApplication 对象阶段,检索.factories中的实现了ApplicationListener 接口的类定义,建立映射关系
2 在SpringApplicationRunListeners listeners = this.getRunListeners(args)过程中,创建事件广播其与监听器进行绑定,将所有监听器实例注册到 listeners容器中。
3 使用listeners.function , 触发事件,发布对应的事件类型,在容器中遍历监听器,执行定义的对应事件触发功能。
在启动过程中会发布的事件有:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ApplicationStartingEvent 这个事件在Spring Boot 应用运行开始时,且进行任何处理之前发送 ApplicationEnvironmentPreparedEvent 这个事件在当已知要在上下文中使用Spring环境 Environment时,在Spring上下文context创建前发送 ApplicationContextInitializedEvent 这个事件在Spring应用上下文ApplicationContext准备好了,并且应用初始化器Application-ContextInitializers已经被调用 在bean definitions被加载前发送 ApplicationPreparedEvent 这个事件是在Spring上下文context刷新之前,且在bean definitions被加载之后发送 ApplicationStartEvent 这个事件是在Spring上下文coontext刷新后,且在任何application/command-line runners被调用之前发送 AvailabilityChangeEvent 这个事件紧随上个事件之后发送,激活状态ReadinessState.CORRECT 表示应用已处于活动状态 ApplicationReadyEvent 这个事件在任何application/command-line runners 调用之后发送 AvailabilityChangeEvent 这个事件紧随上个事件之后发送,激活状态ReadinessState.ACCEPTING_TRAFFIC,表示应用可以开始准备接收请求了 ApplicationFailedEvent 这个事件在应用启动异常时发送。 WebServerInitializedEvent 这个Web服务器初始化事件在WebServer启动之后发送,实现事件类包括ServletWebServerInitializedEvent, ReactiveWebServerInitializedEvent 事件 ContextRefreshedEvent 这个上下文刷新事件在Spring应用上下文ApplicationContext刷新之后发送