springmvc使用定时任务quartz
项目需求
1.定时执行任务
2.可动态新增,修改,删除定时任务
jar包
<!-- 定时器quartz使用jar包-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
</dependency>
java实现类
1.job 工厂实例方法重写QuartzJobFactory
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.scheduling.quartz.AdaptableJobFactory;
import org.springframework.stereotype.Component;
@Component
public class QuartzJobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
/**
* 重写job实例化方法
* 将实例化对象手动添加到spring IOC容器并完成对象注入
*
* @param bundle
* @return
* @throws Exception
*/
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//父类方法调用
Object jobInstance = super.createJobInstance(bundle);
//spring注入
autowireCapableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
2.定时器中心处理器QuartzManager
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
public class QuartzManager {
private static SchedulerFactory schedulerFactory = new StdSchedulerFactory();
@Autowired
private QuartzJobFactory quartzJobFactory;
/**
* 新增一个定时任务
*
* @param jobName 任务名
* @param jobGroupName 任务组名
* @param triggerName 触发器名
* @param triggerGroupName 触发器组名
* @param jobClass 任务类
* @param cron 时间设置
* @param jobDataMap 任务参数
*/
@SuppressWarnings("unchecked")
public void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron, JobDataMap jobDataMap) {
//调度类获取
Scheduler scheduler = null;
try {
scheduler = schedulerFactory.getScheduler();
//设置job实例工厂【实现手动spring注入】
scheduler.setJobFactory(quartzJobFactory);
//job实例与设置其所属身份
JobDetail jobDetail = JobBuilder.newJob(jobClass).usingJobData(jobDataMap).withIdentity(jobName, jobGroupName).build();
//触发器
TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
//触发器身份所属设置
triggerBuilder.withIdentity(triggerName, triggerGroupName);
triggerBuilder.startNow();
// 触发器时间设定
triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
// 创建Trigger对象
CronTrigger trigger = (CronTrigger) triggerBuilder.build();
// 调度容器设置JobDetail和Trigger
scheduler.scheduleJob(jobDetail, trigger);
//启动
if (!scheduler.isShutdown()) {
scheduler.start();
}
System.out.println("新增定时任务=【" + jobName + "=" + jobGroupName + "】" + cron);
} catch (SchedulerException e) {
e.printStackTrace();
System.out.println("新增定时任务【" + jobName + jobGroupName + "】失败");
}
}
/**
* 修改定时任务触发时间
*
* @param jobName 任务名
* @param jobGroupName 任务组名
* @param triggerName 触发器名
* @param triggerGroupName 触发器组名
* @param cron 时间
*/
public void modifyJobTime(String jobName, String jobGroupName, String triggerName, String triggerGroupName, String cron) {
Scheduler scheduler = null;
try {
scheduler = schedulerFactory.getScheduler();
//获取触发器所属key
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
//如果触发器不存在,不往下执行
if (trigger == null) {
return;
}
//获取当前执行时间
String cronExpression = trigger.getCronExpression();
//新触发器时间与旧触发器时间不一致时修改触发器时间
if (!cronExpression.equals(cron)) {
/** 方式一 :调用 rescheduleJob 开始 */
// 触发器
TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
// 触发器名,触发器组
triggerBuilder.withIdentity(triggerName, triggerGroupName);
triggerBuilder.startNow();
// 触发器时间设定
triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));
// 创建Trigger对象
trigger = (CronTrigger) triggerBuilder.build();
// 方式一 :修改一个任务的触发时间(覆盖旧数据)
scheduler.rescheduleJob(triggerKey, trigger);
/** 方式一 :调用 rescheduleJob 结束 */
/** 方式二:先删除,然后在创建一个新的Job */
//JobDetail jobDetail = sched.getJobDetail(JobKey.jobKey(jobName, jobGroupName));
//Class<? extends Job> jobClass = jobDetail.getJobClass();
//removeJob(jobName, jobGroupName, triggerName, triggerGroupName);
//addJob(jobName, jobGroupName, triggerName, triggerGroupName, jobClass, cron);
/** 方式二 :先删除,然后在创建一个新的Job */
System.out.println("更新定时任务=【" + jobName + "=" + jobGroupName + "】" + cron);
}
} catch (SchedulerException e) {
e.printStackTrace();
System.out.println("更新定时任务【" + jobName + jobGroupName + "】失败");
}
}
/**
* 移除一个定时任务
*
* @param jobName 任务名
* @param jobGroupName 任务组名
* @param triggerName 触发器名
* @param triggerGroupName 触发器组名
*/
public void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) {
Scheduler scheduler = null;
try {
scheduler = schedulerFactory.getScheduler();
TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
scheduler.pauseTrigger(triggerKey);// 停止触发器
scheduler.unscheduleJob(triggerKey);// 移除触发器
scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));// 删除任务
System.out.println("移除定时任务=【" + jobName + "=" + jobGroupName + "】");
} catch (SchedulerException e) {
e.printStackTrace();
System.out.println("移除定时任务【" + jobName + jobGroupName + "】失败");
}
}
/**
* 启动所有定时任务
*/
public void startJobs() {
try {
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 关闭所有定时任务
*/
public void shutdownJobs() {
try {
Scheduler scheduler = schedulerFactory.getScheduler();
if (!scheduler.isShutdown()) {
scheduler.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
3自定义定时任务类(具体执行内容)
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
@Component
public class ReminderJob implements Job {
@Autowired
private EduScheduleService eduScheduleService;
/**
* 任务执行内容
*
* @param jobExecutionContext
*/
@Override
public void execute(JobExecutionContext jobExecutionContext) {
//参数获取
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
//逻辑代码编写
}
}
4.使用样例
import org.quartz.JobDataMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
/**
* @author ljc
* @Title:
* @Package
* @Description: 启动监听类【项目启动完成或刷新完成时触发】
* @date 2021/3/116:16
*/
@Service
public class StartupListener implements ApplicationListener<ContextRefreshedEvent> {
@Autowired
private QuartzManager quartzManager;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
String cron = "0 0 12 * * ?";
JobDataMap dataMap = new JobDataMap();
dataMap.put("testName", "a");
quartzManager.addJob("a", "a1", "b", "b1", ReminderJob.class, cron, dataMap);
}
}
cron表达式详解link
5.项目停止时会发现报错
Web应用程序[edu_api]似乎启动了一个名为[DefaultQuartzScheduler_Worker-1]的线程,但未能停止它。这很可能会造成内存泄漏
这是因为项目停止时,而quartz未停止,可以监听事件来停止quartz的全部定时任务
import org.apache.commons.lang3.StringUtils;
import org.quartz.JobDataMap;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.HashMap;
import java.util.List;
public class MyContextListener implements ServletContextListener {
private QuartzManager quartzManager;
private WebApplicationContext webApplicationContext;
public void setQuartzManager(QuartzManager quartzManager) {
this.quartzManager = quartzManager;
}
/**
* 项目初始化
*
* @param sce
*/
@Override
public void contextInitialized(ServletContextEvent sce) {
}
/**
* 项目销毁
*
* @param sce
*/
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("销毁=======》>");
if (webApplicationContext == null)
webApplicationContext = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
quartzManager = (QuartzManager) webApplicationContext.getBean("quartzManager");
quartzManager.shutdownJobs();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
web.xml 文件添加监听
<listener>
<!-- 注意修改类路径 -->
<listener-class>*.*.*MyContextListener</listener-class>
</listener>