Springboot 的启动流程【超级详细 | 附带思维导图】
2. 创建SpringApplication
- 推断应用类型(WebApplicationType)
// SpringBoot会检查classpath中的类来判断应用类型
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", null)) {
return WebApplicationType.REACTIVE; // 响应式Web应用
} else if (ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", null)) {
return WebApplicationType.SERVLET; // 传统Web应用
} else {
return WebApplicationType.NONE; // 普通应用
}
拓展
REACTIVE
响应式Web应用:
在Spring Cloud Gateway
中确实使用了响应式编程模型。Spring Cloud Gateway
是基于Spring WebFlux
构建的,它采用了响应式编程范式,利用了Project Reactor和Netty
等技术。
在Spring Cloud Gateway
中,请求的处理是通过一系列的过滤器(Filter)来实现的。每个过滤器都可以对请求进行拦截、转换、路由等操作。这些过滤器组成了一个过滤器链(Filter Chain),请求会依次通过这些过滤器。
在编写自定义过滤器时,你可以通过chain.filter(exchange)
方法将请求传递给过滤器链中的下一个过滤器,这个方法返回一个Mono<Void>
对象,表示异步地处理请求。
下面是一个自定义过滤器的示例:
@Component
public class CustomFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 在此处进行请求的预处理
ServerHttpRequest request = exchange.getRequest().mutate()
.header("Custom-Header", "Value")
.build();
return chain.filter(exchange.mutate().request(request).build())
.then(Mono.fromRunnable(() -> {
// 在此处进行响应的后处理
}));
}
}
在这个示例中,filter
方法接收一个ServerWebExchange
对象和一个GatewayFilterChain
对象。通过chain.filter(exchange)
将请求传递给下一个过滤器,并在请求处理完成后进行响应的后处理。
总之,在Spring Cloud Gateway中,由于使用了响应式编程模型,因此需要使用Mono
和Flux
等响应式类型,并通过chain.filter(exchange)
方法将请求传递给过滤器链中的下一个过滤器。
3.准备环境(ConfigurableEnvironment)
- 环境配置来源(按优先级从高到低):
// 命令行参数(最高优先级)
java -jar app.jar --server.port=8080
// 系统环境变量
System.getenv("JAVA_HOME")
// application.properties/yml 配置文件
// application-{profile}.properties/yml 特定环境配置
- 具体加载过程:
public class EnvironmentPrepareExample {
// 创建环境对象
ConfigurableEnvironment environment = new StandardEnvironment();
// 添加属性源
environment.getPropertySources().addLast(
new ResourcePropertySource("application.properties")
);
// 设置激活的配置文件
environment.setActiveProfiles("dev", "local");
// 获取配置值
String serverPort = environment.getProperty("server.port");
String dbUrl = environment.getProperty("spring.datasource.url");
}
- 常见配置示例:
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/test
username: root
password: 123456
server:
port: 8080
logging:
level:
root: INFO
# 自定义配置
custom:
property: value
- 配置优先级(从高到低):
1. 命令行参数
2. SPRING_APPLICATION_JSON 环境变量
3. java:comp/env JNDI属性
4. System.getProperties() 系统属性
5. 操作系统环境变量
6. random.* 随机属性
7. application-{profile}.properties/yml
8. application.properties/yml
- 获取配置的方式:
@Value("${server.port}")
private String serverPort;
@ConfigurationProperties(prefix = "custom")
public class CustomProperties {
private String property;
// getter/setter
}
@Autowired
private Environment environment;
String value = environment.getProperty("custom.property");
- 配置刷新机制:
@RefreshScope // 支持配置热更新
@Component
public class ConfigComponent {
@Value("${dynamic.property}")
private String dynamicProperty;
}
关键点:
- 环境准备是为了统一管理各种配置源
- 提供了灵活的配置方式和优先级机制
- 支持profile进行环境切换
- 可以通过多种方式获取配置
- 支持配置热更新
- 配置值可以被覆盖,高优先级的配置会覆盖低优先级的配置
这个阶段就是在应用启动前,把所有的配置信息都准备好,为后续的Bean创建和依赖注入做准备。
4. 创建并刷新 ApplicationContext
- 创建 ApplicationContext:
// 根据应用类型创建对应的上下文
ConfigurableApplicationContext context;
if (webApplicationType == WebApplicationType.SERVLET) {
context = new AnnotationConfigServletWebServerApplicationContext();
} else if (webApplicationType == WebApplicationType.REACTIVE) {
context = new AnnotationConfigReactiveWebServerApplicationContext();
} else {
context = new AnnotationConfigApplicationContext();
}
- 加载配置类:
@Configuration // 配置类
public class AppConfig {
@Bean // 定义Bean
public UserService userService() {
return new UserService();
}
@Bean
public OrderService orderService() {
return new OrderService(userService()); // 依赖注入
}
}
// 启动类中的自动配置
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 注册Bean的几种方式:
// 1. @Component注解
@Component
public class UserService {
}
// 2. @Bean注解
@Bean
public OrderService orderService() {
return new OrderService();
}
// 3. 配置类导入
@Import({PaymentConfig.class})
public class AppConfig {
}
// 4. 实现FactoryBean
public class MyFactoryBean implements FactoryBean<MyObject> {
@Override
public MyObject getObject() {
return new MyObject();
}
}
- 依赖注入方式:
// 1. 构造器注入(推荐)
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
// 2. 字段注入
@Service
public class OrderService {
@Autowired
private UserService userService;
}
// 3. Setter注入
@Service
public class ProductService {
private PriceService priceService;
@Autowired
public void setPriceService(PriceService priceService) {
this.priceService = priceService;
}
}
- Bean的生命周期回调:
@Component
public class LifecycleBean implements InitializingBean, DisposableBean {
@PostConstruct // 构造后
public void init() {
System.out.println("Bean初始化");
}
@Override // InitializingBean接口
public void afterPropertiesSet() {
System.out.println("属性设置完成");
}
@PreDestroy // 销毁前
public void cleanup() {
System.out.println("Bean清理");
}
@Override // DisposableBean接口
public void destroy() {
System.out.println("Bean销毁");
}
}
- 上下文刷新主要步骤:
// AbstractApplicationContext的refresh()方法
public void refresh() {
// 1. 准备刷新上下文
prepareRefresh();
// 2. 创建BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 3. 准备BeanFactory
prepareBeanFactory(beanFactory);
// 4. 执行BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 5. 注册BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 6. 初始化消息源
initMessageSource();
// 7. 初始化事件多播器
initApplicationEventMulticaster();
// 8. 初始化特定的Bean
onRefresh();
// 9. 注册监听器
registerListeners();
// 10. 完成Bean实例化
finishBeanFactoryInitialization(beanFactory);
// 11. 完成刷新
finishRefresh();
}
关键点:
- ApplicationContext是Spring的核心容器
- 负责Bean的创建、配置和管理
- 支持多种依赖注入方式
- 提供Bean生命周期管理
- 刷新过程包含多个重要步骤
- 自动配置和手动配置共同作用
这个阶段完成后,所有的Bean都已经创建完成并完成了依赖注入,应用程序已经准备好处理请求了。
5. 在刷新上下文时启动嵌入式 Web 服务器
- 自动配置Tomcat:
@Configuration
public class EmbeddedTomcatConfiguration {
@Bean
public TomcatServletWebServerFactory tomcatFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
// 配置Tomcat
factory.setPort(8080); // 设置端口
factory.setContextPath("/api"); // 设置上下文路径
factory.addConnectorCustomizers(connector -> {
connector.setMaxPostSize(5000000); // 设置最大POST大小
});
return factory;
}
}
- 注册Servlet:
@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().write("Hello from Servlet!");
}
}
// 通过配置类注册
@Configuration
public class ServletConfig {
@Bean
public ServletRegistrationBean<HelloServlet> helloServlet() {
ServletRegistrationBean<HelloServlet> registration =
new ServletRegistrationBean<>(new HelloServlet(), "/hello");
registration.setLoadOnStartup(1);
return registration;
}
}
- 注册Filter:
@Component
public class LoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
log.info("Request URI: {}", req.getRequestURI());
chain.doFilter(request, response);
}
}
// 通过配置类注册
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<LoggingFilter> loggingFilter() {
FilterRegistrationBean<LoggingFilter> registration =
new FilterRegistrationBean<>(new LoggingFilter());
registration.addUrlPatterns("/*");
registration.setOrder(1);
return registration;
}
}
- 自定义嵌入式服务器配置:
# application.yml
server:
port: 8080
servlet:
context-path: /api
tomcat:
max-threads: 200
min-spare-threads: 10
max-connections: 10000
accept-count: 100
- 编程方式配置:
@Configuration
public class ServerConfiguration {
@Bean
public WebServerFactoryCustomizer<TomcatServletWebServerFactory> customizer() {
return factory -> {
// 配置Tomcat
factory.addConnectorCustomizers(connector -> {
connector.setMaxPostSize(5000000);
});
// 配置错误页面
factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404"));
// 配置SSL
factory.setSsl(getSslStoreProvider());
};
}
}
- 监听服务器启动:
@Component
public class ServerStartupListener implements
ApplicationListener<WebServerInitializedEvent> {
@Override
public void onApplicationEvent(WebServerInitializedEvent event) {
int port = event.getWebServer().getPort();
log.info("Web server started on port: {}", port);
}
}
- 常见的Web容器配置:
// 切换到Jetty
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
关键点:
- 默认使用Tomcat作为嵌入式服务器
- 支持Servlet和Filter的注册
- 提供多种配置方式(配置文件、编程方式)
- 可以自定义错误页面
- 支持SSL配置
- 可以切换到其他Web容器(如Jetty、Undertow)
- 提供监听器机制监控服务器状态
这个阶段完成后:
- Web服务器启动并监听指定端口
- Servlet和Filter被正确注册
- 应用准备好接收HTTP请求
- 所有的Web相关配置生效
6.发布应用已启动事件
发布应用已启动事件(ApplicationStartedEvent)的主要作用是提供一个时机点,让我们能在应用完全启动后,但在开始接收请求之前执行一些初始化操作。
具体示例:
- 基础监听示例:
@Component
public class StartupListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
// 应用启动后的初始化操作
System.out.println("应用已启动,可以执行初始化操作");
}
}
- 常见使用场景:
// 1. 缓存预热
@Component
public class CacheWarmUpListener implements ApplicationListener<ApplicationStartedEvent> {
@Autowired
private ProductService productService;
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
// 启动时预加载热门商品到缓存
List<Product> hotProducts = productService.getHotProducts();
cacheService.warmUp(hotProducts);
}
}
// 2. 系统状态检查
@Component
public class SystemCheckListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
// 检查必要的外部系统连接
checkDatabaseConnection();
checkRedisConnection();
checkExternalAPIs();
}
}
// 3. 初始化数据
@Component
public class DataInitListener implements ApplicationListener<ApplicationStartedEvent> {
@Autowired
private UserService userService;
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
// 检查并初始化管理员账号
if (!userService.hasAdminUser()) {
userService.initializeAdminUser();
}
}
}
// 4. 启动定时任务
@Component
public class SchedulerListener implements ApplicationListener<ApplicationStartedEvent> {
@Autowired
private TaskScheduler scheduler;
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
// 启动定时任务
scheduler.scheduleAtFixedRate(
() -> System.out.println("执行定时任务"),
Duration.ofMinutes(5)
);
}
}
// 5. 记录启动日志
@Component
public class StartupLoggingListener implements ApplicationListener<ApplicationStartedEvent> {
@Override
public void onApplicationEvent(ApplicationStartedEvent event) {
// 记录启动信息
log.info("应用启动完成,启动时间:{}", System.currentTimeMillis());
log.info("系统内存使用情况:{}", Runtime.getRuntime().totalMemory());
}
}
- 使用注解方式监听:
@Component
public class AnnotationStartupListener {
@EventListener(ApplicationStartedEvent.class)
public void onApplicationEvent() {
// 处理启动事件
}
}
主要作用:
- 提供应用启动完成的确切时间点
- 允许执行一些初始化操作
- 进行系统健康检查
- 预热缓存或加载数据
- 启动后台任务
- 记录启动相关的统计信息
优点:
- 确保在应用完全启动后执行
- 可以访问所有已初始化的Spring组件
- 支持多个监听器,可以分模块处理
- 有明确的执行顺序
- 可以处理启动失败的情况
这个事件的触发标志着应用已经完全启动,所有的核心组件都已经初始化完成,可以开始处理业务请求了。
7.执行CommandLineRunner
和 ApplicationRunner
CommandLineRunner
和 ApplicationRunner
是Spring Boot
提供的两个接口,用于在应用启动完成后执行一些初始化代码。它们的执行时机比ApplicationStartedEvent
更晚,是真正的最后一步。
- CommandLineRunner示例:
@Component
@Order(1) // 指定执行顺序,数字越小优先级越高
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// 可以直接访问命令行参数
System.out.println("CommandLineRunner执行,参数:" + Arrays.toString(args));
}
}
- ApplicationRunner示例:
@Component
@Order(2)
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
// 可以获取更多的应用参数信息
System.out.println("选项参数:" + args.getOptionNames());
System.out.println("非选项参数:" + args.getNonOptionArgs());
System.out.println("原始参数:" + Arrays.toString(args.getSourceArgs()));
}
}
- 常见使用场景:
// 1. 数据初始化
@Component
public class DataInitializationRunner implements CommandLineRunner {
@Autowired
private UserService userService;
@Override
public void run(String... args) throws Exception {
// 初始化基础数据
if (userService.count() == 0) {
userService.initializeDefaultUsers();
}
}
}
// 2. 配置检查
@Component
public class ConfigurationValidationRunner implements ApplicationRunner {
@Autowired
private Environment env;
@Override
public void run(ApplicationArguments args) throws Exception {
// 验证必要的配置项
validateDatabaseConfig();
validateRedisConfig();
validateApiKeys();
}
}
// 3. 缓存预热
@Component
public class CacheWarmupRunner implements CommandLineRunner {
@Autowired
private ProductService productService;
@Override
public void run(String... args) throws Exception {
// 预热缓存
productService.warmupCache();
}
}
// 4. 异步任务启动
@Component
public class AsyncTaskRunner implements CommandLineRunner {
@Autowired
private TaskExecutor taskExecutor;
@Override
public void run(String... args) throws Exception {
taskExecutor.execute(() -> {
// 执行耗时的初始化任务
performLongInitialization();
});
}
}
// 5. 条件执行
@Component
public class ConditionalRunner implements CommandLineRunner {
@Value("${app.initialization-required:false}")
private boolean initializationRequired;
@Override
public void run(String... args) throws Exception {
if (initializationRequired) {
// 执行初始化
performInitialization();
}
}
}
- 组合多个Runner:
@Configuration
public class RunnerConfiguration {
@Bean
public CommandLineRunner compositeRunner(
DataInitializationRunner dataRunner,
CacheWarmupRunner cacheRunner) {
return args -> {
// 按顺序执行多个初始化任务
dataRunner.run(args);
cacheRunner.run(args);
};
}
}
- 异常处理:
@Component
public class SafeRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
try {
// 执行可能抛出异常的初始化代码
riskyInitialization();
} catch (Exception e) {
// 记录错误但不中断应用启动
log.error("初始化失败", e);
}
}
}
主要特点:
-
CommandLineRunner
- 直接接收命令行参数
- 接口更简单
-
ApplicationRunner
- 提供更多的参数解析功能
- 可以区分选项参数和非选项参数
这个阶段是应用启动的最后一步,适合执行一些初始化操作,但要注意不要影响应用的正常启动。
8.发布Ready 事件,应用启动完成
ApplicationReadyEvent
是 Spring Boot
应用启动的最后一个事件,标志着应用已经完全准备好处理请求了。这是所有初始化完成后的最终状态。
详细解释:
- 基础监听示例:
@Component
public class ReadyEventListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
log.info("应用已完全准备就绪!");
}
}
- 常见使用场景:
// 1. 应用状态通知
@Component
public class ApplicationStatusNotifier implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// 通知监控系统应用已就绪
notifyMonitoringSystem("APPLICATION_READY");
// 更新应用状态
updateApplicationStatus("RUNNING");
}
}
// 2. 启动完成日志记录
@Component
public class StartupMetricsLogger implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// 记录启动耗时
long startupTime = System.currentTimeMillis() - event.getTimestamp();
// 记录系统信息
Runtime runtime = Runtime.getRuntime();
log.info("应用启动完成!启动耗时: {}ms", startupTime);
log.info("内存使用: {}/{} MB",
(runtime.totalMemory() - runtime.freeMemory()) / 1048576,
runtime.totalMemory() / 1048576);
}
}
// 3. 健康检查
@Component
public class HealthCheckRunner implements ApplicationListener<ApplicationReadyEvent> {
@Autowired
private HealthIndicator healthIndicator;
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
Health health = healthIndicator.health();
if (health.getStatus() == Status.UP) {
log.info("应用健康检查通过");
} else {
log.warn("应用健康检查未通过: {}", health.getDetails());
}
}
}
// 4. 外部系统集成
@Component
public class ServiceRegistrationListener implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// 注册到服务发现系统(如Eureka、Consul)
registerWithDiscoveryService();
// 初始化与外部系统的连接
initializeExternalConnections();
}
}
// 5. 性能监控启动
@Component
public class MetricsCollectorStarter implements ApplicationListener<ApplicationReadyEvent> {
@Autowired
private MetricsCollector metricsCollector;
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
// 启动性能指标收集
metricsCollector.startCollection();
}
}
- 使用注解方式:
@Component
public class ReadyEventHandler {
@EventListener(ApplicationReadyEvent.class)
public void handleReadyEvent() {
// 处理就绪事件
log.info("应用就绪事件处理");
}
}
- 结合异步处理:
@Component
public class AsyncReadyEventHandler {
@Async
@EventListener(ApplicationReadyEvent.class)
public void handleReadyEventAsynchronously() {
// 异步处理耗时操作
performTimeConsumingTasks();
}
}
主要作用:
- 标志应用完全启动完成
- 提供最后的初始化机会
- 适合执行外部系统集成
- 收集和报告启动指标
- 进行最终的健康检查
与其他事件的区别:
- ApplicationStartedEvent:基础启动完成
- ApplicationReadyEvent:完全准备就绪
- 建议将核心初始化放在Started事件
- 将非关键操作放在Ready事件
使用建议:
- 避免在此阶段执行重量级操作
- 需要时使用异步处理
- 主要用于状态通知和监控
- 做好日志记录
- 处理好异常情况
这个事件标志着:
- 应用完全启动完成
- 所有初始化任务执行完毕
- 可以开始处理外部请求
- 系统进入正常运行状态
这是Spring Boot启动流程的最后一个步骤,之后应用就进入正常的运行状态了。
Springboot 启动日志分析
2025-01-25 16:09:22.359 INFO 40940 --- [ main] c.y.bloomfilter.BloomFilterApplication : Starting BloomFilterApplication using Java 1.8.0_402 on xiaoxinpro2022 with PID 40940 (D:\B_IDEA\testE_09\verifyRedisCache\BloomFilter\target\classes started by 86180 in D:\B_IDEA\testE_09\verifyRedisCache)
阶段 1 和 2:应用从main()方法启动(阶段1),并创建SpringApplication对象(阶段2)。日志表明应用开始启动,推断应用类型和主类。
2025-01-25 16:09:22.362 INFO 40940 --- [ main] c.y.bloomfilter.BloomFilterApplication : No active profile set, falling back to 1 default profile: "default"
阶段 3:准备环境时加载配置文件,发现未激活任何配置文件,回退到默认配置。
2025-01-25 16:09:23.960 INFO 40940 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode
创建ApplicationContext时处理Spring Data配置,检测到多个模块并进入严格仓库配置模式。
2025-01-25 16:09:23.967 INFO 40940 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
阶段 4:继续加载ApplicationContext,初始化Spring Data Redis仓库。
2025-01-25 16:09:24.032 INFO 40940 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 19 ms. Found 0 Redis repository interfaces.
阶段 4:完成Spring Data仓库的扫描(此处未找到Redis仓库接口)。
2025-01-25 16:09:25.571 INFO 40940 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
阶段 5:在刷新ApplicationContext时启动嵌入式Tomcat服务器。
2025-01-25 16:09:25.604 INFO 40940 --- [ main]
o.apache.catalina.core.StandardService : Starting service [Tomcat]
阶段 5:Tomcat服务启动,属于嵌入式服务器初始化的一部分。
2025-01-25 16:09:25.604 INFO 40940 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.65]
2025-01-25 16:09:25.857 INFO 40940 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
阶段 4:初始化Spring的嵌入式Web应用上下文(ApplicationContext刷新阶段的一部分)。
2025-01-25 16:09:25.857 INFO 40940 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 3392 ms
阶段 4:ApplicationContext初始化完成,Bean定义加载完毕。
2025-01-25 16:09:27.791 INFO 40940 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
阶段 4:Hikari连接池初始化,属于Bean的创建和依赖注入阶段。
2025-01-25 16:09:32.033 INFO 40940 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
阶段 4:Hikari连接池启动完成,Bean初始化结束。
2025-01-25 16:09:32.513 INFO 40940 --- [ main] c.y.b.service.Impl.UserServiceImpl : 布隆过滤器初始化完成
阶段 4:在Bean初始化阶段完成(例如,通过@PostConstruct或@Bean初始化方法),属于上下文刷新的一部分。
2025-01-25 16:09:33.374 INFO 40940 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
阶段 5:嵌入式Tomcat服务器完全启动,端口和上下文路径就绪。
2025-01-25 16:09:33.392 INFO 40940 --- [ main] c.y.bloomfilter.BloomFilterApplication : Started BloomFilterApplication in 11.851 seconds (JVM running for 18.294)
```阶段 8:应用启动完成,发布ApplicationReadyEvent,进入运行状态。