2实现定时任务,springboot整合Quartz实现动态配置定

作者: 编程应用  发布:2019-09-28

版权注解:本文为博主原创作品,转发请注解出处。

前言

最近几年职业中必要动用定期任务的作用,即便Spring3也自带了贰个轻量级的定期职分落到实处,但认为远远不够灵活,功用也远远不够强大。在思量之后,决定整合更为规范的Quartz来贯彻定期职务功效。

在大家平时的付出中,比比较多时候,定期职责都不是写死的,而是写到数据库中,从而达成定期职责的动态配置,上边就透过贰个简约的演示,来兑现这么些作用。

先是,当然是加多正视的jar文件,小编的连串是maven管理的,以下的我项指标依赖:

一、新建八个springboot工程,并累加注重

图片 1

      <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency> <dependency><!-- 为了方便测试,此处使用了内存数据库 --><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.2.1</version><exclusions><exclusion><artifactId>slf4j-api</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions></dependency><dependency><!-- 该依赖必加,里面有sping对schedule的支持 -->                       <groupId>org.springframework</groupId>                       <artifactId>spring-context-support</artifactId>        </dependency>
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>${mybatis.version}</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.7.4</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>${mybatis.spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>commons-lang</groupId>
        <artifactId>commons-lang</artifactId>
        <version>${commons.lang.version}</version>
    </dependency>
    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>${commons.dbcp.version}</version>
    </dependency>
    <dependency>
        <groupId>com.oracle</groupId>
        <artifactId>ojdbc14</artifactId>
        <version>${ojdbc.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
        <version>${quartz.version}</version>
    </dependency>
</dependencies>

  

图片 2

二、配置文件application.properties

或是你应该看出来了,小编的门类是spring整合了mybatis,如今spring的最新版本现已到了4.x多元,然则最新版的mybatis-spring的整合插件所信任推荐的照旧是spring 3.1.3.RELEASE,所以那边未有用spring的新型版而是用了推介的3.1.3.RELEASE,毕竟最新版本的功能相似景况下也用不到。

# 服务器端口号  server.port=7902# 是否生成ddl语句  spring.jpa.generate-ddl=false  # 是否打印sql语句  spring.jpa.show-sql=true  # 自动生成ddl,由于指定了具体的ddl,此处设置为none  spring.jpa.hibernate.ddl-auto=none  # 使用H2数据库  spring.datasource.platform=h2  # 指定生成数据库的schema文件位置  spring.datasource.schema=classpath:schema.sql  # 指定插入数据库语句的脚本位置  spring.datasource.data=classpath:data.sql  # 配置日志打印信息  logging.level.root=INFO  logging.level.org.hibernate=INFO  logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE  logging.level.org.hibernate.type.descriptor.sql.BasicExtractor=TRACE  logging.level.com.itmuch=DEBUG 

有关quartz,则是用了当前的新星版2.2.1

  

为此在那边特别对版本作一下证实,是因为spring和quartz的结合对版本是有须求的。

三、Entity类

spring3.1以下的版本必得利用quartz1.x多级,3.1上述的本子才支撑quartz 2.x,不然会出错。

package com.chhliu.springboot.quartz.entity; import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id; @Entitypublic class Config {@Id  @GeneratedValue(strategy = GenerationType.AUTO)  private Long id;   @Column  private String cron; /** * @return the id */public Long getId() {return id;}        ……此处省略getter和setter方法……}

有关原因,则是spring对于quartz的帮衬促成,org.springframework.scheduling.quartz.CronTriggerBean承接了org.quartz.CronTrigger,在quartz1.x体系中org.quartz.CronTrigger是个类,而在quartz2.x文山会海中org.quartz.CronTrigger产生了接口,从而致使无法用spring的法子配置quartz的触发器(trigger)。

  

在Spring中应用Quartz有三种艺术完成:第一种是任务类承接QuartzJobBean,第三种则是在配置文件里定义任务类和要试行的格局,类和艺术能够是日常类。很生硬,第三种办法远比第一种艺术来的灵敏。

四、任务类

此处运用的正是第二种艺术。

package com.chhliu.springboot.quartz.entity; import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.context.annotation.Configuration;import org.springframework.scheduling.annotation.EnableScheduling;import org.springframework.stereotype.Component; @Configuration@Component // 此注解必加@EnableScheduling // 此注解必加public class ScheduleTask {private static final Logger LOGGER =  LoggerFactory.getLogger(ScheduleTask.class);public void sayHello(){LOGGER.info("Hello world, i'm the king of the world!!!");}}

spring配置文件:

  

图片 3

五、Quartz配置类

<!-- 使用MethodInvokingJobDetailFactoryBean,任务类可以不实现Job接口,通过targetMethod指定调用方法-->
<bean id="taskJob" class="com.tyyd.dw.task.DataConversionTask"/>
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="group" value="job_work"/>
    <property name="name" value="job_work_name"/>
    <!--false表示等上一个任务执行完后再开启新的任务-->
    <property name="concurrent" value="false"/>
    <property name="targetObject">
        <ref bean="taskJob"/>
    </property>
    <property name="targetMethod">
        <value>run</value>
    </property>
</bean>

<!--  调度触发器 -->
<bean id="myTrigger"
      class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="name" value="work_default_name"/>
    <property name="group" value="work_default"/>
    <property name="jobDetail">
        <ref bean="jobDetail" />
    </property>
    <property name="cronExpression">
        <value>0/5 * * * * ?</value>
    </property>
</bean>

<!-- 调度工厂 -->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="myTrigger"/>
        </list>
    </property>
</bean>
Task类则是一个普通的Java类,没有继承任何类和实现任何接口(当然可以用注解方式来声明bean):

//@Component
public class DataConversionTask{

    /** 日志对象 */
    private static final Logger LOG = LoggerFactory.getLogger(DataConversionTask.class);

    public void run() {

        if (LOG.isInfoEnabled()) {
            LOG.info("数据转换任务线程开始执行");
        }
    }
}

出于springboot追求零xml配置,所以下边会以安顿Bean的格局来兑现

图片 4

package com.chhliu.springboot.quartz.entity; import org.quartz.Trigger;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.scheduling.quartz.CronTriggerFactoryBean;import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;import org.springframework.scheduling.quartz.SchedulerFactoryBean; @Configurationpublic class QuartzConfigration { /**  * attention:  * Details:配置定时任务  */ @Bean(name = "jobDetail") public MethodInvokingJobDetailFactoryBean detailFactoryBean(ScheduleTask task) {// ScheduleTask为需要执行的任务  MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();  /*   * 是否并发执行   * 例如每5s执行一次任务,但是当前任务还没有执行完,就已经过了5s了,   * 如果此处为true,则下一个任务会执行,如果此处为false,则下一个任务会等待上一个任务执行完后,再开始执行   */  jobDetail.setConcurrent;    jobDetail.setName("srd-chhliu");// 设置任务的名字  jobDetail.setGroup;// 设置任务的分组,这些属性都可以存储在数据库中,在多任务的时候使用    /*   * 为需要执行的实体类对应的对象   */  jobDetail.setTargetObject;    /*   * sayHello为需要执行的方法   * 通过这几个配置,告诉JobDetailFactoryBean我们需要执行定时执行ScheduleTask类中的sayHello方法   */  jobDetail.setTargetMethod("sayHello");  return jobDetail; }  /**  * attention:  * Details:配置定时任务的触发器,也就是什么时候触发执行定时任务  */ @Bean(name = "jobTrigger") public CronTriggerFactoryBean cronJobTrigger(MethodInvokingJobDetailFactoryBean jobDetail) {  CronTriggerFactoryBean tigger = new CronTriggerFactoryBean();  tigger.setJobDetail(jobDetail.getObject;  tigger.setCronExpression("0 30 20 * * ?");// 初始时的cron表达式  tigger.setName("srd-chhliu");// trigger的name  return tigger;  }  /**  * attention:  * Details:定义quartz调度工厂  */ @Bean(name = "scheduler") public SchedulerFactoryBean schedulerFactory(Trigger cronJobTrigger) {  SchedulerFactoryBean bean = new SchedulerFactoryBean();  // 用于quartz集群,QuartzScheduler 启动时更新己存在的Job  bean.setOverwriteExistingJobs;  // 延时启动,应用启动1秒后  bean.setStartupDelay;  // 注册触发器  bean.setTriggers(cronJobTrigger);  return bean; }}

于今,简单的组成马到功成,run方法将每隔5秒实践二遍,因为布署了concurrent等于false,所以假诺run方法的试行时间超越5秒,在举办完此前就是时间已经超(英文名:jīng chāo)越了5秒下叁个定时安排实践任务仍不会被拉开,假如是true,则无论是还是不是实行完,时间到了都将开启。

  

 

六、定期查库,并立异职责

接下去,将落到实处怎么样动态的修改按时施行的年月,以及哪些甘休正在施行的职务,待续,,,

package com.chhliu.springboot.quartz.entity; import javax.annotation.Resource; import org.quartz.CronScheduleBuilder;import org.quartz.CronTrigger;import org.quartz.JobDetail;import org.quartz.Scheduler;import org.quartz.SchedulerException;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.scheduling.annotation.EnableScheduling;import org.springframework.scheduling.annotation.Scheduled;import org.springframework.stereotype.Component; import com.chhliu.springboot.quartz.repository.ConfigRepository; @Configuration@EnableScheduling@Componentpublic class ScheduleRefreshDatabase {@Autowiredprivate ConfigRepository repository; @Resource(name = "jobDetail")private JobDetail jobDetail; @Resource(name = "jobTrigger")private CronTrigger cronTrigger; @Resource(name = "scheduler")private Scheduler scheduler; @Scheduled(fixedRate = 5000) // 每隔5s查库,并根据查询结果决定是否重新设置定时任务public void scheduleUpdateCronTrigger() throws SchedulerException {CronTrigger trigger = (CronTrigger) scheduler.getTrigger(cronTrigger.getKey;String currentCron = trigger.getCronExpression();// 当前Trigger使用的String searchCron = repository.findOne.getCron();// 从数据库查询出来的System.out.println(currentCron);System.out.println(searchCron);if (currentCron.equals(searchCron)) {// 如果当前使用的cron表达式和从数据库中查询出来的cron表达式一致,则不刷新任务} else {// 表达式调度构建器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(searchCron);// 按新的cronExpression表达式重新构建triggertrigger = (CronTrigger) scheduler.getTrigger(cronTrigger.getKey;trigger = trigger.getTriggerBuilder().withIdentity(cronTrigger.getKey.withSchedule(scheduleBuilder).build();// 按新的trigger重新设置job执行scheduler.rescheduleJob(cronTrigger.getKey(), trigger);currentCron = searchCron;}}}

 

  

附带贴一下cronExpression表明式备忘:

六、相关脚本

字段 允许值 允许的特殊字符

1、data.sql

秒 0-59 , – * /

insert into config(id,cron) values(1,'0 0/2 * * * ?'); # 每2分钟执行一次定时任务

分 0-59 , – * /

2、schema.sql

小时 0-23 , – * /

drop table config if exists;create table config(id bigint generated by default as identity,cron varchar,primary key;

日期 1-31 , – * ? / L W C

  

月份 1-12 或者 JAN-DEC , – * /

六、运营测验

星期 1-7 或者 SUN-SAT , – * ? / L C #

测量试验结果如下:(Quartz私下认可的线程池大小为10)

年(可选) 留空, 1970-2099 , – * /

0 30 20 * * ?0 0/2 * * * ?2017-03-08 18:02:00.025 INFO 5328 --- [eduler_Worker-1] c.c.s.quartz.entity.ScheduleTask : Hello world, i'm the king of the world!!!2017-03-08 18:04:00.003 INFO 5328 --- [eduler_Worker-2] c.c.s.quartz.entity.ScheduleTask : Hello world, i'm the king of the world!!!2017-03-08 18:06:00.002 INFO 5328 --- [eduler_Worker-3] c.c.s.quartz.entity.ScheduleTask : Hello world, i'm the king of the world!!!2017-03-08 18:08:00.002 INFO 5328 --- [eduler_Worker-4] c.c.s.quartz.entity.ScheduleTask : Hello world, i'm the king of the world!!!

表明式意义

  

"0 0 12 * * ?" 每一天上午12点触发

从下面的日记打字与印刷时间来看,大家落到实处了动态配置,最早的时候,职务是每一天20:30试行,前面通过动态刷新产生了每隔2分钟施行二回。

"0 15 10 ? * *" 每一天早上10:15触及

虽说上边的应用方案尚未利用Quartz推荐的章程周详,但基本上能够满意我们的须求,当然也足以运用触发事件的不二法门来促成,比方当前端修改按期职务的触及时间时,异步的向后台发送通告,后台接受布告后,然后再立异程序,也能够完成动态的按时任务刷新

"0 15 10 * * ?" 每一天下午10:15接触

"0 15 10 * * ? *" 每一日下午10:15接触

"0 15 10 * * ? 贰零零伍" 二零零七年的天天深夜10:15触及

"0 * 14 * * ?" 在每天中午2点到晚上2:59里面包车型地铁每1分钟触发

"0 0/5 14 * * ?" 在每天早上2点到上午2:55中间的每5分钟触发

"0 0/5 14,18 * * ?" 在天天凌晨2点到2:55里面和深夜6点到6:55里面包车型客车每5分钟触发

"0 0-5 14 * * ?" 在天天早晨2点到晚上2:05之间的每1秒钟触发

"0 10,44 14 ? 3 WED" 每年一月的星期一的早晨2:10和2:44触及

"0 15 10 ? * MON-F哈弗I" 星期二至周一的上午10:15接触

"0 15 10 15 * ?" 每月15日上午10:15触发

"0 15 10 L * ?" 每月最终十四日的上午10:15触及

"0 15 10 ? * 6L" 每月的末尾三个星期四下午10:15接触

"0 15 10 ? * 6L 2001-贰零零伍" 2004年至二〇〇七年的每月的最终贰个星期一清晨10:15接触

"0 15 10 ? * 6#3" 每月的第多少个周三中午10:15接触

每日午夜6点

0 6 * * *

每八个钟头

0 */2 * * *

晌午11点到上午8点时期每多少个时辰,下午八点

0 23-7/2,8 * * *

每种月的4号和各样礼拜的星期四到礼拜二的晚上11点

0 11 4 * 1-3

1月1日早上4点

0 4 1 1 *

动态拉长任务

前方,大家早就对Spring 3和Quartz 2用配备文件的秘技张开了整合,假诺急需比较轻便的话应该已经能够满足了。不过众多时候,大家平日会遭受要求动态的增加或涂改任务,而spring中所提供的定期义务组件却只可以够通过修改xml中trigger的配置能力决定按时职务的时间以及职分的启用或终止,那在带给大家有益的同一时间也失去了动态配置职分的油滑。小编找找了有个别互连网的解决形式,都不曾很好的缓和那几个主题材料,并且非常多事关的建设方案都停留在Quartz 1.x文山会海版本上,所用到的代码和API已经不能够适用于新本子的Spring和Quartz。无法只可以靠自身了,花了点时间能够钻研了须臾间Spring和Quartz中有关的代码。

先是大家来回想一下spring中运用quartz的安插代码:

图片 5

<!-- 使用MethodInvokingJobDetailFactoryBean,任务类可以不实现Job接口,通过targetMethod指定调用方法-->
<bean id="taskJob" class="com.tyyd.dw.task.DataConversionTask"/>
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="group" value="job_work"/>
    <property name="name" value="job_work_name"/>
    <!--false表示等上一个任务执行完后再开启新的任务-->
    <property name="concurrent" value="false"/>
    <property name="targetObject">
        <ref bean="taskJob"/>
    </property>
    <property name="targetMethod">
        <value>execute</value>
    </property>
</bean>

<!--  调度触发器 -->
<bean id="myTrigger"
      class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="name" value="work_default_name"/>
    <property name="group" value="work_default"/>
    <property name="jobDetail">
        <ref bean="jobDetail" />
    </property>
    <property name="cronExpression">
        <value>0/5 * * * * ?</value>
    </property>
</bean>

<!-- 调度工厂 -->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="myTrigger"/>
        </list>
    </property>
</bean>

图片 6

有着的配备都在xml中做到,包罗cronExpression表明式,十二分的惠及。可是只要自身的职务消息是保存在数据库的,想要动态的初阶化,並且职分非常多的时候不是得有一大堆的xml配置?或许说小编要修改一下trigger的表明式,使原本5秒运转贰遍的天职形成10秒运转一次,那时难点就来了,试过在安插文件中不扩散cronExpression等参数,不过运行时就报错了,难道自身老是都修改xml文件然后重启应用吗,那鲜明不合适的。最完美的是在与spring整合的还要又能促成动态职务的充足、删除及修改配置。

咱俩来看一下spring完结quartz的方法,先看一下方面配置文件中定义的jobDetail。其实下目生成的jobDetail实际不是大家定义的Bean,因为在Quartz 2.x版本中JobDetail已然是四个接口(当然在此以前的本子也休想直接生成JobDetail):

  1. public interface JobDetail extends Serializable, Cloneable {...}

Spring是因此将其转移为MethodInvokingJob或StatefulMethodInvokingJob类型来促成的,那四个都是静态的在那之中类,MethodInvokingJob类承接于QuartzJobBean,而StatefulMethodInvokingJob则一直接轨于MethodInvokingJob。 那多少个类的贯彻分裂在于有意况和无状态,对应于quartz的Job和StatefulJob,具体可以查阅quartz文书档案,这里不再赘言。先来看一下它们贯彻的QuartzJobBean的显要代码:

图片 7

/**
 * This implementation applies the passed-in job data map as bean property
 * values, and delegates to <code>executeInternal</code> afterwards.
 * @see #executeInternal
 */
public final void execute(JobExecutionContext context) throws JobExecutionException {
    try {
        // Reflectively adapting to differences between Quartz 1.x and Quartz 2.0...
        Scheduler scheduler = (Scheduler) ReflectionUtils.invokeMethod(getSchedulerMethod, context);
        Map mergedJobDataMap = (Map) ReflectionUtils.invokeMethod(getMergedJobDataMapMethod, context);

        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
        MutablePropertyValues pvs = new MutablePropertyValues();
        pvs.addPropertyValues(scheduler.getContext());
        pvs.addPropertyValues(mergedJobDataMap);
        bw.setPropertyValues(pvs, true);
    }
    catch (SchedulerException ex) {
        throw new JobExecutionException(ex);
    }
    executeInternal(context);
}

/**
 * Execute the actual job. The job data map will already have been
 * applied as bean property values by execute. The contract is
 * exactly the same as for the standard Quartz execute method.
 * @see #execute
 */
protected abstract void executeInternal(JobExecutionContext context) throws JobExecutionException;
//还有MethodInvokingJobDetailFactoryBean中的代码:

public void afterPropertiesSet() throws ClassNotFoundException, NoSuchMethodException {
    prepare();

    // Use specific name if given, else fall back to bean name.
    String name = (this.name != null ? this.name : this.beanName);

    // Consider the concurrent flag to choose between stateful and stateless job.
    Class jobClass = (this.concurrent ? MethodInvokingJob.class : StatefulMethodInvokingJob.class);

    // Build JobDetail instance.
    if (jobDetailImplClass != null) {
        // Using Quartz 2.0 JobDetailImpl class...
        this.jobDetail = (JobDetail) BeanUtils.instantiate(jobDetailImplClass);
        BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this.jobDetail);
        bw.setPropertyValue("name", name);
        bw.setPropertyValue("group", this.group);
        bw.setPropertyValue("jobClass", jobClass);
        bw.setPropertyValue("durability", true);
        ((JobDataMap) bw.getPropertyValue("jobDataMap")).put("methodInvoker", this);
    }
    else {
        // Using Quartz 1.x JobDetail class...
        this.jobDetail = new JobDetail(name, this.group, jobClass);
        this.jobDetail.setVolatility(true);
        this.jobDetail.setDurability(true);
        this.jobDetail.getJobDataMap().put("methodInvoker", this);
    }

    // Register job listener names.
    if (this.jobListenerNames != null) {
        for (String jobListenerName : this.jobListenerNames) {
            if (jobDetailImplClass != null) {
                throw new IllegalStateException("Non-global JobListeners not supported on Quartz 2 - " +
                        "manually register a Matcher against the Quartz ListenerManager instead");
            }
            this.jobDetail.addJobListener(jobListenerName);
        }
    }

    postProcessJobDetail(this.jobDetail);
}

图片 8

地方根本看我们最近用的Quartz 2.0版本的兑现部分,到那边大概你曾经明白Spring对Quartz的卷入原理了。Spring便是因此这种情势在终极Job真正奉行时反调用到我们所注入的类和艺术。

今昔,驾驭了Spring的兑现原理后,我们就足以来规划大家和好的了。在筹算时自己想开以下几点:

1、收缩spring的安排文件,为了完结一个定期职责,spring的配置代码太多了。

2、客户能够透过页面等方法丰裕、启用、禁止使用有些职务。

3、顾客可以修改有个别已经在运作任务的周转时刻表达式,CronExpression。

4、为便利维护,简化职分的运维调用处理,职责的运行入口即Job达成类最棒独有二个,该Job运维类相当于工厂类,在实际调用时把职分的连带音信透过参数方式传入,由该工厂类依照任务消息来具体实践需求的操作。

在上头的思绪下来进行我们的付出吧。

一、spring配置文件

透过切磋,开采要促成大家的功效,只须要以下配置:

  1. <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" />
二、职责运营入口,即Job达成类,在此间作者把它当做工厂类:

图片 9

/**
 * 定时任务运行工厂类
 * 
 * User: liyd
 * Date: 14-1-3
 * Time: 上午10:11
 */
public class QuartzJobFactory implements Job {

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("任务成功运行");
        ScheduleJob scheduleJob = (ScheduleJob)context.getMergedJobDataMap().get("scheduleJob");
        System.out.println("任务名称 = [" + scheduleJob.getJobName() + "]");
    }
}

图片 10

此间大家兑现的是无状态的Job,若是要促成有情形的Job在从前是完结StatefulJob接口,在自家利用的quartz 2.2.第11中学,StatefulJob接口已经不推荐使用了,换来了解说的点子,只需求给您兑现的Job类加上评释@DisallowConcurrentExecution就能够达成有动静:

  1. /**
  2. * 定时职分运转工厂类
  3. * <p/>
  4. * User: liyd
  5. * Date: 14-1-3
  6. * Time: 上午10:11
  7. */
  8. @DisallowConcurrentExecution
  9. public class QuartzJobFactory implements Job {...}
三、创立任务

既然要动态的创办职分,大家的天职音讯本来要保留在某些地方了,这里大家新建三个保存职分音讯对应的实体类:

图片 11

/**
 * 计划任务信息
 * 
 * User: liyd
 * Date: 14-1-3
 * Time: 上午10:24
 */
public class ScheduleJob {

    /** 任务id */
    private String jobId;

    /** 任务名称 */
    private String jobName;

    /** 任务分组 */
    private String jobGroup;

    /** 任务状态 0禁用 1启用 2删除*/
    private String jobStatus;

    /** 任务运行时间表达式 */
    private String cronExpression;

    /** 任务描述 */
    private String desc;

    getter and setter ....
}

图片 12

接下去大家创制测量试验数据,实际利用中该多少年足球以保留在数据库等地点,大家把职分的分组名+任务名作为天职的独一key,和quartz中的落成格局同样:

图片 13

/** 计划任务map */
private static Map<String, ScheduleJob> jobMap = new HashMap<String, ScheduleJob>();

static {
    for (int i = 0; i < 5; i++) {
        ScheduleJob job = new ScheduleJob();
        job.setJobId("10001" + i);
        job.setJobName("data_import" + i);
        job.setJobGroup("dataWork");
        job.setJobStatus("1");
        job.setCronExpression("0/5 * * * * ?");
        job.setDesc("数据导入任务");
        addJob(job);
    }
}

/**
 * 添加任务
 * @param scheduleJob
 */
public static void addJob(ScheduleJob scheduleJob) {
    jobMap.put(scheduleJob.getJobGroup() + "_" + scheduleJob.getJobName(), scheduleJob);
}

图片 14

有了调节工厂,有了使时局转入口完成类,有了职分消息,接下去正是创建大家的定期职责了,在此地自个儿把它安排成一个Job对应贰个trigger,两个的分组及称谓一致,方便管理,条理也相比明晰,在开立职责时如若不设有新建二个,借使已经存在则更新职务,主要代码如下:

图片 15

//schedulerFactoryBean 由spring创建注入
Scheduler scheduler = schedulerFactoryBean.getScheduler();

//这里获取任务信息数据
List<ScheduleJob> jobList = DataWorkContext.getAllJob();

for (ScheduleJob job : jobList) {

    TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());

    //获取trigger,即在spring配置文件中定义的 bean id="myTrigger"
    CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

    //不存在,创建一个
    if (null == trigger) {
        JobDetail jobDetail = JobBuilder.newJob(QuartzJobFactory.class)
            .withIdentity(job.getJobName(), job.getJobGroup()).build();
        jobDetail.getJobDataMap().put("scheduleJob", job);

        //表达式调度构建器
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job
            .getCronExpression());

        //按新的cronExpression表达式构建一个新的trigger
        trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup()).withSchedule(scheduleBuilder).build();

        scheduler.scheduleJob(jobDetail, trigger);
    } else {
        // Trigger已存在,那么更新相应的定时设置
        //表达式调度构建器
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job
            .getCronExpression());

        //按新的cronExpression表达式重新构建trigger
        trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
            .withSchedule(scheduleBuilder).build();

        //按新的trigger重新设置job执行
        scheduler.rescheduleJob(triggerKey, trigger);
    }
}

