Backend Development 15 min read

Auto‑Register Executors and Jobs in XXL‑Job with a SpringBoot Starter

This article explains how to eliminate manual configuration of XXL‑Job executors and job handlers by creating a SpringBoot starter that automatically registers them to the XXL‑Job admin center, covering the required APIs, authentication, annotation design, and implementation details with full code examples.

macrozheng
macrozheng
macrozheng
Auto‑Register Executors and Jobs in XXL‑Job with a SpringBoot Starter

xxl-job is a lightweight, easy‑to‑use task scheduling middleware that supports distributed deployment. Registering executors and job handlers manually through the admin UI becomes cumbersome when a project contains dozens or hundreds of scheduled tasks.

To avoid repetitive manual registration, a SpringBoot starter can be created that automatically registers executors and job handlers to the XXL‑Job admin center during application startup.

The automatic registration works by programmatically calling the admin APIs. The key endpoints are:

/jobgroup/pageList

– query executor list

/jobgroup/save

– add executor

/jobinfo/pageList

– query job list

/jobinfo/add

– add job

These APIs require authentication. A login request to

/login

returns a cookie named

XXL_JOB_LOGIN_IDENTITY

, which must be included in subsequent calls.

<code>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(cookie -> cookie.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());
}
</code>

Helper services use this cookie to call the admin APIs.

<code>public List<XxlJobGroup> getJobGroup() {
    String url = adminAddresses + "/jobgroup/pageList";
    HttpResponse response = HttpRequest.post(url)
        .form("appname", appName)
        .form("title", title)
        .cookie(jobLoginService.getCookie())
        .execute();
    JSONArray array = JSONUtil.parse(response.body()).getByPath("data", JSONArray.class);
    return array.stream()
        .map(o -> JSONUtil.toBean((JSONObject) o, XxlJobGroup.class))
        .collect(Collectors.toList());
}

public boolean preciselyCheck() {
    return getJobGroup().stream()
        .anyMatch(g -> g.getAppname().equals(appName) && g.getTitle().equals(title));
}

public boolean autoRegisterGroup() {
    String url = adminAddresses + "/jobgroup/save";
    HttpResponse response = HttpRequest.post(url)
        .form("appname", appName)
        .form("title", title)
        .cookie(jobLoginService.getCookie())
        .execute();
    return JSONUtil.parse(response.body()).getByPath("code").equals(200);
}
</code>

Similarly,

JobInfoService

provides methods to query and add jobs.

<code>public List<XxlJobInfo> getJobInfo(Integer jobGroupId, String executorHandler) {
    String url = adminAddresses + "/jobinfo/pageList";
    HttpResponse response = HttpRequest.post(url)
        .form("jobGroup", jobGroupId)
        .form("executorHandler", executorHandler)
        .form("triggerStatus", -1)
        .cookie(jobLoginService.getCookie())
        .execute();
    JSONArray array = JSONUtil.parse(response.body()).getByPath("data", JSONArray.class);
    return array.stream()
        .map(o -> JSONUtil.toBean((JSONObject) o, XxlJobInfo.class))
        .collect(Collectors.toList());
}

public Integer addJobInfo(XxlJobInfo info) {
    String url = adminAddresses + "/jobinfo/add";
    HttpResponse response = HttpRequest.post(url)
        .form(BeanUtil.beanToMap(info))
        .cookie(jobLoginService.getCookie())
        .execute();
    JSON json = JSONUtil.parse(response.body());
    if (json.getByPath("code").equals(200)) {
        return Convert.toInt(json.getByPath("content"));
    }
    throw new RuntimeException("add jobInfo error!");
}
</code>

A custom annotation

@XxlRegister

is introduced to supply the extra metadata required for automatic registration.

<code>@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;
}
</code>

The core auto‑registration class implements

ApplicationListener&lt;ApplicationReadyEvent&gt;

and runs after the Spring context is ready.

<code>@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 event) {
        addJobGroup(); // register executor
        addJobInfo();   // register jobs
    }

    private void addJobGroup() {
        if (jobGroupService.preciselyCheck()) return;
        if (jobGroupService.autoRegisterGroup())
            log.info("auto register xxl-job group success!");
    }

    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();
                if (m.isAnnotationPresent(XxlRegister.class)) {
                    XxlRegister reg = m.getAnnotation(XxlRegister.class);
                    List<XxlJobInfo> existing = jobInfoService.getJobInfo(group.getId(), entry.getValue().value());
                    if (!existing.isEmpty()) {
                        Optional<XxlJobInfo> opt = existing.stream()
                            .filter(i -> i.getExecutorHandler().equals(entry.getValue().value()))
                            .findFirst();
                        if (opt.isPresent()) continue;
                    }
                    XxlJobInfo info = createXxlJobInfo(group, entry.getValue(), reg);
                    jobInfoService.addJobInfo(info);
                }
            }
        }
    }

    private XxlJobInfo createXxlJobInfo(XxlJobGroup group, XxlJob job, XxlRegister reg) {
        XxlJobInfo info = new XxlJobInfo();
        info.setJobGroup(group.getId());
        info.setExecutorHandler(job.value());
        info.setJobDesc(reg.jobDesc());
        info.setAuthor(reg.author());
        info.setScheduleConf(reg.cron());
        info.setTriggerStatus(reg.triggerStatus());
        return info;
    }
}
</code>

A configuration class scans the starter package and is declared in

META-INF/spring.factories

so that Spring Boot auto‑configures it.

<code>@Configuration
@ComponentScan(basePackages = "com.xxl.job.plus.executor")
public class XxlJobPlusConfig {}
</code>

To use the starter, add the Maven dependency, configure the standard XXL‑Job properties together with the additional

xxl.job.admin.username

,

xxl.job.admin.password

, and

xxl.job.executor.title

properties, and annotate methods with both

@XxlJob

and

@XxlRegister

:

<code>@XxlJob("testJob")
@XxlRegister(cron = "0 0 0 * * ? *", author = "hydra", jobDesc = "测试job")
public void testJob() {
    System.out.println("#公众号:码农参上");
}
</code>

When the application starts, the executor and all annotated jobs are automatically registered in the XXL‑Job admin console, eliminating the need for manual UI operations.

Javatask schedulingSpringBootauto-registrationxxl-jobStarter
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.