Spring+quartz实现动态化定时任务

动态化定时任务主要是为了方便任务的实时开启、暂停、重启、停止。

下面主要记录下具体实现步骤:

本例quartz的版本

 <!--定时任务框架-->
 <dependency>
     <groupId>org.quartz-scheduler</groupId>
     <artifactId>quartz</artifactId>
     <version>2.2.3</version>
  </dependency>

1.自定义实现动态任务工厂类

代码 如下

该类的作用是用来统一化管理任务的开启、暂停、重启、停止。

注意:该类使用了@Componemt注解,因此项目配置必须保证DynamicJobFactory的包能被注解扫描到,

代码中直接使用了job的class名称作为了job名称,因此加了一个jobName前缀,它并没有其它含义

#省略了包名
import org.quartz.*;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
public class DynamicJobFactory {

    @Resource
    private Scheduler scheduler;

    /**
     * 添加job
     *
     * @param className      类名
     * @param cronExpression cron表达式
     * @throws Exception
     */
    public void addJob(String className, String cronExpression) throws Exception {
        Class clazz = Class.forName(className);
        JobDetail jobDetail = JobBuilder.newJob(clazz)
                .withIdentity("JobName:" + className, Scheduler.DEFAULT_GROUP)
                .withDescription("A test job")
                //如果需要给任务传递参数,可以用setJobData来设置参数
                 .setJobData(new JobDataMap())
                .build();
        CronTriggerImpl cronTrigger = new CronTriggerImpl();
        cronTrigger.setName("JobTrigger:" + className);
        cronTrigger.setCronExpression(cronExpression);
        JobKey jobKey = new JobKey("JobName:" + className, Scheduler.DEFAULT_GROUP);
        if(!scheduler.checkExists(jobKey)){
            scheduler.scheduleJob(jobDetail, cronTrigger);
        }
    }

    /**
     * 暂停job
     *
     * @param className 类名
     * @throws Exception
     */
    public void pause(String className) throws Exception {
        JobKey jobKey = new JobKey("JobName:" + className, Scheduler.DEFAULT_GROUP);
        scheduler.pauseJob(jobKey);
    }

    /**
     * 重启job
     *
     * @param className
     * @throws Exception
     */
    public void resume(String className) throws Exception {
        JobKey jobKey = new JobKey("JobName:" + className, Scheduler.DEFAULT_GROUP);
        scheduler.resumeJob(jobKey);
    }

    /**
     * 停止job
     *
     * @param className
     * @throws Exception
     */
    public void stop(String className) throws Exception {
        JobKey jobKey = new JobKey("JobName:" + className, Scheduler.DEFAULT_GROUP);
        scheduler.deleteJob(jobKey);
    }

    /**
     * 修改job的执行时间
     *
     * @param className
     * @param cronExpression
     * @throws Exception
     */
    public void updateJobTime(String className, String cronExpression) throws Exception {
        TriggerKey triggerKey = new TriggerKey("JobTrigger:" + className,Scheduler.DEFAULT_GROUP);
        CronTriggerImpl trigger = (CronTriggerImpl) scheduler.getTrigger(triggerKey);
        if (trigger == null) {
            return;
        }
        String oldTime = trigger.getCronExpression();
        if (!oldTime.equalsIgnoreCase(cronExpression)) {
            trigger.setCronExpression(cronExpression);
            scheduler.rescheduleJob(triggerKey, trigger);
        }
    }

    /**
     * 获取job信息
     * @param className
     * @return
     * @throws Exception
     */
    public JobDetail getJobDetail(String className) throws Exception {
        JobKey jobKey = new JobKey("JobName:" + className, Scheduler.DEFAULT_GROUP);
        JobDetail detail = scheduler.getJobDetail(jobKey);
        return detail;
    }

    /**
     * 启动所有的任务
     *
     * @throws SchedulerException
     */
    public void startJobs() throws SchedulerException {
        scheduler.start();
    }

    /**
     * shutdown所有的任务
     *
     * @throws SchedulerException
     */
    public void shutdownJobs() throws SchedulerException {
        if (!scheduler.isShutdown()) {
            scheduler.shutdown();
        }
    }

}

2.自定义一个任务

public class MyJob extends QuartzJobBean {

    private static final Logger logger = LoggerFactory.getLogger(OpenConnectionJob.class);


    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        if(logger.isDebugEnabled()){
            logger.debug("Validate Job Running task  thread:{} ",Thread.currentThread().getName());
        }
        JobDetail jobDetail = context.getJobDetail();
        JobDataMap dataMap = jobDetail.getJobDataMap();
        System.out.println("jobData:"+ JSON.toJSONString(dataMap));
        JobKey jobKey = jobDetail.getKey();
        System.out.println("jobName:"+jobKey.getName());
    }
}

在MyJob中如果再用Spring 注解注入我们自己所写的服务时,将会出现注入失败,需要编写额外的代码时实现自动注入

