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>