任务调度 只需做三件事,再什么时候在哪里做什么事;
一.工作原理:
1、scheduler是一个计划调度器容器(总部),容器里面可以盛放众多的JobDetail和trigger,当容器启动后,里面的每个JobDetail都会根据trigger按部就班自动去执行。
2、JobDetail是一个可执行的工作,它本身可能是有状态的。
3、Trigger代表一个调度参数的配置,什么时候去调。
4、当JobDetail和Trigger在scheduler容器上注册后,形成了装配好的作业(JobDetail和Trigger所组成的一对儿),就可以伴随容器启动而调度执行了。
5、scheduler是个容器,容器中有一个线程池,用来并行调度执行每个作业,这样可以提高容器效率。
二.集成
1.导包到pom
<!-- quartz任务调度 -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.3</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>2.2.1</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.0</version>
</dependency>
2.创建数据库表
解压缩quartz官方发行包,找到quartz-2.2.3\docs\dbTables 下的 tables_mysql_innodb.sql在navicat中执行即可创建quartz持久化相关的表。
3.将quartz的工厂对象交给spring管理
spring-quartz.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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 配置连接池 -->
<bean id="quartzDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="username" value="root"></property>
<property name="password" value="root"></property>
<property name="url" value="jdbc:mysql:///quartz?characterEncoding=utf-8"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<!-- quartz持久化存储 -->
<bean name="quartzScheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource">
<ref bean="quartzDataSource" />
</property>
<property name="applicationContextSchedulerContextKey" value="applicationContext" />
<!-- 也可以在quartz.properties中配置 -->
<property name="quartzProperties">
<props>
<prop key="org.quartz.scheduler.instanceName">AIGOUscheduler</prop>
<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
<!-- 线程池配置 -->
<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
<prop key="org.quartz.threadPool.threadCount">20</prop>
<prop key="org.quartz.threadPool.threadPriority">5</prop>
<prop key="org.quartz.jobStore.misfireThreshold">120000</prop>
<!-- JobStore 配置 -->
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<!-- 集群配置 -->
<prop key="org.quartz.jobStore.isClustered">true</prop>
<prop key="org.quartz.jobStore.clusterCheckinInterval">15000</prop>
<prop key="org.quartz.jobStore.maxMisfiresToHandleAtATime">1</prop>
<!-- 数据表设置 -->
<prop key="org.quartz.jobStore.tablePrefix">qrtz_</prop>
<prop key="org.quartz.jobStore.dataSource">qzDS</prop>
</props>
</property>
<!-- 可选,QuartzScheduler启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 -->
<property name="overwriteExistingJobs" value="true" />
<!-- 设置自动启动 -->
<property name="autoStartup" value="true" />
</bean>
</beans>
整合dubbo
spring-dubbo-quartz.xml
<?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:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- dubbo服务发布 -->
<dubbo:application name="www-service-common" />
<dubbo:protocol name="dubbo" port="20885"></dubbo:protocol>
<import resource="classpath*:spring-dubbo-common-base.xml"/>
</beans>
spring-service-quartz.xml
spring的配置中
<?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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath*:spring-dubbo-quartz.xml"/>
<import resource="classpath*:spring-quartz.xml"/>
</beans>
创建QuartzUtils工具类,用于动态管理任务
/**
* Quartz调度管理器
*
*/
public class QuartzUtils {
private static String JOB_GROUP_NAME = "JOB_GROUP_SYSTEM";
private static String TRIGGER_GROUP_NAME = "TRIGGER_GROUP_SYSTEM";
/**
* @Description: 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
*
* @param sched
* 调度器
*
* @param jobName
* 任务名
* @param cls
* 任务
* @param params
* 任务参数
* @param time
* 时间设置,参考quartz说明文档
*
* @Title: QuartzManager.java
*/
public static void addJob(Scheduler sched, String jobName, @SuppressWarnings("rawtypes") Class cls, Object params,
String time) {
try {
JobKey jobKey = new JobKey(jobName, JOB_GROUP_NAME);// 任务名,任务组,任务执行类
@SuppressWarnings("unchecked")
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("params", params);
JobDetail jobDetail = newJob(cls).withIdentity(jobKey).setJobData(jobDataMap).build();
TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);// 触发器
Trigger trigger = newTrigger().withIdentity(triggerKey).withSchedule(cronSchedule(time)).build();// 触发器时间设定
sched.scheduleJob(jobDetail, trigger);
if (!sched.isShutdown()) {
sched.start();// 启动
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 添加一个定时任务
*
* @param sched
* 调度器
*
* @param jobName
* 任务名
* @param jobGroupName
* 任务组名
* @param triggerName
* 触发器名
* @param triggerGroupName
* 触发器组名
* @param jobClass
* 任务
* @param params
* 任务参数
* @param time
* 时间设置,参考quartz说明文档
*
* @Title: QuartzManager.java
*/
public static void addJob(Scheduler sched, String jobName, String jobGroupName, String triggerName,
String triggerGroupName, @SuppressWarnings("rawtypes") Class jobClass, Object params, String time) {
try {
JobKey jobKey = new JobKey(jobName, jobGroupName);
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("params", params);
@SuppressWarnings("unchecked")
JobDetail jobDetail = newJob(jobClass).withIdentity(jobKey).setJobData(jobDataMap).build();
// 触发器
TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);
Trigger trigger = newTrigger().withIdentity(triggerKey).withSchedule(cronSchedule(time)).build();
sched.scheduleJob(jobDetail, trigger);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 修改一个任务的触发时间(使用默认的任务组名,触发器名,触发器组名)
*
* @param sched
* 调度器
* @param jobName
* @param time
*
* @Title: QuartzManager.java
*/
@SuppressWarnings("rawtypes")
public static void modifyJobTime(Scheduler sched, String jobName, String time) {
try {
TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(time)) {
JobKey jobKey = new JobKey(jobName, JOB_GROUP_NAME);
JobDetail jobDetail = sched.getJobDetail(jobKey);
Class objJobClass = jobDetail.getJobClass();
Object params = jobDetail.getJobDataMap().get("params");
removeJob(sched, jobName);
System.out.println("修改任务:" + jobName);
addJob(sched, jobName, objJobClass, params,time);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 修改一个任务的触发时间
*
* @param sched
* 调度器 *
* @param sched
* 调度器
* @param triggerName
* @param triggerGroupName
* @param time
*
* @Title: QuartzManager.java
*/
public static void modifyJobTime(Scheduler sched, String triggerName, String triggerGroupName, String time) {
try {
TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);
CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerKey);
if (trigger == null) {
return;
}
String oldTime = trigger.getCronExpression();
if (!oldTime.equalsIgnoreCase(time)) {
// 修改时间
trigger.getTriggerBuilder().withSchedule(cronSchedule(time));
// 重启触发器
sched.resumeTrigger(triggerKey);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 移除一个任务(使用默认的任务组名,触发器名,触发器组名)
*
* @param sched
* 调度器
* @param jobName
*
* @Title: QuartzManager.java
*/
public static void removeJob(Scheduler sched, String jobName) {
try {
TriggerKey triggerKey = new TriggerKey(jobName, TRIGGER_GROUP_NAME);
sched.pauseTrigger(triggerKey);// 停止触发器
sched.unscheduleJob(triggerKey);// 移除触发器
JobKey jobKey = new JobKey(jobName, JOB_GROUP_NAME);
sched.deleteJob(jobKey);// 删除任务
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description: 移除一个任务
*
* @param sched
* 调度器
* @param jobName
* @param jobGroupName
* @param triggerName
* @param triggerGroupName
*
* @Title: QuartzManager.java
*/
public static void removeJob(Scheduler sched, String jobName, String jobGroupName, String triggerName,
String triggerGroupName) {
try {
TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);
sched.pauseTrigger(triggerKey);// 停止触发器
sched.unscheduleJob(triggerKey);// 移除触发器
JobKey jobKey = new JobKey(jobName, jobGroupName);
sched.deleteJob(jobKey);// 删除任务
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description:启动所有定时任务
*
* @param sched 调度器
*
* @Title: QuartzManager.java
*/
public static void startJobs(Scheduler sched) {
try {
sched.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* @Description:关闭所有定时任务
*
* @param sched
* 调度器
*
*/
public static void shutdownJobs(Scheduler sched) {
try {
if (!sched.isShutdown()) {
sched.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
创建任务执行类
public class MainJob implements Job{
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
Map<String, Object> params = (Map<String, Object>) jobDataMap.get("params");
System.out.println("Hello World...."+params);
}
}
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath*:spring-service-quartz.xml")
public class JobTest {
@Autowired
private SchedulerFactoryBean schedulerFactory;
@Test
public void testName() throws Exception {
//schedulerFactory通过spring注入
Scheduler sche = schedulerFactory.getScheduler();
String job_name = "JOB_NAME";
String clazz = "cn.itsource.aigou.job.MainJob";
String cron = "/1 * * * * ? ";
Map<String, Object> params = new HashMap<>();
params.put("firstJob", 1);
params.put("firstName", "myFirstName");
QuartzUtils.addJob(sche, job_name, Class.forName(clazz), params, cron);
// System.in.read();
Thread.sleep(10000);
System.out.println("【移除定时】开始...");
QuartzUtils.removeJob(sche, job_name);
System.out.println("【移除定时】成功");
}
解决quartz的job无法注入spring对象 全网仅此一份
/**
* 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候取出ApplicaitonContext.
*
*/
@Service
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return applicationContext;
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static <T> T getBean(Class<T> requiredType) {
assertContextInjected();
return applicationContext.getBean(requiredType);
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
public static void clearHolder() {
applicationContext = null;
}
/**
* 实现ApplicationContextAware接口, 注入Context到静态变量中.
*/
public void setApplicationContext(ApplicationContext appContext) {
applicationContext = appContext;
}
/**
* 实现DisposableBean接口, 在Context关闭时清理静态变量.
*/
public void destroy() throws Exception {
SpringContextHolder.clearHolder();
}
/**
* 检查ApplicationContext不为空.
*/
private static void assertContextInjected() {
Validate.validState(applicationContext != null, "applicaitonContext属性未注入, 请在applicationContext.xml中定义SpringContextHolder.");
}
}
优点是:解耦性强,不必改别人的代码
还有一种方法可解决,就是用static方法解决,但是这样任务多了,每个类都要写就麻烦了
希望我的文章能帮到你