Quartz与SpringMVC的整合

前言:

     要将quartz的任务持久化,能动态添加任务、修改任务、暂停任务、动态修改触发时间.

    调度器:Scheduler;工作:JobDetail ;触发器:Trigger


一、在pom文件中引入jar包

这里只定义quartz需要的jar包,其它和spring相关的省略

<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>   

二、配置相应的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"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
	<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <!--自己配置的数据源 -->
        <property name="dataSource" ref="dataSource" />
        <!--直接在xml文件中配置属性,当然也可以引入相对应的.properties文件-->
        <property name="quartzProperties">
            <props>
                <!--属性可为任何值,(最好别用DefaultQuartzScheduler),用在 JDBC JobStore 中来唯一标识实例,但是所有集群节点中必须相同-->
                <prop key="org.quartz.scheduler.instanceName">fwoneScheduler</prop>
                <!--属性为 AUTO即可,基于主机名和时间戳来产生实例 ID-->
                <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>
                <!-- JobStore 配置 -->
                <!-- 属性为 JobStoreTX,将任务持久化到数据中。因为集群中节点依赖于数据库来传播 Scheduler 实例的状态,
                你只能在使用 JDBC JobStore 时应用 Quartz 集群。这意味着你必须使用 JobStoreTX 或是
                JobStoreCMT 作为 Job 存储;你不能在集群中使用 RAMJobStore
                -->
                <prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
                <!-- 集群配置 -->
                <!-- 属性为 true,你就告诉了 Scheduler 实例要它参与到一个集群当中。这一属性会贯穿于调度框架的始终,
                用于修改集群环境中操作的默认行为-->
                <prop key="org.quartz.jobStore.isClustered">true</prop>
<!--属性定义了Scheduler 实例检入到数据库中的频率(单位:毫秒)。Scheduler 检查是否其他的实例到了它们应当检入的时候未检入;
这能指出一个失败的 Scheduler 实例,且当前 Scheduler 会以此来接管任何执行失败并可恢复的 Job。
通过检入操作,Scheduler 也会更新自身的状态记录。clusterChedkinInterval 越小,
 Scheduler 节点检查失败的 Scheduler 实例就越频繁。默认值是 15000 (即15 秒)-->
                <prop key="org.quartz.jobStore.clusterCheckinInterval">150000</prop>
                <prop key="org.quartz.jobStore.maxMisfiresToHandleAtATime">1</prop>
                
                <prop key="org.quartz.jobStore.misfireThreshold">12000</prop>
				<!-- 表前缀 -->
                <prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
            </props>
        </property>

        <property name="schedulerName" value="fwoneScheduler" />

        <!--延时启动 -->
        <property name="startupDelay" value="30" />

        <property name="applicationContextSchedulerContextKey" value="applicationContextKey" />

        <!--可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 -->
        <property name="overwriteExistingJobs" value="true" />

        <!-- 设置自动启动   默认为true -->
        <property name="autoStartup" value="true" />
        <!--可以在web关闭的时候关闭线程-->
        <property name="waitForJobsToCompleteOnShutdown" value="true"/>

    </bean>
       <!--测试用的bean-->
	   <bean id="job1" class="com.fwone.demo.Job1Demo"/>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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.3.xsd">
    <!-- 配置一个线程池,定时任务都交给线程池管理 -->
    <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <!-- 线程池维护线程的最少数量 -->
        <property name="corePoolSize" value="5" />
        <!-- 允许的空闲时间 -->
        <property name="keepAliveSeconds" value="200" />
        <!-- 线程池维护线程的最大数量 -->
        <property name="maxPoolSize" value="10" />
        <!-- 缓存队列 -->
        <property name="queueCapacity" value="20" />
        <!-- 对拒绝task的处理策略 -->
        <property name="rejectedExecutionHandler">
            <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
        </property>
    </bean>
</beans>

三、建立相关的数据表

因为要持久化到数据库中,所以要建立相关的数据表

1.1.qrtz_blob_triggers : 以Blob 类型存储的触发器

