基于Spring AI开发本地Jenkins MCP Server服务
前言
首先介绍下MCP是什么?
MCP是由开发了 Claude 模型的 Anthropic 公司2024年11月提出并开源的一项开放标准,全称:Model Context Protocol,它是一个开放协议,它使 LLM 应用与外部数据源和工具之间的无缝集成成为可能。无论你是构建 AI 驱动的 IDE、改善chat 交互,还是构建自定义的 AI 工作流,MCP 提供了一种标准化的方式,将 LLM 与它们所需的上下文连接起来。
大白话
如果不使用 MCP 是什么样的?
就像自己(LLM大模型)学做菜,首先需要学会如何使用刀、锅、锅铲、炉灶,甚至需要自己生火,每一个步骤都需要从头摸索工具,无法专注烹饪本身。
使用 MCP
MCP 协议相当于给大模型配了一个服务员(MCP Client),当食客(LLM大模型)需要吃什么菜时,可以直接根据菜单上的菜品告诉服务员(MCP Client),知道菜品后,服务员(MCP Client)根据菜品是哪个菜系找不同菜系的厨师(MCP Server),厨师(MCP Server)接到炒菜的任务就使用冰箱、食材、锅、锅铲等(工具)完成菜品制作任务,并将菜品(结果)精准端给服务员(MCP Client),让大模型无需直接操作工具就能完成复杂任务。
初衷
最近接触到 MCP 协议,我觉得它在未来AI实际应用中潜力巨大,很可能成为行业趋势。不过,我留意到mcp.so网站上,大部分 MCP Server 是用 Python 编写的,用 Java 开发的极为少见。就连 Spring AI,也是在 2025 年 2 月才开始支持并封装 MCP 协议的大部分逻辑。所以,我希望有更多从事 Java 开发的人员能够关注这项技术,将其广泛运用到实际项目里 。
正文
流程图

