SSM后台调度Quartz定时任务动态管理

之前线上部署几个定时任务,有时候遇到定时任务修改时间的时候都需要更新代码重启服务器,

有时候需要某个月暂停定时任务,也需要重新部署,所以我乘着空闲时间,给公司做了后台

调度管理,很简单的功能,借鉴了git上相关开源项目,推荐star:https://github.com/elunez/eladmin,

https://gitee.com/renrenio/renren-security
因为公司框架比较老,用的ssm,所以仅供该框架参考,springboot直接参考上面的项目。
效果图:(easyui--效果就这样了-- )

 

好了,上代码:pom.xml

 <!-- 定时器 -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.1</version>
        </dependency>

job的实现类ExecutionJoblei,用于执行任务
package com.zhx.quartz.utils;

import com.zhx.quartz.domain.QuartzJob;
import com.zhx.quartz.domain.QuartzLog;
import com.zhx.quartz.service.QuartzJobService;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 参考人人开源,https://gitee.com/renrenio/renren-security
 *
 * @author /
 * @date 2019-01-07
 */
@Async
public class ExecutionJob extends QuartzJobBean {
    @Autowired
    private QuartzJobService quartzJobService;
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    /**
     * 该处仅供参考
     */
    private final static ThreadPoolExecutor EXECUTOR = ThreadPoolExecutorUtil.getPoll();
    
    @Override
    @SuppressWarnings("unchecked")
    protected void executeInternal(JobExecutionContext context) {
        QuartzJob quartzJob = (QuartzJob) context.getMergedJobDataMap().get(QuartzJob.JOB_KEY);
        // 获取spring bean
        QuartzJobService quartzJobService = SpringContextHolder.getBean(QuartzJobService.class);
        QuartzLog log = new QuartzLog();
        log.setJobName(quartzJob.getJobName());
        log.setBeanName(quartzJob.getBeanName());
        log.setMethodName(quartzJob.getMethodName());
        log.setParams(quartzJob.getParams());
        long startTime = System.currentTimeMillis();
        log.setCronExpression(quartzJob.getCronExpression());
        try {
            // 执行任务
            logger.info("任务准备执行,任务名称:{}", quartzJob.getJobName());
            QuartzRunnable task = new QuartzRunnable(quartzJob.getBeanName(), quartzJob.getMethodName(),
                    quartzJob.getParams());
            Future<?> future = EXECUTOR.submit(task);
            future.get();
            long times = System.currentTimeMillis() - startTime;
            log.setTime(times);
            // 任务状态
            log.setIsSuccess(true);
            logger.info("任务执行完毕,任务名称:{} 总共耗时:{} 毫秒", quartzJob.getJobName(), times);
        } catch (Exception e) {
            logger.error("任务执行失败,任务名称:{}" + quartzJob.getJobName(), e);
            long times = System.currentTimeMillis() - startTime;
            log.setTime(times);
            // 任务状态 0:成功 1:失败
            log.setIsSuccess(false);
            log.setExceptionDetail(ThrowableUtil.getStackTrace(e));
            quartzJob.setIsPause(false);
            //更新状态
            quartzJobService.updateIsPause(quartzJob.getId());
        } finally {
            logger.info("------------记录 调度 日志--------");
            quartzJobService.inserLog(log);
        }
    }
}
QuartzManage类用于管理任务的执行,暂停,添加,删除
package com.zhx.quartz.utils;

import com.zhx.quartz.domain.QuartzJob;
import org.quartz.*;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Date;

import static org.quartz.TriggerBuilder.newTrigger;

/**
 * @author Zheng Jie
 * @date 2019-01-07
 */
@Component
public class QuartzManage {
    private static final Logger log = LoggerFactory.getLogger(QuartzManage.class);
    private static final String JOB_NAME = "TASK_";
    @Resource(name = "scheduler")
    private Scheduler scheduler;
    
