首页 Java Java基础 springboot+quartz 以持久化的方式实现定时任务

springboot+quartz 以持久化的方式实现定时任务

Jul 27, 2020 pm 06:33 PM
springboot

springboot+quartz 以持久化的方式实现定时任务

这篇文章给大家介绍springboot+quartz以持久化的方式实现定时任务,详情如下所示:

篇幅较长,耐心的人总能得到最后的答案小生第一次用quartz做定时任务,不足之处多多谅解。

首先

在springboot项目里做定时任务是比较简单的,最简单的实现方式是使用**@Scheduled注解,然后在application启动类上使用@EnableScheduling**开启定时任务。

示例

@SpringBootApplication
@EnableScheduling
public class Application {

 public static void main(String[] args) {
 SpringApplication.run(Application.class, args);
 }
 // cron为每秒执行一次
 @Scheduled(cron = "* * * * * ?")
 public void print(){
 System.out.println("执行定时任务");
 }

}
登录后复制

######结果

执行定时任务
执行定时任务
执行定时任务
执行定时任务
执行定时任务
执行定时任务
执行定时任务
执行定时任务

简单的定时任务就可以用这种方式来做,cron表达式的结果为任务执行的间隔时间。

然而

实际开发中,我们的任务可能有很多,且需要手动操作单个/全部的任务,比如添加、开启、停止、继续等等操作。那么伴随着(千牛B类。。。)的BGM有请quartz登场。

quartz

整合

 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-quartz</artifactId>
 </dependency>
登录后复制

quartz的三要素

  • 调度器Scheduler
  • 启动触发器去执行任务
  • 触发器Trigger

用来定义Job(任务)触发条件、触发时间,触发间隔,终止时间等

任务job

具体要执行的任务内容

使用

使用quartz是需要配置文件的,quartz.properties在quartz的jar包的org.quartz包下可以找到默认的配置文件quartz.properties

# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#
# 名字
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
# 实例化ThreadPool时,使用的线程类为SimpleThreadPool
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
# 线程总个数
org.quartz.threadPool.threadCount: 10
# 线程的优先级
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true

org.quartz.jobStore.misfireThreshold: 60000
# 持久化方式,默认持久化在内存中,后面我们使用db的方式
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
登录后复制

quartz任务持久化到db则需要一些官方定义的数据库表,表的sql文件可以在quartz的jar包里找到

坐标org.quartz.impl.jdbcjobstore,可以看到里面有很多sql文件,有各种数据库的,咱们用MySQL的,咱们不需要手动执行sql语句,后面咱们在启动项目的时候自动初始化。

创建我们自己的properties文件

# 实例化ThreadPool时,使用的线程类为SimpleThreadPool
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
# threadCount和threadPriority将以setter的形式注入ThreadPool实例
# 并发个数
org.quartz.threadPool.threadCount=10
# 优先级
org.quartz.threadPool.threadPriority=5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread=true
org.quartz.jobStore.misfireThreshold=5000
#持久化使用的类
org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX
#数据库中表的前缀
org.quartz.jobStore.tablePrefix=QRTZ_
#数据源命名
org.quartz.jobStore.dataSource=qzDS
#qzDS 数据源,我们使用hikaricp,默认的是c3p0
org.quartz.dataSource.qzDS.provider=hikaricp
org.quartz.dataSource.qzDS.driver=com.mysql.cj.jdbc.Driver
org.quartz.dataSource.qzDS.URL=jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
org.quartz.dataSource.qzDS.user=root
org.quartz.dataSource.qzDS.password=123456
org.quartz.dataSource.qzDS.maxConnections=10
登录后复制

既然我们没有使用默认的连接池,那么就探索一下,上源码!在这个包下:org.quartz.utils,有一个PoolingConnectionProvider,顾名思义,连接池提供者部分源码

public interface PoolingConnectionProvider extends ConnectionProvider {

 /** The pooling provider. */
 String POOLING_PROVIDER = "provider";

 /** The c3p0 pooling provider. */
 String POOLING_PROVIDER_C3P0 = "c3p0";

