Integrating XXL‑JOB Distributed Scheduler into SpringBoot with Minimal Intrusion
This guide walks through XXL‑JOB’s core features, shows how to download and set up the admin console, configure a SpringBoot executor, and implement various job types—including simple, sharding, command‑line, HTTP, and lifecycle tasks—complete with code samples and UI screenshots.
XXL‑JOB overview
XXL‑JOB is a distributed task scheduling platform designed for rapid development, lightweight footprint and extensibility. Core features include web‑based CRUD, dynamic task control, HA for scheduler and executors, rich trigger strategies (Cron, fixed interval, delay, API, manual, parent‑child), fault‑tolerant routing, sharding broadcast, logging, GLUE code editing, script/command/HTTP job handlers, task timeout, retry, alert extensions, multiple routing strategies, data encryption, internationalization, Docker images, thread‑pool isolation, user/permission management, and more.
Admin console setup
Download source from https://gitee.com/xuxueli0323/xxl-job/tags and unzip.
Open the project in an IDE. Modules: xxl-job-admin – scheduler (admin console) xxl-job-core – common library xxl-job-executor-sample-springboot – Spring Boot executor sample (recommended) xxl-job-executor-sample-frameless – framework‑less sample
Initialize the scheduler database by executing /xxl-job/doc/db/tables_xxl_job.sql, which creates the xxl_job schema.
Edit
/xxl-job/xxl-job-admin/src/main/resources/application.propertiesto set the HTTP port (e.g., 9090) and JDBC URL, username and password.
Run the xxl-job-admin module and open http://localhost:9090/xxl-job-admin. Default credentials are admin/123456.
Spring Boot executor configuration
Use the xxl-job-executor-sample-springboot project as a base.
Add the core dependency to pom.xml:
<!-- xxl-job-core -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>${project.parent.version}</version>
</dependency>Configure executor properties in src/main/resources/application.properties (example values):
xxl.job.admin.addresses=http://localhost:9090/xxl-job-admin
xxl.job.accessToken=
xxl.job.executor.appname=demo-executor
xxl.job.executor.address=
xxl.job.executor.ip=
xxl.job.executor.port=9999
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
xxl.job.executor.logretentiondays=30Create a Spring configuration class to instantiate XxlJobSpringExecutor:
@Configuration
public class XxlJobConfig {
@Value("${xxl.job.admin.addresses}") private String adminAddresses;
@Value("${xxl.job.accessToken}") private String accessToken;
@Value("${xxl.job.executor.appname}") private String appname;
@Value("${xxl.job.executor.address}") private String address;
@Value("${xxl.job.executor.ip}") private String ip;
@Value("${xxl.job.executor.port}") private int port;
@Value("${xxl.job.executor.logpath}") private String logPath;
@Value("${xxl.job.executor.logretentiondays}") private int logRetentionDays;
@Bean
public XxlJobSpringExecutor xxlJobExecutor() {
XxlJobSpringExecutor executor = new XxlJobSpringExecutor();
executor.setAdminAddresses(adminAddresses);
executor.setAccessToken(accessToken);
executor.setAppname(appname);
executor.setAddress(address);
executor.setIp(ip);
executor.setPort(port);
executor.setLogPath(logPath);
executor.setLogRetentionDays(logRetentionDays);
return executor;
}
}When the deployment environment has multiple network interfaces or runs inside containers, the author suggests using spring-cloud-commons ’s InetUtils to obtain a non‑loopback IP and set it via executor.setIp(...).
Job handler implementations
All jobs are Spring beans annotated with @XxlJob. The following examples are taken from the sample project.
Simple “Hello World” job
@Component
public class SampleXxlJob {
@XxlJob("demoJobHandler")
public void demoJobHandler() throws Exception {
XxlJobHelper.log("XXL-JOB, Hello World.");
System.out.println(LocalDateTime.now() + " XXL-JOB, Hello World");
for (int i = 0; i < 5; i++) {
XxlJobHelper.log("beat at:" + i);
TimeUnit.SECONDS.sleep(2);
}
// default success
}
}Sharding broadcast job
@XxlJob("shardingJobHandler")
public void shardingJobHandler() throws Exception {
int shardIndex = XxlJobHelper.getShardIndex();
int shardTotal = XxlJobHelper.getShardTotal();
XxlJobHelper.log("Shard params: index=" + shardIndex + ", total=" + shardTotal);
for (int i = 0; i < shardTotal; i++) {
if (i == shardIndex) {
XxlJobHelper.log("Processing shard " + i);
} else {
XxlJobHelper.log("Skipping shard " + i);
}
}
}Command‑line job
@XxlJob("commandJobHandler")
public void commandJobHandler() throws Exception {
String command = XxlJobHelper.getJobParam();
ProcessBuilder pb = new ProcessBuilder();
pb.command(command);
pb.redirectErrorStream(true);
Process process = pb.start();
try (BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = br.readLine()) != null) {
XxlJobHelper.log(line);
}
}
int exit = process.waitFor();
if (exit != 0) {
XxlJobHelper.handleFail("command exit value(" + exit + ") is failed");
}
}HTTP job (cross‑language)
@XxlJob("httpJobHandler")
public void httpJobHandler() throws Exception {
String param = XxlJobHelper.getJobParam();
// param format: url:..., method:..., data:...
String[] lines = param.split("
");
String url = null, method = null, data = null;
for (String line : lines) {
if (line.startsWith("url:")) {
url = line.substring(4).trim();
} else if (line.startsWith("method:")) {
method = line.substring(7).trim().toUpperCase();
} else if (line.startsWith("data:")) {
data = line.substring(5).trim();
}
}
if (url == null || method == null || !Arrays.asList("GET","POST").contains(method)) {
XxlJobHelper.handleFail();
return;
}
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod(method);
conn.setDoOutput("POST".equals(method));
conn.setConnectTimeout(3000);
conn.setReadTimeout(5000);
if ("POST".equals(method) && data != null && !data.isEmpty()) {
try (DataOutputStream out = new DataOutputStream(conn.getOutputStream())) {
out.write(data.getBytes("UTF-8"));
}
}
int status = conn.getResponseCode();
if (status != 200) {
throw new RuntimeException("Http Request StatusCode(" + status + ") Invalid.");
}
try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
XxlJobHelper.log(sb.toString());
}
}Lifecycle‑aware job
@XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy")
public void demoJobHandler2() throws Exception {
XxlJobHelper.log("XXL-JOB, Hello World.");
}
public void init() { logger.info("init"); }
public void destroy() { logger.info("destroy"); }Creating and running tasks in the admin UI
In the XXL‑JOB console, add an executor entry whose AppName matches the value configured in application.properties. Then create a new job, select the execution mode (e.g., “BEAN”), bind the JobHandler name (e.g., demoJobHandler), and set a Cron expression such as */30 * * * * ? to run every 30 seconds. Click “Start” to trigger the job and observe real‑time logs in the console.
The platform also provides additional capabilities—custom routing, alert extensions, Docker images, etc.—documented in the official manual.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
The Dominant Programmer
Resources and tutorials for programmers' advanced learning journey. Advanced tracks in Java, Python, and C#. Blog: https://blog.csdn.net/badao_liumang_qizhi
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
