前面2篇文章介绍了quartz简单的使用以及quartz核心类的作用,这一篇文章主要介绍quartz在项目中的使用
1.自定义注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JobUnit {
/**
* @return
*/
String jobName() default "";
String jobGroup() default "";
String jobDesc() default "";
String jobCorn() default "";
int misfireInstruction() default CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING;
/**
* 是否单例,单例情况下以代码配置为主,系统无法修改 jobName+jobGroup,防止生成多个同任务定时器
*
* @return
*/
boolean singleton() default true;
}
2.任务配置单元数据
public class QuartzJobUnit implements Serializable {
private static final long serialVersionUID = 7669523527816564621L;
/**
* 任务名称
*/
private String jobName;
/**
* 任务分组
*/
private String jobGroup;
/**
* 任务表达式
*/
private String jobCorn;
/**
* 任务描述
*/
private String description;
/**
* 存活时间,单位秒
*/
private long surviveSecond;
/**
* 扩展单元数据
*/
private Map<String, Object> extraUnit;
/**
* 丢失补仓策略
*/
private int misfireInstruction = CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING;
//省略get、set
}
3.定义Job任务
@JobUnit(jobName = "QuartzJob1", jobGroup = "QuartzJob", jobCorn = "*/5 * * * * ?", jobDesc = "Quartz学习")
public class QuartzJob1 implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
JobKey key = jobExecutionContext.getJobDetail().getKey();
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
String jobMsg = jobDataMap.getString("jobMsg");
String triggerMsg = jobDataMap.getString("triggerMsg");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(jobMsg + "=====" + triggerMsg + sdf.format(new Date()));
}
}
4.定义调度监听器
public class QuartzCustomSchedulerListener implements SchedulerListener {
private static final Logger LOG = LoggerFactory.getLogger(QuartzCustomSchedulerListener.class);
/**
* 任务被部署时被执行
* @param trigger
*/
@Override
public void jobScheduled(Trigger trigger) {
LOG.info("任务 [{}] 被部署", trigger.toString());
}
/**
* 任务被卸载时被执行
* @param triggerKey
*/
@Override
public void jobUnscheduled(TriggerKey triggerKey) {
LOG.info("任务 [{}] 被卸载", triggerKey.toString());
}
/**
* 任务完成了它的使命,光荣退休时被执行
* @param trigger
*/
@Override
public void triggerFinalized(Trigger trigger) {
LOG.info("任务 [{}] 结束", trigger.toString());
}
/**
* 一个触发器被暂停时被执行
* @param triggerKey
*/
@Override
public void triggerPaused(TriggerKey triggerKey) {
LOG.info("任务 [{}] 被暂停", triggerKey.toString());
}
/**
* 所在组的全部触发器被停止时被执行
* @param triggerGroup
*/
@Override
public void triggersPaused(String triggerGroup) {
LOG.info("任务组 [{}] 被暂停", triggerGroup);
}
/**
* 一个触发器被恢复时被执行
* @param triggerKey
*/
@Override
public void triggerResumed(TriggerKey triggerKey) {
LOG.info("任务 [{}] 被恢复", triggerKey.toString());
}
/**
* 所在组的全部触发器被回复时被执行
* @param triggerGroup
*/
@Override
public void triggersResumed(String triggerGroup) {
LOG.info("任务组 [{}] 被恢复", triggerGroup);
}
/**
* 一个JobDetail被动态添加进来
* @param jobDetail
*/
@Override
public void jobAdded(JobDetail jobDetail) {
}
/**
* 删除时被执行
* @param jobKey
*/
@Override
public void jobDeleted(JobKey jobKey) {
LOG.info("任务 [{}] 被删除", jobKey.toString());
}
/**
* 暂停时被执行
* @param jobKey
*/
@Override
public void jobPaused(JobKey jobKey) {
LOG.info("任务 [{}] 被暂停", jobKey.toString());
}
/**
* 一组任务被暂定时执行
* @param jobGroup
*/
@Override
public void jobsPaused(String jobGroup) {
LOG.info("任务组 [{}] 被暂停", jobGroup);
}
/**
* 恢复时被执行
* @param jobKey
*/
@Override
public void jobResumed(JobKey jobKey) {
LOG.info("任务 [{}] 被恢复", jobKey.toString());
}
/**
* 一组被恢复时执行
* @param triggerGroup
*/
@Override
public void jobsResumed(String triggerGroup) {
LOG.info("任务组 [{}] 被恢复", triggerGroup);
}
/**
* 出现异常时执行
* @param jobGroup
* @param e
*/
@Override
public void schedulerError(String jobGroup, SchedulerException e) {
LOG.error("jobGroup [{}] deal error: {}", jobGroup, getStackTraceMsg(e.getStackTrace()));
}
/**
* scheduler被设为standBy等候模式时被执行
*/
@Override
public void schedulerInStandbyMode() {
}
/**
* scheduler启动时被执行
*/
@Override
public void schedulerStarted() {
}
/**
* scheduler正在启动时被执行
*/
@Override
public void schedulerStarting() {
}
/**
* scheduler关闭时被执行
*/
@Override
public void schedulerShutdown() {
}
/**
* scheduler正在关闭时被执行
*/
@Override
public void schedulerShuttingdown() {
}
/**
* scheduler中所有数据包括jobs, triggers和calendars都被清空时被执行
*/
@Override
public void schedulingDataCleared() {
}
/**
* 日志栈输出
*
* @param stackTraceElements
* @return
*/
public String getStackTraceMsg(StackTraceElement[] stackTraceElements) {
StringBuilder sb = new StringBuilder();
for (StackTraceElement stackTraceElem : stackTraceElements) {
sb.append(stackTraceElem.toString() + "\n");
}
return sb.toString();
}
}
5.定义任务监听器
public class JobMonitorListener implements JobListener {
private static final ThreadLocal<Long> TIME_THREADLOCAL = new ThreadLocal<Long>() {
@Override
protected Long initialValue() {
return System.currentTimeMillis();
}
};
private static final Logger LOGGER = LoggerFactory.getLogger(JobMonitorListener.class);
@Override
public String getName() {
return "JobListenerName";
}
/**
* 定时任务开始执行
*
* @param context
*/
@Override
public void jobToBeExecuted(JobExecutionContext context) {
String jobName = context.getJobDetail().getKey().toString();
long startTime = System.currentTimeMillis();
TIME_THREADLOCAL.set(System.currentTimeMillis());
String executeTime = DateFormatUtils.format(new Date(startTime), "yyyy-MM-dd HH:mm:ss");
LOGGER.info("Job [{}] is going to start! BeginTime : [{}]", jobName, executeTime);
}
/**
* 定时任务被否决
*
* @param context
*/
@Override
public void jobExecutionVetoed(JobExecutionContext context) {
String executeTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
String jobName = context.getJobDetail().getKey().toString();
LOGGER.warn("Job [{}] is vetoed, DateTime: {}", jobName, executeTime);
}
/**
* 定时任务执行完毕
*
* @param context
* @param jobException
*/
@Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
long endTime = System.currentTimeMillis();
long costTime = System.currentTimeMillis() - TIME_THREADLOCAL.get();
String jobName = context.getJobDetail().getKey().toString();
String executeTime = DateFormatUtils.format(new Date(endTime), "yyyy-MM-dd HH:mm:ss");
LOGGER.info("Job [{}] is finished! EndTime: [{}] , Cost time [{}] ms", jobName, executeTime, costTime);
if (jobException != null && !jobException.getMessage().equals("")) {
LOGGER.error("job [{}] is execute dateTime [{}] error: {}", jobName, executeTime, getStackTraceMsg(jobException.getStackTrace()));
}
}
/**
* 日志栈输出
*
* @param stackTraceElements
* @return
*/
public String getStackTraceMsg(StackTraceElement[] stackTraceElements) {
StringBuilder sb = new StringBuilder();
for (StackTraceElement stackTraceElem : stackTraceElements) {
sb.append(stackTraceElem.toString() + "\n");
}
return sb.toString();
}
}
6.项目启动时初始化Scheduler
@Configuration
public class QuartzConfiguration {
@Bean(name = "scheduler")
public Scheduler scheduler() throws Exception {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.getListenerManager().addSchedulerListener(new QuartzCustomSchedulerListener());
scheduler.getListenerManager().addJobListener(new JobMonitorListener());
return scheduler;
}
}
7.QuartzJobManager管理类
@Service
@AutoConfigureAfter(value = {QuartzConfiguration.class})
public class QuartzJobManager {
private static final Logger LOG = LoggerFactory.getLogger(QuartzJobManager.class);
@Qualifier(value = "scheduler")
@Autowired
private Scheduler scheduler;
/**
* 添加任务</br>
* 同一个任务只能添加一次,时间表达式不一样时会更新定时任务
*
* @param jobUnit
* @param jobClass
*/
public void addJob(QuartzJobUnit jobUnit, @SuppressWarnings("rawtypes") Class jobClass) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobUnit.getJobName(), jobUnit.getJobGroup());
if (!scheduler.checkExists(triggerKey)) {
JobDetail jobDetail = null;
if (StringUtils.isNotBlank(jobUnit.getDescription())) {
jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobUnit.getJobName(), jobUnit.getJobGroup()).withDescription(jobUnit.getDescription()).build();
} else {
jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobUnit.getJobName(), jobUnit.getJobGroup()).build();
}
jobDetail.getJobDataMap().put("scheduleJob", jobUnit);
//表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobUnit.getJobCorn());
CronTriggerImpl trigger = (CronTriggerImpl) TriggerBuilder.newTrigger().withIdentity(jobUnit.getJobName(), jobUnit.getJobGroup()).withSchedule(scheduleBuilder).build();
if (StringUtils.isNotBlank(jobUnit.getDescription())) {
trigger.setDescription(jobUnit.getDescription());
}
if (jobUnit.getSurviveSecond() > 0L) {
trigger.setEndTime(new Date(System.currentTimeMillis() + jobUnit.getSurviveSecond() * 1000));
}
trigger.setMisfireInstruction(jobUnit.getMisfireInstruction());
trigger.setJobDataMap(jobDetail.getJobDataMap());
// 注册job和调度器
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
// LOG.info("add quartz job [{}] success!", triggerKey.toString());
} else {
CronTriggerImpl trigger = (CronTriggerImpl) scheduler.getTrigger(triggerKey);
if (!trigger.getCronExpression().equalsIgnoreCase(jobUnit.getJobCorn()) || (StringUtils.isNotBlank(jobUnit.getDescription()) && !jobUnit.getDescription().equals(trigger.getDescription()))) {
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(jobUnit.getJobCorn());
trigger = (CronTriggerImpl) trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
if (jobUnit.getSurviveSecond() > 0L) {
trigger.setEndTime(new Date(System.currentTimeMillis() + jobUnit.getSurviveSecond() * 1000));
}
if (StringUtils.isNotBlank(jobUnit.getDescription())) {
trigger.setDescription(jobUnit.getDescription());
}
scheduler.rescheduleJob(triggerKey, trigger);
// LOG.info("update quartz job [{}] success!", triggerKey.toString());
}
}
} catch (SchedulerException e) {
LOG.error("add quartz job error: {}", e);
}
}
/**
* 移除任务
*
* @param jobUnit
*/
public void removeJob(QuartzJobUnit jobUnit) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobUnit.getJobName(), jobUnit.getJobGroup());
scheduler.pauseTrigger(triggerKey);// 停止触发器
scheduler.unscheduleJob(triggerKey);// 移除触发器
JobKey jobKey = JobKey.jobKey(jobUnit.getJobName(), jobUnit.getJobGroup());
scheduler.deleteJob(jobKey);// 删除任务
} catch (SchedulerException e) {
//throw new RuntimeException(e);
}
}
/**
* 停止任务
*
* @param jobUnit
*/
public void pauseJob(QuartzJobUnit jobUnit) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobUnit.getJobName(), jobUnit.getJobGroup());
scheduler.pauseTrigger(triggerKey);// 停止触发器
} catch (SchedulerException e) {
LOG.error("暂定任务失败:", e);
}
}
/**
* 恢复任务
*
* @param jobUnit
*/
public void resumeJob(QuartzJobUnit jobUnit) {
try {
JobKey jobKey = JobKey.jobKey(jobUnit.getJobName(), jobUnit.getJobGroup());
scheduler.resumeJob(jobKey);// 停止触发器
} catch (SchedulerException e) {
//throw new RuntimeException(e);
}
}
/**
* 开始所有任务
*/
public void startJobs() {
try {
scheduler.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 停止所有任务
*/
public void shutdownJobs() {
try {
if (!scheduler.isShutdown()) {
scheduler.shutdown();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 立即执行任务
*
* @param jobUnit
*/
public void triggerJob(QuartzJobUnit jobUnit) {
try {
JobKey jobKey = JobKey.jobKey(jobUnit.getJobName(), jobUnit.getJobGroup());
scheduler.triggerJob(jobKey);
} catch (Exception e) {
}
}
}
8.启动类中注册定时任务
@SpringBootApplication
public class ToaliApplication implements CommandLineRunner {
private static final Logger LOGGER = LoggerFactory.getLogger(ToaliApplication.class);
private static final String QUARTZ_JOB_PKG = "com.haidong.yktui.quartz.job";
@Autowired
private QuartzJobManager quartzJobManager;
public static void main(String[] args) {
SpringApplication.run(ToaliApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
registerQuartzJob();
}
/**
* 注册定时任务
*
* @throws Exception
*/
private void registerQuartzJob() throws Exception {
LOGGER.info("start to register quartz job...");
final ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AnnotationTypeFilter(JobUnit.class));
for (final BeanDefinition resourceBean : scanner.findCandidateComponents(QUARTZ_JOB_PKG)) {
try {
final Class resourceClass = getClass().getClassLoader().loadClass(resourceBean.getBeanClassName());
if (null != resourceClass) {
JobUnit jobUnit = (JobUnit) resourceClass.getAnnotation(JobUnit.class);
String jobName = jobUnit.jobName();
if (StringUtils.isBlank(jobName)) {
jobName = resourceClass.getSimpleName();
}
QuartzJobUnit quartzJobUnit = new QuartzJobUnit();
quartzJobUnit.setJobName(jobName);
quartzJobUnit.setJobGroup(jobUnit.jobGroup());
quartzJobUnit.setJobCorn(jobUnit.jobCorn());
quartzJobUnit.setMisfireInstruction(jobUnit.misfireInstruction());
if (StringUtils.isNotBlank(jobUnit.jobDesc())) {
quartzJobUnit.setDescription(jobUnit.jobDesc());
}
quartzJobManager.addJob(quartzJobUnit, resourceClass);
}
} catch (final ClassNotFoundException e) {
LOGGER.error("error to register quartz job bean [{}]", resourceBean.getBeanClassName(), e);
}
}
LOGGER.info("finished register quartz job...");
}
}
9.当我们运行后发现报错了:
这个因为job任务是交给quartz处理的,而job任务中引入的TestAutowired是spring管理的,他们是2个东西,所以引入的TestAutowired是空。具体原因见第四章
2019-12-10 00:55:00,106 ERROR (JobRunShell.java:211)- Job QuartzJob.QuartzJob1 threw an unhandled Exception:
java.lang.NullPointerException: null
at com.yhx.toali.Quartz.QuartzFrame.QuartzJob1.execute(QuartzJob1.java:33)
at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
2019-12-10 00:55:00,107 ERROR (QuartzCustomSchedulerListener.java:157)- jobGroup [Job (QuartzJob.QuartzJob1 threw an exception.] deal error: org.quartz.core.JobRunShell.run(JobRunShell.java:213)
org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
10.添加JobFactory
@Component(value = "quartzCustomAdaptableJobFactory")
public class QuartzCustomAdaptableJobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
//实例化对象
Object jobInstance = super.createJobInstance(bundle);
//进行注入,交给spring管理该bean
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
11.修改Scheduler的获取方式
@Configuration
public class QuartzConfiguration {
/*@Bean(name = "scheduler")
public Scheduler scheduler() throws Exception {
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.getListenerManager().addSchedulerListener(new QuartzCustomSchedulerListener());
scheduler.getListenerManager().addJobListener(new JobMonitorListener());
return scheduler;
}*/
@Bean(value = "schedulerFactory")
public SchedulerFactoryBean schedulerFactoryBean(@Qualifier("quartzCustomAdaptableJobFactory") QuartzCustomAdaptableJobFactory quartzCustomAdaptableJobFactory) {
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setJobFactory(quartzCustomAdaptableJobFactory);
return schedulerFactoryBean;
}
@Bean(name = "scheduler")
public Scheduler scheduler(@Qualifier("schedulerFactory") SchedulerFactoryBean schedulerFactory) throws Exception {
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.getListenerManager().addSchedulerListener(new QuartzCustomSchedulerListener());
scheduler.getListenerManager().addJobListener(new JobMonitorListener());
return scheduler;
}
}
12.注释掉QuartzJobManager中的调度器的启动
// 注册job和调度器
scheduler.scheduleJob(jobDetail, trigger);
// scheduler.start();
来源:CSDN
作者:leo_messi94
链接:https://blog.csdn.net/weixin_39724194/article/details/103465740