利用Spring中的SchedulingConfigurer实现数据库配置化定时任务

目录

1.利用@Scheduled来实现传统的定时任务

2.两者的区别

3.Spring中的SchedulingConfigurer来拓展定时任务的灵活性

1)UrTaskConfig

2)TaskMain

3)BaseTask

4)效果

(1)插入配置定时任务的sql语句

(2)写一个实现类去继承BaseTask

代码示例:

pom.xml

enums枚举类

EnumTask

EnumTaskRule

数据库Dao操作

UrConfigAllDao

UrTaskTestDao

BaseTask

TaskMain

UrTaskConfig

TaskTextJob

数据库

ur_config_all

ur_task_test


1.利用@Scheduled来实现传统的定时任务

实现定时任务的方式有多种,其中最广为人知的一种是利用@Scheduled,来在代码里面实现代码的定时任务。但是这样简单粗暴的方式可以实现定时任务的功能,但是在我们之后的开发过程中会频繁的使用到定时任务,如果拓展定时任务实现方式的灵活性与拓展性就成了问题。

直接使用@Scheduled的方式来实现定时任务有是有效,但是每增加一个定时任务,或者要修改定时任务的规则,比如修改cron表达式为固定频率或者固定次数,每次修改都要重启一下服务,为我们的开发带来了许多不方便的地方。因为想了一下,可以利用SPring中的SchedulingConfigurer来实现定时任务的拓展性

@Component
@Slf4j
public class MonthEndDataSync {@Autowiredprivate CarBrandService carBrandService;@Autowiredprivate CarModelsService carModelsService;@Autowiredprivate CarSeriesService carSeriesService;@Scheduled(cron = "0 0 3 L *  *")public void MonthEndDataSync() {carBrandService.syncCarBrand4Network();carModelsService.syncCarModels4Network();carSeriesService.syncCarSeries4Network();log.debug("[MonthEndDataSync start] 月底同步数据完成");}

2.两者的区别

1.@Scheduled不支持动态修改定时周期,只能停止服务器,修改cron表达式,再启动服务器;SchedulingConfigurer可以动态修改

2.@Scheduled只能是单线程,而SchedulingConfigurer默认是单线程,可以通过添加线程池,实现多线程下定时任务的运行

3.Spring中的SchedulingConfigurer来拓展定时任务的灵活性

总体思路:

1)UrTaskConfig

UrTaskConfig这一类用来获取定时任务的一些配置,定时任务的规则,定时任务的状态,以及定时任务的类型(cron表达式、固定频率、固定间隔)

2)TaskMain

实现SchedulingConfigurer配置Spring的定时任务调度器,在TaskMain类中实现configureTasks方法,根据不同的定时任务规则来实现不同的定时任务。

3)BaseTask

是一个抽象类实现Runnable接口,实现了Runnable接口。这意味着BaseTask的实例可以被当作线程来运行,即可以被提交给线程池或者由Thread对象直接启动。

在BaseTask中定义了三个抽象方法before(),excute(),after(),方法需要由具体的任务子类来实现,可以创建多个不同的任务类,它们都继承自BaseTask,并实现自己的before(),excute(),after()方法。

4)效果

设置一个每5秒执行的定时任务

最终的效果,在我们实际开发的过程中想要实现一个定时任务就只需要做两步

(1)插入配置定时任务的sql语句

例如

INSERT INTO `task_db`.`ur_config_all` (`ID`, `TABLE_NAME`, `COLUMN_NAME`, `S_ID`, `VALUEE`) VALUES (1, 'TASK_WORK', 'TaskTextJob', 'CORN_RULE', '*/5 * * * * *');

INSERT INTO `task_db`.`ur_config_all` (`ID`, `TABLE_NAME`, `COLUMN_NAME`, `S_ID`, `VALUEE`) VALUES (2, 'TASK_WORK', 'TaskTextJob', 'CORN_STATUS', '1');

INSERT INTO `task_db`.`ur_config_all` (`ID`, `TABLE_NAME`, `COLUMN_NAME`, `S_ID`, `VALUEE`) VALUES (3, 'TASK_WORK', 'TaskTextJob', 'CORN_TYPE', '1');

(2)写一个实现类去继承BaseTask

这样就会开启一个定时任务例如:TaskTextJob

代码示例:

