Spring Boot的CommandLineRunner接口是一个函数式接口,用于在Spring Boot应用程序启动后执行一些初始化操作。它提供了一个run方法,该方法在应用程序启动后被调用。
使用CommandLineRunner接口,可以在应用程序启动后执行一些必要的初始化操作,例如加载配置文件、初始化数据库连接、创建默认数据等。可以通过实现CommandLineRunner接口,并重写run方法来定义自己的初始化逻辑。
org.springframework.boot spring-boot-starter-parent3.1.0 org.example springboot-CommandLineRunner1.0-SNAPSHOT Spring Boot banner Spring Boot and commandLineRunner 17 17 UTF-8 org.springframework.boot spring-boot-starter-weborg.projectlombok lombok
server: port: 8080 spring: profiles: active: dev
import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component @Slf4j public class Runner implements CommandLineRunner { @Override public void run(String... args) throws Exception { log.info("The Runner start to initialize ..."); } }
import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @Slf4j public 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的先后顺序,在上面例子的基础上增加
import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Component @Order(1) @Slf4j public class OrderRunner1 implements CommandLineRunner { @Override public void run(String... args) throws Exception { log.info("The OrderRunner1 start to initialize ..."); } }
import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Component @Order(2) @Slf4j public class OrderRunner2 implements CommandLineRunner { @Override public void run(String... args) throws Exception { log.info("The OrderRunner2 start to initialize ..."); } }
可以实现CommandLineRunner接口,在run方法中加载一些初始化数据到数据库等。适合做一些数据预加载工作。
@Component public 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:
@Component public 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 @Slf4j public 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 @Slf4j public 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 @Slf4j class 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 @Slf4j public 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应用启动。
可以在启动时调用外部服务,进行验证、数据同步等操作。
@Component public 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 @Slf4j public class ParameterValidator implements CommandLineRunner { @Override public void run(String... args) throws Exception { // 校验参数1 if(args.length < 2) { log.error("参数不正确,请传入至少2个参数!"); System.exit(1); } // 校验参数2是否为数字 if(!args[1].matches("\d+")) { log.error("第二个参数必须是数字!"); System.exit(1); } // 校验通过,应用继续启动 log.info("参数校验通过,应用启动中..."); } }
在run方法中,我们可以对main方法输入的参数args进行自定义校验:
如果参数不满足需求,可以直接调用System.exit(1)来终止Spring Boot的启动。这样就可以在应用启动前验证参数的正确性,避免应用启动后发生未知错误。
可以根据运行参数等条件动态设置Spring Boot的配置,实现不同环境的适配。
myconfig: foo: 十五 bar: 1
@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 + '}'; } }
@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 @Slf4j public 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等系列文章,一系列干货随时送达!
上一篇:在MySQL中如何修改列名