【SpringBoot】调度和执行定时任务--Quartz(超详细)
Quartz 是一个功能强大的任务调度框架,广泛用于在 Java 应用程序中定时执行任务,同时它支持 Cron 表达式、持久化任务、集群等特性。以下是 Quartz 的详细使用教程,包括安装、基本概念、简单示例和高级功能。
1. 安装 Quartz
首先,在你的项目中添加 Quartz 依赖。对于 Maven 项目,可以在 pom.xml
中添加以下依赖:
<dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.3.2</version>
</dependency>
对于 Gradle 项目,可以在 build.gradle
中添加以下依赖:
implementation 'org.quartz-scheduler:quartz:2.3.2'
2. 基本概念
Quartz 的核心概念包括:
- Scheduler:调度器,是 Quartz 的核心,负责管理和调度任务。
- Job:任务,是实际执行的工作单元。需要实现
Job
接口。 - JobDetail:定义任务的详细信息,包括任务的名称、组、以及任务的类。
- Trigger:触发器,定义任务何时执行。常用的触发器包括 SimpleTrigger 和 CronTrigger。
- JobStore:任务存储,定义任务的存储方式。常见的有 RAMJobStore(内存存储)和 JDBCJobStore(数据库存储)。
3. 简单示例
以下是一个简单的 Quartz 示例,展示如何创建和调度一个任务。
1. 定义 Job 类
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;public class HelloJob implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Hello, Quartz! Current time: " + System.currentTimeMillis());}
}
2. 配置 Scheduler 和 Job
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class QuartzExample {public static void main(String[] args) throws SchedulerException {// 创建 Scheduler 实例Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();// 定义一个 JobDetail 实例JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("helloJob", "group1").build();// 创建一个触发器,每隔5秒执行一次Trigger trigger = TriggerBuilder.newTrigger().withIdentity("helloTrigger", "group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();// 调度任务scheduler.start();scheduler.scheduleJob(job, trigger);}
}
4. 高级功能
1. 使用 CronTrigger
CronTrigger 允许使用 Cron 表达式来定义复杂的调度规则。
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class CronTriggerExample {public static void main(String[] args) throws SchedulerException {Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("cronJob", "group1").build();// 使用 Cron 表达式创建触发器Trigger trigger = TriggerBuilder.newTrigger().withIdentity("cronTrigger", "group1").withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?")).build();scheduler.start();scheduler.scheduleJob(job, trigger);}
}
2. Cron 表达式
Cron 表达式用于定义任务调度的时间规则。它由6或7个字段组成,字段之间用空格分隔。以下是每个字段的含义:
┌───────────── 秒 (0 - 59)
│ ┌───────────── 分 (0 - 59)
│ │ ┌───────────── 小时 (0 - 23)
│ │ │ ┌───────────── 日 (1 - 31)
│ │ │ │ ┌───────────── 月 (1 - 12)
│ │ │ │ │ ┌───────────── 星期几 (0 - 7) (0 和 7 都是星期日)
│ │ │ │ │ │
│ │ │ │ │ │
* * * * * *
2.1 特殊字符
*
: 表示任意值。?
: 仅在日和星期字段中使用,表示不指定值。-
: 表示范围,例如10-12
表示从10到12。,
: 表示列表值,例如1,2,3
表示1、2、3。/
: 表示增量,例如0/15
表示从0开始每15分钟。L
: 表示最后,例如L
在日字段表示月的最后一天。W
: 表示最近的工作日,例如15W
表示最接近15号的工作日。#
: 表示第几个星期几,例如2#1
表示第一个星期一。
2.2 示例
0 0 12 * * ?
: 每天中午12点执行。0 15 10 ? * *
: 每天上午10:15执行。0 15 10 * * ?
: 每天上午10:15执行。0 15 10 * * ? 2024
: 2024年每天上午10:15执行。0 * 14 * * ?
: 每天下午2点到2:59每分钟执行一次。0 0/5 14 * * ?
: 每天下午2点到2:55每5分钟执行一次。0 0/5 14,18 * * ?
: 每天下午2点到2:55每5分钟执行一次,以及每天下午6点到6:55每5分钟执行一次。0 0-5 14 * * ?
: 每天下午2点到2:05每分钟执行一次。0 10,44 14 ? 3 WED
: 每年三月的每个星期三下午2:10和2:44执行。
3. 使用 JobListener
JobListener 可以在任务执行的不同阶段进行拦截和处理。
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;public class MyJobListener implements JobListener {@Overridepublic String getName() {return "MyJobListener";}@Overridepublic void jobToBeExecuted(JobExecutionContext context) {//在任务即将被执行时调用,可以在任务执行前进行一些准备工作或记录日志。System.out.println("Job is about to be executed: " + context.getJobDetail().getKey());}@Overridepublic void jobExecutionVetoed(JobExecutionContext context) {//在任务执行被否决时调用,当某些条件满足时,可以阻止任务的执行,并在此方法中执行相应的处理逻辑。System.out.println("Job execution was vetoed: " + context.getJobDetail().getKey());}@Overridepublic void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {//在任务执行完成后调用,可以在任务执行后进行一些清理工作或记录日志。如果任务执行过程中抛出异常,jobException 将包含该异常信息。System.out.println("Job was executed: " + context.getJobDetail().getKey());if (jobException != null) {System.out.println("Job encountered an exception: " + jobException.getMessage());}}
}
在 Scheduler 中注册 JobListener:
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class JobListenerExample {public static void main(String[] args) throws SchedulerException {Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();//创建任务JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("listenerJob", "group1").build();创建触发器Trigger trigger = TriggerBuilder.newTrigger().withIdentity("listenerTrigger", "group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever()).build();// 创建并注册 JobListenerMyJobListener listener = new MyJobListener();scheduler.getListenerManager().addJobListener(listener);scheduler.start();scheduler.scheduleJob(job, trigger);}
}
5. 持久化支持
Quartz 支持将任务数据持久化到数据库中,以便在系统重启后恢复任务状态。可以使用 JDBCJobStore 来实现这一点。
在 quartz.properties
文件中配置 JDBCJobStore:
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.dataSource = myDS
org.quartz.jobStore.tablePrefix = QRTZ_org.quartz.dataSource.myDS.driver = com.mysql.cj.jdbc.Driver
org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz
org.quartz.dataSource.myDS.user = root
org.quartz.dataSource.myDS.password = password
org.quartz.dataSource.myDS.maxConnections = 5
6. 并发控制
在 Quartz 中,默认情况下,任务是并发执行的。如果需要确保同一个任务实例不被并发执行,可以实现 DisallowConcurrentExecution
接口。为了清楚地展示 DisallowConcurrentExecution
注解的作用,我们可以创建两个示例:一个使用 DisallowConcurrentExecution
注解,另一个不使用该注解。通过这两个示例,我们可以观察到任务在并发执行方面的差异。
示例 1:不使用 DisallowConcurrentExecution
在这个示例中,我们创建一个简单的任务类,它会模拟一个长时间运行的任务,并且不使用 DisallowConcurrentExecution
注解。
1. 定义 Job 类
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;public class ConcurrentJob implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("ConcurrentJob is executing at: " + System.currentTimeMillis());try {// 模拟长时间运行的任务Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("ConcurrentJob finished at: " + System.currentTimeMillis());}
}
2. 配置 Scheduler 和 Job
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class ConcurrentJobExample {public static void main(String[] args) throws SchedulerException {Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();JobDetail job = JobBuilder.newJob(ConcurrentJob.class).withIdentity("concurrentJob", "group1").build();// 创建一个触发器,每隔2秒执行一次Trigger trigger = TriggerBuilder.newTrigger().withIdentity("concurrentTrigger", "group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();scheduler.start();scheduler.scheduleJob(job, trigger);}
}
示例 2:使用 DisallowConcurrentExecution
在这个示例中,我们创建一个类似的任务类,但这次使用 DisallowConcurrentExecution
注解,确保任务不会并发执行。
1. 定义 Job 类
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;@DisallowConcurrentExecution
public class NonConcurrentJob implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("NonConcurrentJob is executing at: " + System.currentTimeMillis());try {// 模拟长时间运行的任务Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("NonConcurrentJob finished at: " + System.currentTimeMillis());}
}
2. 配置 Scheduler 和 Job
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class NonConcurrentJobExample {public static void main(String[] args) throws SchedulerException {Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();JobDetail job = JobBuilder.newJob(NonConcurrentJob.class).withIdentity("nonConcurrentJob", "group1").build();// 创建一个触发器,每隔2秒执行一次Trigger trigger = TriggerBuilder.newTrigger().withIdentity("nonConcurrentTrigger", "group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(2).repeatForever()).build();scheduler.start();scheduler.scheduleJob(job, trigger);}
}
运行示例
当你运行这两个示例时,你会看到以下行为:
不使用 DisallowConcurrentExecution
的输出
ConcurrentJob is executing at: 1633017600000
ConcurrentJob is executing at: 1633017602000
ConcurrentJob finished at: 1633017605000
ConcurrentJob is executing at: 1633017604000
ConcurrentJob finished at: 1633017607000
ConcurrentJob finished at: 1633017609000
可以看到,任务是并发执行的,多个任务实例同时运行。
使用 DisallowConcurrentExecution
的输出
NonConcurrentJob is executing at: 1633017600000
NonConcurrentJob finished at: 1633017605000
NonConcurrentJob is executing at: 1633017605000
NonConcurrentJob finished at: 1633017610000
NonConcurrentJob is executing at: 1633017610000
NonConcurrentJob finished at: 1633017615000
可以看到,任务是顺序执行的,新的任务实例只有在前一个实例完成后才会开始执行。
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;@DisallowConcurrentExecution
public class NonConcurrentJob implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Non-concurrent job is executing at: " + System.currentTimeMillis());}
}
通过这两个示例,我们可以清楚地看到 DisallowConcurrentExecution
注解的作用:它确保同一个任务实例不会并发执行,而是顺序执行。这在某些需要严格控制任务执行顺序的场景中非常有用。
7. 任务依赖管理
Quartz 本身并不直接支持任务依赖管理(即任务之间的依赖关系),但可以通过编程方式实现这一功能。通常的方法是使用多个 Job 和 Trigger,并在 Job 执行完成后手动调度下一个依赖的 Job。
1. 异步任务依赖管理
Quartz 本身并不直接支持任务依赖管理(即任务之间的依赖关系),但可以通过编程方式实现这一功能。通常的方法是使用多个 Job 和 Trigger,并在 Job 执行完成后手动调度下一个依赖的 Job。
以下是一个示例,展示如何在 Quartz 中实现任务依赖管理:
示例场景
假设有三个任务:
- 任务 A:完成后触发任务 B。
- 任务 B:完成后触发任务 C。
- 任务 C:最后执行。
实现步骤
- 定义三个 Job 类。
- 配置 Scheduler 和 Job。
- 在每个 Job 中执行完成后手动调度下一个依赖的 Job。
代码示例
1. 定义 Job 类
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;public class JobA implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Job A executed at: " + System.currentTimeMillis());// 获取 Scheduler 实例Scheduler scheduler = context.getScheduler();try {// 创建 JobB 的 JobDetailJobDetail jobB = JobBuilder.newJob(JobB.class).withIdentity("jobB", "group1").build();// 创建立即触发的 TriggerTrigger triggerB = TriggerBuilder.newTrigger().withIdentity("triggerB", "group1").startNow().build();// 调度 JobBscheduler.scheduleJob(jobB, triggerB);} catch (SchedulerException e) {throw new JobExecutionException(e);}}
}public class JobB implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Job B executed at: " + System.currentTimeMillis());// 获取 Scheduler 实例Scheduler scheduler = context.getScheduler();try {// 创建 JobC 的 JobDetailJobDetail jobC = JobBuilder.newJob(JobC.class).withIdentity("jobC", "group1").build();// 创建立即触发的 TriggerTrigger triggerC = TriggerBuilder.newTrigger().withIdentity("triggerC", "group1").startNow().build();// 调度 JobCscheduler.scheduleJob(jobC, triggerC);} catch (SchedulerException e) {throw new JobExecutionException(e);}}
}public class JobC implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Job C executed at: " + System.currentTimeMillis());}
}
2. 配置 Scheduler 和 Job
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class QuartzDependencyExample {public static void main(String[] args) throws SchedulerException {// 创建 Scheduler 实例Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();// 定义 JobAJobDetail jobA = JobBuilder.newJob(JobA.class).withIdentity("jobA", "group1").build();// 创建立即触发的 TriggerTrigger triggerA = TriggerBuilder.newTrigger().withIdentity("triggerA", "group1").startNow().build();// 调度 JobAscheduler.start();scheduler.scheduleJob(jobA, triggerA);}
}
解释
- JobA:在执行完成后,手动调度 JobB。
- JobB:在执行完成后,手动调度 JobC。
- JobC:最后执行,没有后续任务。
通过这种方式,可以实现任务之间的依赖关系。在每个任务执行完成后,手动调度下一个依赖的任务。这样,即使 Quartz 本身不直接支持任务依赖管理,也可以通过编程方式实现这一功能。
注意事项
- 异常处理:确保在调度下一个任务时正确处理异常,防止由于调度失败导致任务链中断。
- 并发控制:如果有多个任务链,需要确保并发控制,防止多个任务同时调度同一个任务。
- 持久化支持:如果任务链较长或需要持久化,确保使用持久化的 Scheduler 配置,以便在系统重启后恢复任务状态。
2. 同步任务依赖管理
在上面的示例中,任务之间的调度是异步的。每个 Job 在执行完成后,会立即调度下一个 Job,但不会等待下一个 Job 的执行完成。这种方式适用于大多数场景,但如果需要同步执行(即一个任务必须等待前一个任务执行完成后再执行),则需要一些额外的机制来确保任务的同步执行。
为了实现同步的任务依赖管理,可以使用以下方法:
- 使用 JobListener:监听每个任务的执行完成事件,并在事件触发时调度下一个任务。
- 使用共享的状态或信号量:在任务之间共享状态或使用信号量来确保任务按顺序执行。
以下是一个使用 JobListener
实现同步任务依赖管理的示例:
实现步骤
- 定义三个 Job 类。
- 实现 JobListener接口。
- 配置 Scheduler 和 Job。
- 当触发
jobWasExecuted
后调度下一个依赖的 Job。
示例代码
1. 定义 Job 类
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;public class JobA implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Job A executed at: " + System.currentTimeMillis());}
}public class JobB implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Job B executed at: " + System.currentTimeMillis());}
}public class JobC implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("Job C executed at: " + System.currentTimeMillis());}
}
2. 定义 JobListener
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
import org.quartz.SchedulerException;public class DependencyJobListener implements JobListener {private Scheduler scheduler;public DependencyJobListener(Scheduler scheduler) {this.scheduler = scheduler;}@Overridepublic String getName() {return "DependencyJobListener";}@Overridepublic void jobToBeExecuted(JobExecutionContext context) {// No action needed before job execution}@Overridepublic void jobExecutionVetoed(JobExecutionContext context) {// No action needed if job execution is vetoed}@Overridepublic void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {String jobName = context.getJobDetail().getKey().getName();try {if ("jobA".equals(jobName)) {// 调度 JobBJobDetail jobB = JobBuilder.newJob(JobB.class).withIdentity("jobB", "group1").build();Trigger triggerB = TriggerBuilder.newTrigger().withIdentity("triggerB", "group1").startNow().build();scheduler.scheduleJob(jobB, triggerB);} else if ("jobB".equals(jobName)) {// 调度 JobCJobDetail jobC = JobBuilder.newJob(JobC.class).withIdentity("jobC", "group1").build();Trigger triggerC = TriggerBuilder.newTrigger().withIdentity("triggerC", "group1").startNow().build();scheduler.scheduleJob(jobC, triggerC);}} catch (SchedulerException e) {e.printStackTrace();}}
}
3. 配置 Scheduler 和 Job
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class QuartzDependencyExample {public static void main(String[] args) throws SchedulerException {// 创建 Scheduler 实例Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();// 定义 JobAJobDetail jobA = JobBuilder.newJob(JobA.class).withIdentity("jobA", "group1").build();// 创建立即触发的 TriggerTrigger triggerA = TriggerBuilder.newTrigger().withIdentity("triggerA", "group1").startNow().build();// 添加 JobListenerscheduler.getListenerManager().addJobListener(new DependencyJobListener(scheduler));// 调度 JobAscheduler.start();scheduler.scheduleJob(jobA, triggerA);}
}
解释
- JobA、JobB、JobC:分别定义三个独立的 Job 类。
- DependencyJobListener:实现
JobListener
接口,在每个 Job 执行完成后,根据当前执行的 Job 调度下一个 Job。 - Scheduler 配置:在 Scheduler 中注册
DependencyJobListener
,并调度初始的 JobA。
注意事项
- 异常处理:确保在调度下一个任务时正确处理异常,防止由于调度失败导致任务链中断。
- 并发控制:如果有多个任务链,需要确保并发控制,防止多个任务同时调度同一个任务。
- 持久化支持:如果任务链较长或需要持久化,确保使用持久化的 Scheduler 配置,以便在系统重启后恢复任务状态。
3. 任务拦截
在 Quartz 中,可以通过实现 JobListener
来拦截任务的执行。JobListener
提供了几个方法,可以在任务执行前、执行后以及任务被否决时进行拦截和处理。
下面是一个详细的示例代码,展示如何使用 JobListener
来拦截任务的执行,并附有详细注释。
示例代码
1. 定义 Job 类
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;// 定义一个简单的 Job 类
public class SampleJob implements Job {@Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {System.out.println("SampleJob is executing at: " + System.currentTimeMillis());}
}
2. 实现 JobListener
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;// 实现 JobListener 接口
public class MyJobListener implements JobListener {@Overridepublic String getName() {// 返回监听器的名称return "MyJobListener";}@Overridepublic void jobToBeExecuted(JobExecutionContext context) {// 在任务即将执行时调用System.out.println("Job is about to be executed: " + context.getJobDetail().getKey());}@Overridepublic void jobExecutionVetoed(JobExecutionContext context) {// 在任务被否决时调用System.out.println("Job execution was vetoed: " + context.getJobDetail().getKey());}@Overridepublic void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {// 在任务执行完成后调用System.out.println("Job was executed: " + context.getJobDetail().getKey());if (jobException != null) {System.out.println("Job encountered an exception: " + jobException.getMessage());}}
}
3. 配置 Scheduler 和 Job
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;public class QuartzJobListenerExample {public static void main(String[] args) throws SchedulerException {// 创建 Scheduler 实例Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();// 定义一个 JobDetail 实例JobDetail job = JobBuilder.newJob(SampleJob.class).withIdentity("sampleJob", "group1").build();// 创建一个触发器Trigger trigger = TriggerBuilder.newTrigger().withIdentity("sampleTrigger", "group1").startNow().build();// 创建并注册 JobListenerMyJobListener listener = new MyJobListener();scheduler.getListenerManager().addJobListener(listener);// 调度任务scheduler.start();scheduler.scheduleJob(job, trigger);}
}
解释
- SampleJob:定义一个简单的 Job 类,包含一个
execute
方法,打印当前时间。 - MyJobListener:实现
JobListener
接口,提供四个方法:getName
:返回监听器的名称。jobToBeExecuted
:在任务即将执行时调用,打印任务的 Key。jobExecutionVetoed
:在任务被否决时调用,打印任务的 Key。jobWasExecuted
:在任务执行完成后调用,打印任务的 Key 和异常信息(如果有)。
- QuartzJobListenerExample:主类,配置 Scheduler 和 Job:
- 创建 Scheduler 实例。
- 定义一个
JobDetail
实例。 - 创建一个触发器。
- 创建并注册
JobListener
实例。 - 调度任务并启动 Scheduler。
注意事项
- 异常处理:在
jobWasExecuted
方法中处理任务执行中的异常,确保异常信息被记录或处理。 - 并发控制:如果有多个任务并发执行,需要确保监听器的实现是线程安全的。
- 持久化支持:在需要持久化任务状态时,确保 Scheduler 配置支持持久化。
相关文章:
【SpringBoot】调度和执行定时任务--Quartz(超详细)
Quartz 是一个功能强大的任务调度框架,广泛用于在 Java 应用程序中定时执行任务,同时它支持 Cron 表达式、持久化任务、集群等特性。以下是 Quartz 的详细使用教程,包括安装、基本概念、简单示例和高级功能。 1. 安装 Quartz 首先ÿ…...

低代码开发平台系统架构概述
概述 织信低代码开发平台(产品全称:织信Informat)是一款集成了应用设计、运行与管理的综合性平台。它提供了丰富的功能模块,帮助用户快速构建、部署和维护应用程序。织信低代码平台通过集成丰富的功能模块,为用户提供…...
源码编译llama.cpp 、ggml 后端启用自定义BLAS加速
源码编译llama.cpp 、ggml 后端启用自定义BLAS加速 我在llama.cpp 官网上提交了我的解决方案:How to setup OpenBlas on windows? #625 GGML 官网 https://github.com/ggerganov/ggml/issues/959 windows on arm 编译 llama.cpp 、ggml 后端启用自定义BLAS加速 …...

glb数据格式
glb数据格式 glb 文件格式只包含一个glb 文件,文件按照二进制存储,占空间小 浏览 浏览glb工具的很多,ccs,3D查看器等都可以,不安装软件的话用下面网页加载就可以,免费 glTF Viewer (donmccurdy.com) glb…...

手语识别系统源码分享
手语识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …...
Oracle 数据库部署与实施
文章目录 1. macOS 上部署 Oracle 数据库通过 Docker 在 macOS 上部署 2. Linux 上部署 Oracle 数据库直接在 Linux 上部署通过 Docker 在 Linux 上部署 3. Windows 上部署 Oracle 数据库4. 使用 Docker 部署 Oracle 数据库前提条件拉取 Oracle 数据库 Docker 镜像运行 Oracle …...
【Python】 ast.literal_eval 与 eval
一、背景 我在在编写管理后台的过程中,遇到一个小问题,是关于用户名的存储和解码。用户名以base64编码的形式存储在 MySQL 数据库中,并且还保留了b这样的形式,具体为什么要这样存我也不知道,可能是因为有些特殊字符无法直接存储。…...

Java 入门指南:JVM(Java虚拟机)垃圾回收机制 —— 新一代垃圾回收器 ZGC 收集器
文章目录 垃圾回收机制垃圾收集器垃圾收集器分类ZGC 收集器ZGC 的性能优势复制算法指针染色读屏障 ZGC 的工作过程Stop-The-World 暂停阶段并发阶段 垃圾回收机制 垃圾回收(Garbage Collection,GC),顾名思义就是释放垃圾占用的空…...

基于 K8S kubernetes 的常见日志收集方案
目录 1、日志对我们来说到底重不重要? 2、常见的日志收集方案 2.1 EFK 2.2 ELK Stack 2.3 ELKfilebeat 2.4 其他方案 2、elasticsearch组件介绍 3、filebeat组件介绍 3.1 filebeat和beat关系 3.2 filebeat是什么? 3.3 Filebeat工作原理 3.4 …...

Unity3D 小案例 像素贪吃蛇 02 蛇的觅食
Unity3D 小案例 像素贪吃蛇 第二期 蛇的觅食 像素贪吃蛇 食物生成 在场景中创建一个 2D 正方形,调整颜色,添加 Tag 并修改为 Food。 然后拖拽到 Assets 文件夹中变成预制体。 创建食物管理器 FoodManager.cs,添加单例,可以设置…...

【sgCreateCallAPIFunction】自定义小工具:敏捷开发→调用接口方法代码生成工具
<template><div :class"$options.name" class"sgDevTool"><sgHead /><div class"sg-container"><div class"sg-start"><div style"margin-bottom: 10px">调用接口方法定义列表</div…...
京东商品详情的 API 探秘与应用
在当今数字化的商业世界中,获取准确而详细的商品信息对于开发者、商家以及消费者都具有至关重要的意义。京东作为国内领先的电商平台之一,提供了丰富的商品资源和强大的 API 接口,让我们能够轻松获取京东商品的详情信息。本文将带你深入了解如…...

功能测试干了三年,快要废了。。。
8年前刚进入到IT行业,到现在学习软件测试的人越来越多,所以在这我想结合自己的一些看法给大家提一些建议。 最近聊到软件测试的行业内卷,越来越多的转行和大学生进入测试行业,导致软件测试已经饱和了,想要获得更好的待…...

【C++】多态的认识和理解
个人主页 文章目录 ⭐一、多态的概念🎄二、多态的定义及实现1.多态的构成2.实现多态的条件3.虚函数的概念4.虚函数的重写和覆盖5.析构函数的重写6.协变7.override和 final关键字8.重载、重写/覆盖、隐藏这三者的区别 🏠三、纯虚函数和抽象类的关系&#…...
linux-安全管理-用户认证
Linux 安全管理:用户认证 一、概述 用户认证是 Linux 安全管理的核心部分,确保系统能够识别并验证合法用户,同时阻止未经授权的访问。Linux 提供了多种用户认证机制,包括用户名和密码认证、基于密钥的认证、双因素认证ÿ…...

webpack5 构建优化方案看这篇就够了!【Node.js进阶】
无论在面试还是内部晋升,webpack 构建优化方案 一直都是非常重要的部分。 webpack5构建加持 一、项目完成目标二、搭建项目1. 安装koa、koa/router (如果已经配置可路过)2. 创建入口文件3. 安装构建依赖4. 在项目根目录添加 .babelrc 文件5. …...

esp32-C2 对接火山引擎实现智能语音(一)
目录 一、火山引擎大模型简介 1)火山引擎网址: 2)首先需要先注册火山引擎账号 3)语音识别——即语音转为文本 一句话识别 流式语音识别 录音文件识别标准版 录音文件识别极速版 4)语音合成——文本转音频 一、火山引擎大模型简介 火山引擎的智能语音技术,基于业界先…...
【MySQL-初级】mysql基础操作(账户、数据库、表的增删查改)
概述 数据备份与恢复 数据库备份:在cmd下 root用户:sudo mysqldump -u root -p Test > Test.sql普通用户:mysqldump -u zzz -p db_name > db_name.sql 数据库恢复 先创建一个空的数据库在cmd下:sudo mysql -u root -p d…...
centos bash脚本一键运行安装go环境
复制到install_go.sh直接bash install_go.sh运行就完了 echo ----------安装go环境 wget https://go.dev/dl/go1.21.13.linux-amd64.tar.gz tar -zxvf go1.21.13.linux-amd64.tar.gzmkdir /srv cp -r go /srv/echo "PATH$PATH:/srv/go/bin ">> ~/.bashrc echo…...

vue2制作高复用页面
记录一下页面搭建记录,利用vue2组件化开发的思想。这个页面适合于大部分信息管理系统~。模板固定,每次使用,直接修改表单表格参数,api接口等。 以上图页面为例,一个基础数据信息页面可以分为,分类ÿ…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...