环境准备
- Jenkins(需启用「远程访问API」权限)
- JDK 17
- SpringBoot 3.3.6
- IDEA
- Maven 3
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId><version>1.0.0-M6</version>
</dependency>
<dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.0</version>
</dependency>
<dependency><groupId>com.google.inject</groupId><artifactId>guice</artifactId><version>5.1.0</version>
</dependency>
<dependency><groupId>io.github.cdancy</groupId><artifactId>jenkins-rest</artifactId><version>1.0.2</version><exclusions><exclusion><artifactId>guice</artifactId><groupId>com.google.inject</groupId></exclusion></exclusions>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.36</version><scope>provided</scope></dependency>
核心代码
- JenkinsMcpServerConfig.java
MCP Server必须的配置类
package com.agua.ai.mcp.server.config;import com.agua.ai.mcp.server.service.JenkinsApiService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class JenkinsMcpServerConfig {@Beanpublic ToolCallbackProvider jenkinsTools(JenkinsApiService jenkinsApiService) {return MethodToolCallbackProvider.builder().toolObjects(jenkinsApiService).build();}
}
- JenkinsProperties.java
定义Jenkins的配置
package com.agua.ai.mcp.server.properties;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Data
@Component
@ConfigurationProperties("jenkins")
public class JenkinsProperties {/*** 服务URI*/private String serverUri;/*** 用户名*/private String username;/*** 密码/token*/private String password;
}
- JenkinsTemplate.java
对com.cdancy.jenkins(封装了Jenkins Rest API的工具类)已经集成的方法进行再次封装,方便调用
package com.agua.ai.mcp.server.util;import com.cdancy.jenkins.rest.JenkinsClient;
import com.cdancy.jenkins.rest.domain.common.IntegerResponse;
import com.cdancy.jenkins.rest.domain.common.RequestStatus;
import com.cdancy.jenkins.rest.domain.job.BuildInfo;
import com.cdancy.jenkins.rest.domain.job.JobInfo;
import com.cdancy.jenkins.rest.domain.job.JobList;
import com.cdancy.jenkins.rest.domain.job.ProgressiveText;
import com.cdancy.jenkins.rest.features.JobsApi;
import com.agua.ai.mcp.server.properties.JenkinsProperties;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.List;
import java.util.Map;/*** Jenkins 模板类,用于封装 Jenkins API 的调用*/
@Component
public class JenkinsTemplate {private final JenkinsClient jenkinsClient;private JobsApi jobsApi;@Autowiredpublic JenkinsTemplate(JenkinsProperties jenkinsProperties) {this.jenkinsClient = JenkinsClient.builder().endPoint(jenkinsProperties.getServerUri()).credentials(jenkinsProperties.getUsername() + ":" + jenkinsProperties.getPassword()).build();}@PostConstructpublic void init() {this.jobsApi = jenkinsClient.api().jobsApi();}/*** 获取任务列表** @param optionalFolderPath 可选的文件夹路径* @return 任务列表*/public JobList getJobList(String optionalFolderPath) {return jobsApi.jobList(optionalFolderPath);}/*** 获取任务信息** @param optionalFolderPath 可选的文件夹路径* @param jobName 任务名称* @return 任务信息*/public JobInfo getJobInfo(String optionalFolderPath, String jobName) {return jobsApi.jobInfo(optionalFolderPath, jobName);}/*** 使用 XML 文件创建任务** @param optionalFolderPath 可选的文件夹路径* @param jobName 任务名称* @param configXML 任务的配置 XML* @return 请求状态*/public RequestStatus createJob(String optionalFolderPath, String jobName, String configXML) {return jobsApi.create(optionalFolderPath, jobName, configXML);}/*** 删除任务** @param optionalFolderPath 可选的文件夹路径* @param jobName 任务名称* @return 请求状态*/public RequestStatus deleteJob(String optionalFolderPath, String jobName) {return jobsApi.delete(optionalFolderPath, jobName);}/*** 启用任务** @param optionalFolderPath 可选的文件夹路径* @param jobName 任务名称* @return 是否成功*/public boolean enableJob(String optionalFolderPath, String jobName) {return jobsApi.enable(optionalFolderPath, jobName);}/*** 禁用任务** @param optionalFolderPath 可选的文件夹路径* @param jobName 任务名称* @return 是否成功*/public boolean disableJob(String optionalFolderPath, String jobName) {return jobsApi.disable(optionalFolderPath, jobName);}/*** 获取任务配置文件内容** @param optionalFolderPath 可选的文件夹路径* @param jobName 任务名称* @return 配置文件内容*/public String getJobConfig(String optionalFolderPath, String jobName) {return jobsApi.config(optionalFolderPath, jobName);}/*** 更新任务配置文件内容** @param optionalFolderPath 可选的文件夹路径* @param jobName 任务名称* @param configXML 新的配置 XML* @return 是否成功*/public boolean updateJobConfig(String optionalFolderPath, String jobName, String configXML) {return jobsApi.config(optionalFolderPath, jobName, configXML);}/*** 构建任务** @param optionalFolderPath 可选的文件夹路径* @param jobName 任务名称* @return 构建响应*/public IntegerResponse buildJob(String optionalFolderPath, String jobName) {return jobsApi.build(optionalFolderPath, jobName);}/*** 构建带参数的任务** @param optionalFolderPath 可选的文件夹路径* @param jobName 任务名称* @param properties 参数列表* @return 构建响应*/public IntegerResponse buildJobWithParams(String optionalFolderPath, String jobName, Map<String, List<String>> properties) {return jobsApi.buildWithParameters(optionalFolderPath, jobName, properties);}/*** 获取任务上次构建序号** @param optionalFolderPath 可选的文件夹路径* @param jobName 任务名称* @return 构建序号*/public Integer getLastBuildNumber(String optionalFolderPath, String jobName) {return jobsApi.lastBuildNumber(optionalFolderPath, jobName);}/*** 获取任务上次构建时间戳** @param optionalFolderPath 可选的文件夹路径* @param jobName 任务名称* @return 时间戳*/public String getLastBuildTimestamp(String optionalFolderPath, String jobName) {return jobsApi.lastBuildTimestamp(optionalFolderPath, jobName);}/*** 获取构建信息** @param optionalFolderPath 可选的文件夹路径* @param jobName 任务名称* @param buildNumber 构建编号* @return 构建信息*/public BuildInfo getBuildInfo(String optionalFolderPath, String jobName, int buildNumber) {return jobsApi.buildInfo(optionalFolderPath, jobName, buildNumber);}/*** 获取构建控制台输出内容** @param optionalFolderPath 可选的文件夹路径* @param jobName 任务名称* @param buildNumber 构建编号* @param start 开始位置* @return 控制台输出内容*/public ProgressiveText getBuildLog(String optionalFolderPath, String jobName, int buildNumber, int start) {return jobsApi.progressiveText(optionalFolderPath, jobName, buildNumber, start);}/*** 重命名任务** @param optionalFolderPath 可选的文件夹路径* @param currentJobName 当前任务名称* @param newJobName 新任务名称* @return 是否成功*/public boolean renameJob(String optionalFolderPath, String currentJobName, String newJobName) {return jobsApi.rename(optionalFolderPath, currentJobName, newJobName);}/*** 停止任务** @param optionalFolderPath 可选的文件夹路径* @param jobName 任务名称* @param buildNumber 构建编号* @return 是否成功*/public RequestStatus killJob(String optionalFolderPath, String jobName, int buildNumber) {return jobsApi.kill(optionalFolderPath, jobName, buildNumber);}/*** 查看执行日志** @param optionalFolderPath 可选的文件夹路径* @param jobName 任务名称* @param start 开始位置* @return 是否成功*/public ProgressiveText progressiveTextJob(String optionalFolderPath, String jobName, int start) {return jobsApi.progressiveText(optionalFolderPath, jobName, start);}
}
- JenkinsApiService.java
直接暴露给LLM大模型的可调用的工具的Service
package com.agua.ai.mcp.server.service;import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.agua.ai.mcp.server.util.JenkinsTemplate;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;@Service
@AllArgsConstructor
public class JenkinsApiService {private final JenkinsTemplate jenkinsTemplate;@Tool(description = "获取任务列表")public String getJobList(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.getJobList(optionalFolderPath));}@Tool(description = "获取任务信息")public String getJobInfo(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "任务名称") String jobName) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.getJobInfo(optionalFolderPath, jobName));}@Tool(description = "使用 XML 文件创建任务")public String createJob(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "任务名称") String jobName,@ToolParam(description = "任务的配置 XML") String configXML) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.createJob(optionalFolderPath, jobName, configXML));}@Tool(description = "删除任务")public String deleteJob(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "任务名称") String jobName) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.deleteJob(optionalFolderPath, jobName));}@Tool(description = "启用任务")public String enableJob(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "任务名称") String jobName) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.enableJob(optionalFolderPath, jobName));}@Tool(description = "禁用任务")public String disableJob(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "任务名称") String jobName) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.disableJob(optionalFolderPath, jobName));}@Tool(description = "获取任务配置文件内容")public String getJobConfig(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "任务名称") String jobName) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.getJobConfig(optionalFolderPath, jobName));}@Tool(description = "更新任务配置文件内容")public String updateJobConfig(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "任务名称") String jobName,@ToolParam(description = "新的配置 XML") String configXML) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.updateJobConfig(optionalFolderPath, jobName, configXML));}@Tool(description = "构建任务")public String buildJob(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "任务名称") String jobName) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.buildJob(optionalFolderPath, jobName));}@Tool(description = "构建带参数的任务")public String buildJobWithParams(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "任务名称") String jobName,@Schema(description = "参数列表(格式:Map<String, List<String>>)") String properties) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.buildJobWithParams(optionalFolderPath, jobName, JSON.parseObject(properties, new TypeReference<>() {})));}@Tool(description = "获取任务上次构建序号")public String getLastBuildNumber(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "任务名称") String jobName) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.getLastBuildNumber(optionalFolderPath, jobName));}@Tool(description = "获取任务上次构建时间戳")public String getLastBuildTimestamp(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "任务名称") String jobName) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.getLastBuildTimestamp(optionalFolderPath, jobName));}@Tool(description = "获取构建信息")public String getBuildInfo(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "任务名称") String jobName,@ToolParam(description = "构建编号(必须是整数)") String buildNumber,@ToolParam(description = "是否返回变更历史(boolean类型)") String changeSetFlag) {Gson gson = new GsonBuilder().setExclusionStrategies(new ExclusionStrategy() {@Overridepublic boolean shouldSkipField(FieldAttributes f) {return Boolean.parseBoolean(changeSetFlag) && "changeSet".equals(f.getName());}@Overridepublic boolean shouldSkipClass(Class<?> clazz) {return false;}}).create();return gson.toJson(jenkinsTemplate.getBuildInfo(optionalFolderPath, jobName, Integer.parseInt(buildNumber)));}@Tool(description = "获取构建控制台输出内容")public String getBuildLog(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "任务名称") String jobName,@ToolParam(description = "构建编号(必须是整数)") String buildNumber,@ToolParam(description = "开始位置(必须是整数)") String start) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.getBuildLog(optionalFolderPath, jobName, Integer.parseInt(buildNumber), Integer.parseInt(start)));}@Tool(description = "重命名任务")public String renameJob(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "当前任务名称") String currentJobName,@ToolParam(description = "新任务名称") String newJobName) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.renameJob(optionalFolderPath, currentJobName, newJobName));}@Tool(description = "停止任务(必须二次确认)")public String killJob(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "任务名称") String jobName,@ToolParam(description = "构建编号(必须是整数)") String buildNumber) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.killJob(optionalFolderPath, jobName, Integer.parseInt(buildNumber)));}@Tool(description = "查看执行日志")public String progressiveTextJob(@ToolParam(description = "可选的文件夹路径") String optionalFolderPath,@ToolParam(description = "任务名称") String jobName,@ToolParam(description = "开始位置(必须是整数)") String start) {Gson gson = new GsonBuilder().create();return gson.toJson(jenkinsTemplate.progressiveTextJob(optionalFolderPath, jobName, Integer.parseInt(start)));}
}
- application.yml
spring:ai:mcp:server:stdio: truename: jenkins-apiversion: 0.0.1type: SYNCmain:web-application-type: nonebanner-mode: off
jenkins:# jenkins的访问urlserver-uri: ${JENKINS_API_SERVER_URI}username: ${JENKINS_API_USERNAME}password: ${JENKINS_API_TOKEN}
logging:level:root: INFO
使用配置
如果是用 Cursor 作为客户端,那么可以通过一下方式启动 MCP Server ,本地 MCP Server 服务请将{你的路径}替换成实际的 jar 包存放路径
- command方式
java -Dspring.ai.mcp.server.transport=STDIO -Dspring.main.web-application-type=none -jar {你的路径}\mcp-jenkins-server-0.0.1-SNAPSHOT.jar
- mcp.json配置
{"mcpServers": {"jenkins-mcp": {"command": "java","args": ["-jar","{你的路径}\\mcp-jenkins-server-0.0.1-SNAPSHOT.jar"],"env": {"JENKINS_API_SERVER_URI": "jenkins-uri","JENKINS_API_USERNAME": "username","JENKINS_API_TOKEN": "password/token" }}}
}
最终演示效果
用户提问
请部署v1.2.3版本到测试环境
MCP Client解析后调用:
{"tool": "buildJobWithParams","params": {"optionalFolderPath": "","jobName": "qa-system","properties": {"version": ["v1.2.3"], "env": ["test"]}}
}
Jenkins MCP Server执行结果
{"value": "12345","errors": []
}
总结
目前大模型的优势就是它能够一定程度地理解用户所说的内容,并转换成调用工具所需的请求参数,减少人工解析的工作量并且降低人工适配的成本。现在 MCP 还处于初期发展阶段,因此需要广大开发者的支持,才能支撑起庞大 AI 应用生态构建。
相关链接
MCP 介绍
Spring AI MCP
Jenkins 官网
jenkins-rest(Github 地址)
相关文章:
基于Spring AI开发本地Jenkins MCP Server服务
前言 首先介绍下MCP是什么? MCP是由开发了 Claude 模型的 Anthropic 公司2024年11月提出并开源的一项开放标准,全称:Model Context Protocol,它是一个开放协议,它使 LLM 应用与外部数据源和工具之间的无缝集成成为可能…...
基于简单神经网络的线性回归
一、概述 本代码实现了一个简单的神经网络进行线性回归任务。通过生成包含噪声的线性数据集,定义一个简单的神经网络类,使用梯度下降算法训练网络以拟合数据,并最终通过可视化展示原始数据、真实线性关系以及模型的预测结果。 二、依赖库 …...
【nvidia】Windows 双 A6000 显卡双显示器驱动更新问题修复
问题描述:windows自动更新nvidia驱动会导致只检测得到一个A6000显卡。 解决方法 下载 A6000 驱动 572.83-quadro-rtx-desktop-notebook-win10-win11-64bit-international-dch-whql.exehttps://download.csdn.net/download/qq_18846849/90554276 不要直接安装。如…...
《SRv6 网络编程:开启IP网络新时代》第2章、第3章:SRv6基本原理和基础协议
背景 根据工作要求、本人掌握的知识情况,仅针对《SRv6 网络编程:开启IP网络新时代》书籍中涉及的部分知识点进行总结梳理,并与工作小组进行分享,不涉及对原作的逐字搬运。 问题 组内同事提出的问题:本文缺扩展头描述…...
如何将AI模型返回的字符串转为html元素?
场景: 接入deepseek模型的api到我们平台,返回的字符串需要做下格式化处理。 返回的数据是这样的: {"role": "assistant","content": "<think>\n嗯,用户问的是“星体是什么”。首先&am…...
Citus源码(1)分布式表行为测试
最近对citus的实现非常好奇,本篇对citus的行为做一些测试。本篇只测行为,不分析源码。后面会继续写一系列文章分析citus源码。 环境:3节点 PG17 with citus。 SELECT citus_set_coordinator_host(127.0.0.1, 3001); SELECT citus_add_node(1…...
装饰器模式与模板方法模式实现MyBatis-Plus QueryWrapper 扩展
pom <dependency><groupId>com.github.yulichang</groupId><artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 --> </dependency>MPJLambdaWrapperX /*** 拓展 MyBatis Plus Join QueryWrapper 类&…...
【PCIE711-214】基于PCIe总线架构的4路HD-SDI/3G-SDI视频图像模拟源
产品概述 PCIE711-214是一款基于PCIE总线架构的4路SDI视频模拟源。该板卡为标准的PCIE插卡,全高尺寸,适合与PCIE总线的工控机或者服务器,板载协议处理器,可以通过PCIE总线将上位机的YUV 422格式视频数据下发通过SDI接口播放出去&…...
突破反爬困境:SDK开发,浏览器模块(七)
声明 本文所讨论的内容及技术均纯属学术交流与技术研究目的,旨在探讨和总结互联网数据流动、前后端技术架构及安全防御中的技术演进。文中提及的各类技术手段和策略均仅供技术人员在合法与合规的前提下进行研究、学习与防御测试之用。 作者不支持亦不鼓励任何未经授…...
rce操作
Linux命令长度突破限制 源码 <?php $param $_REQUEST[param];if ( strlen($param) < 8 ) {echo shell_exec($param); } echo执行函数,$_REQUEST可以接post、get、cookie传参 源码中对参数长度做了限制,小于8位,可以利用临时函数&…...
LabVIEW高效溢流阀测试系统
开发了一种基于LabVIEW软件和PLC硬件的溢流阀测试系统。通过集成神经网络优化的自适应PID控制器,该系统能自动进行压力稳定性、寿命以及动静态性能测试。该设计不仅提升了测试效率,还通过智能化控制提高了数据的精确性和操作的便捷性。 项目背景&…...
Spring Boot 中 JdbcTemplate 处理枚举类型转换 和 减少数据库连接的方法 的详细说明,包含代码示例和关键要点
以下是 Spring Boot 中 JdbcTemplate 处理枚举类型转换 和 减少数据库连接的方法 的详细说明,包含代码示例和关键要点: 一、JdbcTemplate 处理枚举类型转换 1. 场景说明 假设数据库存储的是枚举的 String 或 int 值,但 Java 实体类使用 enu…...
DataGear 5.3.0 制作支持导出表格数据的数据可视化看板
DataGear 内置表格图表底层采用的是DataTable表格组件,默认并未引入导出数据的JS支持库,如果有导出表格数据需求,则可以在看板中引入导出相关JS支持库,制作具有导出CSV、Excel、PDF功能的表格数据看板。 在新发布的5.3.0版本中&a…...
Web网页内嵌 Adobe Pdf Reader 谷歌Chrome在线预览编辑PDF文档
随着数字化办公的普及,PDF文档已成为信息处理的核心载体,虽然桌面端有很多软件可以实现预览编辑PDF文档,而在线在线预览编辑PDF也日益成为一个难题。 作为网页内嵌本地程序的佼佼者——猿大师中间件,之前发布的猿大师办公助手&am…...
歌词json
绽放(4:17) {"lyrics": [{time: 00:00, text: 作词:郑润泽},{time: 00:01, text: 作曲:郑润泽},{time: 00:02, text: 编曲:赵建飞},{time: 00:03, text: 制作人:李淘/赵建飞},{time: 00:09, tex…...
CNG汽车加气站操作工备考真题及答案解析【判断题】
1、燃气经营许可证按照燃气经营规模和类别实行分级审批。(√) 解析:不同规模和类别的燃气经营,其许可证审批级别不同,以确保经营活动的规范和安全。 2、依照《安全生产法》的规定,安全生产监督检查人员对检…...
Sentinel[超详细讲解]-1
定义一系列 规则 👺,对资源进行 保护 👺, 如果违反的了规则,则抛出异常,看是否有fallback兜底处理,如果没有则直接返回异常信息😎 1. 快速入门 1.1 引入 Sentinel 依赖 <depend…...
CUDA专题8—CUDA L2缓存完全指南:从持久化策略到性能优化实战
1. 设备内存L2缓存访问管理 当CUDA内核反复访问全局内存中的某个数据区域时,此类数据访问可视为持久化(persisting)访问。反之,若数据仅被访问一次,则可视为流式(streaming)访问。 从CUDA 11.0开始,计算能力8.0及以上的设备能够调控L2缓存中数据的持久性,从而可能实现更…...
如何让 SQL2API 进化为 Text2API:自然语言生成 API 的深度解析?
在过去的十年里,技术的进步日新月异,尤其是在自动化、人工智能与自然语言处理(NLP)方面。 随着“低代码”平台的崛起,开发者和非技术人员能够更轻松地构建强大而复杂的应用程序。然而,尽管技术门槛降低了&…...
OCCT(2)Windows平台编译OCCT
文章目录 一、Windows平台编译OCCT1、准备环境2、下载源码3、下载第三方库4、使用 CMake 配置5、编译OCCT源码6、运行示例 一、Windows平台编译OCCT 1、准备环境 安装工具: Visual Studio(推荐 VS2019/2022,选择 C 桌面开发 组件࿰…...
【蓝桥杯—单片机】通信总线专项 | 真题整理、解析与拓展 (更新ing...)
通信总线专项 前言SPI第十五届省赛题 UART/RS485/RS232UARTRS485RS232第十三届省赛题小结和拓展:传输方式的分类第十三届省赛 其他相关考点网络传输速率第十五届省赛题第十二届省赛题 前言 在本文中我会把 蓝桥杯单片机赛道 历年真题 中涉及到通信总线的题目整理出…...
【Golang】泛型与类型约束
文章目录 一、环境二、没有泛型的Go三、泛型的优点四、理解泛型(一)定义(二)调用(三)类型约束(Type Constraint)1)接口与约束2)结构体类型约束3)类…...
Uni-app页面信息与元素影响解析
获取窗口信息uni.getWindowInfo {pixelRatio: 3safeArea:{bottom: 778height: 731left: 0right: 375top: 47width: 375}safeAreaInsets: {top: 47, left: 0, right: 0, bottom: 34},screenHeight: 812,screenTop: 0,screenWidth: 375,statusBarHeight: 47,windowBottom: 0,win…...
CentOS(最小化)安装之后,快速搭建Docker环境
本文以VMware虚拟机中安装最小化centos完成后开始。 1. 检查网络 打开网卡/启用网卡 执行命令ip a查看当前的网络连接是否正常: 如果得到的结果和我一样,有ens网卡但是没有ip地址,说明网卡未打开 手动启用: nmcli device sta…...
【身份证证件OCR识别】批量OCR识别身份证照片复印件图片里的文字信息保存表格或改名字,基于QT和腾讯云api_ocr的实现方式
项目背景 在许多业务场景中,需要处理大量身份证照片复印件,手动输入其中的文字信息效率低下且容易出错。利用 OCR(光学字符识别)技术可以自动识别身份证图片中的文字信息,结合 QT 构建图形用户界面,方便用户操作,同时使用腾讯 OCR API 能够保证较高的识别准确率。 界面…...
IP属地和发作品的地址不一样吗
在当今这个数字化时代,互联网已经成为人们日常生活不可或缺的一部分。随着各大社交平台功能的不断完善,一个新功能——IP属地显示,逐渐走进大众视野。这一功能在微博、抖音、快手等各大平台上得到广泛应用,旨在帮助公众识别虚假信…...
Redis - 概述
目录 编辑 一、什么是redis 二、redis能做什么(有什么特点)? 三、redis有什么优势 四、Redis与其他key-value存储有什么不同 五、Redis命令 六、Redis数据结构 1、基础数据结构 2、高级数据结构 一、什么是redis 1、redis&#x…...
vue3 根据城市名称计算城市之间的距离
<template><div class"distance-calculator"><h1>城市距离计算器</h1><!-- 城市输入框 --><div class"input-group"><inputv-model"city1"placeholder"请输入第一个城市"keyup.enter"cal…...
html 列表循环滚动,动态初始化字段数据
html <div class"layui-row"><div class"layui-col-md4"><div class"boxall"><div class"alltitle">超时菜品排行</div><div class"marquee-container"><div class"scroll-…...
QT基础:安装与简介
QT初级 1、简介1.1 安装1.2 设置1.3 在VS中配置Qt1.3 帮助文档 2、Qt项目2.1 创建项目2.1 项目文件2.2 Qt中的窗口类窗口显示 2.3 坐标体系2.4 内存回收 1、简介 QT是一个跨平台的C应用程序开发框架。几乎支持所有的平台, 可用于桌面程序开发以及嵌入式开发。 Qt是标准 C 的扩…...
