动态化定时任务主要是为了方便任务的实时开启、暂停、重启、中止。java
下面主要记录下具体实现步骤:web
本例quartz的版本spring
<!--定时任务框架--> <dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.2.3</version> </dependency>
代码 以下数据库
该类的做用是用来统一化管理任务的开启、暂停、重启、中止。浏览器
注意:该类使用了@Componemt注解,所以项目配置必须保证DynamicJobFactory的包能被注解扫描到,多线程
代码中直接使用了job的class名称做为了job名称,所以加了一个jobName前缀,它并无其它含义mvc
#省略了包名 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(); } } }
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 注解注入咱们本身所写的服务时,将会出现注入失败,须要编写额外的代码时实现自动注入app
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; } }
注意:涉及的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; } }
编写一个配置文件叫spring-quartz.xml(ps:叫什么名字并不重要,重要的是根据功能切分配置文件,方便识别)ide
里面的配置以下:
<?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
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:spring-quartz.xml </param-value> </context-param>
在浏览器中数据:http://localhost:8080/xx/job/add回车后添加一个定时任务,而后观察控制台就能看到MyJob的输出打印了,同理想要控制任务的运行,从浏览器端输入相应url便可。
最后提示:
本例是在一个配置好的spring mvc项目想构建起来的,所以须要保证项目的web环境正常,对于
DynamicJobFactory这个里面只有四个基础方法,有时候并不能知足实际需求,须要添加一些额外的操做接口,例如:动态修改正在执行任务的cron表达式(修改任务执行时间)、start或者shutdown全部的任务等等
在真实项目场景中,可将class名称和cron表达式从数据库读取过来,实现任务的灵活配置