话不多说,直接上码!
第一步:老样子,先pom下载依赖
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.2.1</version>
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
第二步:新建QuartzConfig类,用于配置Quartz调度!
@Configuration
public class QuartzConfig {
@Autowired
private SpringJobFactory springJobFactory;
@Bean(name = "SchedulerFactory")
public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
SchedulerFactoryBean factory = new SchedulerFactoryBean();
Properties properties=new Properties();
properties.put("org.quartz.threadPool.class","org.quartz.simpl.SimpleThreadPool");
//用于quartz集群,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
factory.setOverwriteExistingJobs(true);
//将quartz交给spring管理,解决了job类无法注入service的问题
factory.setJobFactory(springJobFactory);
return factory;
}
/**
* quartz初始化监听器
* @return
*/
@Bean
public QuartzInitializerListener executorListener() {
return new QuartzInitializerListener();
}
/**
* 通过SchedulerFactoryBean获取Scheduler的实例
* @return
* @throws IOException
*/
@Bean(name = "Scheduler")
public Scheduler scheduler() throws IOException {
Scheduler scheduler = schedulerFactoryBean().getScheduler();
//添加同步任务
startTask(scheduler);
return scheduler;
}
/**
* 新增定时任务
* @param scheduler
*/
private void startTask(Scheduler scheduler){
String startJob = "false";//是否开始
String jobName = "StartTask";//任务名
String jobGroup = "StartTask";//组名
String cron = "0/59 * * * * ? ";//定时的时间设置
String className = QuartzWork.class.getName();//调用实现job类的QuartzWork
if (startJob != null && startJob.equals("true")) {
addCommonCronJob(jobName, jobGroup, cron, scheduler, className);
} else {
deleteCommonJob(jobName, jobGroup, scheduler);
}
}
//end...
private void deleteCommonJob(String jobName, String jobGroup, Scheduler scheduler) {
JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
try {
scheduler.pauseJob(jobKey);//先暂停任务
scheduler.deleteJob(jobKey);//再删除任务
} catch (SchedulerException e) {
e.printStackTrace();
}
}
/**
* 添加一些定时任务,如日志清理任务
* @param jobName
* @param jobGroup
* @param cron
* @param scheduler
* @param className
*/
private void addCommonCronJob(String jobName, String jobGroup, String cron, Scheduler scheduler, String className) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
//任务触发
Trigger checkExist = (CronTrigger) scheduler.getTrigger(triggerKey);
if (checkExist == null) {
JobDetail jobDetail = null;
jobDetail = JobBuilder.newJob((Class<? extends Job>) Class.forName(className))
.requestRecovery(true)//当Quartz服务被中止后,再次启动或集群中其他机器接手任务时会尝试恢复执行之前未完成的所有任务
.withIdentity(jobName, jobGroup)
.build();
jobDetail.getJobDataMap().put("jobName", jobName);
jobDetail.getJobDataMap().put("jobGroup", jobGroup);
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
/*withMisfireHandlingInstructionDoNothing
——不触发立即执行
——等待下次Cron触发频率到达时刻开始按照Cron频率依次执行
withMisfireHandlingInstructionIgnoreMisfires
——以错过的第一个频率时间立刻开始执行
——重做错过的所有频率周期后
——当下一次触发频率发生时间大于当前时间后,再按照正常的Cron频率依次执行
withMisfireHandlingInstructionFireAndProceed
——以当前时间为触发频率立刻触发一次执行
——然后按照Cron频率依次执行*/
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(jobName, jobGroup)
.withSchedule(cronScheduleBuilder.withMisfireHandlingInstructionIgnoreMisfires())
.build();
scheduler.scheduleJob(jobDetail, trigger);
} else {
scheduler.deleteJob(JobKey.jobKey(jobName, jobGroup));
addCommonCronJob(jobName, jobGroup, cron, scheduler, className);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
第三步:新建QuartzWork类,用于实现业务代码!
@DisallowConcurrentExecution
@Component
public class QuartzWork implements Job {
@Resource
private BfhrpposService bfhrpposService;
@Resource
private BfhtreswService bfhtreswService;
@Resource
private IntepayyjtzdqService intepayyjtzdqService;
@Resource
private IntepayzdqService intepayzdqService;
public static final Logger logger = LoggerFactory.getLogger(QuartzWork.class);
private static int counter = 0;
/**
* 实现Job接口,无法更改接口中方法
* @param jobExecutionContext
* @throws JobExecutionException
*/
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
try{
//System.out.println("执行时间:"+ sdf.format(date) + "....." + "quartz:" + "开始执行定时读取ftp文件");
//System.out.println(bfhrpposService.Read());
//System.out.println(bfhtreswService.Read());
//System.out.println(intepayyjtzdqService.Read());
//System.out.println(intepayzdqService.Read());
System.out.println("兽人永不为奴,除非管吃管住!");
}catch (Exception e){
e.printStackTrace();
}
}
}
第四步:再配置一个任务调度监听类,TriggerListenerLogMonitor!
@Component
class TriggerListenerLogMonitor implements TriggerListener {
@Override
public String getName() {
return "TriggerListenerLogMonitor";
}
//当与监听器相关联的 Trigger 被触发,Job 上的 execute() 方法将要被执行时,Scheduler 就调用这个方法。
@Override
public void triggerFired(Trigger trigger, JobExecutionContext context) {
System.out.println("TriggerListenerLogMonitor类:" + context.getTrigger().getKey().getName() + " 被执行");
}
/**
* 在 Trigger 触发后,Job 将要被执行时由 Scheduler 调用这个方法。
* TriggerListener 给了一个选择去否决 Job 的执行。
* 假如这个方法返回 true,这个 Job 将不会为此次 Trigger 触发而得到执行。
*/
@Override
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
return false;
}
/**
* Scheduler 调用这个方法是在 Trigger 错过触发时。
* 如这个方法的 JavaDoc 所指出的,你应该关注此方法中持续时间长的逻辑:
* 在出现许多错过触发的 Trigger 时,
* 长逻辑会导致骨牌效应。
* 你应当保持这上方法尽量的小。
*/
@Override
public void triggerMisfired(Trigger trigger) {
System.out.println("Job错过触发");
}
/**
* Trigger 被触发并且完成了 Job 的执行时,Scheduler 调用这个方法。
* 这不是说这个 Trigger 将不再触发了,而仅仅是当前 Trigger 的触发(并且紧接着的 Job 执行) 结束时。
* 这个 Trigger 也许还要在将来触发多次的。
*/
@Override
public void triggerComplete(Trigger trigger, JobExecutionContext context, Trigger.CompletedExecutionInstruction triggerInstructionCode) {
System.out.println("Job执行完毕,Trigger完成");
}
}
第五步:这个方法选择使用,在注入service的时候会失败,即使代码写入不报错,打印台也会抛出空指针异常,原因是由于quartz和spring是两个容器,不能相结合,
办法就是新建一个SpringJobFactory类,把quartz交给spring管理,两者完美结合!
@Component
public class SpringJobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 调用父类的方法
Object jobInstance = super.createJobInstance(bundle);
// 进行注入
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
业务实现类的业务内容就可以注入service了,我试过在里面写一些业务代码,但是job接口里只有一个方法,很不方便,只能注入service或者其他业务代码,QuartzConfig里新建定时任务来操作业务,我实现的是定时到ftp服务器读取文件解析并插入Oracle数据库,所以service里有各种读取入库的操作,这个博客是我自己的定时项目用到的所有代码,千万别盲目复制,部分代码可参考,等跑起来就看见线程打印了!