基于SpringBoot实现轻量级的动态定时任务调度
在使用SpringBoot框架进行开发时,一般都是通过@Scheduled注解进行定时任务的开发:
@Component
public class TestTask
{@Scheduled(cron="0/5 * * * * ? ") //每5秒执行一次public void execute(){SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); log.info("任务执行" + df.format(new Date()));}
}
但是这种方式存在一个问题,那就是任务的周期控制是死的,必须编写在代码中,如果遇到需要在系统运行过程中想中止、立即执行、修改执行周期等动态操作的需求时,使用注解的方式便不能满足了,当然为了满足此种需求可以额外再引入其他任务调度插件(例如XXL-Job等),但是引入其他组件是需要衡量成本的,额外的依赖成本、组件的维护成本、开发的复杂度等等,所以如果系统体量不是那么大,完全没必要通过增加组件来完成,可以基于SpringBoot框架实现一套内置轻量级的任务调度。
设计思路
整体设计
这里我们把定时任务以类作为基础单位,即一个类为一个任务,然后通过配置数据的方式,进行任务的读取,通过反射生成任务对象,使用SpringBoot本身的线程池任务调度,完成动态的定时任务驱动,同时通过接口支撑实现相应的REST API对外暴露接口
任务模型
首先基于模板模式,设计基础的任务执行流程抽象类,定义出一个定时任务需要执行的内容和步骤和一些通用的方法函数,后续具体的定时任务直接继承该父类,实现该父类的before、start、after三个抽象函数即可,所有公共操作均在抽象父类完成
特殊说明:
基于此方法创建的类是不归Spring的容器管理的,所以自定义的任务子类中是无法使用SpringBoot中的任何注解,尤其在自定义任务类中如果需要依赖其他Bean时,需要借助抽象父类AbstractBaseCronTask中已经实现的<T> T getServer(Class<T> className)来完成,getServer的实现如下:
public <T> T getServer(Class<T> className){return applicationContext.getBean(className);}
是通过SpringBoot中的ApplicationContext接口来获取Spring的上下文,以此来满足可以获取Spring中其他Bean的诉求。
例如,有个定时任务TaskOne类,它需要使用UserService类中的 caculateMoney()的方法,势必这个定时任务需要依赖UserService类,而TaskOne并非是Spring创建的对象,而是我们人为干预生成的对象,所以它是不在Spring的Bean管理范围的,自然也就无法使用@Autowird等方式注入UserService类,此时就需要使用getServer方法来获取UserService对象
//自定义定时任务类
public class TaskOne extends AbstractBaseCronTask {private UserService userService;public TestTask(TaskEntity taskEntity) {super(taskEntity);}@Overridepublic void beforeJob() {//任务运行第一步,先将userService进行变量注入userService = getServer(UserService.class);……}@Overridepublic void startJob() {if(XXXX){//直接调用getServer获取需要的beanUser user = getServer(UserMapper.class).findUser("111223")userService.caluateMoney(user);//……其他代码}}@Overridepublic void afterJob() {}
}
任务对象加载过程
核心逻辑在于利用反射,在SpringBoot启动后动态创建相应的定时任务类,并将其放置到SpringBoot的定时线程池中进行维护,同时将该对象同步存放至内存中一份,便于可以实时调用,当进行修改任务相关配置时,需要重新加载一次内容。
public class TaskScheduleServerImpl implements TaskScheduleServer {//正在运行的任务private static ConcurrentHashMap<String, ScheduledFuture> runningTasks = new ConcurrentHashMap<>();//线程池任务调度private ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();public boolean addTaskToScheduling(TaskEntity task) {if(!runningTasks.containsKey(task.getTaskId())){try{Class<?> clazz = Class.forName(task.getTaskClass());Constructor c = clazz.getConstructor(TaskEntity.class);AbstractBaseCronTask runnable = (AbstractBaseCronTask) c.newInstance(task);//反射方式生成对象不属于Spring容器管控,对于Spring的bean使用需要手动注入runnable.setApplicationContext(context);CronTrigger cron = new CronTrigger(task.getTaskCron());//put到runTasksrunningTasks.put(task.getTaskId(), Objects.requireNonNull(this.threadPoolTaskScheduler.schedule(runnable, cron)));//存入内存中,便于外部调用ramTasks.put(task.getTaskId(),runnable);task.setTaskRamStatus(1);taskInfoOpMapper.updateTaskInfo(task);return true;}catch (Exception e){log.error("定时任务加载失败..."+e);}}return false;}
}
部分源码
这里将配置内容放入数据库中,直接以数据库中的表作为任务配置的基础
/**
* 任务对象
**/
@Data
public class TaskEntity implements Serializable {//任务唯一IDprivate String taskId;//任务名称private String taskName;//任务描述private String taskDesc;//执行周期配置private String taskCron;//任务类的全路径private String taskClass;//任务的额外配置private String taskOutConfig;//任务创建时间private String taskCreateTime;//任务是否启动,1启用,0不启用private Integer taskIsUse;//是否随系统启动立即执行private Integer taskBootUp;//任务上次执行状态private Integer taskLastRun;//任务是否加载至内存中 private Integer taskRamStatus;
}
核心逻辑,加载定时任务接口及其实现类
public interface TaskScheduleServer {ConcurrentHashMap<String, AbstractBaseCronTask> getTaskSchedulingRam();/*** 初始化任务调度*/void initScheduling();/*** 添加任务至内存及容器* @param taskEntity 任务实体* @return boolean*/boolean addTaskToScheduling(TaskEntity taskEntity);/*** 从任务调度器中移除任务* @param id 任务id* @return Boolean*/boolean removeTaskFromScheduling(String id);/*** 执行指定任务* @param id 任务id* @return double 耗时*/double runTaskById(String id);/*** 清空任务*/void claearAllTask();/*** 加载所有任务*/void loadAllTask();/*** 运行开机自启任务*/void runBootUpTask();}@Slf4j
@Component
public class TaskScheduleServerImpl implements TaskScheduleServer {…………@Overridepublic double runTaskById(String id) {TaskEntity task = taskInfoOpMapper.queryTaskInfoById(id);if(null!=task) {if (runningTasks.containsKey(task.getTaskId())){ramTasks.get(task.getTaskId()).run();return ramTasks.get(task.getTaskId()).getRunTime();}}return 0d;}@Overridepublic void claearAllTask() {ramTasks.clear();log.info("【定时任务控制器】清除内存任务 完成");runningTasks.clear();log.info("【定时任务控制器】清除线程任务 完成");threadPoolTaskScheduler.shutdown();}@Overridepublic void loadAllTask() {List<TaskEntity> allTask = taskInfoOpMapper.queryTaskInfo(null);for (TaskEntity task : allTask) {if(addTaskToScheduling(task)){log.info("【定时任务初始化】装填任务:{} [ 任务执行周期:{} ] [ bootup:{}]",task.getTaskName(),task.getTaskCron(),task.getTaskBootUp());}}}@Overridepublic void runBootUpTask() {TaskEntity entity = new TaskEntity().taskBootUp(1);List<TaskEntity> list = taskInfoOpMapper.queryTaskInfo(entity);for(TaskEntity task:list){runTaskById(task.getTaskId());}}
}
在SpringBoot中的加载类
@Order(3)
@Component
@Slf4j
public class AfterAppStarted implements ApplicationRunner {TaskScheduleServer taskScheduleServer;@Autowiredpublic void setTaskScheduleServer(TaskScheduleServer taskScheduleServer) {this.taskScheduleServer = taskScheduleServer;}@Overridepublic void run(ApplicationArguments args) throws Exception {//运行随系统启动的定时任务taskScheduleServer.runBootUpTask();}}
对外暴露控制接口及其Service
@RestController
@RequestMapping("/taskScheduling/manage")
@Api(tags = "数据源管理服务")
public class TaskSchedulingController {TaskScheduleManagerService taskScheduleManagerService;@Autowiredpublic void setTaskScheduleManagerService(TaskScheduleManagerService taskScheduleManagerService) {this.taskScheduleManagerService = taskScheduleManagerService;}@PostMapping("/search")@Operation(summary = "分页查询任务")public Response searchData(@RequestBody SearchTaskDto param){return Response.success(taskScheduleManagerService.searchTaskForPage(param));}@GetMapping("/detail")@Operation(summary = "具体任务对象")public Response searchDetail(String taskId){return Response.success(taskScheduleManagerService.searchTaskDetail(taskId));}@GetMapping("/shutdown")@Operation(summary = "关闭指定任务")public Response shutdownTask(String taskId){return Response.success(taskScheduleManagerService.shutdownTask(taskId));}@GetMapping("/open")@Operation(summary = "开启指定任务")public Response openTask(String taskId){return Response.success(taskScheduleManagerService.openTask(taskId));}@GetMapping("/run")@Operation(summary = "运行指定任务")public Response runTask(String taskId){return Response.success(taskScheduleManagerService.runTask(taskId));}@PostMapping("/update")@Operation(summary = "更新指定任务")public Response updateTask(@RequestBody TaskEntity taskEntity){return Response.success(taskScheduleManagerService.updateTaskBusinessInfo(taskEntity));}}
相关接口实现类
@Service
public class TaskScheduleManagerServiceImpl implements TaskScheduleManagerService {private TaskInfoOpMapper taskInfoOpMapper;private TaskScheduleServer taskScheduleServer;@Autowiredpublic void setTaskInfoOpMapper(TaskInfoOpMapper taskInfoOpMapper) {this.taskInfoOpMapper = taskInfoOpMapper;}@Autowiredpublic void setTaskScheduleServer(TaskScheduleServer taskScheduleServer) {this.taskScheduleServer = taskScheduleServer;}@Overridepublic IPage<TaskEntity> searchTaskForPage(SearchTaskDto dto) {Page<TaskEntity> pageParam = new Page<>(1,10);pageParam.setAsc("task_id");return taskInfoOpMapper.queryTaskInfoPage(pageParam,dto.getFilterKey(),dto.getBootUp(),dto.getLastRunStatus());}@Overridepublic TaskEntity searchTaskDetail(String taskId) {if(!StringUtils.isEmpty(taskId)){return taskInfoOpMapper.queryTaskInfoById(taskId);}return null;}@Overridepublic TaskRunRetDto runTask(String taskId) {AbstractBaseCronTask task = taskScheduleServer.getTaskSchedulingRam().get(taskId);TaskRunRetDto result = new TaskRunRetDto(TaskRunRetDto.TaskOperation.run, 0);if(null != task) {double time = taskScheduleServer.runTaskById(taskId);result.setResult(1);return result.extend(time).taskInfo(task.getThisTaskInfo());} else {return result.extend("任务未启用");}}@Overridepublic TaskRunRetDto shutdownTask(String taskId) {AbstractBaseCronTask task = taskScheduleServer.getTaskSchedulingRam().get(taskId);TaskRunRetDto result = new TaskRunRetDto(TaskRunRetDto.TaskOperation.shutdown, 0);if(null != task) {boolean flag = taskScheduleServer.removeTaskFromScheduling(taskId);if(flag) {result.setResult(1);}return result.extend("任务成功关闭").taskInfo(task.getThisTaskInfo());} else {return result.extend("任务未启用");}}@Overridepublic TaskRunRetDto openTask(String taskId) {TaskEntity task = taskInfoOpMapper.queryTaskInfoById(taskId);TaskRunRetDto result = new TaskRunRetDto(TaskRunRetDto.TaskOperation.open, 0);if(null != task) {if (!taskScheduleServer.getTaskSchedulingRam().containsKey(taskId)) {boolean flag = taskScheduleServer.addTaskToScheduling(task);if(flag) {result.setResult(1);}return result.extend("任务开启成功").taskInfo(task);} else {return result.extend("任务处于启动状态").taskInfo(task);}}else {return result.extend("任务不存在!");}}@Overridepublic TaskRunRetDto updateTaskBusinessInfo(TaskEntity entity) {TaskEntity task = searchTaskDetail(entity.getTaskId());TaskRunRetDto result = new TaskRunRetDto(TaskRunRetDto.TaskOperation.update, 0).taskInfo(entity);String config = entity.getTaskOutConfig();if(null != config && !JSONUtil.isJson(config) && !JSONUtil.isJsonArray(config)){result.setResult(0);result.extend("更新任务失败,任务配置必须为JSON或空");result.taskInfo(entity);return result;}task.setTaskCron(entity.getTaskCron());task.setTaskOutConfig(entity.getTaskOutConfig());task.setTaskName(entity.getTaskName());task.setTaskDesc(entity.getTaskDesc());int num = taskInfoOpMapper.updateTaskInfo(task);if (num == 1) {result.setResult(1);result.extend("成功更新任务");result.taskInfo(entity);//重新刷新任务taskScheduleServer.removeTaskFromScheduling(entity.getTaskId());taskScheduleServer.addTaskToScheduling(task);}return result;}
效果
数据库中配置任务
任务代码
public class TestTask extends AbstractBaseCronTask {public TestTask(TaskEntity taskEntity) {super(taskEntity);}@Overridepublic void beforeJob() {log.info("测试任务开始");}@Overridepublic void startJob() {}@Overridepublic void afterJob() {}
}
任务查看
执行效果
相关文章:
基于SpringBoot实现轻量级的动态定时任务调度
在使用SpringBoot框架进行开发时,一般都是通过Scheduled注解进行定时任务的开发: Component public class TestTask {Scheduled(cron"0/5 * * * * ? ") //每5秒执行一次public void execute(){SimpleDateFormat df new SimpleDateFormat(…...
夸克升级“超级搜索框” 推出AI搜索为中心的一站式AI服务
大模型时代,生成式AI如何革新搜索产品?阿里智能信息事业群旗下夸克“举手答题”。7月10日,夸克升级“超级搜索框”,推出以AI搜索为中心的一站式AI服务,为用户提供从检索、创作、总结,到编辑、存储、分享的一…...
element-ui el-select选择器组件下拉框增加自定义按钮
element-ui el-select选择器组件下拉框增加自定义按钮 先看效果 原理:在el-select下添加禁用的el-option,将其value绑定为undefined,然后覆盖el-option禁用状态下的默认样式即可 示例代码如下: <template><div class…...
Python基于you-get下载网页上的视频
1.python 下载地址 下载 : https://www.python.org/downloads/ 2. 配置环境变量 配置 python_home 地址 配置 python_scripts 地址 在path 中加入对应配置 3. 验证 C:\Users>python --version Python 3.12.4C:\Users>wheel version wheel 0.43.04. 下载 c…...
大模型/NLP/算法面试题总结3——BERT和T5的区别?
1、BERT和T5的区别? BERT和T5是两种著名的自然语言处理(NLP)模型,它们在架构、训练方法和应用场景上有一些显著的区别。以下是对这两种模型的详细比较: 架构 BERT(Bidirectional Encoder Representation…...
vue3项目打包的时候,怎么区别测试环境,和本地环境
在Vue 3项目中区别测试环境和本地环境,并标记接口的方法可以通过环境变量来实现。 首先,你可以在你的项目根目录下创建一个.env文件,并定义你的环境变量。比如,你可以创建.env.local作为本地环境的配置文件,.env.test…...
小特性 大用途 —— YashanDB JDBC驱动的这些特性你都get了吗?
在现代数据库应用场景中,系统的高可用性和负载均衡是确保服务稳定性的基石。YashanDB JDBC驱动通过其创新的多IP配置特性,为用户带来了简洁而强大的解决方案,以实现数据库连接的高可用性和负载均衡,满足企业级应用的高要求。 01 …...
全网最全的软件测试面试八股文
前面看到了一些面试题,总感觉会用得到,但是看一遍又记不住,所以我把面试题都整合在一起,都是来自各路大佬的分享,为了方便以后自己需要的时候刷一刷,不用再到处找题,今天把自己整理的这些面试题…...
VMware虚拟机配置桥接网络
转载:虚拟机桥接网络配置 一、VMware三种网络连接方式 VMware提供了三种网络连接方式,VMnet0, VMnet1, Vmnet8,分别代表桥接,Host-only及NAT模式。在VMware的编辑-虚拟网络编辑器可看到对应三种连接方式的设置(如下图…...
华为机考真题 -- 攀登者1
题目描述: 攀登者喜欢寻找各种地图,并且尝试攀登到最高的山峰。地图表示为一维数组,数组的索引代表水平位置,数组的元素代表相对海拔高度。其中数组元素0代表地面。 一个山脉可能有多座山峰(山峰定义:高度大于相邻位置的高度,或在地图边界且高度大于相邻的高度)。登山者…...
深入理解Python密码学:使用PyCrypto库进行加密和解密
深入理解Python密码学:使用PyCrypto库进行加密和解密 引言 在现代计算领域,信息安全逐渐成为焦点话题。密码学,作为信息保护的关键技术之一,允许我们加密(保密)和解密(解密)数据。P…...
MMSegmentation笔记
如何训练自制数据集? 首先需要在 mmsegmentation/mmseg/datasets 目录下创建一个自制数据集的配置文件,以我的苹果叶片病害分割数据集为例,创建了mmsegmentation/mmseg/datasets/appleleafseg.py 可以看到,这个配置文件主要定义…...
Python基础语法:变量和数据类型详解(整数、浮点数、字符串、布尔值)①
文章目录 变量和数据类型详解(整数、浮点数、字符串、布尔值)一、变量二、数据类型1. 整数(int)2. 浮点数(float)3. 字符串(str)4. 布尔值(bool) 三、类型转换…...
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——红黑树
目录 1 -> 红黑树 1.1 -> 红黑树的概念 1.2 -> 红黑树的性质 1.3 -> 红黑树节点的定义 1.4 -> 红黑树的结构 1.5 -> 红黑树的插入操作 1.6 -> 红黑树的验证 1.8 -> 红黑树与AVL树的比较 2 -> 红黑树模拟实现STL中的map与set 2.1 -> 红…...
MySQL DDL
数据库 1 创建数据库 CREATE DATABASE 数据库名 CREATE DATABASE IF NOT EXISTS 数据库名;(判断是否存在) CREATE DATABASE 数据库名 CHARACTER SET 字符 2 查看数据库 SHOW DATABASES; 查看某个数据库的信息 SHOW CAEATE DATABASE 数据库名 3 修改数据库 …...
从模型到应用:李彦宏解读AI时代的新趋势与挑战
如何理解李彦宏说的“不要卷模型,要卷应用” 开源项目的机遇与挑战 7月4日,2024世界人工智能大会暨人工智能全球治理高级别会议在上海世博中心举办。在产业发展主论坛上,百度创始人、董事长兼首席执行官李彦宏呼吁:“大家不要卷…...
C++ STL 随机数用法介绍
目录 一:C语言中的随机数 二:C中的随机数 1. 生成随机数的例子 2. 随机数引擎 3. 随机数引擎适配器 4. C中预定义的随机数引擎,引擎适配器 5. 随机数分布 一:C语言中的随机数 <stdlib.h>//初始化随机种子 srand(static_ca…...
容器之docker compose
Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具。通过一个 YAML 文件,您可以配置应用程序需要的所有服务,并使用单个命令来创建和启动这些服务。以下是对 Docker Compose 的详细介绍: 核心概念 服务(Services&am…...
MIT机器人运动控制原理浅析-人形机器人
MIT人形机器人基于开发改进的执行器全新设计,通过可感知执行器运动动力学移动规划器(Actuator-Aware Kino-Dynamic Motion Planner)及着地控制器(Landing Controller)等实现机器人的运动控制。 机器人设计 机器人高0.7米,21KG(四肢重量 25%),…...
开源 WAF 解析:选择最适合你的防护利器
前言 随着网络安全风险的增加,Web 应用防火墙(WAF)成为保护网站和应用程序免受攻击的关键工具。在众多的选择中,开源 WAF 以其灵活性、可定制性和成本效益备受青睐。本文将深入探讨几种主流开源 WAF 解决方案,帮助你选…...
AirPods Pro新功能前瞻:iOS 18的五大创新亮点
随着科技的不断进步,苹果公司一直在探索如何通过创新提升用户体验。iOS 18的推出,不仅仅是iPhone的一次系统更新,更是苹果生态链中重要一环——AirPods Pro的一次重大升级。 据悉,iOS 18将为AirPods Pro带来五项新功能࿰…...
JavaScript中的可选链操作符
在JavaScript中,?. 被称为可选链操作符(Optional Chaining Operator)。它允许你访问对象的深层属性而不必显式地检查每一层属性是否存在。如果链中的某个属性不存在,表达式将短路返回undefined,而不是抛出一个TypeErr…...
huggingface笔记:gpt2
0 使用的tips GPT-2是一个具有绝对位置嵌入的模型,因此通常建议在输入的右侧而不是左侧填充GPT-2是通过因果语言建模(CLM)目标进行训练的,因此在预测序列中的下一个标记方面非常强大 利用这一特性,GPT-2可以生成语法连…...
一次业务的批量数据任务的处理优化
文章目录 一次业务的批量数据任务的处理优化业务背景1.0版本 分批处理模式2.0版本 平衡任务队列模式3.0版本 优化调度平衡任务队列模式总结 一次业务的批量数据任务的处理优化 业务背景 一个重新生成所有客户的财务业务指标数据的批量数据处理任务。 1.0版本 分批处理模式 …...
新能源汽车充电站远程监控系统S275钡铼技术无线RTU
新能源汽车充电站的远程监控系统在现代城市基础设施中扮演着至关重要的角色,而钡铼技术的S275无线RTU作为一款先进的物联网数据监测采集控制短信报警终端,为充电站的安全运行和高效管理提供了强大的技术支持。 技术特点和功能 钡铼S275采用了基于UCOSI…...
海外视频媒体发布/发稿:如何在国外媒体以视频的形式宣发
1. 背景介绍 在如今数字化时代,每个国家都拥有着各自的视频媒体平台,而主流媒体也都纷纷加入了视频发布的行列。视频媒体的宣发形式主要包括油管Youtube等视频分享平台,以及图文配合的发布方式。通过在视频中夹带链接,媒体可以以…...
HTML 【实用教程】(2024最新版)
核心思想 —— 语义化 【面试题】如何理解 HTML 语义化 ?仅通过标签便能判断内容的类型,特别是区分标题、段落、图片和表格 增加代码可读性,让人更容易读懂对SEO更加友好,让搜索引擎更容易读懂 html 文件的基本结构 html 文件的文件后缀为 …...
How to Describe Figures in a Research Article
How to Describe Figures in a Research Article DateAuthorVersionNote2024.07.10Dog TaoV1.0Finish the document. 文章目录 How to Describe Figures in a Research ArticleGeneral GuidelinesDetailed DescriptionsCommon Describing Phrases Effective communication of …...
昇思MindSpore学习入门-CELL与参数一
Cell作为神经网络构造的基础单元,与神经网络层(Layer)的概念相对应,对Tensor计算操作的抽象封装,能够更准确清晰地对神经网络结构进行表示。除了基础的Tensor计算流程定义外,神经网络层还包含了参数管理、状态管理等功能。而参数(…...
【k8s中安装rabbitmq】k8s中安装rabbitmq并搭建镜像集群-hostpath版
文章目录 简介一.条件及环境说明二.需求说明三.实现原理及说明四.详细步骤4.1.规划节点标签4.2.创建configmap配置4.3.创建三个statefulset和service headless配置4.4.创建service配置 五.安装完后的配置六.安装说明 简介 k8s集群中搭建rabbitmq集群服务一般都会用到pvc&#x…...
建站网站教程视频教程/网站seo百度百科
ant 使用指南 一、概述 ant 是一个将软件编译、测试、部署等步骤联系在一起加以自动化的一个工具,大多用于Java环境中的软件开发。在实际软件开发中,有很多地方可以用到ant。 开发环境: System:Windows JDK:1.6 IDE&…...
代码网站怎么做的/百度指数怎么看地域数据
五、 部署Exchange 2013 CAS负载均衡高可用 现在的Exchange 2013中客户端的指向的是用户邮箱唯一的GUID。CAS服务器基于Active Directory站点查找这个GUID,然后找到正确的邮箱服务器目标。因此不再需要使用CAS Array对象。这里只需部署NLB高可用。 5.1. 创建NLB群集…...
营销网站模版/成都网站排名生客seo怎么样
异步FIFO为什么要使用格雷码(笔记) 首先要了解的是异步FIFO使用格雷码的唯一目的就是: “即使在亚稳态进行读写指针抽样也能进行正确的空满状态判断”。 那么典型的判断方法是怎样进行的呢?以满状态判断为例,先要对读指针(属于读…...
国外网站 国内做镜像/电话投放小网站
题目要求: **要求写一个案例,使用三个JSlider分别选取R、G、B三原色的值,用户可以通过活动JSlider的滑块来动态的合成一种颜色,合成的颜色显示在界面上。**代码思路解析: 1、所需要的组件 (1)JFrame窗口(2)JLabel的RGB…...
滁州 来安县建设局网站/宁波品牌网站推广优化
苏生不惑第257 篇原创文章,将本公众号设为星标,第一时间看最新文章。之前分享过5分钟搞定b站答题成为正式会员,每天自动刷经验值升级账号,不过那个GitHub仓库已经被删了,作者提供了本地版本和腾讯云版本,我…...
百色高端网站建设/优化大师的三大功能
目录知识点总结: Note: 1.创建一个/server/scripts目录,用于存放脚本(命令:mkdir -p /server/scripts) 2.安装软件时,安装路径统一为/usr/local/软件名-版本号 3.安装完软件后,需做软链接&#…...