扫描二维码关注公众号,回复: 3739755 查看本文章

1.2.qrtz_calendars:存放日历信息, quartz可配置一个日历来指定一个时间范围。

1.3.qrtz_cron_triggers:存放cron类型的触发器。

1.4.qrtz_fired_triggers:存放已触发的触发器。

1.5.qrtz_job_details:存放一个jobDetail信息。

1.6.qrtz_job_listeners:job**监听器**。

1.7.qrtz_locks: 存储程序的悲观锁的信息(假如使用了悲观锁)。

1.8.qrtz_paused_trigger_graps:存放暂停掉的触发器。

1.9.qrtz_scheduler_state:调度器状态。

1.10.qrtz_simple_triggers简单触发器的信息。

1.11.qrtz_trigger_listeners触发器监听器1.12.qrtz_triggers触发器基本信息

DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;


CREATE TABLE QRTZ_JOB_DETAILS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    JOB_CLASS_NAME   VARCHAR(250) NOT NULL,
    IS_DURABLE VARCHAR(1) NOT NULL,
    IS_NONCONCURRENT VARCHAR(1) NOT NULL,
    IS_UPDATE_DATA VARCHAR(1) NOT NULL,
    REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    NEXT_FIRE_TIME BIGINT(13) NULL,
    PREV_FIRE_TIME BIGINT(13) NULL,
    PRIORITY INTEGER NULL,
    TRIGGER_STATE VARCHAR(16) NOT NULL,
    TRIGGER_TYPE VARCHAR(8) NOT NULL,
    START_TIME BIGINT(13) NOT NULL,
    END_TIME BIGINT(13) NULL,
    CALENDAR_NAME VARCHAR(200) NULL,
    MISFIRE_INSTR SMALLINT(2) NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)
        REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_SIMPLE_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    REPEAT_COUNT BIGINT(7) NOT NULL,
    REPEAT_INTERVAL BIGINT(12) NOT NULL,
    TIMES_TRIGGERED BIGINT(10) NOT NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CRON_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    CRON_EXPRESSION VARCHAR(200) NOT NULL,
    TIME_ZONE_ID VARCHAR(80),
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_SIMPROP_TRIGGERS
  (          
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    STR_PROP_1 VARCHAR(512) NULL,
    STR_PROP_2 VARCHAR(512) NULL,
    STR_PROP_3 VARCHAR(512) NULL,
    INT_PROP_1 INT NULL,
    INT_PROP_2 INT NULL,
    LONG_PROP_1 BIGINT NULL,
    LONG_PROP_2 BIGINT NULL,
    DEC_PROP_1 NUMERIC(13,4) NULL,
    DEC_PROP_2 NUMERIC(13,4) NULL,
    BOOL_PROP_1 VARCHAR(1) NULL,
    BOOL_PROP_2 VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) 
    REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_BLOB_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    BLOB_DATA BLOB NULL,
    PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CALENDARS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    CALENDAR_NAME  VARCHAR(200) NOT NULL,
    CALENDAR BLOB NOT NULL,
    PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)
);

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    TRIGGER_GROUP  VARCHAR(200) NOT NULL, 
    PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_FIRED_TRIGGERS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    ENTRY_ID VARCHAR(95) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    FIRED_TIME BIGINT(13) NOT NULL,
    SCHED_TIME BIGINT(13) NOT NULL,
    PRIORITY INTEGER NOT NULL,
    STATE VARCHAR(16) NOT NULL,
    JOB_NAME VARCHAR(200) NULL,
    JOB_GROUP VARCHAR(200) NULL,
    IS_NONCONCURRENT VARCHAR(1) NULL,
    REQUESTS_RECOVERY VARCHAR(1) NULL,
    PRIMARY KEY (SCHED_NAME,ENTRY_ID)
);

CREATE TABLE QRTZ_SCHEDULER_STATE
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
    CHECKIN_INTERVAL BIGINT(13) NOT NULL,
    PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)
);

