文章目录
- 前言
- 一、Springboot 整合
- 1.1 Springboot 初步整合quartz
- 1.1 jar 引入:
- 1.2 添加任务:
- 1.2.1 方式1 PostConstruct 注入任务:
- 1.2.2 方式2 @Bean 注入任务:
- 二、quartz持久化:
- 2.1 mysql 引入:
- 2.2 业务共用一个数据源:
- 2.2.1 配置jdbc datasource:
- 2.2.2 Springboot 整合quartz 持久化:
- 2.3 Springboot 整合quartz 持久化(独立数据源)
- 三、 扩展:
- 3.1 initialize-schema 初始化:
- 四、总结:
- 五、参考:
前言
在 工具篇-- 定时任务quartz 我们已经知道了quartz 的基本特性和简单使用,在项目开发中我们通常都是集成到springboot 项目中。
一、Springboot 整合
1.1 Springboot 初步整合quartz
1.1 jar 引入:
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-quartz -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
1.2 添加任务:
1.2.1 方式1 PostConstruct 注入任务:
JobInit :
import com.example.springmvctest.job.quartz.MyJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
// 定义 JobInit 为一个bean 从而可以被spring 管理
@Component
public class JobInit {
@Autowired
private Scheduler scheduler;
// JobInit 这个bean 在初始化之后调用的方法
@PostConstruct
public void job(){
// 每次执行 都生成新的 MyJob 实例,避免并发情境下,多个线程共用一个MyJob 的实例
JobDetail jobDetail = JobBuilder.newJob(QuartzTest.class)
.usingJobData("job","jobDetail")
.usingJobData("name","jobDetail")
.usingJobData("count",0)
.withIdentity("job","group1").build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1","trigger1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1)
.repeatForever())
.usingJobData("trigger","trigger")
.usingJobData("name","trigger")
.build();
try {
scheduler.scheduleJob(jobDetail,trigger);
} catch (SchedulerException e) {
throw new RuntimeException(e);
}
}
}
任务执行:
(1) 任务定义:
QuartzTest :
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.StringJoiner;
public class QuartzTest extends QuartzJobBean {
@Autowired
private HelloService helloService;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// System.out.println("\"job 执行\" = " + "job 执行" + sdf.format(new Date()));
StringJoiner outStr = new StringJoiner("")
.add("QuartzTest 执行")
.add(sdf.format(new Date()))
.add(Thread.currentThread().getName())
.add(context.getTrigger().getKey().getName())
.add(helloService.toString())
.add(helloService.hello());
System.out.println(outStr);
}
}
(2) HelloService :
import org.springframework.stereotype.Service;
@Service
public class HelloService {
public String hello(){
return "hello";
}
}
1.2.2 方式2 @Bean 注入任务:
JobConfigure :中定义的JobDetail 和Trigger 会自动被 scheduler 关联起来
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JobConfigure {
@Bean
public JobDetail jobDetail1() {
return JobBuilder.newJob(QuartzTest.class)
.usingJobData("job", "jobDetail")
.usingJobData("name", "jobDetail")
.usingJobData("count", 0)
.storeDurably()
.withIdentity("jobConfigure", "group1").build();
}
@Bean
public Trigger trigger1() {
return TriggerBuilder.newTrigger()
.withIdentity("triggerConfigure", "trigger1")
.forJob("jobConfigure","group1")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1)
.repeatForever())
.usingJobData("trigger", "trigger")
.usingJobData("name", "trigger")
.build();
}
@Bean
public JobDetail jobDetail2() {
return JobBuilder.newJob(QuartzTest.class)
.usingJobData("job", "jobDetail2")
.usingJobData("name", "jobDetail2")
.usingJobData("count", 0)
.storeDurably()
.withIdentity("jobConfigure2", "group1").build();
}
@Bean
public Trigger trigger2() {
return TriggerBuilder.newTrigger()
.withIdentity("triggerConfigure2", "trigger2")
.forJob("jobConfigure2","group1")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2)
.repeatForever())
.usingJobData("trigger", "trigger2")
.usingJobData("name", "trigger2")
.build();
}
}
二、quartz持久化:
如果不将Job 进行持久化,那么Job 只存在于内存中,一旦项目重启,之前的任务状态将全部丢失,所以quartz 还支持通过关系型数据库将任务持久化:
2.1 mysql 引入:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!-- 分页 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.1.6</version>
</dependency>
2.2 业务共用一个数据源:
业务系统和定时任务使用同一个数据源。
2.2.1 配置jdbc datasource:
(1)HikariBaseConfig:
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Configuration;
/**
* 线程池
*/
@Configuration
public class HikariBaseConfig {
@Value("${spring.datasource.hikari.pool-name}")
private String poolName;
@Value("${spring.datasource.hikari.maximum-pool-size}")
private Integer maximumPoolSize;
@Value("${spring.datasource.hikari.connection-timeout}")
private Long connectionTimeout;
@Value("${spring.datasource.hikari.minimum-idle}")
private Integer minimumIdle;
@Value("${spring.datasource.hikari.max-lifetime}")
private Long maxLifetime;
@Value("${spring.datasource.hikari.connection-test-query}")
private String connectionTestQuery;
public HikariDataSource getDataSource(String driverClassName, String url, String username, String password){
HikariDataSource hikariDataSource = DataSourceBuilder.create().type(HikariDataSource.class).driverClassName(driverClassName).username(username).url(url).password(password).build();
hikariDataSource.setConnectionTestQuery(connectionTestQuery);
hikariDataSource.setMaxLifetime(maxLifetime);
hikariDataSource.setMinimumIdle(minimumIdle);
hikariDataSource.setConnectionTimeout(connectionTimeout);
hikariDataSource.setPoolName(poolName);
hikariDataSource.setMaximumPoolSize(maximumPoolSize);
return hikariDataSource;
}
}
(2)DataSourceConfig:
//import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
//import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.github.pagehelper.PageHelper;
import javax.sql.DataSource;
import java.util.Properties;
/**
* @Description TODO
* @Date 2021/11/18 11:00
* @Author lgx
* @Version 1.0
*/
// 标明注解
@Configuration
// 开启事务支持后,然后在访问数据库的Service方法上添加注解 @Transactional 便可
@EnableTransactionManagement
// 配置xml 扫描文件位置
@MapperScan(basePackages = {"com.example.springmvctest.mapper"}, sqlSessionFactoryRef = "bluegrassSqlSessionFactory")
public class DataSourceConfig {
@Value("${spring.datasource.bluegrass.jdbc-url}")
private String url;
@Value("${spring.datasource.bluegrass.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource.bluegrass.username}")
private String username;
@Value("${spring.datasource.bluegrass.password}")
private String password;
@Autowired
private HikariBaseConfig hikariBaseConfig;
@Primary
@Bean(name = "bluegrassDataSource")
public DataSource masterDataSource() {
return hikariBaseConfig.getDataSource(driverClassName, url, username, password);
}
@Primary
@Bean
public JdbcTemplate jdbcTemplate(@Qualifier("bluegrassDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
@Primary
@Bean(name = "bluegrassSqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("bluegrassDataSource") DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath*:mapper/quartz/**/*.xml"));
// 全局字段创建人/更新人/创建时间/更新时间 字段的填充
// GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setMetaObjectHandler(new BluegrassMetaHandler());
// sessionFactoryBean.setGlobalConfig(globalConfig);
// sessionFactoryBean.setPlugins(new Interceptor[]{pageHelper()});
Interceptor[] plugins = {paginationInterceptor()};
sessionFactoryBean.setPlugins(plugins);
return sessionFactoryBean.getObject();
}
@Primary
@Bean(name = "bluegrassSqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("bluegrassSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
/**
* 事务管理
*
* @param dataSource
* @return
*/
@Primary
@Bean(name = "bluegrassTransactionManager")
public DataSourceTransactionManager transactionManager(@Qualifier("bluegrassDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
/**
* 分页插件
*/
@Primary
@Bean
public PageHelper pageHelper() {
//分页插件
PageHelper pageHelper = new PageHelper();
Properties properties = new Properties();
properties.setProperty("offsetAsPageNum", "true");
properties.setProperty("rowBoundsWithCount", "true");
properties.setProperty("reasonable", "true");
properties.setProperty("supportMethodsArguments", "true");
properties.setProperty("returnPageInfo", "check");
properties.setProperty("params", "count=countSql");
pageHelper.setProperties(properties);
return pageHelper;
}
/**
* 分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor().setDialectType("mysql");
}
}
(3)数据源配置:
application.yml:
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource #数据源类型
hikari:
pool-name: KevinHikariPool #连接池名称,默认HikariPool-1
maximum-pool-size: 20 #最大连接数,小于等于0会被重置为默认值10;大于零小于1会被重置为minimum-idle的值
connection-timeout: 60000 #连接超时时间:毫秒,小于250毫秒,否则被重置为默认值30秒
minimum-idle: 10 #最小空闲连接,默认值10,小于0或大于maximum-pool-size,都会重置为maximum-pool-size
idle-timeout: 500000 # 只有空闲连接数大于最大连接数且空闲时间超过该值,才会被释放
max-lifetime: 600000 #连接最大存活时间.不等于0且小于30秒,会被重置为默认值30分钟.设置应该比mysql设置的超时时间短
connection-test-query: SELECT 1 #连接测试查询
bluegrass:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3406/quartz?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useAffectedRows=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
username: root
password: ddsoft
2.2.2 Springboot 整合quartz 持久化:
在application.yml: 增加 quartz 持久化参数即可:
spring:
# quartz 定义
quartz:
# 持久化类型为jdbc
job-store-type: jdbc
# 定义任务的初始化类型
jdbc:
initialize-schema: always
# initialize-schema: never
注意第一次启动项目 initialize-schema 可以设置为 always,进行数据库quartz 对应表的生成,后续启动项目 改值可以调整为 never (不出初始化),该参数可以在 三, 扩展 章节详细了解;
2.3 Springboot 整合quartz 持久化(独立数据源)
(1)在application.yml: 增加 quartz 持久化单独的数据源:
spring:
quartz:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3406/quartz-oneself?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useAffectedRows=true&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
username: root
password: ddsoft
(2)标识quartz 数据源:
QuartzDataSourceConfig
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.quartz.QuartzDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class QuartzDataSourceConfig {
@Value("${spring.datasource.quartz.jdbc-url}")
private String url;
@Value("${spring.datasource.quartz.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource.quartz.username}")
private String username;
@Value("${spring.datasource.quartz.password}")
private String password;
@Autowired
private HikariBaseConfig hikariBaseConfig;
@Bean
// 标识quartz 数据源
@QuartzDataSource
public DataSource QuartzDataSource() {
return hikariBaseConfig.getDataSource(driverClassName, url, username, password);
}
}
三、 扩展:
3.1 initialize-schema 初始化:
初始化的方式,其取值可以是always
、never
或embedded
。
always
:表示当Quartz启动时,它始终会尝试自动初始化数据库表结构。如果表已经存在,Quartz将首先删除现有的表,然后创建新的表。这样可以确保数据库表结构始终与Quartz的期望一致。never
:表示Quartz在启动时不会尝试自动初始化数据库表结构。这意味着您必须手动创建和维护Quartz所需的表结构。embedded
:表示Quartz会自动检查数据库中的表结构。如果表已经存在且符合预期的结构,Quartz将继续使用现有的表。否则,它会尝试自动创建所需的表结构。
如果您使用Spring框架与Quartz集成,可以在Spring的配置文件中使用以下属性来设置org.quartz.jobStore.initialize-schema
:
spring.quartz.properties.org.quartz.jobStore.initialize-schema=always
通过根据需要设置org.quartz.jobStore.initialize-schema
参数,可以控制Quartz在启动时如何处理数据库表结构的初始化。
四、总结:
Springboot 通过引入quartz 对应的start jar 包,随后就可以在业务中定义相应的job 和 trigger 然后使用调度器进行关联后,就可以完成任务的调度。
五、参考:
1 spring quartz 参数配置参考;