動態化定時任務主要是爲了方便任務的實時開啓、暫停、重啓、中止。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表達式從數據庫讀取過來,實現任務的靈活配置