图片 16

那般,能够说已经完结了大家的动态职分创造,大功告成了。有了地点的代码,加多和修改职责是否也会了,顺路化解了?

地方大家成立的5个测验任务,都是5秒施行三回,都将调用QuartzJobFactory的execute方法,可是传入的天职消息参数不相同,execute方法中的如下代码正是收获具体的任务音讯,包蕴职分分组和职分名:

  1. ScheduleJob scheduleJob = (ScheduleJob)context.getMergedJobDataMap().get("scheduleJob");

有了职分分组和职责名即显明了该任务的独一性,接下去须求如何操作完毕起来是或不是就很轻易了?

现在须求增添新的按期职务只供给在职责消息列表中加入记录就可以,然后在execute方法中经过推断职责分组和天职名来落到实处您实际的操作。

以瓜时经起来达成了我们要求的功效,扩充和改变也早就足以由此源代码融会贯通出来,然则大家在其实开荒的时候需求开展测验,倘诺一个任务是1个钟头运维二次的,测验起来是否十分不方便人民群众?当然你能够修改职务的周转时刻说明式,但相信那不是最佳的章程,接下去我们就要落到实处在难堪称前职责新闻做别的修改的事态下接触义务,並且该触发只会运作贰遍作测量试验用。待续,,,

动态暂停 复苏 修改和删除职责

