一、Quartz框架简介
什么是Quartz呢?首先我们看百度百科的介绍:
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。Quartz的最新版本为Quartz 2.3.2。
从上面我们可以得出结论:
1.Quartz是开源的;
2.组合Spring的相关框架非常方便;
3.可以处理简单的Jobs,也可以处理复杂的Jobs,也就是说,针对一般的任务它都是可以处理的。
但是至此我们并不是很清楚Quartz到底是什么,以及它能做什么,下面我们接着说。
Quartz是一个由Java编写的开源的作业调度框架,所谓作业调度,用我们通常的话说就是批处理。所谓批处理,一般是针对于项目中需要定时或延时触发的一些操作,没有前端操作来触发。比如,每个月初统计上个月的交易信息、客户信息、报表等信息,再比如,每天凌晨2点统计交易失败的订单或业务进行冲正或生成报表到指定服务器目录,供对应的业务人员或客服人员去下载等。
关于Quartz的介绍网上比较多,但是大多数都是基于java代码形式的开发方式,个人更偏好于采用xml配置的方式——当然这并不是说不需要写代码了,而是说我们的代码只用来做纯粹的业务,关于调度等一些列东西完全不需要在代码中去编写,而是放在xml中。
关于采用代码方式进行任务的开发与调度的文章或视频很多,这里不再赘述,放一篇关于Quartz的头部文章给大家:
定时任务框架Quartz-(一)Quartz入门与Demo搭建
这篇文章介绍的很详细,而且还包括了调度时需要用到的Cron表达式表达式,这里就不再赘述。我们开始介绍SpringBoot整合Quartz框架的XML方式的开发实战。
第一步、pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
当然,如果你是采用Spring方式来开发的话,pom依赖如下:
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.1</version>
</dependency>
第二步、业务逻辑Job类
我们采用xml配置的方式来实现Quartz的话,我们编写的这个Job类,其实就是一个普通的Java类,是纯粹用来处理业务逻辑的,不管是你查询数据库,还是和外系统交互获取数据并处理数据等。
我们就简单的写一个查询用户的方法:
package com.dake.job;
import com.dake.entity.User;
import com.dake.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class QueryUserJob {
private static final Logger LOGGER = LoggerFactory.getLogger(SysDataJob.class);
@Autowired
UserService userService;
public void queryUser() {
LOGGER.info("---------QueryUserJob start---------");
int id = 1;
User user = userService.queryUser(id);
LOGGER.info("---------查询到的user信息:{}---------", user);
LOGGER.info("---------QueryUserJob end---------");
}
}
我们随意写的代码,并且我们随意给一个id=1。这里请注意,类上面的注解:@service,后面我们会针对这个进行特意说明。
关于service和对应dao层代码,我们就不写了。我们也看到了,我们上面的这个类就是一个普通的java Bean,queryUser方法也是一个普通的方法而已,如果可以你可以改成你业务逻辑需要的方法,以及编写你自己的业务逻辑,这里就不赘述了。
还有,和其他的通过java代码方式编写任务调度的方式不同,我们这个类没有继承任何的类,也没有实现任何的接口。
第三步、编写Quartz-config.xml配置文件
这个配置文件的名字大家可以自行命名,只要规范,简明易懂即可。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
</beans>
这是这个xml配置的一个基本信息,然后我们接着配置我们的任务调度信息。
首先,我们配置调度工厂,也就是将来要执行的任务列表。
<!-- 配置调度工厂,订制任务列表 -->
<bean id="springJobSchedulerFactoryBean"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger1"/>
<!-- <ref bean="cronTrigger2"/> -->
</list>
</property>
</bean>
上面的bean的id可以不要,<ref bean=”cronTrigger1″/>锁指向的bean的取值可以自行修改,我们这里以cronTrigger1来作为bean名称。我们看到我还有注释掉的cronTrigger2,这个我们后面会继续讲解。
其次,我们配置trigger触发器。
<!-- 配置tirgger触发器 -->
<bean id="cronTrigger1" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<!-- jobDetail -->
<property name="jobDetail" ref="springQtzJobMethod"></property>
<!-- cron表达式,执行时间 每5秒钟执行一次 -->
<property name="cronExpression" value="0/5 * * * * ?"></property>
</bean>
此时的bean的id就是上面配置的bean工厂所引用bean,两者必须一致。property的name必须和我的一样,2个property的name都是必须和我的一样,因为它们2个都是CronTriggerFactoryBean类的属性:
public class CronTriggerFactoryBean implements FactoryBean<CronTrigger>,
BeanNameAware, InitializingBean {
/** Constants for the CronTrigger class. */
private static final Constants constants = new Constants(CronTrigger.class);
@Nullable
private String name;
@Nullable
private String group;
@Nullable
private JobDetail jobDetail;
@Nullable
private String cronExpression;
}
jobDetail的引用名称可以自行命名,cronExpression的取值根据你的业务的实际情况去配置,关于cron表达式可以自行百度。我们这里的配置是:
每5秒钟执行一次
最后,我们配置Job类。
这个也就是我们刚开始编写的业务类,配置如下:
<!-- 配置Job类 -->
<!-- <bean id="queryUserJob" class="com.dake.job.QueryUserJob"></bean>-->
<!-- 配置JobDetail -->
<bean id="springQtzJobMethod" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<!-- 执行目标job -->
<property name="targetObject" ref="queryUserJob"></property>
<!-- 要执行的方法 -->
<property name="targetMethod" value="queryUser"></property>
<!--配置为false不允许任务并发执行-->
<property name="concurrent" value="false"/>
</bean>
同样的,这个bean的id也必须和trigger触发器中引用的bean的取值保持一致。三个property的name属性也必须和我写的保持一致。其中targetObject和targetMethod是MethodInvokingJobDetailFactoryBean继承的ArgumentConvertingMethodInvoker类,又继承的MethodInvoker中的属性,在这个类中给属性赋值的。
其中targetObject的ref执行的就是你自己的业务逻辑的Job类,也就是那个普通类,我们只不过是以Job结尾来给他它进行命名而已。targetMethod就是你这个业务逻辑类的入口方法。concurrent是MethodInvokingJobDetailFactoryBean中的字段,默认值为true。意思是是否允许并发执行,我们改为false。
至此,我们的配置与代码编写基本完毕了。下面我们接着说说我上面遗留的问题。
1.我们的Job类上加了@Service注解,如果我们不加注解的话,就需要在我们的这个配置文件中将对应的bean配置进来,就是我注释掉的代码:
<!-- <bean id="queryUserJob" class="com.dake.job.QueryUserJob"></bean>-->
如果我们在这里配置,这个id就必须和targetObject的ref保持一致。如果我们不在这里配置,我们的argetObject的ref的取值就必须和我们的Job类的取名保持一致,只需要首字母改成小写即可。或者我们在@service注解中给它起个别名:
@Service("abc")
还有,我们不能既配置了注解又在xml中配置了bean,这样会报错。如果两个都不配置,会找不到对应的bean。
2.我们前面说到的在调度工厂那里注释掉的<ref bean=”cronTrigger2″/>,它被放在了一个list标签中。这个ref是我们的另外的任务的配置,当然假设我们有的话。不过实际项目中肯定不止一个调度任务需要处理,所以肯定会有的。那么另外的调度任务就配置在这里。这个list里面就相当于防止的将要被调度的任务列表。
然后对应的trigger和job类,就和上面类似。
至此,我们的业务逻辑类和配置类都好了,我们启动项目后看看情况。因为我们设置的是每隔5秒执行一次,那我们项目启动后,如果没问题的话就该执行了。
这里打印的三条日志就是我们的普通的Job类里面的日志,执行成功,说明我们的一整套配置是ok的,自动执行了。下面我们改成10秒执行一次,让它多跑几次看看。
可以看到,确实是每隔10秒执行一次,已经执行了三次。
好了,到这里我们的Springboot整合Quartz就基本要结束了。最后要说明的是,我们的配置文件quartz-config.xml要能被spring自动加载才行,如果只是单纯的配置一个xml文件,随便一个地方一丢,这样是不行的。这是spring的知识点,不赘述。此时我们可以在我么的springboot启动类上加上注解,导入我们的配置文件即可。
@SpringBootApplication
@ImportResource("classpath:quartz-config.xml")
public class Application {
private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
try {
LOGGER.info("---------必须配置logback依赖才能打印日志---------");
System.out.println("---------SpringBoot startup begin---------");
SpringApplication.run(Application.class, args);
System.out.println("---------SpringBoot startup end---------");
} catch (Exception e) {
throw new RuntimeException("---------项目启动报错了:", e);
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/2713.html