How to Auto-Register XXL‑Job Executors and Tasks with a Spring Boot Starter
This guide explains how to eliminate manual registration of XXL‑Job executors and job handlers by creating a Spring Boot starter that automatically registers them to the XXL‑Job admin console, covering analysis, code implementation, configuration, and testing steps.
Hello, I am Su San. XXL‑Job is a lightweight, easy‑to‑use distributed task scheduling middleware widely used in projects.
Normally you need to configure executors and jobs manually in the XXL‑Job admin UI, which becomes tedious when there are many scheduled tasks.
Analysis
The solution is to register the executor and each job handler automatically when the application starts, bypassing the admin UI.
Automatic registration requires calling the admin APIs to add executors ( /jobgroup/save) and job handlers ( /jobinfo/add). The necessary APIs are discovered by inspecting the admin UI network requests.
Transformation
We create a starter that, on application startup, registers the executor and job handlers using the following dependencies:
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>1. Interface Calls
Login service obtains the XXL_JOB_LOGIN_IDENTITY cookie and caches it:
private final Map<String,String> loginCookie = new HashMap<>();
public void login() {
String url = adminAddresses + "/login";
HttpResponse response = HttpRequest.post(url)
.form("userName", username)
.form("password", password)
.execute();
List<HttpCookie> cookies = response.getCookies();
Optional<HttpCookie> cookieOpt = cookies.stream()
.filter(c -> c.getName().equals("XXL_JOB_LOGIN_IDENTITY"))
.findFirst();
if (!cookieOpt.isPresent())
throw new RuntimeException("get xxl-job cookie error!");
loginCookie.put("XXL_JOB_LOGIN_IDENTITY", cookieOpt.get().getValue());
}Other services reuse the cached cookie and retry login up to three times if needed.
public String getCookie() {
for (int i = 0; i < 3; i++) {
String cookieStr = loginCookie.get("XXL_JOB_LOGIN_IDENTITY");
if (cookieStr != null) return "XXL_JOB_LOGIN_IDENTITY=" + cookieStr;
login();
}
throw new RuntimeException("get xxl-job cookie error!");
}2. Create New Annotation
Define @XxlRegister to hold additional job metadata:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface XxlRegister {
String cron();
String jobDesc() default "default jobDesc";
String author() default "default Author";
int triggerStatus default 0;
}3. Auto‑Registration Core
The core listener implements ApplicationListener<ApplicationReadyEvent> and triggers executor and job registration after the Spring context is ready:
@Component
public class XxlJobAutoRegister implements ApplicationListener<ApplicationReadyEvent>, ApplicationContextAware {
@Autowired private JobGroupService jobGroupService;
@Autowired private JobInfoService jobInfoService;
private ApplicationContext applicationContext;
@Override public void setApplicationContext(ApplicationContext ctx) { this.applicationContext = ctx; }
@Override public void onApplicationEvent(ApplicationReadyEvent e) {
addJobGroup();
addJobInfo();
}
private void addJobGroup() { if (!jobGroupService.preciselyCheck()) jobGroupService.autoRegisterGroup(); }
private void addJobInfo() {
List<XxlJobGroup> groups = jobGroupService.getJobGroup();
XxlJobGroup group = groups.get(0);
String[] beanNames = applicationContext.getBeanNamesForType(Object.class, false, true);
for (String name : beanNames) {
Object bean = applicationContext.getBean(name);
Map<Method, XxlJob> methods = MethodIntrospector.selectMethods(bean.getClass(),
method -> AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class));
for (Map.Entry<Method, XxlJob> entry : methods.entrySet()) {
Method m = entry.getKey();
XxlJob xxlJob = entry.getValue();
if (m.isAnnotationPresent(XxlRegister.class)) {
XxlRegister reg = m.getAnnotation(XxlRegister.class);
List<XxlJobInfo> existing = jobInfoService.getJobInfo(group.getId(), xxlJob.value());
if (!existing.isEmpty()) {
Optional<XxlJobInfo> first = existing.stream()
.filter(info -> info.getExecutorHandler().equals(xxlJob.value()))
.findFirst();
if (first.isPresent()) continue;
}
XxlJobInfo info = createXxlJobInfo(group, xxlJob, reg);
jobInfoService.addJobInfo(info);
}
}
}
}
// createXxlJobInfo implementation omitted for brevity
}4. Auto‑Wiring
Provide a configuration class to scan the starter’s packages and register it via META-INF/spring.factories:
@Configuration
@ComponentScan(basePackages = "com.xxl.job.plus.executor")
public class XxlJobPlusConfig { } org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.xxl.job.plus.executor.config.XxlJobPlusConfigTest
Create a Spring Boot project, add the starter dependency, and configure XXL‑Job properties (admin address, token, executor app name, etc.) plus the starter‑specific properties (admin username/password, executor title).
<dependency>
<groupId>com.cn.hydra</groupId>
<artifactId>xxljob-autoregister-spring-boot-starter</artifactId>
<version>0.0.1</version>
</dependency>Define beans with both @XxlJob and @XxlRegister annotations:
@XxlJob("testJob")
@XxlRegister(cron = "0 0 0 * * ? *", author = "hydra", jobDesc = "测试job")
public void testJob() { System.out.println("#公众号:码农参上"); }
@XxlJob("testJob222")
@XxlRegister(cron = "59 1-2 0 * * ?", triggerStatus = 1)
public void testJob2() { System.out.println("#作者:Hydra"); }
@XxlJob("testJob444")
@XxlRegister(cron = "59 59 23 * * ?")
public void testJob4() { System.out.println("hello xxl job"); }Run the application; the executor registers automatically (see image) and the annotated jobs appear in the admin UI, ready to be triggered manually.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
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.
