打印需求:

        对所有的接口打印:请求方式,请求路径,请求参数,用户id,访问IP,访问时间

        对增删改操作的接口打印:接口响应

打印方案:

        给GET设置一个白名单(因为get请求大多数是查询,仅有部分增删改操作),在这个白名单里的接口,需要打印响应,不在的话就只打印一下基础信息

        给POST请求设置一个黑名单(因为post请求大多数会对数据进行操作,仅有小部分的查询),在这个黑名单里的查询接口,则不需要打印接口响应

 实现方式:

        在过滤器中进行操作

package com.xxxxxx.gateway.filter;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ValueFilter;
import com.koushare.common.constant.AuthConstants;
import com.koushare.common.constant.TokenConstants;
import com.koushare.common.utils.IPUtils;
import com.koushare.common.utils.StringUtils;
import com.koushare.gateway.model.CheckRequest;
import com.koushare.gateway.service.SysPrintLogService;
import com.koushare.jwt.config.PassJavaJwtProperties;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import javax.annotation.Resource;import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;import static com.koushare.common.convertor.ConvertHelper.bean2map;
import static com.koushare.common.utils.IPUtils.getIpAddrByServerHttpRequest;/*** 自定义全局过滤器*/
@Component
@Slf4j
public class GlobalRequestFilter implements GlobalFilter, Ordered {@Resourceprivate PassJavaJwtProperties jwtProperties;@Autowiredprivate SysPrintLogService sysPrintLogService;private static final String OPENAPI_SERVICE = "/openapi/";private static final String OPENAPI_CODE_PATH = "/code/";@Overridepublic int getOrder() {return -20;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest serverHttpRequest = exchange.getRequest();// 获取原始响应对象和数据缓冲工厂ServerHttpResponse originalResponse = exchange.getResponse();DataBufferFactory bufferFactory = originalResponse.bufferFactory();// 原始响应对象,用于拦截和修改响应内容ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {/*** 重写writeWith方法拦截响应体*/@Overridepublic Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {if (body instanceof Flux) {Flux<? extends DataBuffer> fluxBody = Flux.from(body);return super.writeWith(fluxBody.buffer().map(dataBuffers -> {// 获取一些需要打印的参数long timestamp = System.currentTimeMillis();HttpMethod method = serverHttpRequest.getMethod();String requestUrl = serverHttpRequest.getPath().toString();String userId = Optional.ofNullable(serverHttpRequest.getHeaders().getFirst(AuthConstants.USER_ID)).filter(StringUtils::isNotBlank).orElse("未登录");String ip = IPUtils.getIpAddrByServerHttpRequest(serverHttpRequest);String params = getRequestparams(serverHttpRequest, exchange);log.info("{} ========================接口详细日志========================", timestamp);log.info("{} 请求方式:{}  请求路径: {}", timestamp, method, requestUrl);log.info("{} 请求参数: {}", timestamp, params);log.info("{} 用户ID: {}  访问IP: {}  访问时间:{}", timestamp, userId, ip, new Date());// 判断是否需要打印响应if (isUpdateDate(method, requestUrl)) {// 创建数据缓冲工厂和缓冲区,用于读取响应内容DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();DataBuffer buff = dataBufferFactory.join(dataBuffers);byte[] content = new byte[buff.readableByteCount()];buff.read(content);// 释放缓冲区资源DataBufferUtils.release(buff);// 获取响应内容类型MediaType contentType = originalResponse.getHeaders().getContentType();if (!MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {// 如果不是JSON类型,直接返回原始内容,不进行处理log.info("{} ===============================================================", timestamp);return bufferFactory.wrap(content);}// 将字节数组转换为字符串 对响应体进行统一格式化处理String result = modifyBody(new String(content));log.info("{} 响应结果: {}", timestamp, result);log.info("{} ===============================================================", timestamp);getDelegate().getHeaders().setContentLength(result.getBytes().length);return bufferFactory.wrap(result.getBytes());} else {// 不需要打印响应结果时,直接合并并返回原始数据log.info("{} ===============================================================", timestamp);DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();DataBuffer joinedBuffer = dataBufferFactory.join(dataBuffers);byte[] content = new byte[joinedBuffer.readableByteCount()];joinedBuffer.read(content);DataBufferUtils.release(joinedBuffer);return bufferFactory.wrap(content);}}));} else {return super.writeWith(body);}}};return chain.filter(exchange.mutate().response(decoratedResponse).build());}private static String getRouteName(String requestUrl) {String serviceUrl = requestUrl.substring(requestUrl.indexOf("/") + 1);log.info("getRouteName: " + serviceUrl.substring(0, serviceUrl.indexOf("/")));return serviceUrl.substring(0, serviceUrl.indexOf("/"));}/*** 获取请求token*/private String getToken(ServerHttpRequest request) {String token = request.getHeaders().getFirst(jwtProperties.getHeader());// 如果前端设置了令牌前缀,则裁剪掉前缀if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX)) {token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);}return token;}/*** 获取去除路由后的path** @param requestUrl* @return*/private static String getPath(String requestUrl) {String path = requestUrl.substring(1);log.info("getPath: " + path.substring(path.indexOf("/")));return path.substring(path.indexOf("/"));}/*** 判断是否为增删改接口  只有增删改接口才会打印响应结果*/private boolean isUpdateDate(HttpMethod method, String requestUrl){switch (method) {case PUT:case DELETE:return true;case GET:return sysPrintLogService.checkNeedPrint_GET(requestUrl);case POST:return sysPrintLogService.checkNeedPrint_POST(requestUrl);default:return false;}}/*** 获取请求参数*/private String getRequestparams(ServerHttpRequest serverHttpRequest, ServerWebExchange exchange) {HttpMethod method = serverHttpRequest.getMethod();// 检查是否为文件上传请求,如果是则不打印参数MediaType contentType = serverHttpRequest.getHeaders().getContentType();if (contentType != null && (contentType.includes(MediaType.MULTIPART_FORM_DATA)|| contentType.includes(MediaType.APPLICATION_OCTET_STREAM))) {return "";}if (HttpMethod.GET.equals(method) || HttpMethod.DELETE.equals(method)) {StringBuilder params = new StringBuilder();serverHttpRequest.getQueryParams().forEach((key, value) -> {value.forEach(v -> params.append(key).append("=").append(v).append("&"));});// 移除末尾的 "&"if (params.length() > 0) {params.deleteCharAt(params.length() - 1);}return params.toString();} else if (HttpMethod.POST.equals(method) || HttpMethod.PUT.equals(method)) {return getBodyContent(exchange);}return "";}// 从其他filter中copy过来的 目的是获取post请求的bodyprivate String getBodyContent(ServerWebExchange exchange){Flux<DataBuffer> body = exchange.getRequest().getBody();AtomicReference<String> bodyRef = new AtomicReference<>();// 缓存读取的request body信息body.subscribe(dataBuffer -> {CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer());DataBufferUtils.release(dataBuffer);bodyRef.set(charBuffer.toString());});//获取request bodyreturn bodyRef.get();}/*** 修改响应体内容,统一JSON数据格式*/private String modifyBody(String str){JSONObject json = JSON.parseObject(str, Feature.AllowISO8601DateFormat);JSONObject.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";return JSONObject.toJSONString(json, (ValueFilter) (object, name, value) ->value == null ? "" : value, SerializerFeature.WriteDateUseDateFormat);}}

其中的service实现类:

package com.xxxxxx.gateway.service.impl;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.koushare.gateway.entity.SysPrintLog;
import com.koushare.gateway.mapper.SysPrintLogMapper;
import com.koushare.gateway.service.SysPrintLogService;
import com.koushare.redis.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;@Slf4j
@Service
public class SysPrintLogServiceImpl implements SysPrintLogService {private final static String PrintLogUrlWhitelist_GET = "PrintLog:UrlWhiteList";private final static String PrintLogUrlBlacklist_POST = "PrintLog:UrlBlackList";@Autowiredprivate SysPrintLogMapper sysPrintLogMapper;@Autowiredprivate RedisUtils redisUtils;/*** 检查Get请求是否需要打印* @return true:需要打印; false:不需要打印*/@Overridepublic boolean checkNeedPrint_GET(String requestUrl) {return checkWhiteList(requestUrl);}/*** 检查Post请求是否需要打印* @return true:需要打印; false:不需要打印*/@Overridepublic boolean checkNeedPrint_POST(String requestUrl) {return checkBlackList(requestUrl);}/*** 重新加载redis的Get请求日志打印接口白名单*/@Overridepublic List<SysPrintLog> loadPringLogWhiteList() {List<SysPrintLog> list = sysPrintLogMapper.queryPrintLogWhiteList();JSONArray jsonArray = JSONArray.parseArray(JSON.toJSONString(list));redisUtils.set(PrintLogUrlWhitelist_GET, jsonArray);return list;}/*** 重新加载redis的Post请求日志打印接口黑名单*/@Overridepublic List<SysPrintLog> loadPrintLogBlackList() {List<SysPrintLog> list = sysPrintLogMapper.queryPrintLogBlackList();JSONArray jsonArray = JSONArray.parseArray(JSON.toJSONString(list));redisUtils.set(PrintLogUrlBlacklist_POST, jsonArray);return list;}/*** 读redis中的白名单,不存在则读取数据库*/private List<SysPrintLog> WhiteList(){JSONArray jsonArray = redisUtils.getJsonArray(PrintLogUrlWhitelist_GET);if (jsonArray == null || jsonArray.size() == 0) {return loadPringLogWhiteList();}return jsonArray.toJavaList(SysPrintLog.class);}/*** 读redis中的黑名单,不存在则读取数据库*/private List<SysPrintLog> BlackList(){JSONArray jsonArray = redisUtils.getJsonArray(PrintLogUrlBlacklist_POST);if (jsonArray == null || jsonArray.size() == 0) {return loadPrintLogBlackList();}return jsonArray.toJavaList(SysPrintLog.class);}/*** 白名单列表中查找是否存在匹配的URL*/private boolean checkWhiteList(String requestUrl){return WhiteList().stream().anyMatch(sysPrintLog -> sysPrintLog.getUrl().equals(requestUrl));}/*** 黑名单列表中查找是否存在匹配的URL*/private boolean checkBlackList(String requestUrl){return BlackList().stream().noneMatch(sysPrintLog -> sysPrintLog.getUrl().equals(requestUrl));}}

用到的两个查询:

@Mapper
public interface SysPrintLogMapper {@Select("select id,url,description from sys_print_log_whitelist")List<SysPrintLog> queryPrintLogWhiteList();@Select("select id,url,description from sys_print_log_blacklist")List<SysPrintLog> queryPrintLogBlackList();}

以及数据库实体

@Data
@Builder(setterPrefix = "set")
@NoArgsConstructor
@AllArgsConstructor
public class SysPrintLog {private Integer id;private String url;private String description;}

获取ip地址的工具类:

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.reactive.ServerHttpRequest;import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;/*** IP地址** @author zhanghai on 2018/9/17*/
@Slf4j
public class IPUtils {// 多次反向代理后会有多个ip值 的分割符private final static String IP_UTILS_FLAG = ",";// 未知IPprivate final static String UNKNOWN = "unknown";// 本地 IPprivate final static String LOCALHOST_IP = "0:0:0:0:0:0:0:1";private final static String LOCALHOST_IP1 = "127.0.0.1";/*** 获取IP地址* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址*/public static String getIpAddr(HttpServletRequest request) {String ip = null;try {ip = request.getHeader("x-forwarded-for");if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("Proxy-Client-IP");}if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("WL-Proxy-Client-IP");}if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_CLIENT_IP");}if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeader("HTTP_X_FORWARDED_FOR");}if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}} catch (Exception e) {log.error("IPUtils ERROR ", e);}// 使用代理,则获取第一个IP地址if (StringUtils.isEmpty(ip) && ip.length() > 15) {if (ip.indexOf(",") > 0) {ip = ip.substring(0, ip.indexOf(","));}}return ip;}public static String getIpAddrByServerHttpRequest(ServerHttpRequest request) {// 根据 HttpHeaders 获取 请求 IP地址String ip = request.getHeaders().getFirst("X-Forwarded-For");if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeaders().getFirst("x-forwarded-for");if (ip != null && ip.length() != 0 && !UNKNOWN.equalsIgnoreCase(ip)) {// 多次反向代理后会有多个ip值,第一个ip才是真实ipif (ip.contains(IP_UTILS_FLAG)) {ip = ip.split(IP_UTILS_FLAG)[0];}}}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeaders().getFirst("Proxy-Client-IP");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeaders().getFirst("WL-Proxy-Client-IP");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeaders().getFirst("HTTP_CLIENT_IP");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeaders().getFirst("HTTP_X_FORWARDED_FOR");}if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getHeaders().getFirst("X-Real-IP");}//兼容k8s集群获取ipif (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {ip = request.getRemoteAddress().getAddress().getHostAddress();if (LOCALHOST_IP1.equalsIgnoreCase(ip) || LOCALHOST_IP.equalsIgnoreCase(ip)) {//根据网卡取本机配置的IPInetAddress iNet = null;try {iNet = InetAddress.getLocalHost();} catch (UnknownHostException e) {log.error("getClientIp error: ", e);}ip = iNet.getHostAddress();}}return ip;}/*** 获取IP地址* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址*/public static String getIpAddrByHttp(ServerHttpRequest request) {String ip = null;try {ip = request.getHeaders().getFirst("x-forwarded-for");if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeaders().getFirst("Proxy-Client-IP");}if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeaders().getFirst("WL-Proxy-Client-IP");}if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeaders().getFirst("HTTP_CLIENT_IP");}if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeaders().getFirst("HTTP_X_FORWARDED_FOR");}if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {ip = request.getHeaders().getFirst("X-Real-IP");}} catch (Exception e) {log.error("IPUtils ERROR ", e);}// 使用代理,则获取第一个IP地址if (StringUtils.isEmpty(ip) && ip.length() > 15) {if (ip.indexOf(",") > 0) {ip = ip.substring(0, ip.indexOf(","));}}return ip;}}

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MATLAB实现图像增强(直方图均衡化)

直方图均衡化是一种常用的图像增强技术&#xff0c;它通过重新分布图像的像素强度值来增强图像的对比度。以下是MATLAB中实现直方图均衡化的详细方法。%% 直方图均衡变换 clc;close all;clear all;warning off;%清除变量 rand(seed, 100); randn(seed, 100); format long g;%% …

java15学习笔记-密封类

360:Sealed Classes (Preview) 封闭类&#xff08;预览&#xff09; 总结 使用密封类和接口增强Java编程语言。密封类和接口限制了哪些其他类或接口可以扩展或实现它们。这是JDK 15中的预览语言功能。 目标 允许类或接口的作者控制负责实现它的代码。 提供一种比访问…

西门子PLC通过稳联技术EtherCAT转Profinet网关连接baumuller伺服器的配置案例

西门子PLC用稳联技术的EtherCAT转Profinet网关&#xff0c;连上baumuller伺服器的配置例子本案例实现西门子S71200 PLC通过EtherCAT转Profinet网关对baumuller&#xff08;Baumller&#xff09;伺服器的实时控制&#xff0c;适用于高精度运动控制场景&#xff08;如精密机床、自…

Ansible 详细笔记

Ansible 详细笔记 一、Ansible 基础概述 1.1 定义与定位 Ansible 是由 Red Hat 主导开发的开源自动化运维工具&#xff0c;基于 Python 语言实现&#xff0c;专注于简化 IT 基础设施的配置管理、应用部署、任务编排等操作。它采用无代理架构&#xff0c;通过 SSH 协议与被控节点…

【Java 后端】Spring Boot 集成 JPA 全攻略

Spring Boot 集成 JPA 全攻略 一、前言 在 Java Web 开发中&#xff0c;数据库访问是绕不开的话题。 传统方式使用 JDBC 编写 SQL&#xff0c;维护困难、可读性差。后来有了 MyBatis 这种半自动 ORM 框架&#xff0c;再到 JPA&#xff08;Java Persistence API&#xff09;这…

pytorch学习笔记-加载现有的网络模型(VGG16)、增加/修改其中的网络层(修改为10分类)

写在前面&#xff1a;有些地方和视频里不一样的是因为官方文档更新了&#xff0c;一些参数用法不一样也很正常&#xff0c;包括我现在的也是我这个时间节点最新的&#xff0c;谁知道过段时间会不会更新呢 建议大家不要一味看视频/博客&#xff0c;多看看官方文档才是正道&#…

RocketMQ 4.9.3源码解读-NameServer组件启动流程分析

作者源码阅读笔记主要采用金山云文档记录的,所有的交互图和代码阅读笔记都是记录在云文档里面,本平台的文档编辑实在不方便,会导致我梳理的交互图和文档失去原来的格式,所以整理在文档里面,供大家阅读交流 【金山文档 | WPS云文档】 namesrv 启动流程 相关重要类介绍说明…

《嵌入式 C 语言编码规范与工程实践个人笔记》参考华为C语言规范标准

《嵌入式 C 语言编码规范与工程实践个人笔记》参考华为C语言规范标准 前言 在电子系统开发领域&#xff0c;C 语言作为底层开发的核心语言&#xff0c;其代码质量直接关系到系统的稳定性、可维护性和扩展性。良好的编码规范不仅是团队协作的基础&#xff0c;更是降低生命周期成…

纯半精度模型和全精度模型的耗时分别为248微秒和1400微秒。混合精度模型371微秒比原始模型快大约四倍!

不过有一点需要注意:在上下文管理器内部生成的任何输出,必然会采用该上下文管理器的数据类型。因此,之后我们必须将这些输出转换回FP32(例如,使用float()函数)。 with torch.autocast(device_type="cuda", dtype=torch.float16): res16 = mixed32(torch.randn…

一款开源的远程桌面软件,旨在为用户提供流畅的游戏体验,支持 2K 分辨率、60 FPS,延迟仅为 40ms。

软件介绍 CloudPlayPlus&#xff08;云玩加&#xff09;是一款令人惊艳的开源远程桌面、串流软件&#xff0c;云玩加由个人开发者开发者&#xff0c;具有四大特征&#xff1a;开源、免费、低延迟、安全。 软件使用 客户端支持多个平台&#xff0c;包括 Windows、Mac OS、安卓…

MySql——binlog和redolog的区别

目录一、binlog和redolog的区别一、binlog和redolog的区别 binlog和redolog都是存储修改的新数据&#xff0c;是否保留binlog和redolog中的一个即可。 binlog属于整个mysql&#xff0c;是所有引擎共用的&#xff0c;不是只属于innoDB引擎。而redolog属于InnoDB存储引擎。binlo…

软件著作权产生与登记关键点

知识讲解一、 软件著作权的核心特征与权利内容自动产生原则&#xff1a; 这是软件著作权最核心、最重要的特征。产生时间&#xff1a; 软件著作权自软件开发完成之日起自动产生。法律依据&#xff1a; 《中华人民共和国著作权法》第二条及《计算机软件保护条例》第五条明确规定…

什么是主成分分析(PCA)和数据降维

主成分分析&#xff08;PCA&#xff09;和数据降维是机器学习和统计学中处理高维数据的核心工具。下面用清晰的结构解释其概念、原理和应用&#xff1a; 一、数据降维&#xff08;Dimensionality Reduction&#xff09; 1. 是什么&#xff1f; 目标&#xff1a;将高维数据&…

图论(4)单源赋权最短路径算法实现(BFS实现)

目录 1. 什么是赋权最短路径 2. 赋权最短路径中的关键概念 3. Dijkstra 算法的基本思想 4. Dijkstra 算法实现&#xff08;Java&#xff09; 1. 什么是赋权最短路径 在图论中&#xff0c;最短路径问题是指在图中寻找两点之间路径总权重最小的路径问题。如果图的每条边都带…

【Lua】题目小练9

题目&#xff1a;实现一个简单的“银行账户”类要求&#xff1a;使用 元表 模拟面向对象。支持以下功能&#xff1a;Account:new(owner, balance) 创建账户&#xff08;初始余额可选&#xff0c;默认为 0&#xff09;。deposit(amount) 存款&#xff08;不能为负数&#xff09;…

【二分图】染色问题

核心思想&#xff1a;为每一个未染色的&#xff0c;对它自己和它的邻居进行染色&#xff0c;看是否会出现冲突时间复杂度O&#xff08;nm&#xff09;#include<bits/stdc.h> using namespace std; using lllong long; const int N200010; int n,m; vector<int>edge…

报数游戏(我将每文更新tips)

今日tips&#xff1a;报数游戏题目描述报数游戏的游戏规则如下&#xff1a;对一个区间内的整数进行报数&#xff0c;若遇到的数字是质数或个位数是 1&#xff0c;则不报数&#xff0c;输出 pass。 给定开始游戏的第一个整数 a&#xff0c;及结束游戏时的最后一个整数 b&#xf…

大模型开发 - 基于Spring AI 借助MCP Client 通过STDIO和SSE协议调用MCP Server (上)

文章目录概述MCP协议&#xff1a;为AI应用连接外部世界的桥梁MCP Server&#xff1a;上下文与能力的提供者基于Spring AI 1.0.0的开发之路1. 使用Spring AI构建MCP客户端2. 使用Spring AI构建MCP服务器Mcp Client 实战整体架构概览技术栈Codepom配置mcp servers(sse&stdio)…

分析三个文件--启动文件、链接文件、map文件

目录 启动文件 链接文件 部分map文件内容 FLASH物理地址(0x08000000开始)的映射关系 0x08000000 之前地址空间 启动文件 ;******************** (C) COPYRIGHT 2016 STMicroelectronics ******************** ;* File Name : startup_stm32f40_41xxx.s ;* Author…

从零开始学Python之数据结构(字符串以及数字)

一、字符串 1.1 怎么定义字符串 字符串是Python最常用的数据结构之一。在 Python 里是用于处理文本数据的&#xff0c;比如存储姓名、文章内容等文本信息 。 定义方式&#xff1a; 单引号&#xff1a;用单引号 包裹文本&#xff0c;如 name Alice &#xff0c;单引号内可…