Quartz是啥?
quartz是一个由java编写的任务调度库,由OpenSymphony组织开源出来。绝大多数公司都会用到任务调度这个功能, 比如公司需要定期执行任务调度生成报表, 或者比如博客什么的定时更新之类的,都可以靠Quartz来完成。
任务调度:现在有N个任务(程序),要求在指定时间执行,比如每周二3点执行任务A、每天相隔5s执行任务B等等,这种多任务拥有多种执行策略就是任务调度。
而quartz的核心作用,是使任务调度变得丰富、高效、安全,开发者只需要调几个quartz接口并做简单配置,即可实现上述需求。
quartz号称能够同时对上万个任务进行调度,拥有丰富的功能特性,包括任务调度、任务持久化、可集群化、插件等。
关于Quartz的结构,重点内容都集结在了下面的图中
概念解析
1、Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中;
2、JobDetail:Quartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。
3、Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等等;
4、Calendar:org.quartz.Calendar和java.util.Calendar不同,它是一些日历特定时间点的集合(可以简单地将org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一个日历时间点)。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。假设,我们安排每周星期一早上10:00执行任务,但是如果碰到法定的节日,任务则不执行,这时就需要在Trigger触发机制的基础上使用Calendar进行定点排除。针对不同时间段类型,Quartz在org.quartz.impl.calendar包下提供了若干个Calendar的实现类,如AnnualCalendar、MonthlyCalendar、WeeklyCalendar分别针对每年、每月和每周进行定义;
5、Scheduler: 代表一个Quartz的独立运行容器, Trigger和JobDetail可以注册到Scheduler中, 两者在Scheduler中拥有各自的组及名称, 组及名称是Scheduler查找定位容器中某一对象的依据, Trigger的组及名称必须唯一, JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法, 允许外部通过组及名称访问和控制容器中Trigger和JobDetail。
Scheduler可以将Trigger绑定到某一JobDetail中, 这样当Trigger触发时, 对应的Job就被执行。一个Job可以对应多个Trigger, 但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据,SchedulerContext为保存和获取数据提供了多个put()和getXxx()的方法。 可以通过Scheduler.getContext()获取对应的SchedulerContext实例;
6、ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。
代码示范
1. 简单使用Quartz(单机版)
三大要素(JobDetail、Trigger、Scheduler)我们使用以下工厂进行实现:
- MethodInvokingJobDetailFactoryBean
属性:name、group、targetObject(spring bean)、targetMethod
特点:不需要任务类实现任何接口 - CronTriggerFactoryBean
属性:name、group、jobDetail、cronExpression - SchedulerFactoryBean
- Maven依赖
<dependencies>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
- 定义任务类
package com.golden3young.job;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
System.out.println(sdf.format(new Date()) + " Hello");
}
}
自定义的任务类需要实现org.quartz包中的Job接口,然后覆盖execute()方法,在其中给出具体的任务实现代码。一会这个Job要装配到JobDetail中,作为核心部分。
- 简单测试 开启任务调度
1.案例一:每隔3秒打印一次Hello
package com.golden3young.simple;
import com.golden3young.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
/**
* 每隔3秒打印一次Hello
*/
public class HelloJobMain {
public static void main(String[] args) throws SchedulerException {
//创建 JobDetail
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("hello", "hello")
.build();
//创建 Trigger
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("hello", "hello")
.startNow() //立即执行
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(3) //每隔3秒执行
.repeatForever()) //永远执行
.build(); //调度规则
//创建 Scheduler,并开启调度
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
}
}
2.案例二:两秒后执行任务一次,每三秒打印一次Hello(重复10次)
package com.golden3young.simple;
//静态导入 - 可以将类中所有的方法导入
import static org.quartz.JobBuilder.*;
import static org.quartz.TriggerBuilder.*;
import com.golden3young.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 两秒后执行任务一次,每三秒打印一次Hello(重复10次)
*/
public class SecondMain {
public static void main(String[] args) throws SchedulerException {
//创建JobDetail
JobKey jobKey = new JobKey("hello","hello");
JobDetail jobDetail = newJob(HelloJob.class)
.withIdentity(jobKey)
.build();
//两秒后的时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
System.out.println("当前时间:" + sdf.format(date));
Date twoSecondsAfter = new Date(date.getTime() + 2000L);
SimpleTrigger trigger = newTrigger()
.withIdentity(new TriggerKey("hello", "hello"))
.startAt(twoSecondsAfter) //传入一个Date类型的时间。用来指定一个执行时间
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(3)//每隔3秒一执行
.withRepeatCount(10) //重复10次
).build();
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(jobDetail,trigger);
scheduler.start();
}
}
3.案例三:两秒后执行任务一次,每三秒打印一次Hello,15秒结束任务
package com.golden3young.simple;
import com.golden3young.job.HelloJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
import static org.quartz.JobBuilder.*;
import static org.quartz.TriggerBuilder.*;
public class ThirdMain {
public static void main(String[] args) throws SchedulerException {
//创建JobDetail
JobDetail jobDetail = newJob(HelloJob.class)
.withIdentity(new JobKey("hello", "hello"))
.build();
//两秒后的时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date();
System.out.println("当前时间:" + date);
Date twoSecondsAfter = new Date(date.getTime() + 2000L); //2秒后的时间
Date endDate = new Date(date.getTime() + 15000L); //结束时间,也就是15秒后的时间
//创建Trigger
SimpleTrigger trigger = newTrigger()
.withIdentity(new TriggerKey("hello", "hello"))
.startAt(twoSecondsAfter) //2秒后开始
.endAt(endDate) //15秒后停止
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(3)
.repeatForever()
)
.build();
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(jobDetail,trigger);
scheduler.start();
}
}
2. Spring单独整合Quartz(注解版)
- Maven 依赖
<dependencies>
<!--quartz-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<!--spring-context-support-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
</dependencies>
- 实现任务类
实现的任务比较简单,就是单纯的打印一段文字。
package com.golden3young.job;
public class OrderJob {
public void execute(){
System.out.println("删除订单");
}
}
- 配置类的书写
package com.golden3young.config;
import com.golden3young.job.OrderJob;
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;
import java.util.ArrayList;
import java.util.List;
@Configuration //<beans>
public class QuartzConfig {
// 注册 任务类
@Bean
public OrderJob orderJob(){
return new OrderJob();
}
@Bean
public MethodInvokingJobDetailFactoryBean jobDetailFactoryBean(){
MethodInvokingJobDetailFactoryBean factory = new MethodInvokingJobDetailFactoryBean();
factory.setName("orderJob");
factory.setGroup("orderGroup");
factory.setTargetObject(this.orderJob());
factory.setTargetMethod("execute");
return factory;
}
@Bean
public CronTriggerFactoryBean cronTriggerFactoryBean(){
CronTriggerFactoryBean factoryBean = new CronTriggerFactoryBean();
factoryBean.setName("orderTrigger");
factoryBean.setGroup("orderTrigger");
factoryBean.setJobDetail(this.jobDetailFactoryBean().getObject());
factoryBean.setCronExpression("*/5 * * * * ?");//每5秒执行一次
return factoryBean;
}
@Bean
public SchedulerFactoryBean scheduler(){
SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
//自动转成List
factoryBean.setTriggers(this.cronTriggerFactoryBean().getObject());
return factoryBean;
}
}
- 测试代码
package com.golden3young;
import com.golden3young.config.QuartzConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(QuartzConfig.class);
}
}
3. SpringMVC整合Quartz(xml文件版)
- Maven 依赖
<!-- spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
<!-- spring-context-support spring整合quartz的bean都在这个库中 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>
<!-- quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<!-- thymeleaf -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>
<!-- thymeleaf-spring5 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>
- 配置文件 Web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<!--顺序:context-praram filter filter-mapping listener servlet servlet-mapping-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-root.xml</param-value>
</context-param>
<!--POST请求编码过滤器-->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/※</url-pattern>
</filter-mapping>
<!--listener 加载root容器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--前端控制器-->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 配置Spring-root.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<!-- 注解扫描 -->
<context:component-scan base-package="com.golden">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service" />
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" />
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.RestController" />
<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
<!--DataSource-->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/et1909" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
<!--事务管理器:DataSourceTransactionManager-->
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
<!--JobDetailFactoryBean-->
<bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="name" value="emailJob"/>
<property name="group" value="emailJob"/>
<property name="jobClass" value="com.golden.job.EmailJob"/>
<property name="durability" value="true"/>
</bean>
<!--CronTriggerFactoryBean-->
<bean id="trigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<property name="name" value="emailTrigger"/>
<property name="group" value="emailTrigger"/>
<property name="jobDetail" ref="jobDetail"/>
<property name="cronExpression" value="*/5 * * * * ?"/>
</bean>
<!--SchedulerFactoryBean-->
<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="transactionManager" ref="tx"/>
<property name="configLocation" value="classpath:quartz.properties" />
<property name="triggers">
<list>
<ref bean="trigger" />
</list>
</property>
<!--将Spring的applicationContext容器添加到任务调度器的容器SchedulerContext中,使用key获取spring容器-->
<property name="applicationContextSchedulerContextKey" value="spring" />
<property name="jobFactory" ref="mvcJobFactory"/>
</bean>
<bean id="mvcJobFactory" class="com.golden.factory.MvcJobFactory"/>
</beans>
- 配置Spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 配置扫描器 -->
<context:component-scan base-package="com.golden">
<!-- include-filter -->
<!-- 扫描注解ControllerRestController ControllerAdvice -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.RestController"/>
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
<!-- exclude-filter -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
<!-- 开始spring mvc配置 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 静态文件相关配置 -->
<!-- 将静态文件交给servlet容器处理 -->
<mvc:default-servlet-handler/>
<!-- 整合Thymeleaf -->
<!-- -->
<bean id="templateResolver" class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver" >
<!-- prefix suffix templateMode characterEncoding -->
<property name="prefix" value="/templates/"></property>
<property name="suffix" value=".html"></property>
<property name="characterEncoding" value="UTF-8"></property>
<property name="templateMode" value="HTML"></property>
<!-- 是否进行页面缓存(默认是true),开发阶段设置为false, -->
<property name="cacheable" value="false"></property>
</bean>
<!-- SpringTemplateEngine -->
<bean id="templateEngine" class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver"></property>
</bean>
<!-- ThymeleafViewResolver -->
<bean class="org.thymeleaf.spring5.view.ThymeleafViewResolver" >
<property name="templateEngine" ref="templateEngine"></property>
<property name="characterEncoding" value="UTF-8"></property>
</bean>
</beans>
- quartz.properties配置文件
#可以设置为任意,用在 JDBC JobStore中来唯一标识实例,但是所有集群节点中必须相同。
org.quartz.scheduler.instanceName = SpringClusterTest
# 属性为 AUTO即可,基于主机名和时间戳来产生实例 ID
org.quartz.scheduler.instanceId = AUTO
## 线程
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount = 10
org.quartz.threadPool.threadPriority = 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
## 存储
org.quartz.jobStore.misfireThreshold = 60000
#JobStoreTX,将任务持久化到数据库中。因为集群中节点依赖于数据库来传播 Scheduler实例的状态,你只能在使用 JDBC JobStore 时应用 Quartz 集群。这意味着你必须使用 JobStoreTX 或是 JobStoreCMT 作为 Job 存储,不能在集群中使用 RAMJobStore。
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass=org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.tablePrefix = QRTZ_
org.quartz.jobStore.maxMisfiresToHandleAtATime=10
#值 true,表示 Scheduler实例要参与到一个集群当中。这一属性会贯穿于调度框架的始终,用于修改集群环境中操作的默认行为。
org.quartz.jobStore.isClustered = true
#定义了Scheduler 实例检入到数据库中的频率(单位:毫秒)。Scheduler 检查是否其他的实例到了它们应当检入的时候未检入;这能指出一个失败的 Scheduler 实例,且当前 Scheduler 会以此来接管任何执行失败并可恢复的 Job。通过检入操作,Scheduler 也会更新自身的状态记录。clusterChedkinInterval 越小,Scheduler 节点检查失败的 Scheduler 实例就越频繁。默认值是 15000 (即15 秒)。
org.quartz.jobStore.clusterCheckinInterval = 20000
- 创建服务层
package com.golden3young.service;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
//所有的业务逻辑都在这里处理
public void send(){
System.out.println("邮件发送服务执行中....");
}
}
- 创建Job任务类
package com.golden3young.job;
import com.golden3young.service.EmailService;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class MvcJob extends QuartzJobBean {
@Autowired
EmailService emailService;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("mvc Job 执行邮件下发开始...");
emailService.send();
System.out.println("mvc Job 执行邮件下发...结束");
}
}
- 创建任务工厂类
package com.golden3young.factory;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
public class MvcJobFactory extends SpringBeanJobFactory{
//可以将第三方框架创建的实例 加载到spring容器中
@Autowired
AutowireCapableBeanFactory springIoc;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 调用父类的方法创建任务实例
Object job = super.createJobInstance(bundle);
System.out.println(job.getClass().getName());
//加载到spring容器中
springIoc.autowireBean(job);
return job;
}
}
- 创建控制层Controller
包括调度任务的添加、停止、删除
package com.golden3young.controller;
import com.golden3young.job.MvcJob;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
@Controller
@RequestMapping("/job")
public class MvcController {
@RequestMapping("/index")
public String index(){
return "index";
}
@Autowired
Scheduler scheduler; //由于在xml中已经配置了SchedulerFactoryBean,这里可以直接使用
@RequestMapping(value = "/add", produces = "text/plain;charset=utf-8")
@ResponseBody
public String addJob(
@RequestParam String jobName, @RequestParam String jobGroup,
@RequestParam String triggerName, @RequestParam String triggerGroup,
@RequestParam String cron
) throws SchedulerException {
JobKey jobKey = new JobKey(jobName, jobGroup);
TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroup);
if (scheduler.checkExists(jobKey) || scheduler.checkExists(triggerKey)){
return "任务已存在";
}
// JobDetail
JobDetail jobDetail = JobBuilder.newJob(MvcJob.class).withIdentity(jobKey).build();
//Trigger
CronTrigger trigger = TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
//直接调用scheduler 调度任务
scheduler.scheduleJob(jobDetail,trigger);
return "SUCCESS";
}
@PostMapping(value = "/pause", produces = "text/plain;charset=UTF-8")
@ResponseBody
public String pause(@RequestParam String jobName, @RequestParam String jobGroup) throws SchedulerException {
JobKey jobKey = new JobKey(jobName, jobGroup);
if(!scheduler.checkExists(jobKey)){
return "任务不存在";
}
scheduler.pauseJob(jobKey);
return "SUCCESS";
}
@PostMapping(value="/resume", produces = "text/plain;charset=UTF-8")
@ResponseBody
public String resume(@RequestParam String jobName, @RequestParam String jobGroup) throws SchedulerException {
JobKey jobKey = new JobKey(jobName, jobGroup);
if(!scheduler.checkExists(jobKey)){
return "任务不存在";
}
scheduler.resumeJob(jobKey);
return "SUCCESS";
}
}
常见问题整理:
- Quartz 中的两种存储方式?
RAMJobStore, JobStoreSupport,其中 RAMJobStore 是将 trigger 和 job 存储在内存中,而 JobStoreSupport 是基于 jdbc 将 trigger 和 job 存储到数据库中。RAMJobStore 的存取速度非常快,但是由于其在系统被停止后所有的数据都会丢失,所以在通常应用中,都是使用 JobStoreSupport。
- Quartz实现动态配置定时任务?
在我们日常的开发中,很多时候,定时任务都不是写死的,而是写到数据库中,在数据库中取出需要的数据,从而实现定时任务的动态配置
- Quartz中的Job和StatefulJob的区别?
Job:普通的任务,或者说无状态的任务,在JobDetail执行之后,不会记录状态。
StatefulJob:继承自Job,由于在定义时添加了@PersistJobDataAfterExecution注释。 对于同一个 trigger 来说,有状态的 job 不能被并行执行,只有上一次触发的任务被执行完之后,才能触发下一次执行。
-
除了Quartz以外,其他的定时任务的实现?
①Java自带的类Timer和TimerTask,Timer类是用来记时、定时的类,它接受一个TimerTask做参数;TimerTask里面可以写我们的任务。
②Spring3.0以后自带的task,可以将它看成一个轻量级的Quartz。 -
cron表达式怎么写?
Cron Expressions是七个子表达式组成的字符串,用于描述时间的各个细节。这些子表达式 用空格分隔,并表示:
[秒] [分] [小时] [天] [月] [周] [年]
Seconds Minutes Hours Day-of-Month Month Day-of-Week Year (optional field)
时间字段 是否必填 允许值 特殊字符
秒 是 0-59 ,-*/
分 是 0-59 ,-*/
时 是 0-23 ,-*/
天 是 1-31 ,-*/?LW
月 是 1-12或者JAN-DEC ,-*/
周 是 1-7或者SUN-SAT ,-*/?L#周
天用1表示,依次类推
年 否 空或1970-2099 ,-*/ ,
:表示或的关系
-:范围的关系【比如1-21】
*:每秒、每分、每小时等
/:每天哪个时间执行
L:表示为每月的最后一天,或每个月的最后星期几如“6L”表示“每月的最后一个星期五”
W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。 例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五; 如果15日是星期日,则15W匹配16号星期一; 如果15号是星期二,那15W结果就是15号星期二。 但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星 期一,而非上个月最后的那天。 W字符串只能指定单一日期,而不能指定日期范围; #:是用来指定每月第n个工作日,例在每周(day-of-week)这个字段中内容为"6#3" or "FRI#3" 则表示“每月第三个星期五”
?只能用到天和周上 - 如果天用了*,那么周只能用?
- 如果天用了?,那么周只能用*
- 如果日期(天)确定了,那么周只能用?
- 如果周确定了,那么日期(天)只能用?
- 如何禁止quart的并发操作?
2种方式:
spring中将job的concurrent属性设置为false。默认是true 如下:
<bean id="scheduleJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="scheduleBusinessObject"/>
<property name="targetMethod" value="doIt"/>
<property name="concurrent" value="false"/>
</bean>
job类上加上注解@DisallowConcurrentExecution。
@DisallowConcurrentExecution
public class HelloQuartz implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) {
JobDetail detail = jobExecutionContext.getJobDetail();
String name = detail.getJobDataMap().getString("name");
System.out.println("my job name is " + name + " at " + new Date());
}
}
}
注意:@DisallowConcurrentExecution是对JobDetail实例生效,如果一个job类被不同的jobdetail引用,这样是可以并发执行。