后边我们已经变成了spring 3和quartz 2的咬合以及动态增加定期任务,大家跟着来全面它,使之能协理更多的操作,比如暂停、复苏、修改等。

在动态增添定期职分中实际上早已涉嫌到了里面包车型地铁一些代码,这里大家再来细化的理一理。先来看一下我们开始要兑现的靶子意义图,这里大家只在内部存款和储蓄器中操作,并未把quartz的其余信息保存到数据库,即选取的是RAMJobStore,当然要是您有需求,能够实现成JDBCJobStore,那样任务音讯将会更健全,貌似还恐怕有特意的监察工具,不过自身尚未用过:

图片 17

如上海教室,大家要先列出安插中的按期任务以及正在进行中的定时任务,这里的正在实践中指的是天职已经触发线程还没实践完的处境。例如每一天2点实践三个数额导入操作,这么些操作实行时间供给5秒钟,在那5分钟之内这几个任务才是运作中的职分。当职务平常时能够运用暂停按键,职责暂停时得以行使复苏按键。

trigger各状态表明:

None:Trigger已经成功,且不会在实践,或许找不到该触发器,可能Trigger已经被去除
NORMAL:不奇怪景况
PAUSED:暂停状态
COMPLETE:触发器完结,不过义务或然还正在奉行中
BLOCKED:线程阻塞状态
E卡宴RO库罗德:出现错误

