Spring+Quartz定时任务集群及其实现

news2024/12/26 21:54:06

原文写的不全执行代码不成功,经我修改后可以正常执行。

原文链接:https://blog.csdn.net/qq_22193961/article/details/137743746

Quartz 是一个开源的作业调度框架,它完全由 Java 写成,并设计用于 J2SE 和 J2EE 应用中。它提供了巨大的灵 活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。它有很多特征,如:数据库支持,集群,插件,EJB 作业预构 建,JavaMail 及其它,支持 cron-like 表达式等等。

quick-start

三个核心类

  • JobDetail 任务类

  • Trigger 触发器

  • Scheduler 调度器

Quartz集群原理
        Quartz的集群部署方案是分布式的,没有负责集中管理的节点,而是利用数据库行锁的方式来实现集群环境下的并发控制。

向Mysql获取行锁的语句:

select * from {0}LOCKS where sched_name = ? and lock_name = ? for update
 {0}会替换为配置文件默认配置的QRTZ_。sched_name为应用集群的实例名,lock_name就是行级锁名。Quartz主要由两个行级锁。 

lock_name    desc
STATE_ACCESS    状态访问锁
TRIGGER_ACCESS    触发器访问锁
        同一集群下,instanceName必须相同,instanceId可自动生成,isClustered为true,持久化存储。 

总结:

数据库存储:Quartz集群使用一个共享的数据库来存储任务和调度信息。每个Quartz实例都连接到同一个数据库,并共享这些任务和调度信息,以确保任务的一致性和可靠性。
选举机制:在Quartz集群中,每个实例都有一个唯一的标识符,称为实例ID。当一个Quartz实例启动时,它会尝试成为集群的主节点。如果没有当前的主节点,那么该实例将成为主节点。如果已经有主节点存在,那么该实例将成为备用节点,并等待主节点故障时接管。
心跳检测:每个Quartz实例都定期发送心跳信号给其他实例,以保持集群的健康状态。如果一个实例在一段时间内没有收到其他实例的心跳信号,那么它会认为主节点已经故障,并尝试成为新的主节点。
Quartz数据库表简介
        Quartz集群依赖于数据库,所以必须首先创建Quartz数据库表。Quartz发布包中包括了所有被支持的数据库平台的SQL脚本,版本:2.3.0, SQL文件是在这个路径下:quartz-2.3.0-SNAPSHOT\src\org\quartz\impl\jdbcjobstore\tables_mysql.sql,总共11张表。

如果不愿意找MySQL脚本看下面

#
# Quartz seems to work best with the driver mm.mysql-2.0.7-bin.jar
#
# In your Quartz properties file, you'll need to set 
# org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
#

DROP TABLE IF EXISTS QRTZ_JOB_LISTENERS;
DROP TABLE IF EXISTS QRTZ_TRIGGER_LISTENERS;
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;


