SpringBoot最佳实践之 - 项目中统一记录正常和异常日志

1. 前言

        此篇博客是本人在实际项目开发工作中的一些总结和感悟。是在特定需求背景下,针对项目中统一记录日志(包括正常和错误日志)需求的实现方式之一,并不是普适的记录日志的解决方案。所以阅读本篇博客的朋友,可以参考此篇博客中记录日志的方式,可能会对你有些许帮助和启发。

2. 需求描述

        项目的大背景是:前后端分离项目,后端框架使用SpringBoot+MyBatis,前端使用Vue。前端调用后端API接口时,需要在请求头中传递一个唯一身份标识(为了让服务端知道此次是谁在调用我的接口)。

        在一次完整的调用中,可能会有两种情况:
        (a)调用过程正常,后端给前端返回想要的数据(通过统一返回对象返回数据);
        (b)调用过程异常,后端抛出异常,并通过全局异常处理类进行异常处理,然后通过统一返回对象返回异常码和异常消息。

           现在的需求是:在情况(a),即一次请求正常完成,需要在日志中记录本次的请求和响应的相关信息。包括:
        1)请求身份唯一标识(我需要知道是哪个人访问了我);

        2)请求的接口名(我需要知道你请求的是哪个接口);

        3)请求的参数(我需要知道你此次请求这个接口传递了哪些参数);

        4)请求的时间(我需要知道你此次请求的开始时间);

        5)响应时间(我需要知道此次请求服务端给客户端响应的时间);

        6)请求处理时间(我需要知道此次请求耗费了多少时间);

        7)响应的结果信息(我需要知道此次请求的响应结果是什么);

        8)请求的客户端IP地址(我需要知道此次请求的客户端IP地址);

        9)此次请求是否成功的标识码(如果是成功日志,则标识位是0;失败日志,标识位是1)。

        在情况(b),即此次请求出现异常,也需要在日志中记录本次的请求和响应信息。包括:

        1)请求身份唯一标识(我需要知道是哪个人访问了我);

        2)请求的接口名(我需要知道你请求的是哪个接口);

        3)请求的参数(我需要知道你此次请求这个接口传递了哪些参数);

        4)请求的时间(我需要知道你此次请求的开始时间);

        5)响应时间(我需要知道此次请求服务端给客户端响应的时间);

        6)请求处理时间(我需要知道此次请求耗费了多少时间);

        7)响应的异常信息(我需要知道此次请求的响应的异常信息是什么]);

        8)请求的客户端IP地址(我需要知道此次请求的客户端IP地址);

        9)此次请求是否成功的标识码(如果是成功日志,则标识位是0;失败日志,标识位是1)。

        总结需求就一句话:正常和异常请求我都需要记录日志,只不过正常请求后记录的是正常返回的结果信息,而异常请求后记录的是出现异常的原因。

3. 代码实现

       3.1 logback日志配置

         因为要记录日志,在SpringBoot项目中通常是使用SLF4J作为日志门面,Logback作为底层日志框架实现配合进行日志记录。关于logback的配置信息,可以参考我之前写的一篇博客,你也可以直接把这里的配置信息放到SpringBoot项目的resources目录即可。logback-spring.xml文件的一些记录

      3.2 需要的组件

        实现此需求,需要如下一些功能组件:过滤器、拦截器、存放日志对象的ThreadLocal、统一返回结果对象、统一异常处理、统一响应结果处理器。此处先简单介绍下各个组件在需求中的作用是什么,主要还是靠理解代码实现逻辑。

        1)过滤器:因为需要在请求到达Controller之前获取请求体中的请求参数,而 HttpServletReqeust 获取输入流时仅允许读取一次,如果你直接在拦截器里面获取输入流,拿到里面的请求参数,后续处理过程中就会报java.io.IOException: Stream closed。具体可以参考这篇博文:SpringBoot如何在拦截器中获取@RequestBody参数;

        2)拦截器:在请求进来的时候,可以在拦截器里面获取一些请求信息,如此次请求的身份唯一标识、接口名、请求参数、请求时间、客户端ip地址。然后创建日志记录对象并把这些信息设置到日志记录对象中;

        3)存放日志对象的ThreadLocal:为了把第 2)步中拦截器里面的日志对象放到ThreadLocal里面,便于后续使用;

        4)统一返回结果:SpringBoot的最佳实践之一就是定义全局的统一返回对象,便于和前端联调;

        5)统一异常处理:SpringBoot的最佳实践之一就是在业务处理过程中,如果某些条件校验没通过,就直接抛出异常,然后由统一异常处理类进行统一处理,如打印异常堆栈信息,记录异常日志、通过统一返回结果对象给前端封装错误信息并返回;

        6)统一响应结果处理器:在给前端响应数据之前,可以对要响应的结果进行拦截并做一些事情,此处主要是为了记录一次正常请求过程中的日志信息