CREATE TABLE QRTZ_LOCKS
  (
    SCHED_NAME VARCHAR(120) NOT NULL,
    LOCK_NAME  VARCHAR(40) NOT NULL, 
    PRIMARY KEY (SCHED_NAME,LOCK_NAME)
);


四、具体实现

1、我本地自己建立的一个保存任务的数据表

DROP TABLE IF EXISTS `schedule_job`;
CREATE TABLE `schedule_job` (
  `job_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任务id',
  `bean_name` varchar(200) DEFAULT NULL COMMENT 'spring bean名称',
  `method_name` varchar(100) DEFAULT NULL COMMENT '方法名',
  `params` varchar(2000) DEFAULT NULL COMMENT '参数',
  `cron_expression` varchar(100) DEFAULT NULL COMMENT 'cron表达式',
  `status` tinyint(4) DEFAULT NULL COMMENT '任务状态',
  `remark` varchar(255) DEFAULT NULL COMMENT '备注',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`job_id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='定时任务';

2、项目启动时先初始化

说明:我的项目集成了mybatis,像数据库调用的那些就不说了。

ScheduleJobEntity这个是对应表`schedule_job`建立的实体类,schedulerJobDao是数据调用接口。

	/**
	 * 项目启动时,初始化定时器
	 */
	@PostConstruct
	public void myInit(){
		//先查出数据库中有多少个任务,将其初始化
		List<ScheduleJobEntity> list=schedulerJobDao.queryList(null);
		if(list!=null)
		{
			list.forEach
				(
					scheduleJob->
					{
						//获取各个任务对应的触发器,如果不为空,则更新;为空则创建任务;
						Trigger trigger= MyScheduleUtils.getTrigger(scheduler,scheduleJob.getJobId());
						if(trigger==null)
                             MyScheduleUtils.createScheduleJob(scheduler,scheduleJob);
						else
							 MyScheduleUtils.updateScheduleJob(scheduler,scheduleJob);
					}
			    );
		}
	}

3、建立工具类MyScheduleUtils

 

package com.fwone.utils;

import com.fwone.entity.ScheduleJobEntity;
import org.quartz.*;

public class MyScheduleUtils {
    private static final String JOB_NAME="TASK_";
    /**
     * 获取触发器Key
     * @param jobId
     * @return
     */
    public static TriggerKey getTriggerKey(Long jobId){
        return TriggerKey.triggerKey(JOB_NAME+jobId);
    }

    public static JobKey getJobKey(Long jobId){
        return JobKey.jobKey(JOB_NAME+jobId);
    }
    /**
     * 获取触发器
     * @param scheduler
     * @param jobId
     * @return
     */
    public static CronTrigger getTrigger(Scheduler scheduler, Long jobId){
        try {
            JobDetail jobDetail=scheduler.getJobDetail(getJobKey(jobId));
            Class c=jobDetail.getJobClass();
            System.out.println(c);
            return (CronTrigger)scheduler.getTrigger(getTriggerKey(jobId));
        } catch (SchedulerException e) {
            throw new RRException("获取定时任务CronTrigger出现异常",e);
        }
    }

    /**
     * 创建任务
     * @param scheduler
     * @param scheduleJob
     */
    public static void createScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob){
        try {
            /** 重要:
             *  定义一个job,并绑定到我们自定义的MyScheduleJob的class对象
             *  这里并不会马上创建一个MyScheduleJob实例,实例创建是在scheduler安排任务触发执行时创建的
             *  这种机制也为后面使用Spring集成提供了便利
             */
            JobDetail jobDetail = JobBuilder.newJob(MyScheduleJob.class)
                    .withIdentity(getJobKey(scheduleJob.getJobId()))
                    .build();
            /** 重要:
             *  定义一个表达式调度构建器:不触发立即执行,等待下次Cron触发频率到达时刻开始按照Cron频率依次执行
             *  如果任务失败,策略为:所有的misfire不管,执行下一个周期的任务
             */
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder
                    .cronSchedule(scheduleJob.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();

            /**重要:
             * 按新的cronExpression表达式构建一个新的trigger
             */
            Trigger trigger = TriggerBuilder
                    .newTrigger()
                    .withIdentity(getTriggerKey(scheduleJob.getJobId()))
                    .withSchedule(cronScheduleBuilder)
                    .build();
            //放入参数,运行时的方法可以获取
            jobDetail.getJobDataMap().put(ScheduleJobEntity.JOB_PARAM_KEY, scheduleJob);

            /**重要:
             * 把定义好的job放入调度器scheduler
             */
            scheduler.scheduleJob(jobDetail, trigger);

            //判断是否暂停任务
            if(scheduleJob.getStatus()==null||
                    scheduleJob.getStatus()== Constant.ScheduleStatus.PAUSE.getValue())
                pauseJob(scheduler,scheduleJob.getJobId());
        }catch (SchedulerException e){
            throw new RRException("创建定时任务失败", e);
        }

    }

    /**
     * 更新任务
     * @param scheduler
     * @param scheduleJob
     */
    public static void updateScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob){
        try {
        CronScheduleBuilder scheduleBuilder=CronScheduleBuilder
                .cronSchedule(scheduleJob.getCronExpression())
                .withMisfireHandlingInstructionDoNothing();

        CronTrigger trigger=getTrigger(scheduler,scheduleJob.getJobId());
        //按新的cronExpression表达式重新构建trigger
        trigger=trigger.getTriggerBuilder()
                .withIdentity(getTriggerKey(scheduleJob.getJobId()))
                .withSchedule(scheduleBuilder)
                .build();
        trigger.getJobDataMap().put(ScheduleJobEntity.JOB_PARAM_KEY,scheduleJob);
        scheduler.rescheduleJob(getTriggerKey(scheduleJob.getJobId()),trigger);

        //判断是否暂停任务
        if(scheduleJob.getStatus()==null||
                 scheduleJob.getStatus()== Constant.ScheduleStatus.PAUSE.getValue())
             pauseJob(scheduler,scheduleJob.getJobId());
        } catch (SchedulerException e) {
           throw new RRException("更新定时任务失败",e);
        }
    }

    /**
     * 暂停任务
     * @param scheduler
     * @param jobId
     */
    public static void pauseJob(Scheduler scheduler,Long jobId){
        try {
            scheduler.pauseJob(getJobKey(jobId));
        } catch (SchedulerException e) {
            throw new RRException("暂停定时任务失败", e);
        }
    }

    /**
     * 立即运行一次任务
     * @param scheduler
     * @param scheduleJob
     */
    public static void run(Scheduler scheduler,ScheduleJobEntity scheduleJob){
        try {
            JobDataMap jobDataMap=new JobDataMap();
            jobDataMap.put(ScheduleJobEntity.JOB_PARAM_KEY,scheduleJob);
            scheduler.triggerJob(getJobKey(scheduleJob.getJobId()),jobDataMap);
        } catch (SchedulerException e) {
            throw new RRException("立即执行任务失败",e);
        }
    }

    /**
     *恢复任务
     * @param scheduler
     * @param jobId
     */
    public static void resumeJob(Scheduler scheduler,Long jobId){
        try {
            scheduler.resumeJob(getJobKey(jobId));
        } catch (SchedulerException e) {
           throw new RRException("恢复任务失败",e);
        }
    }
}

