SpringBoot的出现给我们带来了很多方便,基本上可以基于注解解决掉所有的配置,但是正因为它的迅速迭代,相关文档跟不上,所以有很多坑需要我们去踩,下面将给大家介绍Spinigboot2.0版本对接Quartz任务。
首先我们来了解一下spring常用的任务框架:
1.Spring Schedule
2.Quartz
Spring Schedule是Spring自带的任务框架,简单说他就是一个简化版本的Quartz,但是正因为它有个 与Springboot的集成非常简单,
@SpringBootApplication
@ComponentScan(basePackages = {"com.hik.tool","com.hik.dealexcel"})
@EnableCaching
@EnableScheduling
public class ProjectApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(ProjectApplication.class, args);
}
}
只需要在springboot的启动类上加上
@EnableScheduling
然后写一个任务执行类,其中cron表示任务执行的时间表示式
@Component
public class ScheduledTask {
@Scheduled(cron = "0 11 15 * * *")
public void reportCurrentTimeCron() throws InterruptedException {
System.out.println("任务执行!");
}
}
这样就可以完成一个简单的定时任务,不需要依赖其他包,不需要任何配置,是不是很简单,但是这个定时其有个问题,据一位大佬的博客说,一旦某个任务在执行过程中抛出异常,则整个定时器生命周期就结束,以后永远不会再执行定时器任务,但是Quartz就不一样,上次任务出错不会影响下次任务的执行,所以我还是选择了quartz,那么我们开始进入今天正题。
springboot在2.0版本之前,还没有出quartz的starter,所以集成起来相对麻烦点,需要引入多个jar包,然后做相关配置,但是2.0后,只需要引入starter就可以了,以下就是相关步骤(quartz的用法分为内存版本和数据库版本,这里主要讲解内存版本,springboot官方文档里面有讲到直接在application配置文件中如何切换成数据库版本,已经数据库相关信息的配置,总体比之前使用非starter简单很多):
1.引入starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
2.编写任务类继承QuartzJobBean类,实现excuteInternal方法,方法中写自己需要定时执行的
public class UploadTask extends QuartzJobBean {
@Resource
private TencentYunService tencentYunService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("任务开始");
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务结束");
}
}
3.创建一个配置类,分别制定具体任务类和触发的规则,我们可以和之前的xml配置对应起来看,
uploadTaskDetail指定了具体需要执行的类,只不过具体的方法就是我们需要实现的excuteInternal
uploadTaskTrigger指定了触发的规则
@Configuration
public class QuartzConfig {
@Bean
public JobDetail uploadTaskDetail() {
return JobBuilder.newJob(UploadTask.class).withIdentity("uploadTask").storeDurably().build();
}
@Bean
public Trigger uploadTaskTrigger() {
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("*/5 * * * * ?");
return TriggerBuilder.newTrigger().forJob(uploadTaskDetail())
.withIdentity("uploadTask")
.withSchedule(scheduleBuilder)
.build();
}
}
<bean id="cleanupStatusTaskMethod" class= "org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="cleanupStatusTask"/>
<property name="targetMethod" value="cleanupStatus"/>
<property name="concurrent" value="false"/>
</bean>
<bean id="cleanupStatusTrigger" class= "org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="cleanupStatusTaskMethod"/>
<property name="startDelay" value="300000"/>
<property name="repeatInterval" value="60000"/>
</bean>
如此一个Quartz集成Springboot就完成了,是不是很简单,在集成的过程中遇到了一个坑,我们看到在xml配置时我们配置了concurrent为false,这个意思是是否并发执行,系统默认为true,即第一个任务还未执行完整,第二个任务如果到了执行时间,则会立马开启新线程执行任务,这样如果我们是从数据库读取信息,两次重复读取可能出现重复执行任务的情况,所以我们需要将这个值设置为false,这样第二个任务会往后推迟,只有在第一个任务执行完成后才会执行第二个任务。
我们只需要在任务类上加入disallowconcurrentExeution就可以了
@DisallowConcurrentExecution
public class UploadTask extends QuartzJobBean {
@Resource
private TencentYunService tencentYunService;
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("任务开始");
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务结束");
}
}
我们可以看一下效果,我们规定了任务每5s执行一次,而线程等待6秒,如果是并发执行会导致任务开始和任务结束杂乱出现
但是如果我们加上如上注解,那么只会成对先后出现开始和结束