前言
大家應該都有所體會,使用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" ?> < beans xmlns = "http://www.springframework.org/schema/beans" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:context = "http://www.springframework.org/schema/context" xmlns:task = "http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd"> < 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