Dynamic Switching Between Spring @Scheduled and XXL‑JOB with Automatic Task Registration
This article explains how to flexibly switch between Spring's built‑in @Scheduled tasks and XXL‑JOB implementations without changing existing code, by using conditional auto‑configuration, annotation scanning, task registration, log redirection, and a ready‑to‑use starter, complete with configuration examples and runtime verification.
The author introduces a scenario where a Spring Boot application sometimes must use the native @Scheduled mechanism due to deployment constraints, but wants the ability to switch to XXL‑JOB dynamically through configuration.
Problem Analysis
By reading the requirements, the solution focuses on detecting the xxl.job.enable property and conditionally enabling XXL‑JOB while keeping Spring's scheduler untouched when disabled.
Implementation Details
Conditional Auto‑Configuration
@Configuration
@ConditionalOnProperty(name = "xxl.job.enable", havingValue = "true")
@ComponentScan("com.teoan.job.auto.core")
public class XxlJobAutoConfiguration { }The above class is loaded only when xxl.job.enable is true, otherwise Spring's default scheduling remains.
Scanning Annotations
After the application is ready, a component scans for @Scheduled methods using Spring's MethodIntrospector and registers them to XXL‑JOB.
@Component
@Slf4j
public class JobAutoRegister {
@EventListener(ApplicationReadyEvent.class)
@Async
public void onApplicationEvent(ApplicationReadyEvent event) {
// scan and register
}
}Stopping Spring Scheduler
The ScheduledAnnotationBeanPostProcessor is invoked via postProcessBeforeDestruction to cancel the original Spring tasks before registering them to XXL‑JOB.
@Override
public void postProcessBeforeDestruction(Object bean, String beanName) {
Set
tasks;
synchronized (this.scheduledTasks) {
tasks = this.scheduledTasks.remove(bean);
}
if (tasks != null) {
for (ScheduledTask task : tasks) {
task.cancel();
}
}
}Registering Job Handlers
Each discovered method is wrapped in a MethodJobHandler and registered via XxlJobExecutor.registJobHandler . The handler name follows the convention beanClass#methodName .
private void registJobHandler(String handlerName, Method executeMethod) {
Object bean = applicationContext.getBean(executeMethod.getDeclaringClass());
XxlJobExecutor.registJobHandler(handlerName, new MethodJobHandler(bean, executeMethod, null, null));
}Automatic Executor and Job Registration
Using HTTP calls to the XXL‑JOB admin API, the starter auto‑registers the executor and job information, handling token acquisition, duplicate checks, and error logging.
Starter Usage
Add the starter and XXL‑JOB dependencies to pom.xml and configure the required properties in application.yml , including the toggle xxl.job.enable , admin address, credentials, and executor details.
Sample Job
@Slf4j
@Component
public class XxlJobAutoSamplesJob {
@Scheduled(fixedRate = 10000)
public void samplesJob() {
log.info("samplesJob executor success!");
}
}Running the application with the toggle set to false uses Spring's scheduler; setting it to true registers the job with XXL‑JOB, as demonstrated by the console logs and screenshots of the admin UI.
Considerations
Task updates (e.g., cron changes) are not automatically synchronized; changes should be made in the admin console.
Direct database manipulation was avoided in favor of the official HTTP API for better portability.
The starter does not force XXL‑JOB as a transitive dependency, keeping it optional.
Conclusion
The author reflects on the learning experience of extending a middleware, reading source code, and building a reusable starter, and provides the open‑source repository for interested developers.
Project repository: https://github.com/Teoan/xxl-job-auto
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.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.