public class JobFactory extends AdaptableJobFactory {

    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;

    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //调用父类的方法
        Object jobInstance = super.createJobInstance(bundle);
        //进行注入
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

3.通过controller来测试任务

注意:涉及的CommonResult请自定修改为自己喜欢的返回方式

@RestController
@RequestMapping("job")
public class JobController {

    @Resource
    private DynamicJobFactory dynamicJobFactory;

    String className = "com.hunao.log.Job.MyJob";
    String cronExpression = "0/3 * * * * ?";

    /**
     * 添加一个新任务
     * @return
     */
    @RequestMapping(value = "/add")
    public CommonResult add(){
        CommonResult result = new CommonResult();
        try {
            dynamicJobFactory.addJob(className,cronExpression);
            result.setMessage("添加动态任务成功!");
            result.setSuccess(true);
        }catch (Exception e){
            e.printStackTrace();
            result.setMessage("添加任务出现异常");
        }
        return result;
    }

    /**
     * 暂停任务
     * @return
     */
    @RequestMapping(value="/pause")
    public CommonResult pause(){
        CommonResult result = new CommonResult();
        try {
            dynamicJobFactory.pause(className);
            result.setMessage("暂停任务成功!");
            result.setSuccess(true);
        }catch (Exception e){
            e.printStackTrace();
            result.setMessage("暂停任务出现异常");
        }
        return result;
    }

    /**
     * 重启任务
     * @return
     */
    @RequestMapping(value="/resume")
    public CommonResult resume(){
        CommonResult result = new CommonResult();
        try {
            dynamicJobFactory.resume(className);
            result.setMessage("重启任务成功!");
            result.setSuccess(true);
        }catch (Exception e){
            e.printStackTrace();
            result.setMessage("重启任务出现异常");
        }
        return result;
    }

    /**
     * 停止任务
     * @return
     */
    @RequestMapping(value = "/stop")
    public CommonResult stop(){
        CommonResult result = new CommonResult();
        try{
            dynamicJobFactory.stop(className);
            result.setMessage("任务停止成功");
            result.setSuccess(true);
        }catch (Exception e){
            e.printStackTrace();
            result.setMessage("任务停止失败");
        }
        return result;
    }

    /**
     * 修改任务执行时间
     * @return
     */
    @RequestMapping(value = "/update")
    public CommonResult update(){
        CommonResult result = new CommonResult();
        try {
            dynamicJobFactory.updateJobTime(className,"0/30 * * * * ?");
            result.setMessage("更新任务执行时间成功");
            result.setSuccess(true);
        }catch (Exception e){
            e.printStackTrace();
            result.setMessage("更新任务执行时间失败");
        }
        return result;
    }

    /**
     * 查看任务状态
     * @return
     */
    @RequestMapping(value = "/detail")
    public CommonResult getJobDetail(){
        CommonResult result = new CommonResult();
        JobDetail jobDetail;
        try {
            jobDetail = dynamicJobFactory.getJobDetail(className);
            result.setMessage("获取任务信息成功");
            result.setSuccess(true);
            if(null != jobDetail){
                //JobDetail中的JobBuilder是不能序列化的。因此不能放JobDetail
                result.setData(jobDetail.getDescription());
            }
        }catch (Exception e){
            e.printStackTrace();
            result.setMessage("获消息异常");
        }
        return result;
    }
}

4.配置quartz

编写一个配置文件叫spring-quartz.xml(ps:叫什么名字并不重要,重要的是根据功能切分配置文件,方便识别)

里面的配置如下:

<?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.xsd">

    <!--自动注入工厂-->
    <bean id="jobFactory" class="com.hunao.log.Job.JobFactory"/>
    <!-- 总配置 -->
    <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="jobFactory" ref="jobFactory"></property>
        <!-- 加载 quartz.properties-->
        <property name="configLocation" value="classpath:quartz.properties"/>

    </bean>
</beans>

注意:quartz.properties是quartz框架默认加载的配置文件,我们可以在该配置文件中自定义quartz的任务线程数,因此也说明quartz默认就是多线程来跑定时任务的,如果需要单线程执行的,则需要将jobDetail的concurrent属性改为false

quartz.properties中的大致内容:

# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#

org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false

org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 16
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true

org.quartz.jobStore.misfireThreshold: 60000

org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

5.在web.xml中加载spring-quartz.xml

 <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            classpath:spring-quartz.xml
        </param-value>
    </context-param>

6.启动web服务测试动态定时任务

在浏览器中数据:http://localhost:8080/xx/job/add回车后添加一个定时任务,然后观察控制台就能看到MyJob的输出打印了,同理想要控制任务的运行,从浏览器端输入相应url即可。

最后提示:

本例是在一个配置好的spring mvc项目想构建起来的,因此需要保证项目的web环境正常,对于

DynamicJobFactory这个里面只有四个基础方法,有时候并不能满足实际需求,需要添加一些额外的操作接口,例如:动态修改正在执行任务的cron表达式(修改任务执行时间)、start或者shutdown所有的任务等等

在真实项目场景中,可将class名称和cron表达式从数据库读取过来,实现任务的灵活配置

转载于:https://my.oschina.net/u/1760791/blog/887956

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值