3.2.1 过滤器 (HttpServletRequestFilter)

package com.shg.component;import cn.hutool.extra.servlet.ServletUtil;
import org.springframework.stereotype.Component;import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;/**** HttpServletRequest 过滤器* 解决: request.getInputStream()只能读取一次的问题* 目标: 流可重复读**/
@Component
public class HttpServletRequestFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {ServletRequest requestWrapper = null;if (servletRequest instanceof HttpServletRequest) {requestWrapper = new RequestWrapper((HttpServletRequest) servletRequest);}//获取请求中的流,将取出来的字符串,再次转换成流,然后把它放入到新 request对象中.在chain.doFiler方法中传递新的request对象if (null == requestWrapper) {filterChain.doFilter(servletRequest, servletResponse);} else {filterChain.doFilter(requestWrapper, servletResponse);}}@Overridepublic void destroy() {}/**** HttpServletRequest 包装器* 解决: request.getInputStream()只能读取一次的问题* 目标: 流可重复读*/public class RequestWrapper extends HttpServletRequestWrapper {/*** 请求体*/private String mBody;public RequestWrapper(HttpServletRequest request) {super(request);// 将body数据存储起来mBody = getBody(request);}/*** 获取请求体** @param request 请求* @return 请求体*/private String getBody(HttpServletRequest request) {return ServletUtil.getBody(request);}/*** 获取请求体** @return 请求体*/public String getBody() {return mBody;}@Overridepublic BufferedReader getReader() throws IOException {return new BufferedReader(new InputStreamReader(getInputStream()));}@Overridepublic ServletInputStream getInputStream() throws IOException {// 创建字节数组输入流final ByteArrayInputStream bais = new ByteArrayInputStream(mBody.getBytes(StandardCharsets.UTF_8));return new ServletInputStream() {@Overridepublic boolean isFinished() {return false;}@Overridepublic boolean isReady() {return false;}@Overridepublic void setReadListener(ReadListener readListener) {}@Overridepublic int read() throws IOException {return bais.read();}};}}
}

3.2.2 拦截器 (RequestGlobalInterceptor)