安顿中的任务

指那贰个曾经增多到quartz调整器的职分,因为quartz并从未直接提供这么的询问接口,所以大家要求组合JobKey和Trigger来完结,宗旨代码:

图片 18

Scheduler scheduler = schedulerFactoryBean.getScheduler();
GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
for (JobKey jobKey : jobKeys) {
    List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
    for (Trigger trigger : triggers) {
        ScheduleJob job = new ScheduleJob();
        job.setJobName(jobKey.getName());
        job.setJobGroup(jobKey.getGroup());
        job.setDesc("触发器:" + trigger.getKey());
        Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
        job.setJobStatus(triggerState.name());
        if (trigger instanceof CronTrigger) {
            CronTrigger cronTrigger = (CronTrigger) trigger;
            String cronExpression = cronTrigger.getCronExpression();
            job.setCronExpression(cronExpression);
        }
        jobList.add(job);
    }
}

图片 19

上边代码中的jobList就是大家必要的陈设中的职分列表,须求注意贰个job大概会有多少个trigger的景色,在底下讲到的立刻运维三遍职分的时候,会变卦多个临时的trigger也会出现在这。这里把多少个Job有多少个trigger的情状作为是多个义务。我们如今包罗在骨子里项目中常常用到的都是CronTrigger ,所以那边大家器重管理了下CronTrigger的事态。

