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


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

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

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

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

     

    好了,上代码:pom.xml----也可以直接拉到最底下上github下载下来运行一下就哦了。

     <!-- 定时器 -->
            <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"/>
    <!-- Dispatcher-Servlet.xml配置 -->
    <!--初始化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
  • 相关阅读:
    基于Qt的图像处理技术和算法
    项目中使用到的算法
    C++将数组的元素顺序随机打乱
    Windows环境下 RabbitMQ集群配置
    Nginx负载均衡配置
    SQL Server Profiler:使用方法和指标说明
    如何查看IIS并发连接数
    23种设计模式的趣味理解
    C# 开源框架(整理)
    git 简易教程
  • 原文地址:https://www.cnblogs.com/SimonHu1993/p/12891479.html
Copyright © 2020-2023  润新知