package com.shg.component;import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.shg.common.ResponseCodeEnum;
import com.shg.exception.BizException;
import com.shg.model.pojo.RecordLog;
import com.shg.utils.LogUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.Objects;@Slf4j
@Component
public class RequestGlobalInterceptor implements HandlerInterceptor {private final StringRedisTemplate stringRedisTemplate;public RequestGlobalInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if ("OPTIONS".equals(request.getMethod())) {return true;}if (request.getRequestURI().contains("/swagger-ui.html") ||request.getRequestURI().contains("/webjars/springfox-swagger-ui") ||request.getRequestURI().contains("swagger") ||request.getRequestURI().contains("webjars") ||request.getRequestURI().contains("images") ||request.getRequestURI().contains("api-docs") ||request.getRequestURI().contains("configuration/ui") ||request.getRequestURI().contains("configuration/security")) {return true;}String appId = null;try {appId = request.getHeader("appId");String appSecret = request.getHeader("appSecret");// 校验appId和appSecret是否合法 TODOif(Objects.isNull(appId)){throw new BizException(ResponseCodeEnum.APP_ID_NOT_PASSED);}} finally {// --- 日志相关 ---// 1. 接口名称String requestURI = request.getRequestURI();String interfaceName;if (requestURI.contains("/api/crypto")) {int startIndex = requestURI.indexOf("/api/crypto");interfaceName = requestURI.substring(startIndex + "/api/crypto/".length());} else {interfaceName = requestURI;}// 2. 请求参数String requestParameter = "";if (request instanceof HttpServletRequestFilter.RequestWrapper) {HttpServletRequestFilter.RequestWrapper repeatedlyRequest = ((HttpServletRequestFilter.RequestWrapper) request);requestParameter = StrUtil.removeAny(StrUtil.removeAllLineBreaks(repeatedlyRequest.getBody()), " ");//log.info("body参数: " + requestParameter);}if (StrUtil.isEmpty(requestParameter)) {requestParameter = JSONUtil.toJsonStr(request.getParameterMap());//log.info("查询字符串参数: " + requestParameter);}// 创建日志对象RecordLog recordLog = new RecordLog();recordLog.setAppId(appId);recordLog.setInterfaceName(interfaceName);recordLog.setArguments(requestParameter);recordLog.setRequestTime(DateUtil.format(new Date(), DatePattern.NORM_DATETIME_MS_PATTERN));recordLog.setServerIp(request.getRemoteAddr());// 把日志对象放到ThreadLocal里面,便于后续使用LogUtil.set(recordLog);}return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {LogUtil.remove();}
}

3.2.3 存放日志对象的ThreadLocal (LogUtil)

package com.shg.utils;import com.shg.model.pojo.RecordLog;public class LogUtil {private static final ThreadLocal<RecordLog> appCacheDtoThreadLocal = new ThreadLocal<>();public static void set(RecordLog log) {appCacheDtoThreadLocal.set(log);}public static RecordLog get() {return appCacheDtoThreadLocal.get();}public static void remove() {appCacheDtoThreadLocal.remove();}
}

3.2.4 控制器Controller

package com.shg.controller;import com.shg.common.ResultMessage;
import com.shg.model.vo.RandomRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;@RestController
public class TestController {@GetMapping("/test1")public ResultMessage<String> test1(@RequestBody RandomRequest randomRequest) {int i = 1/0;// TODO 调用Service层的业务逻辑return ResultMessage.success("这是测试接口..." +randomRequest.getLength());}
}

3.2.5 统一返回结果对象 (ResultMessage)

package com.shg.common;import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResultMessage<T> {private Integer code;private String message;private T data;private long timestamp = System.currentTimeMillis();public ResultMessage() {this(ResponseCodeEnum.SUCCESS.getCode(), ResponseCodeEnum.SUCCESS.getMessage(), null);}public ResultMessage(Integer code, String message, T data) {this.code = code;this.message = message;this.data = data;}public static <T> ResultMessage<T> success() {return new ResultMessage<>();}public static <T> ResultMessage<T> success(T data) {ResultMessage<T> resultMessage = new ResultMessage<>();resultMessage.setMessage(ResponseCodeEnum.SUCCESS.getMessage());resultMessage.setData(data);return resultMessage;}public static <T> ResultMessage<T> success(String message, T data) {ResultMessage<T> resultMessage = new ResultMessage<>();resultMessage.setMessage(message);resultMessage.setData(data);return resultMessage;}public static <T> ResultMessage<T> success(String message, boolean success) {ResultMessage<T> resultMessage = new ResultMessage<>();resultMessage.setMessage(message);return resultMessage;}public static <T> ResultMessage<T> errorResult(ResponseCodeEnum responseCodeEnum) {return new ResultMessage<>(responseCodeEnum.getCode(), responseCodeEnum.getMessage(), null);}public static <T> ResultMessage<T> errorResult(Integer code, String message) {return new ResultMessage<>(code, message, null);}public static <T> ResultMessage<T> errorResult(Integer code, String message, T data) {return new ResultMessage<>(code, message, data);}
}

3.2.6 统一异常处理 (GlobalExceptionHandler)

package com.shg.exception;import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.shg.common.ResponseCodeEnum;
import com.shg.common.ResultMessage;
import com.shg.constant.CommonConstant;
import com.shg.model.pojo.RecordLog;
import com.shg.utils.LogUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.Date;
import java.util.Objects;@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Throwable.class)@ResponseBodypublic ResultMessage error(Throwable e) {e.printStackTrace();recordLog(e, e.getMessage());return ResultMessage.errorResult(ResponseCodeEnum.SYSTEM_EXCEPTION.getCode(), e.getMessage());}@ExceptionHandler(BizException.class)@ResponseBodypublic ResultMessage error(BizException e) {recordLog(e, e.getMessage());e.printStackTrace();return ResultMessage.errorResult(e.getCode(), e.getMessage());}private void recordLog(Throwable e, String message) {RecordLog recordLog = LogUtil.get();if (!Objects.isNull(recordLog)) { // 有一种情况是,当请求方法不支持时,经过过滤器进行放行之后,会直接抛出异常,然后就会在这里进行处理了,此时RecordLog还没有对象,所以这里要判断recordLog是否为空String responseTime = DateUtil.format(new Date(), DatePattern.NORM_DATETIME_MS_PATTERN);String requestTime = recordLog.getRequestTime();long costTime = getCostTime(responseTime, requestTime);recordLog.setResponseTime(responseTime);recordLog.setResponseInterval(costTime);recordLog.setMessage(message);recordLog.setResult(CommonConstant.ONE);String myLogJson = JSON.toJSONString(recordLog);log.error("[EX]:" + System.currentTimeMillis() + " " + myLogJson, e);} else {log.error(e.getMessage(), e);}}private long getCostTime(String responseTime, String requestTime) {Date endTime = DateUtil.parse(responseTime);Date startTime = DateUtil.parse(requestTime);return DateUtil.between(endTime, startTime, DateUnit.MS);}
}

package com.shg.common;public enum ResponseCodeEnum {SUCCESS(0, "success"),SYSTEM_EXCEPTION(500, "System internal exception"),APP_ID_NOT_PASSED(1001, "The appId is not passed");private final int code;private final String message;ResponseCodeEnum(int code, String message) {this.code = code;this.message = message;}public int getCode() {return code;}public String getMessage() {return message;}}

3.2.7 统一响应结果处理器 (ResponseBodyAdviceAdapter)

package com.shg.component;import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.shg.common.ResponseCodeEnum;
import com.shg.common.ResultMessage;
import com.shg.constant.CommonConstant;
import com.shg.model.pojo.RecordLog;
import com.shg.utils.LogUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;import java.util.Date;
import java.util.Objects;/*** @DESCRIPTION: 在返回结果前进行日志记录* @USER: shg* @DATE: 2024/10/24 22:55*/
@Slf4j
@ControllerAdvice
public class ResponseBodyAdviceAdapter implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {return true;}@Overridepublic Object beforeBodyWrite(Object body,MethodParameter returnType,MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request,ServerHttpResponse response) {if (body instanceof ResultMessage) {ResultMessage resultMessage = ((ResultMessage<?>) body);if (resultMessage.getCode() == ResponseCodeEnum.SUCCESS.getCode()) {recordLog(JSONUtil.toJsonStr(resultMessage));}}return body;}private void recordLog(String message) {RecordLog recordLog = LogUtil.get();String responseTime = DateUtil.format(new Date(), DatePattern.NORM_DATETIME_MS_PATTERN);String requestTime = recordLog.getRequestTime();long costTime = getCostTime(responseTime, requestTime);recordLog.setResponseTime(responseTime);recordLog.setResponseInterval(costTime);recordLog.setMessage(message);recordLog.setResult(CommonConstant.ZERO);String myLogJson = JSON.toJSONString(recordLog);if (!Objects.isNull(recordLog.getAppId())) {log.info("[SUCCESS RESULT:]" + myLogJson);}}private long getCostTime(String responseTime, String requestTime) {Date endTime = DateUtil.parse(responseTime);Date startTime = DateUtil.parse(requestTime);return DateUtil.between(endTime, startTime, DateUnit.MS);}}

4. 效果演示

4.1. 请求正常执行,记录日志