运转中的职分

福如东海和布署中的职务类似,核心代码:

图片 20

Scheduler scheduler = schedulerFactoryBean.getScheduler();
List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
for (JobExecutionContext executingJob : executingJobs) {
    ScheduleJob job = new ScheduleJob();
    JobDetail jobDetail = executingJob.getJobDetail();
    JobKey jobKey = jobDetail.getKey();
    Trigger trigger = executingJob.getTrigger();
    job.setJobName(jobKey.getName());
    job.setJobGroup(jobKey.getGroup());
    job.setDesc("触发器:" + trigger.getKey());
    Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
    job.setJobStatus(triggerState.name());
    if (trigger instanceof CronTrigger) {
        CronTrigger cronTrigger = (CronTrigger) trigger;
        String cronExpression = cronTrigger.getCronExpression();
        job.setCronExpression(cronExpression);
    }
    jobList.add(job);
}

图片 21

暂停任务

那几个相比较轻巧,宗旨代码:

  1. Scheduler scheduler = schedulerFactoryBean.getScheduler();
  2. JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
  3. scheduler.pauseJob(jobKey);
复原职分

和间断职分相对,主旨代码:

  1. Scheduler scheduler = schedulerFactoryBean.getScheduler();
  2. JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
  3. scheduler.resumeJob(jobKey);
