之前线上部署几个定时任务,有时候遇到定时任务修改时间的时候都需要更新代码重启服务器,
有时候需要某个月暂停定时任务,也需要重新部署,所以我乘着空闲时间,给公司做了后台
调度管理,很简单的功能,借鉴了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