行业知识
SpringBoot中CommandLineRunner详解(含源码)
文章目录
- 前言
- 实例
- 导入库
- application.yaml
- Runner
- SpringBootCommandLineRunnerApplication
- 执行结果
- 先后顺序示例
- OrderRunner1
- OrderRunner2
- 执行结果
- 通常用法
- 加载初始化数据
- 示例
- 启动后打印应用信息
- 示例
- 启动异步任务
- 示例
- 接口健康检查
- 示例
- 外部服务调用
- 示例
- 参数校验
- 示例
- 动态设置配置
- 示例
- application.yaml
- MyConfig
- ConfigRunner
- 启动阻塞
- 总结
- 源码获取
- 写在最后
前言
Spring Boot的CommandLineRunner接口是一个函数式接口,用于在Spring Boot应用程序启动后执行一些初始化操作。它提供了一个run方法,该方法在应用程序启动后被调用。
使用CommandLineRunner接口,可以在应用程序启动后执行一些必要的初始化操作,例如加载配置文件、初始化数据库连接、创建默认数据等。可以通过实现CommandLineRunner接口,并重写run方法来定义自己的初始化逻辑。
实例
导入库
org.springframework.boot spring-boot-starter-parent 3.1.0org.examplespringboot-CommandLineRunner1.0-SNAPSHOTSpring Boot bannerSpring Boot and commandLineRunner 17 17 UTF-8 org.springframework.boot spring-boot-starter-web org.projectlombok lombok
application.yaml
server: port: 8080spring: profiles: active: dev
Runner
import lombok.extern.slf4j.Slf4j;import org.springframework.boot.CommandLineRunner;import org.springframework.stereotype.Component;@Component@Slf4jpublic class Runner implements CommandLineRunner { @Override public void run(String... args) throws Exception { log.info("The Runner start to initialize ..."); }}SpringBootCommandLineRunnerApplication
import lombok.extern.slf4j.Slf4j;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication@Slf4jpublic class SpringBootCommandLineRunnerApplication { public static void main(String[] args) { SpringApplication.run(SpringBootCommandLineRunnerApplication.class, args); log.info("The service to end"); }}执行结果
在上面的示例中,我们创建了一个名为MyCommandLineRunner的类,并实现了CommandLineRunner接口。在run方法中,我们可以编写需要在应用程序启动后执行的初始化逻辑。
需要注意的是,实现CommandLineRunner接口的类需要被Spring容器扫描到,可以使用@Component注解或其他方式将其注册为Spring Bean。
先后顺序示例
可以通过@Order()来设置Runner的先后顺序,在上面例子的基础上增加
OrderRunner1
import lombok.extern.slf4j.Slf4j;import org.springframework.boot.CommandLineRunner;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;@Component@Order(1)@Slf4jpublic class OrderRunner1 implements CommandLineRunner { @Override public void run(String... args) throws Exception { log.info("The OrderRunner1 start to initialize ..."); }}OrderRunner2
import lombok.extern.slf4j.Slf4j;import org.springframework.boot.CommandLineRunner;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;@Component@Order(2)@Slf4jpublic class OrderRunner2 implements CommandLineRunner { @Override public void run(String... args) throws Exception { log.info("The OrderRunner2 start to initialize ..."); }}执行结果
通常用法
加载初始化数据
可以实现CommandLineRunner接口,在run方法中加载一些初始化数据到数据库等。适合做一些数据预加载工作。
示例
@Componentpublic class DataInitializer implements CommandLineRunner { @Autowired private UserRepository userRepository; @Override public void run(String... args) throws Exception { // 创建初始用户 User admin = new User("admin", "123456"); userRepository.save(admin); User normalUser = new User("user", "123456"); userRepository.save(normalUser); System.out.println("数据加载完毕!"); }}这里创建了一个 DataInitializer 类,实现 CommandLineRunner 接口。在 run() 方法中,我们注入了 UserRepository,然后创建了两个用户对象保存到数据库中。这个类会在 Spring Boot 应用启动完成后执行,从而实现了数据预加载的效果。通过 CommandLineRunner,我们可以灵活地在 Spring Boot 启动时进行一些初始化操作,如预先加载测试数据、插入管理员账户等,很好地增强了应用的功能。
假设我们有一个User模型和用户Repository,需要在Spring Boot启动时预加载几个用户数据,可以这样使用CommandLineRunner:
@Componentpublic class DataInitializer implements CommandLineRunner { @Autowired private UserRepository userRepository; @Override public void run(String... args) throws Exception { // 清除所有数据 userRepository.deleteAll(); // 创建几个用户 User user1 = new User("John", "john@example.com"); User user2 = new User("Mary", "mary@example.com"); userRepository.save(user1); userRepository.save(user2); // 打印已保存用户数 System.out.println("Number of users saved: " + userRepository.count()); }}这里我们实现了CommandLineRunner接口,然后注入UserRepository bean。在run方法中,首先清空所有数据,然后创建两个用户对象并保存,最后打印已保存的用户数。这样在Spring Boot应用启动完成后,就会自动执行run方法,预加载指定的用户数据。
启动后打印应用信息
可以打印出一些应用启动信息,如启动端口、运行环境信息等,用于确认应用配置。
示例
@Component@Slf4jpublic class AppInfoPrinter implements CommandLineRunner { @Autowired private Environment environment; @Override public void run(String... args) throws Exception { log.info("========= 打印启动信息 ========="); // 打印应用端口 log.info(("端口号: " + environment.getProperty("server.port"))); // 打印当前环境 log.info("当前环境: " + environment.getProperty("spring.profiles.active")); // 打印JDK版本 log.info("JDK 版本: " + System.getProperty("java.version")); log.info("========= 打印启动信息结束 ========="); }}执行打印结果
启动异步任务
可以使用多线程启动一些异步任务,进行后台数据处理等复杂业务逻辑。
示例
@Component@Slf4jpublic class AsyncTaskRunner implements CommandLineRunner { @Autowired private AsyncTaskService asyncTaskService; @Override public void run(String... args) throws Exception { log.info("========= 执行任务 ========="); // 在新线程中执行任务 new Thread(() -> { asyncTaskService.doTaskOne(); asyncTaskService.doTaskTwo(); asyncTaskService.doTaskThree(); }).start(); }}@Service@Slf4jclass AsyncTaskService { public void doTaskOne() { log.info("执行任务1"); } public void doTaskTwo() { log.info("执行任务2"); } public void doTaskThree() { log.info("执行任务3"); }}执行结果
[ main] org.example.runner.AsyncTaskRunner : ========= 执行任务 =========[ Thread-1] org.example.runner.AsyncTaskService : 执行任务1[ Thread-1] org.example.runner.AsyncTaskService : 执行任务2[ Thread-1] org.example.runner.AsyncTaskService : 执行任务3
接口健康检查
可以调用并验证依赖服务的健康状态,如果不正常可以终止Spring Boot启动。
示例
@Component@Slf4jpublic class HealthCheckRunner implements CommandLineRunner { @Autowired private DatabaseService databaseService; @Autowired private MessageQueueService messageQueueService; @Override public void run(String... args) throws Exception { if(!databaseService.isConnected()) { log.error("数据库服务不可用,退出应用!"); System.exit(1); } if(!messageQueueService.isConnected()) { log.error("消息队列服务不可用,退出应用!"); System.exit(1); } log.info("所有服务正常,应用启动。"); }}这里我们注入两个依赖服务 DatabaseService 和 MessageQueueService。在run方法中,调用它们的健康检查方法,如果任何一个服务不可用,则直接调用System.exit(1)退出Spring Boot应用启动。
外部服务调用
可以在启动时调用外部服务,进行验证、数据同步等操作。
示例
@Componentpublic class OtherServiceCheckRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { // 健康检查的URL String healthCheckUrl = "http://localhost:8080/actuator/health"; RestTemplate restTemplate = new RestTemplate(); // 发送GET请求进行健康检查 String response = restTemplate.getForObject(healthCheckUrl, String.class); // 根据响应判断健康状态 if (response.contains("\"status\":\"UP\"")) { System.out.println("Application is healthy"); } else { System.out.println("Application is not healthy"); } }}参数校验
可以对输入的运行参数做校验,如果不满足条件可以终止Spring Boot启动。
示例
@Component@Slf4jpublic class ParameterValidator implements CommandLineRunner { @Override public void run(String... args) throws Exception { // 校验参数1 if(args.length在run方法中,我们可以对main方法输入的参数args进行自定义校验:
- 检查参数数量
- 校验参数类型
如果参数不满足需求,可以直接调用System.exit(1)来终止Spring Boot的启动。这样就可以在应用启动前验证参数的正确性,避免应用启动后发生未知错误。
动态设置配置
可以根据运行参数等条件动态设置Spring Boot的配置,实现不同环境的适配。
示例
application.yaml
myconfig: foo: 十五 bar: 1
MyConfig
@Component@Data@ConfigurationProperties(prefix = "myconfig")public class MyConfig { private String foo; private int bar;// getter和setter方法省略 @Override public String toString() { return "MyConfig{" + "foo='" + foo + '\'' + ", bar=" + bar + '}'; }}ConfigRunner
@Component@EnableConfigurationProperties(MyConfig.class)public class ConfigRunner implements CommandLineRunner { @Autowired private MyConfig myConfig; @Override public void run(String... args) throws Exception {// 打印当前配置 System.out.println("Current config: " + myConfig);// 动态设置配置 myConfig.setFoo("new value"); myConfig.setBar(100);// 打印更新后的配置 System.out.println("Updated config: " + myConfig); }}启动阻塞
可以使应用启动后阻塞住主线程,防止main方法直接退出,从而保持Spring Boot应用运行。
示例
@Component@Slf4jpublic class StartBlocker implements CommandLineRunner { @Override public void run(String... args) throws Exception { // 加载提示信息 log.info("正在等待管理员授权..."); // 等待授权,阻塞启动流程 waitAuth(); // 授权完成后继续启动 log.info("管理员已授权,应用启动中..."); } private void waitAuth() { // ***循环模拟等待管理员操作授权 while(true) { try { Thread.sleep(1000); } catch (InterruptedException e) { break; } } }}总结
通过 CommandLineRunner,我们可以深度控制 Spring Boot 应用的启动流程,在应用启动阶段增强各种自定义逻辑。是 Spring Boot 提供的一个很实用的扩展点。
源码获取
如果需要完整源码请关注公众号"架构殿堂" ,回复 "SpringBoot+CommandLineRunner"即可获得
写在最后
感谢您的支持和鼓励!
如果大家对相关文章感兴趣,可以关注公众号"架构殿堂",会持续更新AIGC,java基础面试题, netty, spring boot, spring cloud等系列文章,一系列干货随时送达!