pom.xml
<?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><groupId>org.example</groupId>
<artifactId>untitled</artifactId><version>1.0-SNAPSHOT</version><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>16</source><target>16</target></configuration></plugin></plugins></build><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring-boot-starter.version>3.1.1</spring-boot-starter.version><mybatis-plus-boot-starter.version>3.5.3.1</mybatis-plus-boot-starter.version><fastjson.version>2.0.32</fastjson.version><mysql-connector-j.version>8.0.33</mysql-connector-j.version><disruptor.version>3.4.2</disruptor.version><spring-boot-starter-web.version>3.1.1</spring-boot-starter-web.version></properties><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.1</version><relativePath/> <!-- lookup parent from repository --></parent><dependencies><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatis-plus-boot-starter.version}</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson.version}</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>${mysql-connector-j.version}</version></dependency><dependency><groupId>com.lmax</groupId><artifactId>disruptor</artifactId><version>${disruptor.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>3.1.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>${spring-boot-starter-web.version}</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.18</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId><version>3.1.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId><version>3.1.1</version></dependency></dependencies></project>
enums枚举类
EnumTask
public enum EnumTask {corn_task("TASK_WORK", "类名", "CORN_RULE", "定时任务"),corn_task_status("TASK_WORK", "类名", "CORN_STATUS", "定时任务开关"),corn_task_type("TASK_WORK", "类名", "CORN_TYPE", "定时任务类型");private final String tableName;private final String columnName;private final String sId;private final String valuee;EnumTask(String tableName, String columnName, String sId, String valuee) {this.columnName = columnName;this.sId = sId;this.valuee = valuee;this.tableName = tableName;}public String getTableName() {return tableName;}public String getColumnName() {return columnName;}public String getsId() {return sId;}public String getValuee() {return valuee;}
}
EnumTaskRule
public enum EnumTaskRule {ONE("1","corn表达式"),TWO("2","固定频率"),THREE("3","固定间隔"),STATUS1("1","开启"),STATUS0("0","禁用");private final String code;private final String msg;EnumTaskRule(String code, String msg) {this.code = code;this.msg = msg;}public String getCode() {return code;}public String getMsg() {return msg;}
}
数据库Dao操作
UrConfigAllDao
public interface UrConfigAllDao extends BaseMapper<UrConfigAll> {@Select("""select TABLE_NAME, COLUMN_NAME, S_ID, VALUEE,IDfrom ur_config_allwhere TABLE_NAME = #{tableName}and COLUMN_NAME = #{columnName}and S_ID = #{sId}""")UrConfigAll queryUrConfigAll(String tableName, String columnName, String sId);
}
UrTaskTestDao
public interface UrTaskTestDao extends BaseMapper<UrTaskTest> {@Select("""select ID, TASK_NAME, PROCESS_ID, CREATE_TIME, UPDATE_TIME,VERSION,STATUS,CLASS_NAME,METHON,CRON,CRON_TYPE,NEXT_EXEC_TIMEfrom ur_task_testwhere TASK_NAME = #{taskName}""")UrTaskTest selectByTaskName(String taskName);@Update("""update ur_task_test setPROCESS_ID = #{processId},UPDATE_TIME = #{updateTime},VERSION = VERSION+1where  TASK_NAME = #{taskName} and  VERSION=#{version}""")int updateProcessorIDandVersionByTaskName(UrTaskTest urTaskTest);@Delete("""DELETE FROM ur_task_test;""")int deleteAll();@Insert("""insert into ur_task_test (TASK_NAME, PROCESS_ID,CREATE_TIME, UPDATE_TIME,VERSION,STATUS,CLASS_NAME,METHON,CRON,CRON_TYPE,NEXT_EXEC_TIME)values (#{taskName}, #{processId},#{createTime},  #{updateTime},#{version},  #{status},  #{className},  #{methon} ,  #{cron}, #{cronType},#{nextExecTime})""")int insert2(UrTaskTest urTaskTest);
}
BaseTask
@Component
public abstract class BaseTask implements Runnable{private final static Logger log = LoggerFactory.getLogger(BaseTask.class);@Autowiredprivate UrConfigAllService urConfigAllService;@Autowiredprivate UrTaskTestService taskTestService ;private String taskName;public String getTaskName() {return taskName;}public void setTaskName(String taskName) {this.taskName = taskName;}//获取数据库中关于定时任务的配置UrTaskConfig getUrTaskConfig(){UrTaskConfig urTaskConfig=new UrTaskConfig();return urTaskConfig.getUrTaskConfig(taskName,urConfigAllService);}//执行前public abstract void before();//执行public abstract void execute();//执行后public abstract void after();@Overridepublic  void run(){//生成任务进程uuidString uuid = UUID.randomUUID().toString();//根据taskName在ur_task_test表中查询执行进程UrTaskTest urTaskTest = taskTestService.selectByTaskName(taskName);//获取任务进程的statusif (urTaskTest==null){System.out.println("任务 :" + taskName + " 进程不存在");return;}//判断status,如果是禁用就直接returnif (StringUtils.equals(String.valueOf(urTaskTest.getStatus()),EnumTaskRule.STATUS0.getCode()))return;//uuid设置为进程idurTaskTest.setProcessId(uuid);//版本也是urTaskTest.setVersion(urTaskTest.getVersion());//更新ur_task_test中的字段System.out.println("更新ur_task_test中的字段"+urTaskTest);int i=taskTestService.updateProcessIDandVersionByTaskName(urTaskTest);//更新返回为0,则该任务已经在其他节点执行if (i==0){System.out.println("任务 :" + taskName + " 已经在其他节点执行");return;}//更新成功则任务进程开始System.out.println("任务 :" + uuid + " 执行开始");try {//前置处理before();execute();//后置处理after();}catch (Exception e){throw new RuntimeException(e);}finally {urTaskTest.setTaskName(taskName);urTaskTest.setProcessId(null);urTaskTest.setUpdateTime(new Date());System.out.println("任务进程查看urTaskTest的值 :" + urTaskTest + " 运行结束");taskTestService.updateProcessIDandVersionByTaskName(urTaskTest);System.out.println("任务进程 :" + uuid + " 运行结束");}}}
TaskMain
@Component
public class TaskMain implements SchedulingConfigurer {@Autowiredprivate ApplicationContext applicationContext;@Autowiredprivate UrTaskTestService urTaskTestService;private final static Logger log = LoggerFactory.getLogger(TaskMain.class);//注册定时任务@Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar) {//获取所有的定时任务,它们都是BaseTask类的实例或其子类Map<String, BaseTask> map = applicationContext.getBeansOfType(BaseTask.class);System.out.println("获取所有的定时任务"+map);//开始线程数taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));//清除所有的进程urTaskTestService.deleteAll();System.out.println("获取所有的定时任务111"+map);//遍历map(存储所有BaseTask bean的Map),针对每个bean执行相应的操作for (String key:map.keySet()){//为每个定时任务创建一个UrTaskTest对象,并设置其属性,如创建时间、任务名称、版本等System.out.println("为每个定时任务创建一个UrTaskTest对象"+key);UrTaskTest urTaskTest=new UrTaskTest().setCreateTime(new Date()).setTaskName(key).setVersion(0L);//从Map中获取当前key对应的BaseTask对象,并设置其任务名称taskNmaeBaseTask baseTask = map.get(key);baseTask.setTaskName(key);//从BaseTask对象中获取TaskConfigAll对象,并获取其配置信息UrTaskConfig urTaskConfig = baseTask.getUrTaskConfig();String cornStatus = urTaskConfig.getCornStatus();String taskName = urTaskConfig.getTaskName();//如果定时任务的cronStatus为TaskEnum.status0.getCode(),表示任务已停止,则跳过当前循环。if (StringUtils.equals(cornStatus, EnumTaskRule.STATUS0.getCode())){System.out.println("{} 定时任务配置状态为禁用"+urTaskConfig);continue;}//设置urTaskControl的状态为TaskEnum.status1.getCode()urTaskTest.setStatus(EnumTaskRule.STATUS1.getCode());//如果cron(定时规则)为空,则记录日志并跳过当前循环String corn = urTaskConfig.getCorn();if (StringUtils.isEmpty(corn)){System.out.println("{} 定时任务没配置定时的规则规则"+taskName);continue;}//设置urTaskControlAll的其他属性,如cronType、cron、methon和classNameurTaskTest.setCronType(urTaskConfig.getCornModern());urTaskTest.setCron(corn);urTaskTest.setMethon("execute()");urTaskTest.setClassName(baseTask.getClass().getName());//根据urTaskConfig.getCornModern()的值判断注册何种类型的定时任务try {//如果urTaskConfig.getCornModern()的值为TaskEnum.ONE.getCode(),则注册一个基于CronTrigger的触发任务if (EnumTaskRule.ONE.getCode().equals(urTaskConfig.getCornModern())){taskRegistrar.addTriggerTask(baseTask,sheduledConfig->{Date date = new CronTrigger(corn).nextExecutionTime(sheduledConfig);SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String formattedDate = sdf.format(date);// 打印格式化后的日期System.out.println(taskName + "执行定时任务的时间:" + formattedDate);return date.toInstant();});//如果urTaskConfig.getCornModern()的值为TaskEnum.TWO.getCode(),则注册一个固定频率的定时任务}else if (EnumTaskRule.TWO.getCode().equals(urTaskConfig.getCornModern())){int parseInt = Integer.parseInt(corn);taskRegistrar.addFixedRateTask(baseTask,parseInt);System.out.println("已添加固定频率任务,频率为:" + parseInt);//如果urTaskConfig.getCornModern()的值为TaskEnum.THREE.getCode(),则注册一个固定次数的定时任务}else if (EnumTaskRule.THREE.getCode().equals(urTaskConfig.getCornModern())){int fixedDelay = Integer.parseInt(corn);taskRegistrar.addFixedDelayTask(baseTask, fixedDelay);}else {//最后判断是否有定时任务的规则System.out.println("{} 定时任务未找到动态方法"+taskName);continue;}//插入urTaskTest一条数据到定时任务的进程表中urTaskTestService.insert2(urTaskTest);}catch (Exception e){throw new RuntimeException(e);}}}
}
UrTaskConfig
@Data
@Accessors(chain = true)
public class UrTaskConfig {private final static Logger log = LoggerFactory.getLogger(UrTaskConfig.class);//定时任务类名private String taskName;//定时任务规则private String corn;//定时任务状态private String cornStatus;//定时任务的类型private String cornModern;public UrTaskConfig getUrTaskConfig(String taskName, UrConfigAllService urConfigAllService){//获取定时任务规则UrConfigAll urConfigAll = urConfigAllService.getUrConfigAll(EnumTask.corn_task.getTableName(), taskName, EnumTask.corn_task.getsId());String rule = Optional.ofNullable(urConfigAll).map(UrConfigAll::getValuee).orElse(null);//获取定时任务状态UrConfigAll urConfigAll1 = urConfigAllService.getUrConfigAll(EnumTask.corn_task_status.getTableName(), taskName, EnumTask.corn_task_type.getsId());String ruleState = Optional.ofNullable(urConfigAll1).map(UrConfigAll::getValuee).orElse(null);//定时任务任务类型UrConfigAll urConfigAll2 = urConfigAllService.getUrConfigAll( EnumTask.corn_task_type.getTableName(), taskName,EnumTask.corn_task_type.getsId());String ruleType = Optional.ofNullable(urConfigAll2).map(UrConfigAll::getValuee).orElse(EnumTaskRule.ONE.getCode());UrTaskConfig urTaskConfig=new UrTaskConfig().setTaskName(taskName).setCorn(rule).setCornStatus(ruleState).setCornModern(ruleType);System.out.println("定时任务配置参数"+JSON.toJSONString(urTaskConfig) );return urTaskConfig;}
}
TaskTextJob
@Component
public class TaskTextJob extends BaseTask {@Overridepublic void before() {}@Overridepublic void execute() {// 获取当前时间LocalDateTime currentTime = LocalDateTime.now();// 定义格式化模板,精确到秒DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");// 格式化当前时间String formattedTime = currentTime.format(formatter);// 输出格式化后的时间System.out.println("执行定时任务测试: " + formattedTime);}@Overridepublic void after() {}
}

数据库
ur_config_all
CREATE TABLE `ur_config_all` (`ID` int NOT NULL AUTO_INCREMENT COMMENT '主键',`TABLE_NAME` varchar(255) NOT NULL,`COLUMN_NAME` varchar(255) NOT NULL,`S_ID` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,`VALUEE` varchar(255) NOT NULL,PRIMARY KEY (`ID`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
ur_task_test
CREATE TABLE `ur_task_test` (`ID` int NOT NULL AUTO_INCREMENT COMMENT '任务ID',`TASK_NAME` varchar(255) NOT NULL COMMENT '任务名',`PROCESS_ID` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '进程ID',`CREATE_TIME` datetime NOT NULL COMMENT '创建时间',`UPDATE_TIME` datetime DEFAULT NULL COMMENT '更新时间',`VERSION` bigint NOT NULL COMMENT '版本',`STATUS` varchar(50) NOT NULL COMMENT '状态',`CLASS_NAME` varchar(255) NOT NULL COMMENT '类路径',`METHON` varchar(255) NOT NULL COMMENT '方法',`CRON` varchar(255) DEFAULT NULL COMMENT '定时规则 有2种值,表达式和毫秒',`CRON_TYPE` varchar(50) DEFAULT NULL COMMENT '定时任务类型 1:cron表达式,2:固定频率,3:固定间隔',`NEXT_EXEC_TIME` datetime DEFAULT NULL COMMENT '任务名',PRIMARY KEY (`ID`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

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

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

相关文章

windows10开机自动启动 - 添加启动项 - 设置软件自动启动

前言 无需安装额外软件&#xff0c;可手动决定开机自动启动什么软件。 步骤 1&#xff09;开始&#xff0c;运行"shell:startup"&#xff0c;即可打开自启动目录 shell:startup 2&#xff09;将软件的快捷方式 扔进去&#xff0c;ok&#xff01; 3&#xff09;重启…

【大语言模型LLM】- Meta开源推出的新一代大语言模型 Llama 3

&#x1f525;博客主页&#xff1a;西瓜WiFi &#x1f3a5;系列专栏&#xff1a;《大语言模型》 很多非常有趣的模型&#xff0c;值得收藏&#xff0c;满足大家的收集癖&#xff01; 如果觉得有用&#xff0c;请三连&#x1f44d;⭐❤️&#xff0c;谢谢&#xff01; 长期不…

“五之链”第十六期沙龙活动在呆马科技成功举办

2024年4月19日&#xff0c;由临沂呆码区块链网络科技有限公司&#xff08;呆马科技&#xff09;承办的第十六期“五之链”物流主题沙龙活动成功举办。此次活动邀请了政府相关部门、知名科研院所、物流企业等20余家单位参与&#xff0c;共同探讨物流数据要素流通与智能应用的发展…

2024深圳杯数学建模挑战赛B题:批量工件并行切割下料问题思路代码成品论文分析

更新完整代码和成品完整论文 《2024深圳杯&东三省数学建模思路代码成品论文》↓↓↓ https://www.yuque.com/u42168770/qv6z0d/zx70edxvbv7rheu7?singleDoc# 问题重述 深圳杯数学建模挑战赛2024B题&#xff1a;批量工件并行切割下料问题 板材切割下料是工程机械领域重要…

信息系统项目管理师0066:过程管理(5信息系统工程—5.1软件工程—5.1.6过程管理)

点击查看专栏目录 文章目录 5.1.6过程管理1.成熟度模型2.成熟度等级5.1.6过程管理 软件过程能力是组织基于软件过程、技术、资源和人员能力达成业务目标的综合能力。包括治理能力、开发与交付能力、管理与支持能力、组织管理能力等方面。软件过程能力成熟度是指组织在提升软件产…

一个docker配置mysql主从服务器

这也就是因为穷&#xff0c;不然谁用一个docker配置主从&#xff0c;哈哈 既然成功了就记录下。过程挺折磨人的。 首先要保证你的电脑安装好了docker 为了保证docker当中主从能正常连网&#xff0c;现在docker里面创建一个网络环境 docker network create --driver bridge mysq…

开曼群岛:Web3企业的乐园

开曼群岛&#xff1a;Web3企业的理想之地 开曼群岛&#xff0c;在数字革命中大放异彩。近年来&#xff0c;该地区成立的Web3企业数量显著增加&#xff0c;如果保持目前的发展速度&#xff0c;并持续优化立法&#xff0c;那么扩展的速度将无可限量。本文将探讨推动这一增长的关…

Linux防火墙相关命令以及ip白名单配置

Linux防火墙相关命令以及ip白名单配置 firewall防火墙基础命令查看防火墙的服务状态查看防火墙的状态服务的开启、关闭和重启查看防火墙规则端口的查询、开放和关闭重启防火墙 防火墙白名单配置部分参数介绍 firewall防火墙基础命令 查看防火墙的服务状态 systemctl status f…

SAP ECC VS S4 HANA FICO

由于SAP 对于ECC官网的支持将于2027年12月31日结束&#xff0c;也就是说ECC系统从那时候起将不再得到SAP的官网支持与维护&#xff0c;所以近些年&#xff0c;很多企业都在进行从ECC到S4HANA版本的升级。 总的来说&#xff0c;两个版本的差别&#xff0c;在各个模块中&#xf…

文字转粤语语音怎么转?文字转语音

文字转粤语语音怎么转&#xff1f;文字转粤语语音的应用&#xff0c;不仅展现了现代科技的魅力&#xff0c;也为我们提供了更加便捷的交流方式。它们将文字转化为粤语发音&#xff0c;让我们能够更直观地感受粤语的韵味和魅力。同时&#xff0c;这些软件还具备高度的可定制性&a…

普乐蛙VR航天航空体验馆VR双人旋转座椅元宇宙VR飞船

多长假来袭&#xff01;&#xff01;想为门店寻找更多新鲜有趣的吸粉体验&#xff1f;想丰富景区体验&#xff1f;别着急&#xff0c;小编为你准备了一款爆款设备——时光穿梭机&#xff0c;720无死角旋转&#xff01;&#xff01;吸睛、刺激体验&#xff0c;将亲子、闺蜜、情侣…

探索GNDVI:绿光归一化植被指数的意义与应用

随着遥感技术的发展&#xff0c;植被指数成为了评估地表植被覆盖和健康状况的重要工具之一。GNDVI&#xff08;Green Normalized Difference Vegetation Index&#xff0c;绿光归一化植被指数&#xff09;作为一种新型的植被指数&#xff0c;在区分不同类型的植被覆盖和监测植被…

android openGL ES详解

1、渲染线程与主线程的通信 两个线程之间的通信可以用如下方法: 在主线程中的 GLSurfaceView 实例可以调用 queueEvent( &#xff09;方法传递一个 Runnable 给后台渲染线程&#xff0c;渲染线程可以调用 Activity 的 runOnUIThread()来传递事件 (event) 给主线程。 2、顶点…

FPGA秋招-笔记整理(1)

一、关键路径 关键路径通常是指同步逻辑电路中&#xff0c;组合逻辑时延最大的路径&#xff08;这里我认为还需要加上布线的延迟&#xff09;&#xff0c;也就是说关键路径是对设计性能起决定性影响的时序路径。也就是静态时序报告中WNS&#xff08;Worst Nagative Slack&…

异地组网、内部网络需求同时满足,贝锐企业路由器G300开箱体验

由于公司最近新增了办事处&#xff0c;作为IT管理员的我不仅需要搞定办事处的网络&#xff0c;还需要解决远程访问公司内部办公系统以及文件存储服务器的问题。 在此之前&#xff0c;只有少量人员出差时&#xff0c;我们采用了虚拟专网方案来进行远程访问。然而&#xff0c;新…

JVM--Java对象到底存在哪?

Java对象存放在堆中&#xff0c;但堆又分为新生代和老年代&#xff0c;新生代又细分为 Eden、From Survivor、To Survivor。那我们创建的对象到底在哪里&#xff1f; 堆分为新生代和老年代&#xff0c;新生代用于存放使用后就要被回收的对象&#xff08;朝生夕死&#xff09;&a…

如何将web content项目导入idea并部署到tomcat

将Web Content项目导入IntelliJ IDEA并部署到Tomcat主要涉及以下几个步骤&#xff1a; 1. 导入Web Content项目 打开IntelliJ IDEA。选择“File” -> “New” -> “Project from Existing Sources…”。浏览到你的Web Content项目的文件夹&#xff0c;并选择它。Intell…

一文整理完MySQL关系型数据库相关知识

MySQL关系型数据库 1. 介绍1.1 MySQL 2. 安装3. SQL语句4. SQL分类5. DDL5.1 库的DDL5.2 表、列的DDL 6. DML6.1 添加数据6.2 修改数据6.3 删除数据 7. DQL7.1 基础查询7.2 条件查询7.3 排序查询7.4 聚合函数7.5 分组查询7.6 分页查询 8. 约束8.1 约束分类 9. 多表查询9.1 内连…

fnm:Rust开发的高效Node版本管理工具

简介 fnm 是一个基于 Rust 开发的 Node 版本管理工具&#xff0c;它的目标是提供一个快速、简单且可靠的方式来管理 Node.js 的不同版本。同时&#xff0c;它是跨平台的&#xff0c;支持 macOS、Linux、Windows。&#x1f680; Fast and simple Node.js version manager, buil…

【QT进阶】Qt http编程之实现websocket server服务器端

往期回顾 【QT进阶】Qt http编程之json解析的简单介绍-CSDN博客 【QT进阶】Qt http编程之nlohmann json库使用的简单介绍-CSDN博客 【QT进阶】Qt http编程之websocket的简单介绍-CSDN博客 【QT进阶】Qt http编程之实现websocket server服务器端 一、最终效果 通过ip地址和端口…