    public void addJob(QuartzJob quartzJob) {
        try {
            // 构建job信息
            JobDetail jobDetail = JobBuilder.newJob(ExecutionJob.class).
                    withIdentity(JOB_NAME + quartzJob.getId()).build();
            //通过触发器名和cron 表达式创建 Trigger
            Trigger cronTrigger = newTrigger()
                    .withIdentity(JOB_NAME + quartzJob.getId())
                    .startNow()
                    .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression()))
                    .build();
            cronTrigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob);
            //重置启动时间
            ((CronTriggerImpl) cronTrigger).setStartTime(new Date());
            //执行定时任务
            scheduler.scheduleJob(jobDetail, cronTrigger);
            // 如果任务是暂停的,就停止
            if (quartzJob.getIsPause()) {
                pauseJob(quartzJob);
            }
        } catch (Exception e) {
            log.error("创建定时任务失败", e);
        }
    }
    
    /**
     * 更新job cron表达式
     *
     * @param quartzJob /
     */
    public void updateJobCron(QuartzJob quartzJob) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 如果不存在则创建一个定时任务
            if (trigger == null) {
                addJob(quartzJob);
                trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            }
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression());
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            //重置启动时间
            ((CronTriggerImpl) trigger).setStartTime(new Date());
            trigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob);
            scheduler.rescheduleJob(triggerKey, trigger);
            // 暂停任务
            if (quartzJob.getIsPause()) {
                pauseJob(quartzJob);
            }
        } catch (Exception e) {
            log.error("更新定时任务失败", e);
        }
    }
    
    /**
     * 删除一个job
     *
     * @param quartzJob /
     */
    public void deleteJob(QuartzJob quartzJob) {
        try {
            JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
            scheduler.pauseJob(jobKey);
            scheduler.deleteJob(jobKey);
        } catch (Exception e) {
            log.error("删除定时任务失败", e);
        }
    }
    
    /**
     * 恢复一个job
     *
     * @param quartzJob /
     */
    public void resumeJob(QuartzJob quartzJob) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 如果不存在则创建一个定时任务
            if (trigger == null) {
                addJob(quartzJob);
            }
            JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
            scheduler.resumeJob(jobKey);
        } catch (Exception e) {
            log.error("恢复定时任务失败", e);
        }
    }
    
    /**
     * 立即执行job
     *
     * @param quartzJob /
     */
    public void runJobNow(QuartzJob quartzJob) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 如果不存在则创建一个定时任务
            if (trigger == null) {
                addJob(quartzJob);
            }
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(QuartzJob.JOB_KEY, quartzJob);
            JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
            scheduler.triggerJob(jobKey, dataMap);
        } catch (Exception e) {
            log.error("定时任务执行失败", e);
        }
    }
    
    /**
     * 暂停一个job
     *
     * @param quartzJob /
     */
    public void pauseJob(QuartzJob quartzJob) {
        try {
            JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
            scheduler.pauseJob(jobKey);
        } catch (Exception e) {
            log.error("定时任务暂停失败", e);
        }
    }
}
package com.zhx.quartz.utils;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.concurrent.Callable;

/**
 * 执行定时任务
 *
 * @author /
 */
public class QuartzRunnable implements Callable {
    private static final Logger log = LoggerFactory.getLogger(QuartzRunnable.class);
    private Object target;
    private Method method;
    private String params;
    
    QuartzRunnable(String beanName, String methodName, String params)
            throws NoSuchMethodException, SecurityException {
        this.target = SpringContextHolder.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 Object call() throws Exception {
        ReflectionUtils.makeAccessible(method);
        if (StringUtils.isNotBlank(params)) {
            method.invoke(target, params);
        } else {
            method.invoke(target);
        }
        return null;
    }
}
InitQuartzJob类用于初始化执行库里存在且未暂停的job
package com.zhx.quartz.utils;

import com.zhx.quartz.domain.QuartzJob;
import com.zhx.quartz.mapper.QuartzJobMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: SimonHu
 * @Date: 2020/5/14 8:49
 * @Description:
 */
@Component
public class InitQuartzJob {
    private static final Logger logger = LoggerFactory.getLogger(InitQuartzJob.class);
    @Autowired
    private QuartzManage quartzManage;
    @Autowired
    private QuartzJobMapper quartzJobMapper;
    
    /**
     * @return void
     * @Description:初始化定时任务 @PostConstruct注解启动时执行该方法
     * @Author:SimonHu
     * @Date: 2020/5/14 8:59
     */
    @PostConstruct
    public void init() {
        // 这里从数据库中获取任务信息数据
        Map map = new HashMap(16);
        //获取未暂停的任务
        map.put("isPause","0");
        List<QuartzJob> quartzJobs = quartzJobMapper.queryJob(map);
        for (QuartzJob job : quartzJobs) {
            logger.info("-----init job----" + job.getJobName());
            quartzManage.addJob(job);
        }
    }
}
SpringContextHolder用于静态类中获取spring中管理的bean
package com.zhx.quartz.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @Author: SimonHu
 * @Date: 2020/5/13 16:07
 * @Description:
 */
@Component
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
    private static final Logger log = LoggerFactory.getLogger(SpringContextHolder.class);
    private static ApplicationContext applicationContext = null;
    
    /**
     * 从静态变量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);
    }
    
    /**
     * 检查ApplicationContext不为空.
     */
    private static void assertContextInjected() {
        if (applicationContext == null) {
            throw new IllegalStateException("applicaitonContext属性未注入, 请在applicationContext" +
                    ".xml中定义SpringContextHolder或在SpringBoot启动类中注册SpringContextHolder.");
        }
    }
    
    /**
     * 清除SpringContextHolder中的ApplicationContext为Null.
     */
    private static void clearHolder() {
        log.debug("清除SpringContextHolder中的ApplicationContext:"
                + applicationContext);
        applicationContext = null;
    }
    
