定时任务(Quartz)
Quartz介绍
- Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或EJBs
Quartz使用思路
主要围绕三个环节进行开发
- job - 任务 -你要做什么事
- Trigger -触发器 -你什么时候去做
- Scheduled -任务调度 -你什么时候要去做什么事
springboot 结合Quartz
实现简单的定时任务,首先导入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- scheduled所属资源为spring-context-support,在Spring中对Quartz的支持,是集成在spring-context-support包中。
org.springframework.scheduling.quartz
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!--Quartz-->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
<!-- Quartz默认需要slf4j支持。springboot中,提供了更高版本的slf4j -->
<exclusions>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- Spring tx 坐标,quartz可以提供分布式定时任务环境。多个分布点上的Quartz任务,是通过数据库实现任务信息传递的。
通过数据库中的数据,保证一个时间点上,只有一个分布环境执行定时任务。
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
启动器添加注解@EnableScheduling:
SpringBootApplication
@EnableScheduling //开启定时任务机制
public class ScheduleApplication {
public static void main(String[] args) {
SpringApplication.run(ScheduleApplication.class, args);
}
}
定义JOB任务以及JOB任务调用的模拟业务对象:
**
* 简单Job类
*/
public class QuartzDemo implements Job {
@Autowired
private UserService userService;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("定时任务"+new Date());
this.userService.UserList();
}
}
@Service
public class UserService {
public void UserList(){
System.out.println("查询User列表");
}
}
定义配置类。主要围绕三个环节进行开发:
/**
* Quartz配置类
*/
@Configuration
public class QuartzConfig {
/**
* 1.创建Job对象。
* 在Spring环境中,创建一个类型的对象的时候,很多情况下,都是通过FactoryBean来间接创建的。
* 如果有多个Job对象,定义多次方法。
*
* 在JobDetailFactoryBean类型中,用于创建JobDetail对象的方法,其底层使用的逻辑是:Class.newInstance()
* 也就是说,JobDetail对象不是通过Spring容器管理的。
* 因为Spring容器不管理JobDetail对象,那么Job中需要自动装配的属性,就无法实现自动状态。如JOB会报空指针异常。
*
* 解决方案是: 将JobDetail加入到Spring容器中,让Spring容器管理JobDetail对象。
* 需要重写Factory相关代码。实现Spring容器管理JobDetail。
* @return
*/
@Bean
public JobDetailFactoryBean jobDetailFactoryBean(){
JobDetailFactoryBean factoryBean=new JobDetailFactoryBean();
//关联我们自己的Job类
factoryBean.setJobClass(QuartzDemo.class);
return factoryBean;
}
/**
* 2.创建Trigger对象
* CronTrigger - 就是Trigger的一个实现类型。 其中用于定义周期时间的是CronSchedulerBuilder
* 实际上,CronTrigger是用于管理一个Cron表达式的类型。
* @param jobDetailFactoryBean - 上一个方法初始化的JobDetailFactoryBean
* @return
*/
@Bean
public CronTriggerFactoryBean cronTriggerFactoryBean(JobDetailFactoryBean jobDetailFactoryBean){
CronTriggerFactoryBean factoryBean=new CronTriggerFactoryBean();
//关联jobDetail对象
factoryBean.setJobDetail(jobDetailFactoryBean.getObject());
//设置触发时间
factoryBean.setCronExpression("0/2 * * * * ?");//两秒触发一次
return factoryBean;
}
/**
* 3.创建Scheduled对象
* @param cronTriggerFactoryBean - 上一个方法初始化的CronTriggerFactoryBean
*/
@Bean
public SchedulerFactoryBean schedulerFactoryBean(MyJobFactory myJobFactory,CronTriggerFactoryBean[] cronTriggerFactoryBean){
SchedulerFactoryBean factoryBean=new SchedulerFactoryBean();
//关联Trigger对象,可能有多个对象
CronTrigger[] triggers = new CronTrigger[cronTriggerFactoryBean.length];
for(int i = 0; i < cronTriggerFactoryBean.length; i++){
triggers[i] = cronTriggerFactoryBean[i].getObject();
}
factoryBean.setTriggers(triggers);
// 为Scheduler设置JobDetail的工厂。可以覆盖掉SpringBoot提供的默认工厂,保证JobDetail中的自动装配有效。
factoryBean.setJobFactory(myJobFactory);
return factoryBean;
}
}
重写Job工厂
@Component
public class MyJobFactory extends AdaptableJobFactory {
/**
* AutowireCapableBeanFactory : 简单理解为Spring容器,是Spring容器Context的一个Bean对象管理工程。
* 可以实现自动装配逻辑,和对象创建逻辑。
* 是SpringIoC容器的一个重要组成部件。
*/
@Autowired
private AutowireCapableBeanFactory autowireCapableBeanFactory;
/**
* 在JobDetailFactoryBean类型中,用于创建JobDetail对象的方法,其底层使用的逻辑是:Class.newInstance()
* 也就是说,JobDetail对象不是通过Spring容器管理的,而是反射。
* 因为Spring容器不管理JobDetail对象,那么Job中需要自动装配的属性,就无法实现自动状态。如JOB会报空指针异常。
*/
@Override
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
// 通过父类型中的方法,创建JobDetail对象。
Object obj=super.createJobInstance(bundle);
// 将JobDetail对象加入到Spring容器中,让Spring容器管理,并实现自动装配逻辑。
this.autowireCapableBeanFactory.autowireBean(obj);
return obj;
}
}
注:如果不将job对象放入ioc容器中,就不会被spring管理,userService就无法自动注入,将会报:
解决就是在 第三步3.创建Scheduled对象 中 将JobDetail对象加入到Spring容器中,让Spring容器管理,并实现自动装配逻辑。
//factoryBean.setJobFactory(myJobFactory);
java.lang.NullPointerException: null
at com.huang.schedule.quartz.QuartzDemo.execute(QuartzDemo.java:23) ~[classes/:na]
at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[quartz-2.3.0.jar:na]
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [quartz-2.3.0.jar:na]
运行:
到此为止,我们的简单的使用Quartz执行定时任务就完成了。