Java编程中怎样调度Spring 集成任务功能,架构师必看!

Java编程中怎样调度Spring 集成任务功能架构师必看!


如果程序员想在Java编程Spring中使用任务调度功能,除了集成调度框架Quartz这种方式,也可以使用Spring自己的调度任务框架。使用Spring的调度框架,优点是:支持注解@Scheduler,可以省去大量的配置。

实时触发调度任务

TaskScheduler接口

Spring3引入了TaskScheduler接口,这个接口定义了调度任务的抽象方法。TaskScheduler接口的声明:

public interface TaskScheduler {    ScheduledFuture schedule(Runnable task, Trigger trigger);    ScheduledFuture schedule(Runnable task, Date startTime);    ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);    ScheduledFuture scheduleAtFixedRate(Runnable task, long period);    ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);    ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);}

从以上方法可以看出TaskScheduler有两类重要参数:

[if !supportLists]§ [endif]一个是要调度的方法,即一个实现了Runnable接口的线程类的run()方法;

[if !supportLists]§ [endif]另一个就是触发条件。

TaskScheduler接口的实现类它有三个实现类:DefaultManagedTaskScheduler、ThreadPoolTaskScheduler、TimerManagerTaskScheduler。

[if !supportLists]§ [endif]DefaultManagedTaskScheduler:基于JNDI的调度器。

[if !supportLists]§ [endif]TimerManagerTaskScheduler:托管commonj.timers.TimerManager实例的调度器

[if !supportLists]§ [endif]ThreadPoolTaskScheduler:提供线程池管理的调度器,它也实现了TaskExecutor接口,从而使的单一的实例可以尽可能快地异步执行。

Trigger接口

Trigger接口抽象了触发条件的方法。Trigger接口的声明:

public interface Trigger {    Date nextExecutionTime(TriggerContext triggerContext);}

Trigger接口的实现类CronTrigger:实现了cron规则的触发器类(和Quartz的cron规则相同)。PeriodicTrigger:实现了一个周期性规则的触发器类(例如:定义触发起始时间、间隔时间等)。

完整范例

实现一个调度任务的功能有以下几个关键点:(1) 定义调度器在spring-bean.xml中进行配置使用task:scheduler标签定义一个大小为10的线程池调度器,spring会实例化一个ThreadPoolTaskScheduler。

<?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:mvc="http://www.springframework.org/schema/mvc"       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.1.xsd       http://www.springframework.org/schema/mvc       http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd       http://www.springframework.org/schema/task       http://www.springframework.org/schema/task/spring-task-3.1.xsd">  <mvc:annotation-driven/>  <task:scheduler id="myScheduler" pool-size="10"/></beans>

注:不要忘记引入xsd:

http://www.springframework.org/schema/taskhttp://www.springframework.org/schema/task/spring-task-3.1.xsd

(2) 定义调度任务定义实现Runnable接口的线程类。

import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class DemoTask implements Runnable {    final Logger logger = LoggerFactory.getLogger(this.getClass());    @Override    public void run() {        logger.info("call DemoTask.run");    }}

(3) 装配调度器,并执行调度任务在一个Controller类中用@Autowired注解装配TaskScheduler。然后调动TaskScheduler对象的schedule方法启动调度器,就可以执行调度任务了。

@Controller@RequestMapping("/scheduler")public class SchedulerController {     @Autowired    TaskScheduler scheduler;    @RequestMapping(value = "/start", method = RequestMethod.POST)    public void start() {        scheduler.schedule(new DemoTask(), new CronTrigger("0/5 * * * * *"));    }}

访问/scheduler/start接口,启动调度器,可以看到如下日志内容:

13:53:15.010 myScheduler-1 o.zp.notes.spring.scheduler.DemoTask.run - call DemoTask.run13:53:20.003 myScheduler-1 o.zp.notes.spring.scheduler.DemoTask.run - call DemoTask.run13:53:25.004 myScheduler-2 o.zp.notes.spring.scheduler.DemoTask.run - call DemoTask.run13:53:30.005 myScheduler-1 o.zp.notes.spring.scheduler.DemoTask.run - call DemoTask.run

@Scheduler的使用方法

Spring的调度器一个很大的亮点在于@Scheduler注解,这可以省去很多繁琐的配置。

启动注解

使用@Scheduler注解先要使用<task:annotation-driven>启动注解开关。例:

<?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:mvc="http://www.springframework.org/schema/mvc"        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.1.xsd       http://www.springframework.org/schema/mvc       http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd       http://www.springframework.org/schema/task       http://www.springframework.org/schema/task/spring-task-3.1.xsd">  <mvc:annotation-driven/>  <task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>  <task:executor id="myExecutor" pool-size="5"/>  <task:scheduler id="myScheduler" pool-size="10"/></beans>

@Scheduler定义触发条件

例:使用fixedDelay指定触发条件为每5000毫秒执行一次。注意:必须在上一次调度成功后的5000秒才能执行。