删去职分

剔除职务后,所对应的trigger也将被剔除

  1. Scheduler scheduler = schedulerFactoryBean.getScheduler();
  2. JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
  3. scheduler.deleteJob(jobKey);
那时运维职责

此间的及时运转,只会运作三次,方便测验时用。quartz是通过有的时候改动二个trigger的不二等秘书诀来完毕的,那个trigger将要这一次任务运营实现未来自动删除。trigger的key是随机生成的,比如:DEFAULT.MT_4k9fd10jcn9mg。在自己的测量试验中,后边的DEFAULT.MT是固定的,后面部分才随机生成。

  1. Scheduler scheduler = schedulerFactoryBean.getScheduler();
  2. JobKey jobKey = JobKey.jobKey(scheduleJob.getJobName(), scheduleJob.getJobGroup());
  3. scheduler.triggerJob(jobKey);
履新职分的小时表达式

履新之后,任务将立刻按新的时日表明式实践:

图片 22

Scheduler scheduler = schedulerFactoryBean.getScheduler();

TriggerKey triggerKey = TriggerKey.triggerKey(scheduleJob.getJobName(),
    scheduleJob.getJobGroup());

//获取trigger,即在spring配置文件中定义的 bean id="myTrigger"
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob
    .getCronExpression());

//按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey)
    .withSchedule(scheduleBuilder).build();

//按新的trigger重新设置job执行
scheduler.rescheduleJob(triggerKey, trigger);

图片 23

到这里,大家的spring3 整合quartz 2的定时职务作用终于是终止了,对常用的一对效应举办了达成,相信能够满足通常品种的急需了

本文由金沙澳门官网送注册58发布于编程应用,转载请注明出处:2实现定时任务,springboot整合Quartz实现动态配置定

关键词:

上一篇:系统的法子就必定是最棒的
下一篇:没有了