在日常开发和运维工作中,我们经常需要编写命令行脚本处理批量任务、数据初始化、定时运维等工作。传统的 Shell/Python 脚本虽然轻便,但在依赖管理、代码复用、企业级功能(数据库操作、缓存、消息队列) 上力不从心。
而Spring Boot完全可以用来开发命令行脚本!它继承了 Spring 生态的所有优势:自动配置、依赖注入、开箱即用的中间件集成,还能打包成单一可执行 JAR,跨平台运行,完美解决了传统脚本的痛点。
今天就带大家从零开始,用 Spring Boot 编写专业的命令行脚本!
一、为什么用 Spring Boot 写命令行脚本?
- 零配置:自动配置,无需繁琐的 XML / 配置文件
- 生态强大:无缝集成 MySQL、Redis、MQ、MyBatis 等企业级组件
- 代码复用:直接复用项目中的 Service、DAO、工具类
- 打包便捷:打包成单个 JAR,
java -jar直接运行,无环境依赖 - 健壮性:支持日志、异常处理、参数校验、退出码等专业特性
- 无冗余:可以完全不启动 Web 容器,纯命令行运行,轻量高效
二、环境准备
- JDK 8+
- Maven/Gradle
- IDEA/Eclipse
- Spring Boot 2.x/3.x(本文用 3.x)
核心:不要引入 Web 依赖,命令行脚本不需要启动 Tomcat!
三、快速入门:第一个 Spring Boot 命令行脚本
1. 创建 Spring Boot 项目
通过 Spring Initializr 创建项目,仅选择核心依赖:
xml
<!-- 仅需Spring Boot核心启动器,无Web依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 测试依赖(可选) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2. 核心:实现命令行执行接口
Spring Boot 提供两个核心接口用于编写命令行逻辑,项目启动后会自动执行:
CommandLineRunner:最基础,参数为字符串数组ApplicationRunner:进阶版,支持优雅的参数解析
最简示例(CommandLineRunner)
java
运行
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;
// 启动类
@SpringBootApplication
public class CommandLineScriptApplication {
public static void main(String[] args) {
SpringApplication.run(CommandLineScriptApplication.class, args);
}
}
// 命令行执行逻辑
@Component
public class SimpleScript implements CommandLineRunner {
/**
* 项目启动后自动执行该方法
* @param args 命令行传入的参数
*/
@Override
public void run(String... args) throws Exception {
System.out.println("========= Spring Boot 命令行脚本执行成功 =========");
System.out.println("传入参数:" + String.join(", ", args));
}
}
3. 运行脚本
方式 1:IDE 直接运行
启动项目,控制台直接输出执行结果。
方式 2:打包为可执行 JAR 运行
bash
运行
# 打包
mvn clean package -DskipTests
# 运行脚本(带参数)
java -jar target/command-line-script-0.0.1-SNAPSHOT.jar hello springboot
输出结果:
plaintext
========= Spring Boot 命令行脚本执行成功 =========
传入参数:hello, springboot
四、核心接口对比:CommandLineRunner vs ApplicationRunner
1. CommandLineRunner
- 优点:简单直接
- 缺点:参数是原生字符串数组,解析复杂参数(如
--name=test --age=18)需要手动处理
2. ApplicationRunner(推荐)
- 优点:内置
ApplicationArguments对象,自动解析命名参数、选项参数、无名称参数,开发更高效
示例:优雅解析命令行参数
java
运行
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class AdvancedScript implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("========= 进阶参数解析 =========");
// 1. 获取所有原生参数
System.out.println("所有参数:" + args.getSourceArgs());
// 2. 获取无名称参数(直接传的值)
System.out.println("无名称参数:" + args.getNonOptionArgs());
// 3. 获取命名参数(--key=value格式)
if (args.containsOption("name")) {
System.out.println("参数name:" + args.getOptionValues("name").get(0));
}
if (args.containsOption("age")) {
System.out.println("参数age:" + args.getOptionValues("age").get(0));
}
}
}
运行命令:
bash
运行
java -jar xxx.jar --name=张三 --age=20 测试参数
输出:
plaintext
========= 进阶参数解析 =========
所有参数:[--name=张三, --age=20, 测试参数]
无名称参数:[测试参数]
参数name:张三
参数age:20
五、进阶实战:企业级命令行脚本
实际开发中,我们需要依赖注入、业务逻辑、数据库操作等功能,Spring Boot 可以无缝支持!
场景:数据初始化命令行脚本
- 定义 Service 层
java
运行
@Service
public class DataInitService {
public void initData() {
// 模拟数据库初始化
System.out.println("执行数据初始化:插入默认用户、配置数据...");
}
}
- 命令行类注入 Service
java
运行
@Component
public class DataInitScript implements ApplicationRunner {
// 依赖注入,直接复用项目业务代码
private final DataInitService dataInitService;
// 构造器注入(Spring 4.3+推荐)
public DataInitScript(DataInitService dataInitService) {
this.dataInitService = dataInitService;
}
@Override
public void run(ApplicationArguments args) {
System.out.println("开始执行数据初始化脚本...");
dataInitService.initData();
System.out.println("数据初始化完成!");
}
}
- 运行效果
plaintext
开始执行数据初始化脚本...
执行数据初始化:插入默认用户、配置数据...
数据初始化完成!
六、高级技巧:生产级必备功能
1. 多脚本执行顺序
如果有多个Runner,用@Order注解指定执行顺序(数字越小,优先级越高):
java
运行
@Component
@Order(1) // 第一个执行
public class FirstScript implements CommandLineRunner {}
@Component
@Order(2) // 第二个执行
public class SecondScript implements CommandLineRunner {}
2. 自定义退出码
命令行脚本通常需要返回退出码(0 = 成功,非 0 = 失败),方便 Shell 脚本调用:
java
运行
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.stereotype.Component;
@Component
public class ExitCodeScript implements CommandLineRunner, ExitCodeGenerator {
private int exitCode;
@Override
public void run(String... args) {
try {
// 执行业务逻辑
exitCode = 0;
} catch (Exception e) {
exitCode = 1;
throw e;
}
}
// 自定义退出码
@Override
public int getExitCode() {
return exitCode;
}
}
3. 配置文件支持
和普通 Spring Boot 项目一样,支持application.yml/application.properties配置:
yaml
# application.yml
script:
name: 数据初始化脚本
max-count: 1000
直接用@Value注入使用:
java
运行
@Value("${script.name}")
private String scriptName;
4. 日志优化
替换System.out为专业日志框架(Spring Boot 默认集成 Logback):
java
运行
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class LogScript implements CommandLineRunner {
@Override
public void run(String... args) {
log.info("脚本开始执行,参数:{}", args);
log.error("执行异常", new RuntimeException("测试错误"));
}
}
七、打包与部署(一键运行)
Spring Boot 命令行脚本打包方式和普通项目完全一致:
bash
运行
# Maven打包
mvn clean package -DskipTests
# 运行
java -jar xxx.jar [参数]
✅ 优势:
- 单一 JAR 文件,无依赖冲突
- 跨平台(Windows/Linux/Mac 都能运行)
- 可配合 Linux 定时任务(crontab)实现自动化执行
八、避坑指南
- 不要引入 spring-boot-starter-web:会启动 Tomcat Web 容器,浪费资源
- 禁止长时间阻塞:命令行脚本执行完成后会自动退出,不要写死循环
- 参数校验:使用
ApplicationArguments判断参数是否存在,避免空指针 - 异常处理:必须捕获异常,保证脚本健壮性
九、适用场景
Spring Boot 命令行脚本非常适合以下工作:
- 数据初始化、数据迁移、数据清洗
- 定时运维脚本(配合 crontab)
- 批量导入 / 导出文件
- 企业内部小工具
- 项目启动前的预处理任务
总结
Spring Boot 让命令行脚本开发告别了「简陋」,走向了工程化、标准化、企业级。它既保留了命令行脚本的轻便,又拥有 Spring 生态的强大能力,是后端开发和运维的绝佳工具。
只需要实现CommandLineRunner/ApplicationRunner接口,就能快速开发出专业的命令行应用,打包即用,快来试试吧!
核心知识点回顾
- 核心接口:
CommandLineRunner(基础)、ApplicationRunner(推荐,参数解析更优雅) - 无 Web 依赖:纯命令行运行,轻量高效
- 无缝集成:依赖注入、Service 复用、配置、日志全支持
- 一键打包:单一 JAR,跨平台直接运行
- 生产级特性:执行顺序、退出码、异常处理、参数校验