CREATE TABLE QRTZ_JOB_DETAILS
  (
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    JOB_CLASS_NAME   VARCHAR(250) NOT NULL,
    IS_DURABLE VARCHAR(1) NOT NULL,
    IS_VOLATILE VARCHAR(1) NOT NULL,
    IS_STATEFUL VARCHAR(1) NOT NULL,
    REQUESTS_RECOVERY VARCHAR(1) NOT NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_JOB_LISTENERS
  (
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    JOB_LISTENER VARCHAR(200) NOT NULL,
    PRIMARY KEY (JOB_NAME,JOB_GROUP,JOB_LISTENER),
    FOREIGN KEY (JOB_NAME,JOB_GROUP)
        REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_TRIGGERS
  (
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    JOB_NAME  VARCHAR(200) NOT NULL,
    JOB_GROUP VARCHAR(200) NOT NULL,
    IS_VOLATILE VARCHAR(1) NOT NULL,
    DESCRIPTION VARCHAR(250) NULL,
    NEXT_FIRE_TIME BIGINT(13) NULL,
    PREV_FIRE_TIME BIGINT(13) NULL,
    PRIORITY INTEGER NULL,
    TRIGGER_STATE VARCHAR(16) NOT NULL,
    TRIGGER_TYPE VARCHAR(8) NOT NULL,
    START_TIME BIGINT(13) NOT NULL,
    END_TIME BIGINT(13) NULL,
    CALENDAR_NAME VARCHAR(200) NULL,
    MISFIRE_INSTR SMALLINT(2) NULL,
    JOB_DATA BLOB NULL,
    PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (JOB_NAME,JOB_GROUP)
        REFERENCES QRTZ_JOB_DETAILS(JOB_NAME,JOB_GROUP)
);

CREATE TABLE QRTZ_SIMPLE_TRIGGERS
  (
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    REPEAT_COUNT BIGINT(7) NOT NULL,
    REPEAT_INTERVAL BIGINT(12) NOT NULL,
    TIMES_TRIGGERED BIGINT(10) NOT NULL,
    PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_CRON_TRIGGERS
  (
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    CRON_EXPRESSION VARCHAR(200) NOT NULL,
    TIME_ZONE_ID VARCHAR(80),
    PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_BLOB_TRIGGERS
  (
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    BLOB_DATA BLOB NULL,
    PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP),
    FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
);

CREATE TABLE QRTZ_TRIGGER_LISTENERS
  (
    TRIGGER_NAME  VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    TRIGGER_LISTENER VARCHAR(200) NOT NULL,
    PRIMARY KEY (TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_LISTENER),
    FOREIGN KEY (TRIGGER_NAME,TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP)
);


CREATE TABLE QRTZ_CALENDARS
  (
    CALENDAR_NAME  VARCHAR(200) NOT NULL,
    CALENDAR BLOB NOT NULL,
    PRIMARY KEY (CALENDAR_NAME)
);

CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
  (
    TRIGGER_GROUP  VARCHAR(200) NOT NULL, 
    PRIMARY KEY (TRIGGER_GROUP)
);

CREATE TABLE QRTZ_FIRED_TRIGGERS
  (
    ENTRY_ID VARCHAR(95) NOT NULL,
    TRIGGER_NAME VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    IS_VOLATILE VARCHAR(1) NOT NULL,
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    FIRED_TIME BIGINT(13) NOT NULL,
    PRIORITY INTEGER NOT NULL,
    STATE VARCHAR(16) NOT NULL,
    JOB_NAME VARCHAR(200) NULL,
    JOB_GROUP VARCHAR(200) NULL,
    IS_STATEFUL VARCHAR(1) NULL,
    REQUESTS_RECOVERY VARCHAR(1) NULL,
    PRIMARY KEY (ENTRY_ID)
);

CREATE TABLE QRTZ_SCHEDULER_STATE
  (
    INSTANCE_NAME VARCHAR(200) NOT NULL,
    LAST_CHECKIN_TIME BIGINT(13) NOT NULL,
    CHECKIN_INTERVAL BIGINT(13) NOT NULL,
    PRIMARY KEY (INSTANCE_NAME)
);

CREATE TABLE QRTZ_LOCKS
  (
    LOCK_NAME  VARCHAR(40) NOT NULL, 
    PRIMARY KEY (LOCK_NAME)
);


INSERT INTO QRTZ_LOCKS values('TRIGGER_ACCESS');
INSERT INTO QRTZ_LOCKS values('JOB_ACCESS');
INSERT INTO QRTZ_LOCKS values('CALENDAR_ACCESS');
INSERT INTO QRTZ_LOCKS values('STATE_ACCESS');
INSERT INTO QRTZ_LOCKS values('MISFIRE_ACCESS');


commit;

Quartz集群实现方式

引入依赖

 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.9</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.example</groupId>
    <artifactId>demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <mysql.driver.version>8.0.13</mysql.driver.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>

        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.driver.version}</version>
            <scope>runtime</scope>
        </dependency>

        <!-- orm -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>3.0.3</version>
                <configuration>
                    <mainClass>com.example.demo.DemoApplication</mainClass>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

application.properties

# 应用服务 WEB 访问端口
server.port=18080

spring.datasource.url=jdbc:mysql://10.146.4.28:3306/yizhuang?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC

spring.datasource.username=root
spring.datasource.password=uEyeT231$jd#2@R
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.max-active=1000
spring.datasource.max-idle=20
spring.datasource.min-idle=5
spring.datasource.initial-size=10

spring-quartz.properties

#============================================================================
# 配置JobStore
#============================================================================
# JobDataMaps是否都为String类型,默认false
# 若是true的话,便可不用让更复杂的对象以序列化的形式保存到BLOB列中
org.quartz.jobStore.useProperties=false

# 存储相关信息表的前缀,默认QRTZ_
org.quartz.jobStore.tablePrefix = QRTZ_

# 是否加入集群
#是否是应用在集群中,当应用在集群中时必须设置为TRUE,否则会出错。
#如果有多个Quartz实例在用同一套数据库时,必须设置为true。
org.quartz.jobStore.isClustered = true

# 调度实例失效的检查时间间隔 ms
#只用于设置了isClustered为true的时候,设置一个频度(毫秒),用于实例报告给集群中的其他实例。
#这会影响到侦测失败实例的敏捷度。
org.quartz.jobStore.clusterCheckinInterval = 5000

# 当设置为“true”时,此属性告诉Quartz 在非托管JDBC连接上调用setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED)。
org.quartz.jobStore.txIsolationLevelReadCommitted = true

# 数据保存方式为数据库持久化,选择JDBC的存储方式
org.quartz.jobStore.class=org.springframework.scheduling.quartz.LocalDataSourceJobStore

# 数据库代理类,一般org.quartz.impl.jdbcjobstore.StdJDBCDelegate可以满足大部分数据库
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate

#这个值必须datasource的配置信息
org.quartz.jobStore.dataSource = myDataSource

#最大能忍受的触发超时时间,如果超时则认为“失误”
org.quartz.jobStore.misfireThreshold = 60000

#这是JobStore能处理的错过触发的Trigger的最大数量。处理太多(2打)很快就会导致数据库表被锁定够长的时间,
#这样会妨碍别的(还未错过触发)trigger执行的性能。
org.quartz.jobStore.maxMisfiresToHandleAtATime=20

#设置这个参数为true会告诉Quartz从数据源获取连接后不要调用它的setAutoCommit(false)方法。
#在少数情况下是有用的,比如有一个驱动本来是关闭的,但是又调用这个关闭的方法。但是大部分情况下驱动都要求调用setAutoCommit(false)
org.quartz.jobStore.dontSetAutoCommitFalse=false

#这必须是一个从LOCKS表查询一行并对这行记录加锁的SQL。假设没有设置,默认值如下。
#{0}会在运行期间被前面配置的TABLE_PREFIX所代替
org.quartz.jobStore.selectWithLockSQL=SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE

#值为true时告知Quartz(当使用JobStoreTX或CMT)调用JDBC连接的setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE) 方法。这有助于某些数据库在高负载和长时间事务时锁的超时。
org.quartz.jobStore.txIsolationLevelSerializable=false

#============================================================================
# Scheduler 调度器属性配置
#============================================================================
# 调度标识名 集群中每一个实例都必须使用相同的名称,可以为任意字符串,对于scheduler来说此值没有意义,但是可以区分同一系统中多个不同的实例
org.quartz.scheduler.instanceName = ClusterQuartz
# ID设置为自动获取 每一个必须不同
org.quartz.scheduler.instanceId= AUTO

org.quartz.scheduler.rmi.export = false
org.quartz.scheduler.rmi.proxy = false

# 默认false,若是在执行Job之前Quartz开启UserTransaction,此属性应该为true。
#Job执行完毕,JobDataMap更新完(如果是StatefulJob)事务就会提交。默认值是false,可以在job类上使用@ExecuteInJTATransaction注解,
# 以便在各自的job上决定是否开启JTA事务。
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
#一个scheduler节点允许接收的trigger的最大数,默认是1,这个值越大,定时任务执行的越多,但代价是集群节点之间的不均衡。
org.quartz.scheduler.batchTriggerAcquisitionMaxCount=1


#============================================================================
# 配置ThreadPool
#============================================================================
# 线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool

# 指定线程数,一般设置为1-100直接的整数,根据系统资源配置
org.quartz.threadPool.threadCount = 10

# 设置线程的优先级(可以是Thread.MIN_PRIORITY(即1)和Thread.MAX_PRIORITY(这是10)之间的任何int 。默认值为Thread.NORM_PRIORITY(5)。)
org.quartz.threadPool.threadPriority = 5

#加载任务代码的ClassLoader是否从外部继承
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread= true

#是否设置调度器线程为守护线程
org.quartz.scheduler.makeSchedulerThreadDaemon=true
QuartzJob.java
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.quartz.SchedulerException;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.util.Date;

/**
 * @description:
 * @create: 2024-04-13 22:58
 **/
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class QuartzJob extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        try {
            Thread.sleep(2000);
            System.out.println(context.getScheduler().getSchedulerInstanceId());
            System.out.println("taskName=" + context.getJobDetail().getKey().getName());
            System.out.println("执行时间:" + new Date());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

SchedulerConfig.java
import org.quartz.Scheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.Executor;

/**
 * @description: 调度器配置
 * @author: 黎剑
 * @create: 2024-04-13 23:04
 **/
@Configuration
public class SchedulerConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public Scheduler scheduler() throws IOException {
        return schedulerFactoryBean().getScheduler();
    }

    @Bean
    public SchedulerFactoryBean schedulerFactoryBean() throws IOException {
        SchedulerFactoryBean factory = new SchedulerFactoryBean();
        factory.setSchedulerName("cluster_scheduler1");
        // 注入数据源
        factory.setDataSource(dataSource);
        // 选填
        factory.setApplicationContextSchedulerContextKey("application");
        factory.setQuartzProperties(quartzProperties());
        // 读线程池配置
        factory.setTaskExecutor(schedulerThreadPool());
        // 等待设置其他属性
        return factory;
    }

    @Bean
    public Properties quartzProperties() throws IOException {
        PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
        propertiesFactoryBean.setLocation(new ClassPathResource("/spring-quartz.properties"));
        propertiesFactoryBean.afterPropertiesSet();
        return propertiesFactoryBean.getObject();
    }

    @Bean
    public Executor schedulerThreadPool() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        // 处理器的核心数
        taskExecutor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
        // 最大线程数
        taskExecutor.setMaxPoolSize(Runtime.getRuntime().availableProcessors());
        // 容量
        taskExecutor.setQueueCapacity(Runtime.getRuntime().availableProcessors());
        return taskExecutor;
    }
}
StartApplicationListener.java
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @description:
 * @author: 黎剑
 * @create: 2024-04-13 23:23
 **/
@Component
public class StartApplicationListener implements ApplicationListener<ContextRefreshedEvent> {

    @Autowired
    private Scheduler scheduler;

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        // 开启调度
        // 可以先判断触发器是否存在
        /**
         * JobDetail有一个类型为JobKey的重要属性key,相当于是该任务的键值,JobDetail注册到任务调度器Schedule中的时候,key值不允许重复。***整个任务调度过程中,Quartz都是通过Jobkey来唯一识别JobDetail的。试图将重复键值的JobDetail注册到任务调度器中而不指定覆盖的话,是不被允许的。***
         *
         * JobKey可以通过JobBuiler的withIdentity方法指定,该方法接收name或name+group参数,从而唯一确定一个任务JobDetail。
         *
         * 如果在JobDetail创建过程中不指定JobKey的话,Quartz会通过UUID的方式为该任务生成一个唯一的key值。
         *
         * ***所以,同一个Job实现类(也就是同一个任务),可以通过不同的JobKey值注册到任务调度器中、绑定不同的触发器执行!
         */
        TriggerKey triggerKey = TriggerKey.triggerKey("trigger1", "group1");
        try {
            Trigger trigger = scheduler.getTrigger(triggerKey);
            if (trigger == null) {
                Date date=new Date();
                // 触发器
                CronTrigger trigger1=(CronTrigger)TriggerBuilder.newTrigger().withIdentity(triggerKey)
                        .startAt(date)
                        .withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?"))
                        .build();
                //
                JobDetail jobDetail = JobBuilder.newJob(QuartzJob.class)
                        .withIdentity("job1", "group1")
                        .build();
                scheduler.scheduleJob(jobDetail, trigger1);
                scheduler.start();
            }
        } catch (SchedulerException e) {
            e.printStackTrace();
        }


    }
}

 

 

                      

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2178049.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++学习9.26

