记录一下在Spring中整合Quartz的配置,当时遇到的问题有以下两个个方面:
- 能自动配置Job,新建Job无需手动修改配置。
- Job中的依赖需要Spring来装配
Job标记注解
用于识别是否是Quartz的Job实现类
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | import java.lang.annotation.*;
 
 
 
 
 
 @Target({ ElementType.TYPE })
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface JobLabel {
 }
 
 | 
Job配置信息注解
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | import java.lang.annotation.*;
 
 
 
 
 
 @Target({ ElementType.METHOD, ElementType.PARAMETER })
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
 public @interface JobAnnotation {
 String name();
 String group() default "DEFAULT_GROUP";
 String cronExp();
 String timeZone() default "Asia/Shanghai";
 String description();
 }
 
 | 
自动配置Job
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 
 | import org.quartz.*;import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationListener;
 import org.springframework.context.event.ContextRefreshedEvent;
 import org.springframework.util.ReflectionUtils;
 import org.springframework.util.ReflectionUtils.MethodCallback;
 
 import java.lang.reflect.Method;
 import java.util.*;
 
 
 
 
 
 
 public class QuartzJobSchedulingListener implements ApplicationListener<ContextRefreshedEvent> {
 
 private Logger logger = LoggerFactory.getLogger(QuartzJobSchedulingListener.class);
 private boolean initialed;
 @Autowired
 private Scheduler scheduler;
 
 @Override
 public synchronized void onApplicationEvent(ContextRefreshedEvent event) {
 if(!initialed) {
 try {
 logger.info("Quartz清理全部任务...");
 scheduler.clear();
 } catch (SchedulerException e) {
 logger.error(e.getMessage(), e);
 }
 initialed = true;
 ApplicationContext applicationContext = event.getApplicationContext();
 Map<JobDetail, CronTrigger> jobDetailCronTriggerMap = this.loadJobAndCronTriggers(applicationContext);
 logger.info("loadJobAndCronTriggers size:" + jobDetailCronTriggerMap.size());
 Set<Map.Entry<JobDetail, CronTrigger>> entries = jobDetailCronTriggerMap.entrySet();
 for (Map.Entry<JobDetail, CronTrigger> entry : entries) {
 try {
 scheduler.scheduleJob(entry.getKey(), entry.getValue());
 } catch (SchedulerException e) {
 logger.error(e.getMessage(), e);
 }
 }
 }
 }
 
 private Map<JobDetail, CronTrigger> loadJobAndCronTriggers(ApplicationContext applicationContext) {
 
 final Map<String, Object> quartzJobBeans = applicationContext.getBeansWithAnnotation(JobLabel.class);
 Set<String> beanNames = quartzJobBeans.keySet();
 final Map<JobDetail, CronTrigger> jobDetailCronTriggerMap = new HashMap<>();
 for (final String beanName : beanNames) {
 final Object bean = quartzJobBeans.get(beanName);
 ReflectionUtils.doWithMethods(bean.getClass(), new MethodCallback() {
 @Override
 public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
 if (method.isAnnotationPresent(JobConfig.class)) {
 JobConfig jobConfig = method.getAnnotation(JobConfig.class);
 logger.debug("Scheduling a job for " + bean.getClass() + " and method " + method.getName());
 Class jobClass = bean.getClass();
 String className = jobClass.getName();
 String jobName = className + "." + jobConfig.name();
 String jobGroup = jobConfig.group();
 String cronExpression = jobConfig.cronExp();
 
 JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroup).storeDurably(true).build();
 
 CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression)
 .inTimeZone(TimeZone.getTimeZone(jobConfig.timeZone()));
 GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone(jobConfig.timeZone()));
 
 CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName, jobGroup)
 .withSchedule(scheduleBuilder).startAt(calendar.getTime()).build();
 jobDetailCronTriggerMap.put(jobDetail, trigger);
 }
 }
 }
 );
 }
 return jobDetailCronTriggerMap;
 }
 }
 
 | 
Job自动装配
在Job实现类中直接使用Spring注入是没有效果的,需要重新定义一个Job工厂类,重写createJobInstance方法。至于为什么,请查看org.springframework.scheduling.quartz.AdaptableJobFactory的源码就清楚了。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 
 | import org.quartz.spi.TriggerFiredBundle;import org.springframework.context.ApplicationContext;
 import org.springframework.scheduling.quartz.AdaptableJobFactory;
 
 
 
 
 
 
 public class AutowiringSpringBeanJobFactory extends AdaptableJobFactory {
 
 private ApplicationContext applicationContext;
 
 public AutowiringSpringBeanJobFactory(ApplicationContext applicationContext){
 this.applicationContext = applicationContext;
 }
 
 @Override
 protected Object createJobInstance(final TriggerFiredBundle bundle) throws Exception {
 
 
 
 
 final Object job = applicationContext.getBean(bundle.getJobDetail().getJobClass());
 return job;
 }
 }
 
 | 
Spring配置
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 
 | import org.slf4j.Logger;import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.core.io.ClassPathResource;
 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
 import org.springframework.scheduling.quartz.AdaptableJobFactory;
 import org.springframework.scheduling.quartz.SchedulerFactoryBean;
 
 import javax.sql.DataSource;
 
 
 
 
 
 
 
 @Configuration
 public class QuartzConfig {
 private Logger logger = LoggerFactory.getLogger(QuartzConfig.class);
 @Autowired
 private ApplicationContext applicationContext;
 @Autowired
 private DataSource dataSource;
 @Autowired
 private DataSourceTransactionManager transactionManager;
 
 @Bean
 public AdaptableJobFactory springBeanJobFactory() {
 AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(applicationContext);
 logger.debug("Configuring Job factory");
 return jobFactory;
 }
 
 @Bean
 public QuartzJobSchedulingListener quartJobSchedulingListener(){
 return new QuartzJobSchedulingListener();
 }
 
 @Bean
 public SchedulerFactoryBean scheduler() {
 SchedulerFactoryBean schedulerFactory = new SchedulerFactoryBean();
 schedulerFactory.setConfigLocation(new ClassPathResource("quartz.properties"));
 schedulerFactory.setDataSource(dataSource);
 schedulerFactory.setTransactionManager(transactionManager);
 schedulerFactory.setApplicationContext(applicationContext);
 schedulerFactory.setSchedulerName("AneNetBillScheduler");
 
 schedulerFactory.setStartupDelay(10);
 schedulerFactory.setApplicationContextSchedulerContextKey("applicationContext");
 
 schedulerFactory.setOverwriteExistingJobs(true);
 schedulerFactory.setAutoStartup(true);
 schedulerFactory.setJobFactory(springBeanJobFactory());
 return schedulerFactory;
 }
 }
 
 | 
使用
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 
 | import org.quartz.DisallowConcurrentExecution;import org.quartz.Job;
 import org.quartz.JobExecutionContext;
 import org.quartz.JobExecutionException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 @Component
 @DisallowConcurrentExecution
 @JobLabel
 public class SendMessageJob implements Job {
 @Autowired
 private MessageService messageService;
 
 @Override
 @JobConfig(name = "send", cronExp = "0 */10 * * * ?", description = "")
 public void execute(JobExecutionContext context) throws JobExecutionException {
 messageService.send("消息内容");
 }
 }
 
 |