 /** The Hikari pooling provider. */
 String POOLING_PROVIDER_HIKARICP = "hikaricp";

}
登录后复制

然后HikariCpPoolingConnectionProvider这个类实现了PoolingConnectionProvider,自行查看。我们可以在org.quartz.impl下的StdSchedulerFactory中搜索c3p0找到

if(poolingProvider != null && poolingProvider.equals(PoolingConnectionProvider.POOLING_PROVIDER_HIKARICP)) {
  cpClass = "org.quartz.utils.HikariCpPoolingConnectionProvider";
  }
  else {
  cpClass = "org.quartz.utils.C3p0PoolingConnectionProvider";
  }
登录后复制

剩下的自己多看看吧,起始源码研究起来没有想象中那么难那么乏味(我也不喜欢看源码),但是这个源码看起来确实小有成就感。

回到正题频道,配置application.yml

spring:
 datasource:
 driver-class-name: com.mysql.cj.jdbc.Driver
 password: 123456
 url: jdbc:mysql://localhost:3306/quartz?characterEncoding=UTF8&useSSL=false&serverTimezone=GMT%2B8
 username: root
 quartz:
 jdbc:
 initialize-schema: always
 job-store-type: jdbc
登录后复制

initialize-schema: always每次启动项目,总是初始化数据库表自动创建表的关键地方,流程是先删除数据库表,再创建,如果表不存在,则抛异常,但是不会影响后面的生成表,下次再启动项目的时候,由于表已经存在了,所以不会再抛异常了job-store-type: jdbc就是任务持久化类型,我们用jdbc

我们可能要在job里注入spring对象,不做配置,是无法注入的。

/**
 * @author: taoym
 * @date: 2020/6/4 11:32
 * @desc: 一定要自定义JobFactory重写SpringBeanJobFactory的createJobInstance方法,否则在job中是获取不到spring容器中的bean的
 */
@Component
public class JobFactory extends SpringBeanJobFactory {

 @Autowired
 private AutowireCapableBeanFactory beanFactory;

 /**
 * 这里覆盖了super的createJobInstance方法,对其创建出来的类再进行autowire
 */
 @Override
 protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
 Object jobInstance = super.createJobInstance(bundle);
 beanFactory.autowireBean(jobInstance);
 return jobInstance;
 }
}
登录后复制

创建quartz的配置文件

@Configuration
public class QuartzConfig {

 @Autowired
 private JobFactory jobFactory;

 /**
 * 读取quartz.properties 文件
 * 将值初始化
 *
 * @return
 */
 @Bean
 public Properties quartzProperties() throws IOException {
 PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
 propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
 propertiesFactoryBean.afterPropertiesSet();
 return propertiesFactoryBean.getObject();
 }

 @Bean
 public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
 SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
 schedulerFactoryBean.setJobFactory(jobFactory);
 schedulerFactoryBean.setQuartzProperties(quartzProperties());
 return schedulerFactoryBean;
 }

 /**
 * 初始化监听器
 *
 * @return
 */
 @Bean
 public QuartzInitializerListener executorListener() {
 return new QuartzInitializerListener();
 }


 @Bean(name = "scheduler")
 public Scheduler scheduler() throws IOException {
 return schedulerFactoryBean().getScheduler();
 }
}
登录后复制

创建触发器组件

public class TriggerComponent {

 /**
 * @author: taoym
 * @date: 2020/6/1 10:35
 * @desc: 构建cron触发器
 */
 public static Trigger cronTrigger(String cron) {
 CronTrigger cronTrigger = TriggerBuilder.newTrigger()
 .withSchedule(CronScheduleBuilder.cronSchedule(cron).withMisfireHandlingInstructionDoNothing())
 .build();
 return cronTrigger;
 }

 public static Trigger cronTrigger(String cron, JobDataMap jobDataMap) {
 CronTrigger cronTrigger = TriggerBuilder.newTrigger()
 .withSchedule(CronScheduleBuilder.cronSchedule(cron).withMisfireHandlingInstructionDoNothing())
 .usingJobData(jobDataMap)
 .build();
 return cronTrigger;
 }
}
登录后复制