1、 1、什么是虚函数&#xff1f;什么是纯虚函数&#xff1f; 虚函数就是在基类中声明为 virtual的成员函数&#xff0c;允许在派生类中重写。 纯虚函数就是一个没有函数体额虚函数&#xff0c;在类声明中使用0来特指它是纯虚函数 2、基类为甚么要虚析构函数&#xff1f; 虚…

CC-LINK IE Field Basic通讯设置

一、设备简介 硬件&#xff1a;R08EN、FR-E840-EPB&#xff1b; 软件&#xff1a;GX Works3、FR Configurator2&#xff1b; 二、硬件展示 三、PLC侧参数设置 1.登录配置文件&#xff08;配置文件前期博文已经分享了&#xff0c;自行下载&#xff09; 2.导航→参数→模块参数…

大端、小端区分与判断

大小端的判断是根据系统如何存储二进制数据来判断的 大端顾名思义&#xff0c;以数据的高位做开端的操作系统、小端也是以数据的低位做开端的操作系统 用最简单的例子&#xff1a; 对于数据0x01来说&#xff0c;高位为0低位为1&#xff0c;转十进制&#xff1a; 0x01 0 * 1…

存储技术(CXL、open-channel SSD)

一、CXL技术 1.1 CXL技术要解决的问题 1、对系统和设备的一致性访问 传统的是使用Cache&#xff08;L1/L2/L3&#xff09;和内存的方式实现一致性访问的&#xff0c;通过PCIE总线访问的方式通常是非一致性的读写。 主机对连接到 PCIe 设备内存的每次访问也要由 PCIe 设备处理…