自定义工作类,用于 执行任务

package com.fwone.utils;

import com.fwone.entity.ScheduleJobEntity;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.util.concurrent.Future;

/**
**另外Trigger中也可以设置JobDataMap属性,这是为了在多个Trigger中使用相同的Job。
**JobExecutionContext 将会合并JobDetail与Trigger的JobDataMap,如果其中属性名相同,后者将覆盖前者。
**可以使用JobExecutionContext.getMergedJobDataMap()方法来获取合并后的JobDataMap。
 */
public class MyScheduleJob extends QuartzJobBean {
    private Logger logger= LoggerFactory.getLogger(MyScheduleJob.class);
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        ScheduleJobEntity scheduleJob=(ScheduleJobEntity)context
                .getMergedJobDataMap().get(ScheduleJobEntity.JOB_PARAM_KEY);
        ThreadPoolTaskExecutor taskExecutor=SpringContextUtil.getBean("taskExecutor");
        //任务开始时间
        long startTime = System.currentTimeMillis();
        logger.info("我的任务准备执行,任务ID:" + scheduleJob.getJobId());
        try {
            /**
             * ScheduleRunnable是一个任务类,通过反射执行真正的任务
             */
            ScheduleRunnable task=new ScheduleRunnable(scheduleJob.getBeanName(),
                                  scheduleJob.getMethodName(),scheduleJob.getParams());
            Future<?> future=taskExecutor.submit(task);
            future.get();//线程结束之前下面语句不会执行
            long times=System.currentTimeMillis()-startTime;
            logger.info("我的任务执行完毕,任务ID:" + scheduleJob.getJobId() + "  总共耗时:" + times + "毫秒");

        } catch (Exception e) {
            logger.error("我的任务执行失败,任务ID:" + scheduleJob.getJobId(), e);
        }
    }
}