@Scheduled(fixedDelay=5000)public void doSomething() {    // something that should execute periodically}

例:使用fixedRate指定触发条件为每5000毫秒执行一次。注意:无论上一次调度是否成功,5000秒后必然执行。

@Scheduled(fixedRate=5000)public void doSomething() {    // something that should execute periodically}

例:使用initialDelay指定方法在初始化1000毫秒后才开始调度。

@Scheduled(initialDelay=1000, fixedRate=5000)public void doSomething() {    // something that should execute periodically}

例:使用cron表达式指定触发条件为每5000毫秒执行一次。cron规则和Quartz中的cron规则一致。

@Scheduled(cron="*/5 * * * * MON-FRI")public void doSomething() {    // something that should execute on weekdays only}

完整范例

(1) 启动注解开关,并定义调度器和执行器

<?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:mvc="http://www.springframework.org/schema/mvc"        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.1.xsd       http://www.springframework.org/schema/mvc       http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd       http://www.springframework.org/schema/task       http://www.springframework.org/schema/task/spring-task-3.1.xsd">  <mvc:annotation-driven/>  <task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>  <task:executor id="myExecutor" pool-size="5"/>  <task:scheduler id="myScheduler" pool-size="10"/></beans>

(2) 使用@Scheduler注解来修饰一个要调度的方法下面的例子展示了@Scheduler注解定义触发条件的不同方式。

/** * @description 使用@Scheduler注解调度任务范例 * @author Vicotr Zhang * @date 2016年8月31日 */@Componentpublic class ScheduledMgr {    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    final Logger logger = LoggerFactory.getLogger(this.getClass());    /**     * 构造函数中打印初始化时间     */    public ScheduledMgr() {        logger.info("Current time: {}", dateFormat.format(new Date()));    }    /**     * fixedDelay属性定义调度间隔时间。调度需要等待上一次调度执行完成。     */    @Scheduled(fixedDelay = 5000)    public void testFixedDelay() throws Exception {        Thread.sleep(6000);        logger.info("Current time: {}", dateFormat.format(new Date()));    }    /**     * fixedRate属性定义调度间隔时间。调度不等待上一次调度执行完成。     */    @Scheduled(fixedRate = 5000)    public void testFixedRate() throws Exception {        Thread.sleep(6000);        logger.info("Current time: {}", dateFormat.format(new Date()));    }    /**     * initialDelay属性定义初始化后的启动延迟时间     */    @Scheduled(initialDelay = 1000, fixedRate = 5000)    public void testInitialDelay() throws Exception {        Thread.sleep(6000);        logger.info("Current time: {}", dateFormat.format(new Date()));    }    /**     * cron属性支持使用cron表达式定义触发条件     */    @Scheduled(cron = "0/5 * * * * ?")    public void testCron() throws Exception {        Thread.sleep(6000);        logger.info("Current time: {}", dateFormat.format(new Date()));    }}

我刻意设置触发方式的间隔都是5s,且方法中均有Thread.sleep(6000);语句。从而确保方法在下一次调度触发时间点前无法完成执行,来看一看各种方式的表现吧。启动spring项目后,spring会扫描@Component注解,然后初始化ScheduledMgr。接着,spring会扫描@Scheduler注解,初始化调度器。调度器在触发条件匹配的情况下开始工作,输出日志。截取部分打印日志来进行分析。

10:58:46.479 localhost-startStop-1 o.z.n.s.scheduler.ScheduledTasks.<init- Current time: 2016-08-31 10:58:4610:58:52.523 myScheduler-1 o.z.n.s.scheduler.ScheduledTasks.testFixedRate - Current time: 2016-08-31 10:58:5210:58:52.523 myScheduler-3 o.z.n.s.scheduler.ScheduledTasks.testFixedDelay - Current time: 2016-08-31 10:58:5210:58:53.524 myScheduler-2 o.z.n.s.scheduler.ScheduledTasks.testInitialDelay - Current time: 2016-08-31 10:58:5310:58:55.993 myScheduler-4 o.z.n.s.scheduler.ScheduledTasks.testCron - Current time: 2016-08-31 10:58:5510:58:58.507 myScheduler-1 o.z.n.s.scheduler.ScheduledTasks.testFixedRate - Current time: 2016-08-31 10:58:5810:58:59.525 myScheduler-5 o.z.n.s.scheduler.ScheduledTasks.testInitialDelay - Current time: 2016-08-31 10:58:5910:59:03.536 myScheduler-3 o.z.n.s.scheduler.ScheduledTasks.testFixedDelay - Current time: 2016-08-31 10:59:0310:59:04.527 myScheduler-1 o.z.n.s.scheduler.ScheduledTasks.testFixedRate - Current time: 2016-08-31 10:59:0410:59:05.527 myScheduler-4 o.z.n.s.scheduler.ScheduledTasks.testInitialDelay - Current time: 2016-08-31 10:59:0510:59:06.032 myScheduler-2 o.z.n.s.scheduler.ScheduledTasks.testCron - Current time: 2016-08-31 10:59:0610:59:10.534 myScheduler-9 o.z.n.s.scheduler.ScheduledTasks.testFixedRate - Current time: 2016-08-31 10:59:1010:59:11.527 myScheduler-10 o.z.n.s.scheduler.ScheduledTasks.testInitialDelay - Current time: 2016-08-31 10:59:1110:59:14.524 myScheduler-4 o.z.n.s.scheduler.ScheduledTasks.testFixedDelay - Current time: 2016-08-31 10:59:1410:59:15.987 myScheduler-6 o.z.n.s.scheduler.ScheduledTasks.testCron - Current time: 2016-08-31 10:59:15

构造方法打印一次,时间点在10:58:46。testFixedRate打印四次,每次间隔6秒。说明,fixedRate不等待上一次调度执行完成,在间隔时间达到时立即执行。testFixedDelay打印三次,每次间隔大于6秒,且时间不固定。说明,fixedDelay等待上一次调度执行成功后,开始计算间隔时间,再执行。testInitialDelay第一次调度时间和构造方法调度时间相隔7秒。说明,initialDelay在初始化后等待指定的延迟时间才开始调度。testCron打印三次,时间间隔并非5秒或6秒,显然,cron等待上一次调度执行成功后,开始计算间隔时间,再执行。此外,可以从日志中看出,打印日志的线程最多只有10个,说明2.1中的调度器线程池配置生效。

专注于Java架构师技术分享,撩我免费送Java全套架构师晋级资料

(Java架构师交流企*-**-*鹅*-*裙*-*:445*-**-*820*-**-*908

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容