京东一面:如何在SpringBoot启动时执行特定代码?有哪些方式?
引言
Spring Boot 提供了许多便捷的功能和特性,使得开发者可以更加轻松地构建强大、高效的应用程序。然而,在应用程序启动时执行一些初始化操作是至关重要的,它可以确保应用程序在启动后处于预期的状态,从而提供更好的用户体验和稳定性。
在应用程序启动时执行初始化操作有许多好处。首先,它可以确保应用程序在启动后的初始状态是正确的,避免了在应用程序运行时出现意外情况。其次,它可以在应用程序准备好接受请求之前完成一些必要的设置,例如加载配置、建立数据库连接、缓存预热等。总的来说,执行初始化操作可以确保应用程序以正确的方式启动,并为后续操作提供一个稳定的基础。
监听 ApplicationContext事件
Spring Boot应用程序启动时执行初始化操作的方法是通过监听ApplicationContext
事件。ContextRefreshedEvent
事件表示ApplicationContext
被初始化或刷新时触发的事件。通过监听这个事件,开发者可以在应用程序启动后执行一些必要的初始化操作。
示例:
@Component
public class MyContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("监听到ContextRefreshedEvent事件,开始初始化操作。。。。。。。");
}
}
这种方式适合以下场景:
执行一次性初始化操作: 当应用程序启动时,可能需要执行一些只需在应用程序初始化阶段执行一次的操作,例如加载基础数据、建立连接等。通过监听
ContextRefreshedEvent
事件,可以确保这些初始化操作在应用程序启动后立即执行。初始化缓存或缓存刷新: 如果应用程序使用了缓存,可能需要在应用程序启动时初始化缓存或定期刷新缓存。通过监听
ContextRefreshedEvent
事件,可以在应用程序启动后立即执行缓存初始化或刷新操作,确保缓存数据是最新的。执行与外部系统的交互: 在应用程序启动时,可能需要与外部系统进行交互,例如检查外部系统的可用性、加载配置信息等。通过监听
ContextRefreshedEvent
事件,可以在应用程序启动后立即执行与外部系统的交互操作,确保应用程序在启动后处于正常工作状态。执行与 Spring Bean 相关的初始化操作: 在应用程序启动时,可能需要执行一些与 Spring Bean 相关的初始化操作,例如在数据库连接池初始化后执行数据库迁移、在消息队列连接初始化后执行订阅操作等。通过监听
ContextRefreshedEvent
事件,可以确保这些初始化操作在 Spring Bean 初始化完成后立即执行
这种方式能够确保在 ApplicationContext 被完全初始化或刷新后执行初始化操作,可以在这个时机执行一些需要ApplicationContext
完全准备好的操作。但是需要注意的是,ContextRefreshedEvent 事件可能会在应用程序的刷新周期内多次触发,因此在处理这个事件时需要谨慎处理,避免重复执行初始化逻辑。
实现CommandLineRunner接口
CommandLineRunner
是Spring Boot提供的一个接口,它有一个run
方法,当Spring Boot应用上下文初始化完成后,会自动查找并执行所有实现了CommandLineRunner
接口的Bean的run
方法。CommandLineRunner
接口实际上是Spring Boot对Spring框架生命周期管理的一个扩展,通过对接口的实现,我们可以在Spring Boot应用启动后的特定阶段执行自定义的初始化逻辑。
示例:
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("MyCommandLineRunner.run()方法执行了");
}
}
使用场景:
- 命令行参数处理:
CommandLineRunner
接口常用于处理从命令行传入的参数,例如运行不同模式下的任务(如dev模式、prod模式)、读取配置项等。 - 应用启动后的一次性操作:在应用启动后,可能需要进行一些一次性执行的任务,如数据库表结构检查、初始化缓存、发送通知邮件等。
使用CommandLineRunner
接口这种方式是,我们只需要实现接口,无需关注容器的生命周期事件或手动注册监听器。但是如果是多个CommandLineRunner
之间的执行顺序无法保证,可能会带来不确定性(如果是不关心顺序,那就不是缺点了)。另外,我们不应该在``
run
方法中实现过多或较为复杂的任务。
实现ApplicationRunner接口
ApplicationRunner
是Spring Boot提供的另一个接口,它也有一个run
方法,与CommandLineRunner
接口非常相似。当Spring Boot应用启动并且ApplicationContext初始化完成后,Spring Boot会查找并执行所有实现了ApplicationRunner
接口的Bean的run
方法。
ApplicationRunner
的主要特点是其run
方法接收一个ApplicationArguments
参数,它可以更好地解析和处理命令行参数,包括选项参数(键值对)和非选项参数。
示例:
@Component
public class ApplicationArgumentProcessor implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("ApplicationArgumentProcessor.run()方法执行了");
}
}
使用场景:
- 命令行参数解析:由于
ApplicationArguments
提供了丰富的参数解析能力,因此更适合处理带有键值对形式的命令行参数,如--server-port=8080
,然后根据这些参数执行不同的初始化操作。
@Component
public class ApplicationArgumentProcessor implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
Optional<Integer> port = args.getOptionValues("server-port").stream()
.map(Integer::parseInt)
.findFirst();
if (port.isPresent()) {
// 根据端口号进行特定的初始化操作
}
}
}
- 启动时初始化:同
CommandLineRunner
,也可用于执行启动后的一次性操作,例如读取配置、初始化缓存、检查系统资源等,同时可以根据解析的命令行参数决定初始化的具体内容。
相比较于CommandLineRunner
,ApplicationRunner
提供了更强大的命令行参数解析功能,可以轻松处理各种类型的参数。可以根据命令行参数灵活调整启动时的初始化逻辑。但是其缺点同CommandLineRunner
。
ApplicationRunner
和CommandLineRunner
都可以用来在Spring Boot启动时执行特定代码,两者在应用场景上略有差异,具体选择哪种取决于项目的实际需求和命令行参数的复杂程度。
使用@PostConstruct注解
@PostConstruct
注解是JSR-250规范的一部分,Spring框架对此提供了支持。当Spring容器管理的Bean完成依赖注入后,会自动调用标注有@PostConstruct
的方法。这个注解应用于无参或void返回值的方法上,表明该方法应在依赖注入完成后,但在Bean实例正式投入使用之前调用。
在Spring Boot启动时,当Spring容器初始化并创建Bean时,如果发现某个Bean上有@PostConstruct
注解的方法,则会在Bean的生命周期的初始化阶段调用这个方法。
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@PostConstruct
public void init() {
// 在依赖注入完成后,执行初始化操作
System.out.println("UserService初始化...");
// 初始化数据库连接、缓存或者其他内部状态
}
}
使用场景:
- 单个Bean初始化:对于某个特定的Bean,在其所有依赖项注入完成后,需要执行一些特定的初始化操作,例如数据库连接初始化、缓存预热、初始化内部状态等。
- 资源初始化:对于一些公共资源,如线程池、数据库连接池等,可以在对应的配置类或服务类中使用
@PostConstruct
来完成初始化设置。
@PostConstruct
注解只需要在需要执行初始化操作的方法上加上即可,无需额外实现接口或关注Spring容器的生命周期事件。并且针对性强,仅针对单个Bean进行初始化操作,有助于提高代码的模块化和复用性。
但是如果有多个具有@PostConstruct
注解的方法,它们之间没有明确的执行顺序,除非通过Bean间的依赖关系隐式确定顺序。并且针对单个Bean进行初始化操作,所以他并不适合做全局性初始化操作。
@Bean注解中指定初始化方法
@Bean
注解在Spring框架中用于定义一个Bean的实例化逻辑,通常在配置类中使用。通过在@Bean
注解中指定initMethod
属性,可以设置一个在Bean实例化并完成依赖注入后执行的方法。当Spring容器创建并注入完所有依赖关系后,会自动调用该Bean上指定的初始化方法。
@Configuration
public class PrePostConfig {
/**
* 指定初始化init
* @return
*/
@Bean(initMethod = "init")
BeanWayService beanWayService(){
return new BeanWayService();
}
}
public class BeanWayService {
public void init() {
System.out.println("@Bean-init-method");
}
public BeanWayService(){
super();
System.out.println("初始化构造函数-BeanWayService");
}
}
适用场景:
- 资源初始化:例如,初始化数据库连接、网络连接、线程池等资源。
- Bean状态设置:在Bean实例化后,对其进行额外的状态设定或配置。
- 缓存预热:在服务启动时预先加载部分数据至缓存中。
Bean实例上定义初始化方法,与Bean紧密关联,可以精确地控制Bean在何时执行初始化操作,与Spring容器的生命周期绑定,尤其适用于那些需要在Bean实例化后立即执行的操作。。但是如果多个Bean都有初始化方法,它们之间的执行顺序难以控制,除非依赖于Spring容器中Bean的依赖注入顺序。
实现InitializingBean接口
InitializingBean
是Spring框架中的一个接口,它包含一个方法afterPropertiesSet()
。当Spring容器完成了对一个Bean的所有必要属性的依赖注入后,如果该Bean实现了InitializingBean
接口,Spring会自动调用其afterPropertiesSet()
方法。
@Component
public class MyService implements InitializingBean {
@Autowired
private Dependency dependency;
@Override
public void afterPropertiesSet() throws Exception {
// 在所有依赖注入完成后执行的初始化逻辑
System.out.println("MyService初始化...");
// 初始化资源、设置状态或执行其他操作
}
// 其他业务方法...
}
适用场景:
- 资源初始化:如初始化数据库连接、网络连接、线程池等资源。
- Bean状态设置:在依赖注入完成后,设置Bean的初始状态或执行特定的配置操作。
afterPropertiesSet()
方法会在所有属性注入完成后执行,确保Bean在使用前完成初始化。不需要额外的注解,只需实现接口就可以定义初始化逻辑。但是其要求Bean实现特定接口,增加了类的耦合度,同时也不符合Spring倡导的基于注解的编程风格。并且需要显式抛出异常。
相比较于@PostConstruct
,@PostConstruct
注解更具语义化且不强制类实现接口,降低了耦合度。推荐优先考虑使用@PostConstruct
注解进行初始化逻辑的编写。
@EventListener注解
@EventListener
注解在Spring应用程序中定义事件监听器。通过监听 ApplicationReadyEvent
事件,我们可以确保在应用程序完全启动并准备好接受请求时执行初始化逻辑。通过在监听器方法上添加 @EventListener
注解,并指定要监听的事件类型,可以在事件发生时执行相应的初始化操作。
@Component
public class StartupEventListener {
@EventListener(ApplicationReadyEvent.class)
public void onApplicationReadyEvent(ApplicationReadyEvent event) {
System.out.println("Spring Boot应用已启动并准备就绪,开始执行初始化操作...");
// 在这里执行需要在应用启动后进行的初始化代码
}
}
适用场景:
- 应用启动后执行一次性操作:如数据初始化、缓存预热、统计信息收集等。
- 等待所有Bean初始化后再执行:当需要确保所有Bean都已经初始化完毕再执行某些操作时。
通过事件驱动的方式,将初始化逻辑与Bean的创建逻辑解耦开来,并且可以监听多种事件类型(例如:ContextRefreshedEvent
),不仅仅是应用启动事件,还可用于其他业务场景。相比于@PostConstruct
、CommandLineRunner
或ApplicationRunner
等机制,@EventListener
监听的ApplicationReadyEvent
在Spring Boot启动流程中的执行时机较晚,所有Bean都已经初始化并准备就绪后才会触发。
总结
本文全面探讨了Spring Boot启动阶段执行初始化操作的几种常见方法,包括监听事件、实现接口以及使用注解等多种策略,具体如下:
监听ApplicationContext事件:通过实现
ApplicationListener<ContextRefreshedEvent>
接口,监听ContextRefreshedEvent
事件,可在Spring容器初始化完成后执行初始化逻辑。这种方式适用于需要在所有Bean加载完毕后进行全局性初始化操作的场景。实现CommandLineRunner接口:Spring Boot启动后,会自动调用实现了
CommandLineRunner
接口的Bean的run
方法,该方法可以处理命令行参数并执行启动时的特定操作。适用于需要根据命令行参数执行初始化逻辑或进行启动后一次性任务的情况。实现ApplicationRunner接口:与
CommandLineRunner
类似,ApplicationRunner
也在Spring Boot启动后执行其run
方法,但其参数为ApplicationArguments
,提供了更强大的命令行参数解析功能。适合处理键值对形式的命令行参数并据此执行初始化任务。使用@PostConstruct注解:在Bean的方法上添加
@PostConstruct
注解,Spring会在该Bean的所有依赖注入完成后调用该方法进行初始化。这种方法用于单个Bean初始化完成后的特定逻辑,增强了代码的模块化和可维护性。@Bean注解中指定初始化方法:通过
@Bean
注解中的initMethod
属性指定Bean的初始化方法,该方法在Bean实例化并完成注入后由Spring容器调用。这种方法适用于需要对特定Bean进行精细化初始化管理的场景。实现InitializingBean接口:Bean实现
InitializingBean
接口并重写afterPropertiesSet
方法,也能实现在依赖注入完成后执行初始化逻辑。虽然传统但不如使用@PostConstruct
注解优雅,且增加了类的耦合度。使用@EventListener注解:通过监听
ApplicationReadyEvent
等事件,可以在Spring Boot应用启动并准备就绪后执行初始化任务。这种方式延迟执行,适用于在所有Bean初始化完毕且应用已经完全启动后才需要进行的操作。
每种方法均有其适用场景和优缺点,我们应根据项目需求和具体情况选择最适合的初始化方式。通过熟练掌握和灵活运用这些方法,能够有效地管理和优化Spring Boot应用的启动流程,确保应用程序在启动之初即进入正常运作状态。
本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等
京东一面:如何在SpringBoot启动时执行特定代码?有哪些方式?的更多相关文章
- 2-Qt关闭子窗口时执行特定代码
https://blog.csdn.net/naibozhuan3744/article/details/82689434 本文主要总结在关闭qt的QWidget子窗口瞬间,执行特定代码.由于主窗口关 ...
- SpringBoot程序启动时执行初始化代码
因项目集成了Redis缓存部分数据,需要在程序启动时将数据加载到Redis中,即初始化数据到Redis. 在SpringBoot项目下,即在容器初始化完毕后执行我们自己的初始化代码. 第一步:创建实现 ...
- springboot启动时执行任务CommandLineRunner
# SpringBoot中CommandLineRunner的作用> 平常开发中有可能需要实现在项目启动后执行的功能,SpringBoot提供的一种简单的实现方案就是添加一个model并实现Co ...
- Spring Boot学习--项目启动时执行特定方法
Springboot给我们提供了两种"开机启动"某些方法的方式:ApplicationRunner和CommandLineRunner. 这两种方法提供的目的是为了满足,在项目启动 ...
- springboot 启动时执行方法
Springboot提供了两种“开机启动”某些方法的方式:ApplicationRunner和CommandLineRunner.下面简单介绍下ApplicationRunner 1.创建个Tests ...
- 在web项目启动时执行某个方法
在web项目中有很多时候需要在项目启动时就执行一些方法,而且只需要执行一次,比如:加载解析自定义的配置文件.初始化数据库信息等等,在项目启动时就直接执行一些方法,可以减少很多繁琐的操作. 在工作中遇到 ...
- 详解如何在 Linux 启动时自动执行命令或脚本
我一直很好奇,在启动 Linux 系统并登录的过程中到底发生了什么事情.按下开机键或启动一个虚拟机,你就启动了一系列事件,之后会进入到一个功能完备的系统中,有时,这个过程不到一分钟.当你注销或者关机时 ...
- Servlet的init()方法如何才会在服务器启动时执行
如果要想让 servlet 的 init () 方法在服务器启动 时就被执行,则需要在 web.xml 中相应的 servlet 下配置 <servlet > <servlet -n ...
- Linux开机启动时执行脚本的方法
方法 1 – 使用 rc.local利用 /etc/ 中的 rc.local 文件在启动时执行脚本与命令.我们在文件中加上一行来执行脚本,这样每次启动系统时,都会执行该脚本.不过我们首先需要为 /et ...
- Spring在Web容器启动时执行初始化方法
需求:在tomcat启动时开启一个定时任务. 想法:容器启动时执行方法,最容易想到的就是servlet中可以配置load-on-startup,设置一个正整数也就可以随容器一起启动. 问题:上面的方法 ...
随机推荐
- Java 常用类 String的常用方法(3)
1 /** 2 * String常用方法3 3 * 替换: 4 * String replace(char oldChar,char newChar): 返回一个新的字符串,它是通过 5 * 用new ...
- 【MongoDB详细步骤】(内附源码)
第01章-MongoDB 1.安装和启动(docker方式) 1.1.拉取镜像 docker pull mongo:4.4.8 1.2.创建和启动容器 docker run -d --restart= ...
- ENVI为遥感影像设置空间坐标系的方法
本文介绍基于ENVI软件,对不含有任何地理参考信息的栅格遥感影像添加地理坐标系或投影坐标系等地理参考信息的方法. 我们先来看一下本文需要实现的需求.现有以下两景遥感影像,其位于不同的空间位置: ...
- manjaro安装/卸载gnome/kde桌面环境
安装gnome桌面环境 步骤 1. 在运行以下教程之前,请确保我们的系统是最新的: sudo pacman -Syu 步骤 2. 在 Manjaro 20 上安装 GNOME 桌面. 现在我们通过执行 ...
- 阿里云Python UDP Server和client基础教程
壹: socket通信是常用的一种通信方式,熟练掌握,快速的入戏,是一个程序员必备的素质. 贰: 注意:udp和tcp的套接字: 服务端代码: #!/usr/bin/env python3 # -*- ...
- 瑞云科技CTO赵志杰出席广州广告数字创意峰会并发表演讲
3月23日下午,广州广告数字创意峰会暨穗广协企业家大讲堂年度巡礼活动在广州图书馆圆满举行.本次峰会由广州市人民政府统筹,中共广州市委宣传部.广州市文化广电旅游局.中共广州市天河区委.广州市天河区人民政 ...
- 总体最小二乘法(Total Least Squares)拟合直线
前言 最小二乘法是最小化每个点到直线的垂直误差,由于误差采用的是垂直误差,导致越接近垂直线(平行于\(y\)轴),拟合效果越差,无法拟合垂直线. 通过最小化每个点到直线的距离误差可以解决最小二乘法无法 ...
- 使用Go语言开发一个短链接服务:六、链接跳转
章节 使用Go语言开发一个短链接服务:一.基本原理 使用Go语言开发一个短链接服务:二.架构设计 使用Go语言开发一个短链接服务:三.项目目录结构设计 使用Go语言开发一个短链接服务:四.生成 ...
- 揭秘镭速传输点对点传输技术,NAT+Raysync强强组合
点对点传输是一种文件即时传输方式用于实现数据的快速联动,为所有客户端提供资源,包括带宽.存储空间.计算能力.点对点传输技术有很多应用,包括共享各种格式音频.视频.数据等. 在5G重新定义带宽,信息技术 ...
- matlab学习系列
matlab系列学习 1.学习缘由 本来已经学习过这个软件,了解了包括电路仿真在内的诸多功能,能够比较熟练地编写m文件和函数. 但是,在最近的依次练习中发现之前的许多操作都忘记了.有一些基本的语法都不 ...