定义任务类ScheduleRunnable,通过反射执行真正的任务

package com.fwone.utils;

import org.apache.commons.lang.StringUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;

/**
 * 执行定时任务
 * 
 * @author chenshun
 * @email [email protected]
 * @date 2016年11月30日 下午12:49:33
 */
public class ScheduleRunnable implements Runnable {
	private Object target;//bean名称
	private Method method;//bean的方法名称
	private String params;//bean的参数

	/**
	 * 初始化
	 * @param beanName
	 * @param methodName
	 * @param params
	 * @throws NoSuchMethodException
	 * @throws SecurityException
     */
	public ScheduleRunnable(String beanName, String methodName, String params) throws NoSuchMethodException, SecurityException {
		this.target = SpringContextUtil.getBean(beanName);
		this.params = params;
		
		if(StringUtils.isNotBlank(params)){
			this.method = target.getClass().getDeclaredMethod(methodName, String.class);
		}else{
			this.method = target.getClass().getDeclaredMethod(methodName);
		}
	}

	@Override
	public void run() {
		try {
			//让方法跳过安全检查
			ReflectionUtils.makeAccessible(method);
			if(StringUtils.isNotBlank(params)){
				method.invoke(target, params);
			}else{
				method.invoke(target);
			}
		}catch (Exception e) {
			throw new RRException("执行定时任务失败", e);
		}
	}

}

最后,为了安全,还要在spring容器关闭时把调度器、线程池关闭。我试过如果不手动关闭,会发出警告说内存有可能溢出。

定义监听器,这样在容器关闭时能做一些处理工作.

package com.fwone.listener;


import com.fwone.utils.RRException;
import com.fwone.utils.SpringContextUtil;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

@WebListener
public class MyContextLoaderListener implements ServletContextListener{
    @Override
    public void contextInitialized(ServletContextEvent sce) {
    }

    /**
     * 容器关闭时触发
     * @param sce
     */
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        /**
         * SpringContextUtil是我实现的一个获取bean的工具类,网上有很多例子,就不写出来了
         * 这里主要是把调度器Scheduler和线程池ExecutorService关闭
         */
        ExecutorService taskExecutor=SpringContextUtil.getBean("taskExecutor");
        Scheduler scheduler=(Scheduler)SpringContextUtil.getBean("scheduler");
        if(scheduler!=null){
            try {
                if(scheduler.isStarted()){
                    scheduler.shutdown();
                }
            } catch (SchedulerException e) {
                  throw new RRException("定时容器关闭失败");
            }
        }
        try {
            taskExecutor.shutdown();
            if(taskExecutor.awaitTermination(2000l, TimeUnit.MILLISECONDS))
                  taskExecutor.shutdownNow();
        }catch (InterruptedException e){
            e.printStackTrace();
            taskExecutor.shutdown();
        }
    }
}
至此大功告成,以后想添加定时任务,只需把写好的bean名称传进去就可以了。

猜你喜欢

转载自blog.csdn.net/lqzxpp/article/details/80305516