在Spring中实现任务调度自由
Mr.Lee 2026-06-04 11:16:23 Spring
早就该整理成文章了, 今天看之间的代码想到的....就来水一篇吧
之前要用定时任务, 常规的做法是不是要在Application 类上加@EnableScheduling注解, 然后在任务方法上@Scheduled(cron = "${sync.interval.cron}")
这种方式不能灵活的取消或改变任务内容.
所以...就有下面的尝试
❯ tree own-ext
own-ext
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── striveonger
│ │ │ └── common
│ │ │ └── ext
│ │ │ ├── config
│ │ │ │ └── ExtAutoConfiguration.java
│ │ │ ├── expr
│ │ │ │ └── package-info.java
│ │ │ └── schedule
│ │ │ └── ScheduleRegistrar.java
└─ └─ └── resources
└── META-INF
└── spring
└── org.springframework.boot.autoconfigure.AutoConfiguration.imports
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
org.springframework.boot.autoconfigure.AutoConfiguration.imports 这可以 SpringBoot 自动配置的关键
# 自动装载
com.striveonger.common.ext.config.ExtAutoConfiguration
1
2
2
# 自动装载类
/**
* @author Mr.Lee
* @since 2024-10-27 13:19
*/
@AutoConfiguration
@AutoConfigurationPackage(basePackages = {"com.striveonger.common.ext.*"})
@EnableScheduling
public class ExtAutoConfiguration {
private final Logger log = LoggerFactory.getLogger(ExtAutoConfiguration.class);
/**
* 定时任务注册器
*/
@Bean
public ScheduledTaskRegistrar scheduledTaskRegistrar() {
return new ScheduledTaskRegistrar();
}
/**
* 定时任务注解处理器
*/
@Bean
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationBeanPostProcessor(ScheduledTaskRegistrar scheduledTaskRegistrar) {
return new ScheduledAnnotationBeanPostProcessor(scheduledTaskRegistrar);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 定时任务注册器
/**
* @author Mr.Lee
* @since 2025-10-15 15:58
*/
@Component
public class ScheduleRegistrar {
private final Logger log = LoggerFactory.getLogger(ScheduleRegistrar.class);
/**
* 定时任务注册器
*/
@Resource
private ScheduledTaskRegistrar registrar;
/**
* 已注册的定时任务(分布式环境下 可以考虑在Redis中做存储抢号执行的操作, 谁抢到key的锁, 谁来执行这次的任务)
*/
private final Map<String, ScheduledTask> tasks = new ConcurrentHashMap<>();
/**
* 注册定时任务
* @param key 任务键
* @param cron 定时表达式
* @param runnable 任务 Runnable
*/
public void register(String key, String cron, Runnable runnable) {
synchronized (key.intern()) {
if (tasks.containsKey(key)) {
log.warn("Schedule already registered: {}", key);
throw new OwnException("Schedule already registered: " + key);
}
log.info("Register schedule: {} -> {}", key, cron);
ScheduledTask task = registrar.scheduleCronTask(new CronTask(runnable, cron));
tasks.put(key, task);
}
}
/**
* 取消注册定时任务
* @param key 任务键
*/
public void unregister(String key) {
synchronized (key.intern()) {
log.info("Unregister schedule: {}", key);
ScheduledTask task = tasks.remove(key);
if (task != null) {
task.cancel();
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 单元测试
@SpringBootTest(classes = {ExtAutoConfiguration.class, ScheduleRegistrar.class})
public class ScheduleRegistrarTest {
private final Logger log = LoggerFactory.getLogger(ScheduleRegistrarTest.class);
@Resource
private ScheduleRegistrar registrar;
@Test
public void test() {
registrar.register("a", "*/5 * * * * ?", () -> log.info("a"));
registrar.register("b", "*/5 * * * * ?", () -> log.info("b"));
ThreadKit.sleep(15, TimeUnit.SECONDS);
registrar.unregister("a");
ThreadKit.sleep(10, TimeUnit.SECONDS);
registrar.unregister("b");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
测试结果:
2026-06-04 21:52:10.801 INFO 62145 --- [ main] c.s.c.ext.schedule.ScheduleRegistrar :[ 46]: Register schedule: a -> */5 * * * * ?
2026-06-04 21:52:10.804 INFO 62145 --- [ main] o.s.s.config.TaskSchedulerRouter :[ 225]: No TaskScheduler/ScheduledExecutorService bean found for scheduled processing
2026-06-04 21:52:10.805 INFO 62145 --- [ main] c.s.c.ext.schedule.ScheduleRegistrar :[ 46]: Register schedule: b -> */5 * * * * ?
2026-06-04 21:52:15.005 INFO 62145 --- [pool-3-thread-1] c.s.c.e.schedule.ScheduleRegistrarTest :[ 26]: b
2026-06-04 21:52:15.006 INFO 62145 --- [pool-3-thread-1] c.s.c.e.schedule.ScheduleRegistrarTest :[ 25]: a
2026-06-04 21:52:20.003 INFO 62145 --- [pool-3-thread-1] c.s.c.e.schedule.ScheduleRegistrarTest :[ 25]: a
2026-06-04 21:52:20.004 INFO 62145 --- [pool-3-thread-1] c.s.c.e.schedule.ScheduleRegistrarTest :[ 26]: b
2026-06-04 21:52:25.005 INFO 62145 --- [pool-3-thread-1] c.s.c.e.schedule.ScheduleRegistrarTest :[ 26]: b
2026-06-04 21:52:25.006 INFO 62145 --- [pool-3-thread-1] c.s.c.e.schedule.ScheduleRegistrarTest :[ 25]: a
2026-06-04 21:52:25.808 INFO 62145 --- [ main] c.s.c.ext.schedule.ScheduleRegistrar :[ 58]: Unregister schedule: a
2026-06-04 21:52:30.001 INFO 62145 --- [pool-3-thread-1] c.s.c.e.schedule.ScheduleRegistrarTest :[ 26]: b
2026-06-04 21:52:35.004 INFO 62145 --- [pool-3-thread-1] c.s.c.e.schedule.ScheduleRegistrarTest :[ 26]: b
2026-06-04 21:52:35.814 INFO 62145 --- [ main] c.s.c.ext.schedule.ScheduleRegistrar :[ 58]: Unregister schedule: b
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13