 前端收到的响应结果:

记录的正常执行日志信息如下:

4.2. 请求异常,记录错误日志

4.21. 异常示例一

前端收到的响应结果如下:

记录的异常日志如下:

4.2.2  异常示例二

模拟业务执行过程中出现异常。前端收到的响应结果如下:

 记录的异常日志如下:

5. 其他

        记录项目运行过程中的正常和异常日志,还有很多其他方法。比如使用AOP等。这里只是我个人在项目开发过程中为了实现需要而使用的一种方式。

        具体代码示例请参考码云:​​​​​​springboot-best-practice: 初次提交

        如果此篇文章对你有帮助,感谢点个赞~~

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

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

相关文章

【问题解决】三维相关:​Unity Package Manager中没有Newtonsoft Json‌​

问题&#xff1a; 在Unity开发中&#xff0c;用到复杂的json的数据格式&#xff0c;需要将对象和json数据之间相互转换。Unity原生json支持不适用复杂json&#xff08;例如嵌套数组、动态键值对等&#xff09;。大部分人推荐直接在Package Manager中搜索导入(如怎么在unity3D工…

Jupyter Notebook 中使用render_notebook渲染pyecharts图像不显示的一种情况

一开始我发现自己的jupyter文件在渲染pyecharts图片时一开始可以显示&#xff0c;但后来不知道怎么的就不显示了&#xff0c;查找了很多方法&#xff0c;但是没有效果&#xff0c;都是改js渲染什么的&#xff0c;还有就是参数不对的&#xff0c;对于我来说都没什么用&#xff0…

excel中,将时间戳(ms或s)转换成yyyy-MM-dd hh:mm.ss或毫秒格式

问题 在一些输出为时间戳的文本中&#xff0c;按照某种格式显示更便于查看。 如下&#xff0c;第一列为时间戳(s)&#xff0c;第二列是转换后的格式。 解决方案&#xff1a; 在公式输入框中输入&#xff1a;yyyy/mm/dd hh:mm:ss TEXT((A18*3600)/8640070*36519, "yyy…

从传统到智能,从被动监控到主动预警,解锁视频安防平台EasyCVR视频监控智能化升级的关键密钥

视频监控技术从传统监控到智能化升级的过程是一个技术革新和应用场景拓展的过程。智能视频监控系统通过集成AI和机器学习算法&#xff0c;能够实现行为分析、人脸识别和异常事件检测等功能&#xff0c;提升了监控的准确性和响应速度。这些系统不仅用于传统的安全防护&#xff0…

Ribbon客户端负载均衡策略测试及其改进

文章目录 一、目的概述二、验证步骤1、源码下载2、导入IDE3、运行前修改配置4、策略说明5、修改策略 三、最终结论四、改进措施1. 思路分析2. 核心代码3. 测试页面 一、目的概述 为了验证Ribbon客户端负载均衡策略在负载节点失效的情况下&#xff0c;是否具有故障转移的功能&a…

一家生物技术企业终止,科创属性可能不足,报告期内专利数猛增

轩凯生物九成以上营业收入来源于植物营养领域&#xff0c;收入来源结构单一&#xff0c;产品下游应用领域较为集中。报告期内公司应收账款账面价值逐年上升&#xff0c;回款比例显著低于前两年&#xff0c;遭交易所问询是否存在较大的坏账风险。 轩凯生物核心技术是否成熟以及是…

【SDL】微软SDL建设指南

【SDL】微软SDL建设指南 1.建立安全标准、指标和治理2.要求使用经过验证的安全功能、语言和框架3.执行安全设计审查和威胁建模4.定义并使用密码学标准5.确保软件供应链安全6.确保工程环境安全7.执行安全测试8.确保运营平台安全9.实施安全监控和响应&#xff08;态势管理或漏洞管…

二十、Innodb底层原理与Mysql日志机制深入剖析

文章目录 一、MySQL的内部组件结构1、Server层1.1、连接器1.2、查询缓存1.3、分析器1.4、优化器1.5、执行器 2、存储引擎层 二、Innodb底层原理与Mysql日志机制1、redo log重做日志关键参数2、binlog二进制归档日志2.1、binlog日志文件恢复数据 3、undo log回滚日志4、错误日志…

群晖通过 Docker 安装 Firefox

1. 获取 firefox 镜像 在注册表搜索 jlesage/firefox&#xff0c;并且下载 2. 创建容器 运行映像 jlesage/firefox&#xff0c;开始创建容器 3. 配置容器 启用自动重新启动&#xff0c;重点配置存储空间和环境变量&#xff0c;其他默认。 创建文件夹&#xff0c;及子文件夹…

高效设备管理:中小企业的Spring Boot解决方案

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理中小企业设备管理系统的相关信息成为必然。…

Lucas带你手撕机器学习——SVM支持向量机

#1024程序员节&#xff5c;征文# 支持向量机&#xff08;SVM&#xff09;的详细讲解 什么是SVM&#xff1f; 支持向量机&#xff08;Support Vector Machine&#xff0c;SVM&#xff09;是一种用于分类和回归的监督学习算法。它的主要任务是从给定的数据中找到一个最佳的决策…

原来“有符号数变成无符号数,并不是-1变成1,-15变成15”!!

不怕大家伙笑话&#xff0c;我以前一直以为在C语言中&#xff0c;有符号变无符号仅仅就是去掉数字前面的符号就行&#xff0c;如今做了一道题&#xff0c;细细研究&#xff0c;才发现&#xff0c;原来不是&#xff01; 如果你也感兴趣&#xff0c;那就学学今天这节吧~ 话不多说…

前端必知必会-JavaScript 简介

文章目录 JavaScript 简介JavaScript 可以更改 HTML 内容JavaScript 可以更改 HTML 属性值JavaScript 可以更改 HTML 样式 (CSS)JavaScript 可以隐藏 HTML 元素JavaScript 可以显示 HTML 元素 总结 JavaScript 简介 本页包含一些 JavaScript 功能的示例。 JavaScript 可以更改…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-20

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-20 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-20目录1. FLARE: Faithful Logic-Aided Reasoning and Exploration摘要研究背景问题与挑战如何解决创新点算法模型实验效果重要数…

多线程进阶——线程池的实现

什么是池化技术 池化技术是一种资源管理策略&#xff0c;它通过重复利用已存在的资源来减少资源的消耗&#xff0c;从而提高系统的性能和效率。在计算机编程中&#xff0c;池化技术通常用于管理线程、连接、数据库连接等资源。 我们会将可能使用的资源预先创建好&#xff0c;…

Ubuntu22.04虚拟机安装

一、安装介质下载&#xff1a; 在官网下载安装镜像&#xff0c;下载地址https://releases.ubuntu.com/22.04/ubuntu-22.04.5-live-server-amd64.iso 二、操作系统安装&#xff1a; step 1:进入ubuntu的安装界面&#xff0c;直接回车安装。 step 2:选择语言&#xff0c;直接回…

liunx线程互斥

临界资源和临界区 临界资源&#xff1a;多线程执行流共享的资源就叫临界资源。 临界区&#xff1a;每个线程中&#xff0c;访问临界区的代码&#xff0c;就叫临界区。 互斥&#xff1a;任何时候&#xff0c;互斥保证只有一个执行流进入临界区&#xff0c;访问临界资源&#…

NXP Smart Access Car-车用产品整合应用

在汽车技术不断进步的今天&#xff0c;智能化已成为汽车行业发展的主要趋势之一。本次研讨会将深入探讨NXP的Smart Access Car技术&#xff0c;说明如何通过NXP 产品设计提升汽车的安全性、便利性和使用者体验。研讨会将涵盖NXP MCU/NFC等方面的最新解决方案&#xff0c;并探讨…

Qt调用Yolov11导出的Onnx分类模型开发分类检测软件

软件视频地址:视频地址 代码开源地址 之前用Python配合YOLOV11开发一个了分类训练软件&#xff0c;软件只要准备好数据&#xff0c;然后导入就可以训练数据&#xff0c;训练完成后还可以验证&#xff0c;测试&#xff0c;但是要真正落地&#xff0c;还是有点欠缺。配合YOLOV1…

入门数据结构JAVADS——如何通过遍历顺序构建二叉树

目录 前言 构建二叉树的前提&#xff1a; 为什么需要两个不同类型的遍历&#xff1a; 前序遍历 中序遍历 我们的算法思路如下: 举例&#xff1a; 代码实现 后序遍历 中序遍历 结尾 前言 入门数据结构JAVA DS——二叉树的介绍 (构建,性质,基本操作等) (1)-CSDN博客 在上…