触发器就用这个组件来获取就行了。

创建任务

@DisallowConcurrentExecution
public class TestJob extends QuartzJobBean {
 @Override
 protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
 
 }
}
登录后复制

jobExecutionContext这里面可以获取任务组、任务名、触发器组、触发器名、jobdetail等信息。那个注解是为了让同一个实例(jobdetail)只能单线程执行。可以这么理解,job为接口,jobdetail为实现类,a是其中一个实现类,a需要花费100s执行一定的操作,而你给的定时器是没50s就执行一次操作,a在执行到一半的时候又需要开启一个线程来执行。使用了DisallowConcurrentExecution就相当于a没有把操作执行完的时候,a不允许开启线程再执行当前操作。不知道我的描述是否易懂!

按需创建自己的任务表,我是用定时任务做爬虫的(小爬虫)

CREATE TABLE `quartz_job` (
 `id` int(11) NOT NULL AUTO_INCREMENT COMMENT &#39;编号&#39;,
 `job_name` varchar(50) DEFAULT &#39;&#39; COMMENT &#39;任务名&#39;,
 `job_group` varchar(50) DEFAULT &#39;&#39; COMMENT &#39;任务组名称&#39;,
 `job_desc` varchar(255) DEFAULT &#39;&#39; COMMENT &#39;job描述&#39;,
 `cron` varchar(50) DEFAULT &#39;&#39; COMMENT &#39;cron表达式&#39;,
 `status` tinyint(1) DEFAULT &#39;0&#39; COMMENT &#39;状态&#39;,
 `url` varchar(255) DEFAULT &#39;&#39; COMMENT &#39;请求地址&#39;,
 `param` varchar(255) DEFAULT &#39;&#39; COMMENT &#39;参数&#39;,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8;
登录后复制

我们添加任务的时候不和quartz打交道,把任务放到数据库即可。别慌,后面有用到他的地方。这个表需要有增删改查操作,我们会在系统中查询任务列表选择单个或者所有任务开始执行

执行任务

@Resource
 private QuartzJobMapper quartzJobMapper;
 @Autowired
 private Scheduler scheduler;
 
 
 @Override
 public String start(Integer id) {
 
		JobDataMap jobDataMap = new JobDataMap();
 jobDataMap.put(k,v);
 
 QuartzJob quartzJob = quartzJobMapper.selectByPrimaryKey(id);
 
 JobKey jobKey = JobKey.jobKey(quartzJob.getJobName(), quartzJob.getJobGroup());
 
 jobDetail = JobBuilder.newJob(TestJob.class).withIdentity(jobKey).storeDurably().build();
 
 Trigger trigger = TriggerComponent.cronTrigger(quartzJob.getCron(), jobDataMap);
 try {
 scheduler.scheduleJob(jobDetail, trigger);
 quartzJobMapper.updateStatus(true, id);
 return "开始任务执行成功";
 } catch (SchedulerException se) {
 log.info("开始任务的时候发生了错误");
 }
 return "开始任务的时候发生了错误,请检查日志";
 }
登录后复制

最后我又按照此教程上的内容粘贴了一遍代码,可以正常运行。

推荐教程:《PHP

以上是springboot+quartz 以持久化的方式实现定时任务的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Springboot怎么集成Jasypt实现配置文件加密 Springboot怎么集成Jasypt实现配置文件加密 Jun 01, 2023 am 08:55 AM

Jasypt介绍Jasypt是一个java库,它允许开发员以最少的努力为他/她的项目添加基本的加密功能,并且不需要对加密工作原理有深入的了解用于单向和双向加密的高安全性、基于标准的加密技术。加密密码,文本,数字,二进制文件...适合集成到基于Spring的应用程序中,开放API,用于任何JCE提供程序...添加如下依赖:com.github.ulisesbocchiojasypt-spring-boot-starter2.1.1Jasypt好处保护我们的系统安全,即使代码泄露,也可以保证数据源的

SpringBoot怎么集成Redisson实现延迟队列 SpringBoot怎么集成Redisson实现延迟队列 May 30, 2023 pm 02:40 PM

使用场景1、下单成功,30分钟未支付。支付超时,自动取消订单2、订单签收,签收后7天未进行评价。订单超时未评价,系统默认好评3、下单成功,商家5分钟未接单,订单取消4、配送超时,推送短信提醒……对于延时比较长的场景、实时性不高的场景,我们可以采用任务调度的方式定时轮询处理。如:xxl-job今天我们采

怎么在SpringBoot中使用Redis实现分布式锁 怎么在SpringBoot中使用Redis实现分布式锁 Jun 03, 2023 am 08:16 AM

一、Redis实现分布式锁原理为什么需要分布式锁在聊分布式锁之前,有必要先解释一下,为什么需要分布式锁。与分布式锁相对就的是单机锁,我们在写多线程程序时,避免同时操作一个共享变量产生数据问题,通常会使用一把锁来互斥以保证共享变量的正确性,其使用范围是在同一个进程中。如果换做是多个进程,需要同时操作一个共享资源,如何互斥呢?现在的业务应用通常是微服务架构,这也意味着一个应用会部署多个进程,多个进程如果需要修改MySQL中的同一行记录,为了避免操作乱序导致脏数据,此时就需要引入分布式锁了。想要实现分

springboot读取文件打成jar包后访问不到怎么解决 springboot读取文件打成jar包后访问不到怎么解决 Jun 03, 2023 pm 04:38 PM

springboot读取文件,打成jar包后访问不到最新开发出现一种情况,springboot打成jar包后读取不到文件,原因是打包之后,文件的虚拟路径是无效的,只能通过流去读取。文件在resources下publicvoidtest(){Listnames=newArrayList();InputStreamReaderread=null;try{ClassPathResourceresource=newClassPathResource("name.txt");Input

Springboot+Mybatis-plus不使用SQL语句进行多表添加怎么实现 Springboot+Mybatis-plus不使用SQL语句进行多表添加怎么实现 Jun 02, 2023 am 11:07 AM

在Springboot+Mybatis-plus不使用SQL语句进行多表添加操作我所遇到的问题准备工作在测试环境下模拟思维分解一下:创建出一个带有参数的BrandDTO对象模拟对后台传递参数我所遇到的问题我们都知道,在我们使用Mybatis-plus中进行多表操作是极其困难的,如果你不使用Mybatis-plus-join这一类的工具,你只能去配置对应的Mapper.xml文件,配置又臭又长的ResultMap,然后再去写对应的sql语句,这种方法虽然看上去很麻烦,但具有很高的灵活性,可以让我们

SpringBoot与SpringMVC的比较及差别分析 SpringBoot与SpringMVC的比较及差别分析 Dec 29, 2023 am 11:02 AM

SpringBoot和SpringMVC都是Java开发中常用的框架,但它们之间有一些明显的差异。本文将探究这两个框架的特点和用途,并对它们的差异进行比较。首先,我们来了解一下SpringBoot。SpringBoot是由Pivotal团队开发的,它旨在简化基于Spring框架的应用程序的创建和部署。它提供了一种快速、轻量级的方式来构建独立的、可执行

SpringBoot怎么自定义Redis实现缓存序列化 SpringBoot怎么自定义Redis实现缓存序列化 Jun 03, 2023 am 11:32 AM

1、自定义RedisTemplate1.1、RedisAPI默认序列化机制基于API的Redis缓存实现是使用RedisTemplate模板进行数据缓存操作的,这里打开RedisTemplate类,查看该类的源码信息publicclassRedisTemplateextendsRedisAccessorimplementsRedisOperations,BeanClassLoaderAware{//声明了key、value的各种序列化方式,初始值为空@NullableprivateRedisSe

SpringBoot+Dubbo+Nacos 开发实战教程 SpringBoot+Dubbo+Nacos 开发实战教程 Aug 15, 2023 pm 04:49 PM

本文来写个详细的例子来说下dubbo+nacos+Spring Boot开发实战。本文不会讲述太多的理论的知识,会写一个最简单的例子来说明dubbo如何与nacos整合,快速搭建开发环境。

See all articles