    @Override
    public void destroy() {
        SpringContextHolder.clearHolder();
    }
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (SpringContextHolder.applicationContext != null) {
            log.warn("SpringContextHolder中的ApplicationContext被覆盖, 原有ApplicationContext为:" + SpringContextHolder.applicationContext);
        }
        SpringContextHolder.applicationContext = applicationContext;
    }
}
TheadFactoryName自定义线程名
package com.zhx.quartz.utils;

import org.springframework.stereotype.Component;

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 自定义线程名称
 *
 * @author Zheng Jie
 * @date 2019年10月31日17:49:55
 */
@Component
public class TheadFactoryName implements ThreadFactory {
    private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;
    
    public TheadFactoryName() {
        this("el-pool");
    }
    
    private TheadFactoryName(String name) {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                Thread.currentThread().getThreadGroup();
        //此时namePrefix就是 name + 第几个用这个工厂创建线程池的
        this.namePrefix = name +
                POOL_NUMBER.getAndIncrement();
    }
    
    @Override
    public Thread newThread(Runnable r) {
        //此时线程的名字 就是 namePrefix + -thread- + 这个线程池中第几个执行的线程
        Thread t = new Thread(group, r,
                namePrefix + "-thread-" + threadNumber.getAndIncrement(),
                0);
        if (t.isDaemon()) {
            t.setDaemon(false);
        }
        if (t.getPriority() != Thread.NORM_PRIORITY) {
            t.setPriority(Thread.NORM_PRIORITY);
        }
        return t;
    }
}
package com.zhx.quartz.utils;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 用于获取自定义线程池
 *
 * @author Zheng Jie
 * @date 2019年10月31日18:16:47
 */
public class ThreadPoolExecutorUtil {
    public static ThreadPoolExecutor getPoll() {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                20,
                100,
                300,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(50),
                new TheadFactoryName()
        );
        return threadPoolExecutor;
    }
}
package com.zhx.quartz.utils;

import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * 异常工具 2019-01-06
 *
 * @author Zheng Jie
 */
public class ThrowableUtil {
    /**
     * 获取堆栈信息--分行显示
     */
    public static String getStackTrace(Throwable throwable) {
        StringWriter sw = new StringWriter();
        try (PrintWriter pw = new PrintWriter(sw)) {
            throwable.printStackTrace(pw);
            return sw.toString();
        }
    }
    /**
      * @Description:获取堆栈信息--单行显示
      * @Author:SimonHu
      * @Date: 2020/5/14 15:59
      * @param throwable
      * @return java.lang.String
      */
    public static String getStackTrace2(Throwable throwable) {
        StringWriter sw = new StringWriter();
        StackTraceElement[] error = throwable.getStackTrace();
        for (StackTraceElement stackTraceElement : error) {
            sw.append(stackTraceElement.toString());
        }
        return sw.toString();
    }
}
package com.zhx.quartz.config;

import org.quartz.Scheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

/**
 * 定时任务配置
 *
 * @author /
 * @date 2019-01-07
 */
@Configuration
public class QuartzConfig {
    /**
     * 注入scheduler到spring
     *
     * @param quartzJobFactory /
     * @return Scheduler
     * @throws /
     */
    @Bean(name = "scheduler")
    public Scheduler scheduler(QuartzJobFactory quartzJobFactory) throws Exception {
        SchedulerFactoryBean factoryBean = new SchedulerFactoryBean();
        factoryBean.setJobFactory(quartzJobFactory);
        factoryBean.afterPropertiesSet();
        Scheduler scheduler = factoryBean.getScheduler();
        scheduler.start();
        return scheduler;
    }
}
package com.zhx.quartz.config;
/**
 * @Author: SimonHu
 * @Date: 2020/5/13 16:47
 * @Description:
 */

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;

/**
 * 解决Job中注入Spring Bean为null的问题
 */
public class QuartzJobFactory extends AdaptableJobFactory {
    @Autowired
    private AutowireCapableBeanFactory capableBeanFactory;
    
    @Override
    protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
        //调用父类的方法
        Object jobInstance = super.createJobInstance(bundle);
        capableBeanFactory.autowireBean(jobInstance);
        return jobInstance;
    }
}

最后就是配置文件applicationContext.xml

 <!--初始化 SpringContextHolder-->
    <bean id="SpringContextHolder" class="com.zhx.quartz.utils.SpringContextHolder"/>
<!--初始化Scheduler-->
    <bean id="quartzJobFactory" class="com.zhx.quartz.config.QuartzJobFactory"></bean>
    <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="jobFactory" ref="quartzJobFactory"></property>
    </bean>

配置文件放到bean最后

完整代码我就放在github上了,可以的话给个star= =(csdn上那些坑分的→_→)
https://github.com/SimonHu1993/QuartzDemo

猜你喜欢

转载自www.cnblogs.com/SimonHu1993/p/12891479.html