【Mybatis篇】动态SQL的详细带练

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【计算机网络】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 文章目录 &#x1f3af;一.动态SQL简单介绍 &#x1f6a6;动态S…

机器学习:opencv--背景建模

目录 一、背景建模是什么&#xff1f; 二、背景建模的目的 三、背景建模的方法及原理 四、代码实现 1.创建卷积核 2.创建混合高斯模型 3.处理图像 4.绘制人形轮廓 5.条件退出 一、背景建模是什么&#xff1f; 指在计算机视觉中&#xff0c;从视频序列中提取出静态背景…

深入工作流调度的内核

在大数据时代&#xff0c;工作流任务调度系统成为了数据处理和业务流程管理的核心组件&#xff0c;在大数据平台的构建和开发过程中尤为重要。随着数据量的激增和业务需求的多样化&#xff0c;合理的任务调度不仅能够提高资源利用率&#xff0c;还能保证业务流程的稳定和高效运…

Cannon-es.js之Distance Constrait物体约束详解

本文目录 前言最终效果1、Distance Constrait的基本属性和方法1.1 属性1.2 方法 2、Distance Constrait的约束详解2.1 前置代码准备2.2 效果 3、模拟生成小球撞击3.1 代码3.2 效果 前言 在Cannon-es.js中&#xff0c;DistanceConstraint&#xff08;距离约束&#xff09;是一种…

