前言
大家应该都有所体会,使用Spring的任务调度给我们的开发带来了极大的便利,不过当我们的任务调度配置完成后,很难再对其进行更改,除非停止服务器,修改配置,然后再重启,显然这样是不利于线上操作的,为了实现动态的任务调度修改,我在网上也查阅了一些资料,大部分都是基于quartz实现的,使用Spring内置的任务调度则少之又少,而且效果不理想,需要在下次任务执行后,新的配置才能生效,做不到立即生效。本着探索研究的原则,查看了一下Spring的源码,下面为大家提供一种Spring内置任务调度实现添加、取消、重置的方法。话不多说了,来一起看看详细的介绍 吧。
实现方法如下
首先,我们需要启用Spring的任务调度
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
xsi:schemaLocation="http://www.springframework.org/schema/beans
<
task:annotation-driven
executor
=
"jobExecutor"
scheduler
=
"jobScheduler"
/>
<
task:executor
id
=
"jobExecutor"
pool-size
=
"5"
/>
<
task:scheduler
id
=
"jobScheduler"
pool-size
=
"10"
/>
</
beans
>
|
这一部分配置在网上是很常见的,接下来我们需要联合使用@EnableScheduling与org.springframework.scheduling.annotation.SchedulingConfigurer
便携我们自己的调度配置,在SchedulingConfigurer接口中,需要实现一个void configureTasks(ScheduledTaskRegistrar taskRegistrar);
方法,传统做法是在该方法中添加需要执行的调度信息。网上的基本撒谎那个也都是使用该方法实现的,使用addTriggerTask添加任务,并结合cron表达式动态修改调度时间,这里我们并不这样做。
查看一下ScheduledTaskRegistrar源码,我们发现该对象初始化完成后会执行scheduleTasks()
方法,在该方法中添加任务调度信息,最终所有的任务信息都存放在名为scheduledFutures的集合中。
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
|
protected
void
scheduleTasks() {
long
now = System.currentTimeMillis();
if
(
this
.taskScheduler ==
null
) {
this
.localExecutor = Executors.newSingleThreadScheduledExecutor();
this
.taskScheduler =
new
ConcurrentTaskScheduler(
this
.localExecutor);
}
if
(
this
.triggerTasks !=
null
) {
for
(TriggerTask task :
this
.triggerTasks) {
this
.scheduledFutures.add(
this
.taskScheduler.schedule(
task.getRunnable(), task.getTrigger()));
}
}
if
(
this
.cronTasks !=
null
) {
for
(CronTask task :
this
.cronTasks) {
this
.scheduledFutures.add(
this
.taskScheduler.schedule(
task.getRunnable(), task.getTrigger()));
}
}
if
(
this
.fixedRateTasks !=
null
) {
for
(IntervalTask task :
this
.fixedRateTasks) {
if
(task.getInitialDelay() >
0
) {
Date startTime =
new
Date(now + task.getInitialDelay());
this
.scheduledFutures.add(
this
.taskScheduler.scheduleAtFixedRate(
task.getRunnable(), startTime, task.getInterval()));
}
else
{
this
.scheduledFutures.add(
this
.taskScheduler.scheduleAtFixedRate(
task.getRunnable(), task.getInterval()));
}
}
}
if
(
this
.fixedDelayTasks !=
null
) {
for
(IntervalTask task :
this
.fixedDelayTasks) {
if
(task.getInitialDelay() >
0
) {
Date startTime =
new
Date(now + task.getInitialDelay());
this
.scheduledFutures.add(
this
.taskScheduler.scheduleWithFixedDelay(
task.getRunnable(), startTime, task.getInterval()));
}
else
{
this
.scheduledFutures.add(
this
.taskScheduler.scheduleWithFixedDelay(
task.getRunnable(), task.getInterval()));
}
}
}
}
|
所以我的思路就是动态修改该集合,实现任务调度的添加、取消、重置。实现代码如下:
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
package
com.jianggujin.web.util.job;
import
java.util.Map;
import
java.util.Set;
import
java.util.concurrent.ConcurrentHashMap;
import
java.util.concurrent.ScheduledFuture;
import
org.springframework.scheduling.TaskScheduler;
import
org.springframework.scheduling.annotation.EnableScheduling;
import
org.springframework.scheduling.annotation.SchedulingConfigurer;
import
org.springframework.scheduling.config.ScheduledTaskRegistrar;
import
org.springframework.scheduling.config.TriggerTask;
import
com.jianggujin.web.util.BeanUtils;
/**
* 默认任务调度配置
*
* @author jianggujin
*
*/
@EnableScheduling
public
class
DefaultSchedulingConfigurer
implements
SchedulingConfigurer
{
private
final
String FIELD_SCHEDULED_FUTURES =
"scheduledFutures"
;
private
ScheduledTaskRegistrar taskRegistrar;
private
Set<ScheduledFuture<?>> scheduledFutures =
null
;
private
Map<String, ScheduledFuture<?>> taskFutures =
new
ConcurrentHashMap<String, ScheduledFuture<?>>();
@Override
public
void
configureTasks(ScheduledTaskRegistrar taskRegistrar)
{
this
.taskRegistrar = taskRegistrar;
}
@SuppressWarnings
(
"unchecked"
)
private
Set<ScheduledFuture<?>> getScheduledFutures()
{
if
(scheduledFutures ==
null
)
{
try
{
scheduledFutures = (Set<ScheduledFuture<?>>) BeanUtils.getProperty(taskRegistrar, FIELD_SCHEDULED_FUTURES);
}
catch
(NoSuchFieldException e)
{
throw
new
SchedulingException(
"not found scheduledFutures field."
);
}
}
return
scheduledFutures;
}
/**
* 添加任务
*
* @param taskId
* @param triggerTask
*/
public
void
addTriggerTask(String taskId, TriggerTask triggerTask)
{
if
(taskFutures.containsKey(taskId))
{
throw
new
SchedulingException(
"the taskId["
+ taskId +
"] was added."
);
}
TaskScheduler scheduler = taskRegistrar.getScheduler();
ScheduledFuture<?> future = scheduler.schedule(triggerTask.getRunnable(), triggerTask.getTrigger());
getScheduledFutures().add(future);
taskFutures.put(taskId, future);
}
/**
* 取消任务
*
* @param taskId
*/
public
void
cancelTriggerTask(String taskId)
{
ScheduledFuture<?> future = taskFutures.get(taskId);
if
(future !=
null
)
{
future.cancel(
true
);
}
taskFutures.remove(taskId);
getScheduledFutures().remove(future);
}
/**
* 重置任务
*
* @param taskId
* @param triggerTask
*/
public
void
resetTriggerTask(String taskId, TriggerTask triggerTask)
{
cancelTriggerTask(taskId);
addTriggerTask(taskId, triggerTask);
}
/**
* 任务编号
*
* @return
*/
public
Set<String> taskIds()
{
return
taskFutures.keySet();
}
/**
* 是否存在任务
*
* @param taskId
* @return
*/
public
boolean
hasTask(String taskId)
{
return
this
.taskFutures.containsKey(taskId);
}
/**
* 任务调度是否已经初始化完成
*
* @return
*/
public
boolean
inited()
{
return
this
.taskRegistrar !=
null
&&
this
.taskRegistrar.getScheduler() !=
null
;
}
}
|
其中用到的BeanUtils源码如下:
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
package
com.jianggujin.web.util;
import
java.lang.reflect.Field;
import
java.lang.reflect.Method;
import
java.lang.reflect.Modifier;
import
java.util.ArrayList;
import
java.util.Collections;
import
java.util.HashMap;
import
java.util.List;
import
java.util.Map;
public
class
BeanUtils
{
public
static
Field findField(Class<?> clazz, String name)
{
try
{
return
clazz.getField(name);
}
catch
(NoSuchFieldException ex)
{
return
findDeclaredField(clazz, name);
}
}
public
static
Field findDeclaredField(Class<?> clazz, String name)
{
try
{
return
clazz.getDeclaredField(name);
}
catch
(NoSuchFieldException ex)
{
if
(clazz.getSuperclass() !=
null
)
{
return
findDeclaredField(clazz.getSuperclass(), name);
}
return
null
;
}
}
public
static
Method findMethod(Class<?> clazz, String methodName, Class<?>... paramTypes)
{
try
{
return
clazz.getMethod(methodName, paramTypes);
}
catch
(NoSuchMethodException ex)
{
return
findDeclaredMethod(clazz, methodName, paramTypes);
}
}
public
static
Method findDeclaredMethod(Class<?> clazz, String methodName, Class<?>... paramTypes)
{
try
{
return
clazz.getDeclaredMethod(methodName, paramTypes);
}
catch
(NoSuchMethodException ex)
{
if
(clazz.getSuperclass() !=
null
)
{
return
findDeclaredMethod(clazz.getSuperclass(), methodName, paramTypes);
}
return
null
;
}
}
public
static
Object getProperty(Object obj, String name)
throws
NoSuchFieldException
{
Object value =
null
;
Field field = findField(obj.getClass(), name);
if
(field ==
null
)
{
throw
new
NoSuchFieldException(
"no such field ["
+ name +
"]"
);
}
boolean
accessible = field.isAccessible();
field.setAccessible(
true
);
try
{
value = field.get(obj);
}
catch
(Exception e)
{
throw
new
RuntimeException(e);
}
field.setAccessible(accessible);
return
value;
}
public
static
void
setProperty(Object obj, String name, Object value)
throws
NoSuchFieldException
{
Field field = findField(obj.getClass(), name);
if
(field ==
null
)
{
throw
new
NoSuchFieldException(
"no such field ["
+ name +
"]"
);
}
boolean
accessible = field.isAccessible();
field.setAccessible(
true
);
try
{
field.set(obj, value);
}
catch
(Exception e)
{
throw
new
RuntimeException(e);
}
field.setAccessible(accessible);
}
public
static
Map<String, Object> obj2Map(Object obj, Map<String, Object> map)
{
if
(map ==
null
)
{
map =
new
HashMap<String, Object>();
}
if
(obj !=
null
)
{
try
{
Class<?> clazz = obj.getClass();
do
{
Field[] fields = clazz.getDeclaredFields();
for
(Field field : fields)
{
int
mod = field.getModifiers();
if
(Modifier.isStatic(mod))
{
continue
;
}
boolean
accessible = field.isAccessible();
field.setAccessible(
true
);
map.put(field.getName(), field.get(obj));
field.setAccessible(accessible);
}
clazz = clazz.getSuperclass();
}
while
(clazz !=
null
);
}
catch
(Exception e)
{
throw
new
RuntimeException(e);
}
}
return
map;
}
/**
* 获得父类集合,包含当前class
*
* @param clazz
* @return
*/
public
static
List<Class<?>> getSuperclassList(Class<?> clazz)
{
List<Class<?>> clazzes =
new
ArrayList<Class<?>>(
3
);
clazzes.add(clazz);
clazz = clazz.getSuperclass();
while
(clazz !=
null
)
{
clazzes.add(clazz);
clazz = clazz.getSuperclass();
}
return
Collections.unmodifiableList(clazzes);
}
}
|
因为加载的延迟,在使用这种方法自定义配置任务调度是,首先需要调用inited()
方法判断是否初始化完成,否则可能出现错误。
接下来我们来测试一下:
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
package
com.jianggujin.zft.job;
import
java.util.Calendar;
import
org.springframework.beans.factory.InitializingBean;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.scheduling.config.TriggerTask;
import
org.springframework.scheduling.support.CronTrigger;
import
org.springframework.stereotype.Component;
import
com.jianggujin.web.util.job.DefaultSchedulingConfigurer;
public
class
TestJob
implements
InitializingBean
{
@Autowired
private
DefaultSchedulingConfigurer defaultSchedulingConfigurer;
public
void
afterPropertiesSet()
throws
Exception
{
new
Thread() {
public
void
run()
{
try
{
// 等待任务调度初始化完成
while
(!defaultSchedulingConfigurer.inited())
{
Thread.sleep(
100
);
}
}
catch
(InterruptedException e)
{
e.printStackTrace();
}
System.out.println(
"任务调度初始化完成,添加任务"
);
defaultSchedulingConfigurer.addTriggerTask(
"task"
,
new
TriggerTask(
new
Runnable() {
@Override
public
void
run()
{
System.out.println(
"run job..."
+ Calendar.getInstance().get(Calendar.SECOND));
}
},
new
CronTrigger(
"0/5 * * * * ? "
)));
};
}.start();
new
Thread() {
public
void
run()
{
try
{
Thread.sleep(
30000
);
}
catch
(Exception e)
{
}
System.out.println(
"重置任务............"
);
defaultSchedulingConfigurer.resetTriggerTask(
"task"
,
new
TriggerTask(
new
Runnable() {
@Override
public
void
run()
{
System.out.println(
"run job..."
+ Calendar.getInstance().get(Calendar.SECOND));
}
},
new
CronTrigger(
"0/10 * * * * ? "
)));
};
}.start();
}
}
|
在该类中,我们首先使用一个线程,等待我们自己的任务调度初始化完成后向其中添加一个每五秒钟打印一句话的任务,然后再用另一个线程过30秒后修改该任务,修改的本质其实是现将原来的任务取消,然后再添加一个新的任务。
在配置文件中初始化上面的类
1
2
|
<bean id=
"defaultSchedulingConfigurer"
class
=
"com.jianggujin.web.util.job.DefaultSchedulingConfigurer"
/>
<bean id=
"testJob"
class
=
"com.jianggujin.zft.job.TestJob"
/>
|
运行程序,观察控制台输出
这样我们就实现了动态的重置任务了。以上为个人探索出来的方法,如有更好的解决方案,欢迎指正。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。
原文链接:http://blog.csdn.net/jianggujin/article/details/77937316