【华为HCIP实战课程三】动态路由OSPF的NBMA环境建立邻居及排错,网络工程师

一、NBMA环境下的OSPF邻居建立问题 上节我们介绍了NBMA环境下OSPF邻居建立需要手动指定邻居,因为NBMA环境是不支持广播/组播的 上一节AR1的配置: ospf 1 peer 10.1.1.4 //手动指定邻居的接口地址,而不是RID peer 10.1.1.5 area 0.0.0.0 手动指定OSPF邻居后抓包查看OSP…

Linux命令:块设备信息查看命令lsblk的具体介绍

目录 一.lsblk的介绍 二.基础用法 2.1基础命令和示意图 2.2详细说明 三.进阶用法 3.1列出指定的块设备信息 3.2查看设备完整路径 3.3强制以树形式输出信息 3.4其他参数 一.lsblk的介绍 lsblk是Linux系统中的一个命令行工具&#xff0c;用于列出所有可用的块设备&#…

基于单片机的可调式中文电子日历系统

** 文章目录 前言概要功能设计软件设计效果图 程序文章目录 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师&#xff0c;一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们…

RK3588主板PCB设计学习(六)

可以在其它层对过孔进行削盘处理&#xff0c; 可以看到&#xff0c;这里有些过孔用不上&#xff0c;在这一层进行了削盘处理&#xff1a; 对于这种电源层进行铺铜操作的时候&#xff0c;如果不进行削盘处理的话这些焊盘可能导致这个电源层面不完整&#xff0c;存在割裂的风险&a…

平安养老险肇庆中心支公司开展“2024年金融教育宣传月”活动

为加强消费者金融教育宣传&#xff0c;切实提升社会公众金融素养&#xff0c;有效防范化解金融风险&#xff0c;营造和谐健康金融环境&#xff0c;在肇庆金融监管分局指导下&#xff0c;平安养老险肇庆中心支公司开展金融教育宣传月暨反洗钱宣传月系列活动。 9月11日&#xff…

基于Springboot+Vue的高校教室资源管理系统的设计与实现(含源码+数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 这个系…

学习docker第二弹------基本命令[帮助启动类命令、镜像命令、容器命令]

docker目录 前言基本命令帮助启动类命令停止docker服务查看docker状态启动docker重启docker开机启动docker查看概要信息查看总体帮助文档查看命令帮助文档 镜像命令查看所有的镜像 -a查看镜像ID -q在仓库里面查找redis拉取镜像查看容器/镜像/数据卷所占内存删除一个镜像删除多个…

美图AI短片创作工具MOKI全面开放 支持生成配乐、细节修改

人工智能 - Ai工具集 - 集合全球ai人工智能软件的工具箱网站 美图公司近日宣布&#xff0c;其研发的AI短片创作工具MOKI已正式向所有用户开放。这款专注于AI短片创作的工具&#xff0c;提供了包括动画短片、网文短剧等多种类型视频内容的生成能力&#xff0c;致力于为用户带来…

Foo a30 = Foo(123);会调用哪些构造函数

一、解答 在您提供的代码中&#xff0c;表达式 Foo a30 Foo(123); 会直接调用 Foo 类中接受一个 int 类型参数的构造函数。这里是构造函数的调用过程&#xff1a; Foo(123) 创建了一个临时的 Foo 对象&#xff0c;使用的是接受 int 参数的构造函数。这个构造函数内部会执行 c…

6.数据结构与算法-线性表的链式表示和实现-单链表

链式存储结构 与链式存储有关的术语 单链表&#xff0c;双链表&#xff0c;循环链表 头指针&#xff0c;头节点&#xff0c;首元节点 有无头节点的区别 如何表示空表 设置头节点的好处 头节点的数据域内装什么 链表&#xff08;链式存储&#xff09;的特点 带头节点的单链表 单…

【中级通信工程师】综合能力:2024年真题回顾(附答案)

【零基础3天通关中级通信工程师】 综合能力&#xff1a;2024年真题回顾 本文是根据参加考试的回忆并且结合网上几版资料复原的2024年通信考试中级《综合能力》的真题考卷&#xff0c;旨在为广大考生提供复习和备考的参考&#xff0c;试卷大体和真题相符&#xff0c;部分选项回…

算法闭关修炼百题计划(一)

多看优秀的代码一定没有错&#xff0c;此篇博客属于个人学习记录 1.两数之和2.前k个高频元素3.只出现一次的数字4.数组的度5.最佳观光组合6.整数反转7.缺失的第一个正数8.字符串中最多数目的子序列9.k个一组翻转链表10.反转链表II11. 公司命名12.合并区间13.快速排序14.数字中的…