当前位置: 首页 > news >正文

初始JavaEE篇 —— SpringBoot 统一功能处理

找往期文章包括但不限于本期文章中不懂的知识点:

个人主页:我要学编程程(ಥ_ಥ)-CSDN博客

所属专栏:JavaEE

目录

前言

拦截器

基本使用

拦截器的路径配置

统一数据返回格式

统一异常处理


前言

在实际开发中,某些功能需要强制用户登录才能使用。例如,csdn与博客园的点赞、收藏、评论,甚至于经常使用的抖音,每次打开界面如果不登录的话,就是提示你要登录(登录之后,就可以记录用户的使用啥操作....让一切操作有迹可循)。面对这种强制登录的情况,该如何实现呢?

对应到代码的话,就是下面:

@RestController
@RequestMapping("/user")
@Slf4j
public class LoginController {/*** 登录接口* @return true-登录成功,false-登录失败*/@RequestMapping("/login")public boolean login(String username, String password) {// 检验参数是否正常if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return false;}// 检验账号密码是否正确if ("admin".equals(username) && "123456".equals(password)) {return true;}return false;}/*** 测试不登录是否可以访问该接口* @return*/@RequestMapping("/testLogin")public boolean testLogin() {log.info("成功访问到testLogin接口");return true;}}

我们前面学习的是通过Cookie-Session机制来判断用户是否登录。先来回顾一下流程:客户端首次登录时,会将用户名和密码通过HTTP请求发送到服务器,服务器验证正确之后,就会将该用户信息存储到session中,服务器就会将该session存储到本地,同时将session-id存储到HTTP响应头的set-Cookie中,当客户端接收到该请求之后,就会将set-Cookie中的session-id存储到本地,后续再次请求时,就会将session-id存储到HTTP请求头的Cookie中,这样服务器拿到Cookie中session-id之后,就会去本地查找对应的session信息,如果找到并且session并未过期,就会认为用户已经登录;反之,则会让用户重新登录。

@RestController
@RequestMapping("/user")
@Slf4j
public class LoginController {/*** 登录接口* @return true-登录成功,false-登录失败*/@RequestMapping("/login")public boolean login(String username, String password, HttpSession session) {// 检验参数是否正常if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return false;}// 检验账号密码是否正确if ("admin".equals(username) && "123456".equals(password)) {// 设置session信息,检验后续访问其他接口时,判断用户是否登录session.setAttribute("user", username);return true;}return false;}/*** 测试不登录是否可以访问该接口* @return*/@RequestMapping("/testLogin")public boolean testLogin(HttpServletRequest request) {// 检验用户是否登录HttpSession session = request.getSession(false);if (session != null && session.getAttribute("username") != null) {// 这里处理具体的逻辑log.info("成功访问到testLogin接口");return true;}return false;}}

 通过上述代码中的判断session信息是否正确的方式,可以来拦截一下并未登录的用户。

上述方式虽能正确处理强制用户登录的情况,但需要在每个接口的逻辑中都添加判断session信息是否存在,非常的麻烦。而今天,我们就来学习SpringBoot的统一功能处理。

拦截器

要实现上述统一拦截请求,并进行Session校验的流程就需要用到拦截器。那什么是拦截器呢?拦截器是Spring框架提供的核心功能之一,主要用来拦截用户的请求,在指定方法前后,根据业务需要执行预先设定的代码。也就是说,允许开发人员提前预定义一些逻辑,在用户的请求响应前后执行,也可以在用户请求前阻止其执行。在拦截器当中,开发人员可以在应用程序中做一些通用性的操作,比如通过拦截器来拦截前端发来的请求,判断Session中是否有登录用户的信息,如果有就可以放行,如果没有就进行拦截。这样就避免了在每个方法内部重复编码的操作。

基本使用

拦截器的使用分为两个步骤:

1、定义拦截器;

2、注册拦截器;

自定义拦截器:实现HandlerInterceptor接口,并重写所有方法。

public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return HandlerInterceptor.super.preHandle(request, response, handler);}@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 {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}

注意:实现该接口之后,可以选择啥也不重写或者选择性重写部分方法。 

默认重写完成之后,就是上述代码。

preHandle方法的含义是目标方法执行前执行,如果该方法的返回值为true的话,就代表当前方法放行;反之,则代表当前方法被拦截,就不能到达Controller层的逻辑了。

postHandle方法的含义是目标方法执行后执行(也就是HTTP请求响应返回前执行)。

afterCompletion()方法:视图渲染完毕后执行,这是最后执行的,但由于现在都是前后端分离开发,因此这里无需了解。

注意:这里的目标方法就是HTTP请求的资源路径对应的方法(并不是完整的URL),也就是Controller层@RequestMapping所对应的value值。

接下来,就是注册配置拦截器:

@Configuration // 加上,是为了spring能够在初始化容器时,去执行下面的方法
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册拦截器并配置拦截路径(不拦截路径)LoginInterceptor loginInterceptor = new LoginInterceptor();// 方法需要的参数是拦截器对象// "/**" 表示拦截所有的请求registry.addInterceptor(loginInterceptor).addPathPatterns("/**");}
}

上述就是实现拦截器的基本框架,如果想要实现拦截未登录的用户的话,需要将 preHandle 方法内部的逻辑具体修改:

// 拦截器
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {// 父类默认返回的是true,即对拦截的请求都放行@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("目标方法执行前...");HttpSession session = request.getSession(false);// 检验用户是否登录,如果登录放行该请求;反之,则拦截改请求if (session != null && session.getAttribute("username") != null) {return true;}return false;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {log.info("目标方法执行后...");HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}

接下来,测试未登录的用户是否可以访问该方法:

由此看来,当我们注册并配置了拦截器之后,同时在拦截器中实现了具体要拦截的请求是啥样时,Spring会准确无误的帮我们拦截该请求。 

拦截器的路径配置

拦截器路径是指我们定义的拦截器,需要对哪些路径进行拦截。前面在注册拦截器时,我们是通过 addPathPatterns() 方法指定要拦截哪些路径下的请求,也可以通过 excludePathPatterns() 方法指定哪些请求不拦截。

拦截路径含义说明
/*一级路径只能拦截 /login,而不能拦截 /user/login
/**任意级路径能拦截 /login,  /user/login
/user/*/user 下的一级路径只能拦截 /user/login,而不能拦截 /user/login/123 
/user/**/user 下的任意级路径能拦截 /user/login,/user/login/123 

当拦截的是一级路径,我们来观察一级路径以及二级路径的访问情况:

@Slf4j
@RestController
@RequestMapping("/user")
public class LoginController {@RequestMapping("/login")public Boolean login(String username, String password, HttpSession session) {// 检验参数是否正常if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return false;}// 判断参数是否正确if ("admin".equals(username) && "123456".equals(password)) {// 存储用户信息到sessionlog.info("用户登录成功...");session.setAttribute("username", username);return true;}return false;}@RequestMapping("/testLogin")public String testLogin(HttpServletRequest request) {log.info("用户已经访问该接口");// 检验用户是否登录HttpSession session = request.getSession(false);if (session != null && session.getAttribute("username") != null) {return "用户已经登录: "+session.getAttribute("username");}return "用户并未登录";}
}@RestController
@Slf4j
public class LoginController2 {@RequestMapping("/testLogin2")public String testLogin2(HttpServletRequest request) {log.info("用户已经成功访问该接口");// 检验用户是否登录HttpSession session = request.getSession(false);if (session != null && session.getAttribute("username") != null) {return "用户已经登录: "+session.getAttribute("username");}return "用户并未登录";}
}

上面是访问二级路径的情况,我们再来看访问一级路径:

我们会发现,如果只要是经过了 拦截器的方法 都会是打印出 目标方法执行前 这个日志的,这也从侧面体现出 打印日志 的重要性。 

至于其余的情况这里就不再演示了。

注意:如果我们在配置拦截器时,将任意级路径都拦截的话,不仅是后端接口会被拦截掉,连前端等静态资源也会被拦截。如下所示:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>测试静态资源是否会被拦截</title>
</head>
<body><div style="color: orangered;">测试静态资源是否会被拦截</div>
</body>
</html>

启动项目之后,通过浏览器访问到对应的地址,然而我们会发现页面信息的展示是乱码,如果我们再通过 vscode 去打开对应的页面信息,发现可以正常显示:

这是由于配置的拦截器信息对 静态资源 也进行了拦截(/*),在 Spring Boot 中处理静态资源(比如.html,·js,·css,·png 等)时,默认是由ResourceHttpRequestHandler 处理的。这个处理器会自动根据文件后缀设置对应的 Content-Type。虽然设置了 Content-Type: text/html,但并没有去设置编码信息,因此浏览器会根据系统语言来自动解析编码,我们的Windows系统语言默认为中文,因此解码使用的是 GBK,即出现了乱码的情况。

如果我们把编码信息手动设置为 UTF-8 或者不拦截所有的一级路径的话,最终页面就可以正常显示。

 接下来,继续访问:

因此得排除这些路径:

由于我们这里的静态资源只有 html 文件,因此只排出 html 文件,实际开发中,可能会出现多种静态资源文件。例如,html、css、js、jpg等。如果想要正常显示的话,都得进行排除。

统一数据返回格式

在前后端分离开发中,接口返回的数据格式设计至关重要。例如,对于登录接口,如果后端返回的数据格式不统一,前端在处理时会面临诸多不便。假设后端返回的数据格式为对象或JSON数据,前端需要通过对象的属性来判断登录是否成功。但如果后端直接返回布尔类型(Boolean),前端只需判断值是否为true即可。这仅仅是处理成功情况的对比。对于HTTP请求失败的情况,前端还需要根据后端的返回来处理错误,这也增加了前端的开发复杂性。在这种情况下,前端每次编写代码时都需要参考后端的具体实现,这无疑增加了开发成本和沟通成本。那么,前后端分离开发的意义何在呢?

因此,后端接口在返回数据时,必须设置一个统一的数据返回格式。这样,前端只需熟悉一次返回格式,即可高效地进行开发,大大减少重复劳动和沟通成本,真正发挥前后端分离开发的优势。

现在我们就可以对前面登录接口和测试登录接口来进行一个封装:

@Data
public class Result {ResultCode code; // 业务响应码String message; // 业务信息Object data; // 响应的具体数据public static Result success() {Result result = new Result();result.setCode(ResultCode.success);result.setMessage("success");return result;}public static Result success(Object data) {Result result = new Result();result.setCode(ResultCode.success);result.setMessage("success");result.setData(data);return result;}public static Result error(String message) {Result result = new Result();result.setCode(ResultCode.client_error);result.setMessage(message);return result;}public static Result error() {Result result = new Result();result.setCode(ResultCode.server_error);result.setMessage("服务开小差了,请稍后再试...");return result;}
}public enum ResultCode {success(200),client_error(400),server_error(500);private int code;private ResultCode(int code) {this.code = code;}
}

封装成上述对象之后,接口在返回数据时,就需要严格按照该对象来,这里就省略了。

接下来,测试上述封装是否成功(先把配置拦截器的代码注释掉):

如果要想把这里的code改成具体的数值的话,可以在 ResultCode 类中,加上下面的代码:

    @JsonValuepublic int getCode() {return code;}

这表明在序列化时,将枚举对象序列化为对应的数字。 这个注解的作用:把枚举对象序列化时,使用 getCode() 返回的值。

上面的方式就是对返回结果进行了一个封装,但还是不够完美。如果每个接口返回的结果都需要进行封装的话,那也是比较麻烦的,因此Spring也提供了类似拦截器这样的方式,来让我们可以对返回结果进行统一处理。

1、自定类实现 ResponseBodyAdvice,并重写父类的 supports 和 beforeBodyWrite 方法;

2、将 supports 方法的内容,改为返回 true,接着在 beforeBodyWrite 方法内,使用统一数据返回对象来封装 body对象,再返回即可。

@ControllerAdvice // 交由Spring管理
public class ResponseAdvice implements ResponseBodyAdvice {/**** @param returnType the return type* @param converterType the selected converter type* @return false 表示不处理,true 表示处理*/@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {// 这里同样也是和拦截器的处理方式一样,都是可以使用 if 语句来进行判断是否需要进行处理的// 为了先观察结果,我们先统一设置返回true,即表示都进行处理。// 这里的处理是指是否去执行下面的方法return true;}/**** @param body 表示返回的正文信息* @param returnType the return type of the controller method* @param selectedContentType the content type selected through content negotiation* @param selectedConverterType the converter type selected to write to the response* @param request the current request* @param response the current response* @return*/@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {// 这里的Body表示HTTP请求需要返回的正文信息// 如果需要对响应数据进行统一处理的话,也是在这里进行响应数据的封装return Result.success(body); // 对 body 进行封装}
}

接着再次启动项目,使用 Postman 去访问测试登录接口:

之所以会出现封装了两层的现象,是因为我们前面已经将登录接口的返回对象进行了封装,而这里再次对 body 进行了一层封装。因此,我们可以先将前面封装的结果修改为最原始的状态,再次启动项目,访问接口观察:

发现服务器发生错误,我们再去看另外一个接口:

具体封装失败的原因,需要我们去看错误日志:

那为什么会出现需要将 Result 类型转换为 String 类型的情况呢?这是因为Postman(前端)请求的方法的返回值为 String 类型,但经过统一数据格式的处理之后,实际的返回类型变为 Result 类型,Spring 看到方法的返回类型是String,就默认用 StringHttpMessageConverter 去处理,但却遇到了一个非字符串对象,于是报错。而我们要做的就是将 Result对象 转换为 String对象,怎么做呢?很简单,通过 Spring 内置的 Jackson 将 Result对象 序列化为 JSON字符串,这下就可以兼容了。

// 将 Result 对象序列化为 JSON数据
ObjectMapper objectMapper = new ObjectMapper();
try {return objectMapper.writeValueAsString(Result.success(body));
} catch (JsonProcessingException e) {throw new RuntimeException(e);
}

而当我们加上上述转换为 JSON 的处理之后,会导致所有的数据都会转换为 JSON,但实际上只需要 String 类型,因此我们可以先判断 是否为 String 类型的数据。 

 // 将 Result 对象序列化为 JSON数据if (body instanceof String) {ObjectMapper objectMapper = new ObjectMapper();try {return objectMapper.writeValueAsString(Result.success(body));} catch (JsonProcessingException e) {throw new RuntimeException(e);}}return Result.success(body); // 对 body 进行封装

注意:可能只有当方法的返回值为 String 时,才会出现上述情况,而当方法的返回值为 int 或者 Boolean等类型时,却不会出现上述情况呢?这是因为Spring在传输数据时,使用的JSON数据格式,其本质是字符串,因此当方法返回值为非 String 类型时,会自动转换为 JSON数据,Boolean类型或 int类型转换字符串,Result对象 转换为 JSON数据,在传输时,JSON数据和String类型的数据并无啥区别,但是对于方法的返回值为String类型的话,Spring就不会去转换为 JSON数据了,但是在准备传输时却发现不是String类型,于是强行转换为 String 就报错了。

统一异常处理

我们在编码时,对于可能抛出异常的代码需要手动进行 try-catch,但即使我们再细致,也可能会忽略某些异常的处理,倘若恰好这个异常信息直接导致了整个程序崩溃了,如果是在测试环境倒还好,就怕是发生在实际的生产环境中。因此,我们接下来要学习如何对异常进行统一处理。

自定类异常处理类,加上 @ControllerAdvice、@ResponseBody,定义异常处理方法,加上@ExceptionHandler,方法内部定义异常处理逻辑。

@ControllerAdvice // 交由Spring管理
@ResponseBody // 返回的具体数据,而不是页面
public class ExceptionAdvice {@ExceptionHandler // 处理异常public Result handleException(Exception e) {// 对所有的异常进行返回return Result.error(e.getMessage());}
}@RestController
@RequestMapping("/test")
public class TestController {/*** 下面是常见的异常,看是否可以捕获到*/// 除0异常@RequestMapping("/t1")public Integer test1() {int a = 10 / 0;return a;}// 数组越界异常@RequestMapping("/t2")public Boolean test2() {int[] array = new int[10];int a = array[11];return a > 0;}// 空指针异常@RequestMapping("/t3")public String test3() {int[] array = null;int len = array.length;return array.toString();}
}

接下来就需要对上述代码进行测试,观察是否捕获到了异常信息:

都是能够正常捕获的,这里就只给出 test1 方法的测试结果。

但在实际开发中,可以异常的处理也分的非常细致,需要我们对不同的异常信息,对前端或者直接打印在控制台的信息中,发送不同的错误信息。

@Slf4j
@ControllerAdvice // 交由Spring管理
@ResponseBody // 返回的具体数据,而不是页面
// 也可以使用 @RestControllerAdvice 代替上面两个注解
public class ExceptionAdvice {@ExceptionHandler // 处理异常public Result handleException(Exception e) {log.error(e.getMessage());return Result.error("服务器内部错误,请联系管理员...");}@ExceptionHandler // 处理异常public Result handleException(NullPointerException e) {log.error(e.getMessage());return Result.error("空指针异常,请联系管理员...");}@ExceptionHandler // 处理异常public Result handleException(ArrayIndexOutOfBoundsException e) {log.error(e.getMessage());return Result.error("数组越界异常,请联系管理员...");}
}

在捕获异常时,和我们前面学习的 try-catch 并不是一样的,try-catch的方式是从上往下依次去捕获异常信息,如果遇到了 父类的话,就直接走父类的处理流程了,用一句话概括的话,就是谁先catch到了,就走谁的流程,但是这里在捕获异常时,是优先去匹配与当前异常信息一致的异常,其次再是匹配度稍低的异常。如果实在是匹配不到的话,就会先把异常抛出去。因此一般我们都会定义一个顶级Exception来处理预料之外的情况。

在处理详细的异常时,有两种方式:
1、在参数列表中指定异常信息的种类;
2、为@ExceptionHandler注解的value赋值为具体的异常类

上述代码使用的是第一种方式,接下来,我们使用第二种方式:

@Slf4j
@ControllerAdvice // 交由Spring管理
@ResponseBody // 返回的具体数据,而不是页面
public class ExceptionAdvice {@ExceptionHandler(Exception.class) // 处理异常public Result handleException(Exception e) {log.error(e.getMessage());return Result.error("服务器内部错误,请联系管理员...");}@ExceptionHandler(NullPointerException.class) // 处理异常public Result handleException2(Exception e) {log.error(e.getMessage());return Result.error("空指针异常,请联系管理员...");}@ExceptionHandler(ArrayIndexOutOfBoundsException.class) // 处理异常public Result handleException(Exception e) {log.error(e.getMessage());return Result.error("数组越界异常,请联系管理员...");}}

注意:当通过注解的方式来表示是那个异常信息之后,参数列表内部的异常信息就会失效,也就是说Spring会优先根据注解去捕获异常信息,而不是参数列表。

好啦!本期 初始JavaEE篇 —— SpringBoot 统一功能处理 的学习之旅 就到此结束啦!我们下一期再一起学习吧!

相关文章:

初始JavaEE篇 —— SpringBoot 统一功能处理

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;JavaEE 目录 前言 拦截器 基本使用 拦截器的路径配置 统一数据返回格式 统一异常处理 前言 在实际开发中&#xff0c;某些功能需要强…...

Spring AI Alibaba 文档检索使用

一、文档检索 (Document Retriever)简介 1、核心概念 文档检索&#xff08;DocumentRetriever&#xff09;是一种信息检索技术&#xff0c;旨在从大量未结构化或半结构化文档中快速找到与特定查询相关的文档或信息。文档检索通常以在线(online)方式运行。 DocumentRetriever通…...

遍历算法及其应用详解

李升伟 整理 什么是遍历&#xff1f; 遍历是指按照某种规则或顺序&#xff0c;系统地访问数据结构&#xff08;如树、图等&#xff09;中的每个节点一次且仅一次的过程。遍历是算法设计中的基本操作&#xff0c;用于访问、检查或修改数据结构中的所有元素。 主要遍历算法 1…...

.NET-EFCore基础知识

.NET EF Core&#xff08;Entity Framework Core&#xff09;是微软开发的一款开源的对象关系映射&#xff08;ORM&#xff09;框架&#xff0c;用于在.NET 应用程序中与数据库进行交互。以下是一些.NET EF Core 的基础知识&#xff1a; 1. 什么是 EF Core EF Core 是.NET 平…...

R语言基础包可视化(一:axis函数)

R语言基础包可视化(一:axis函数) 背景axis函数(坐标轴函数)各参数的图片示例hadj和padjline和poslty,lwd,lwd.ticksgap.axis总结背景 之前在介绍正态Q-Q图的过程中,画过标准正态分布的随机数、分数数、分布函数、密度函数的图像,相关的文章连接参考此处:R语言正态Q-Q图…...

Axure疑难杂症:垂直菜单折叠与展开(玩转垂直菜单)

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! 课程主题:垂直菜单折叠与展开 主要内容:折叠与展开效果 应用场景:PC后台菜单、动态下拉菜单、商品分类选择等折叠与展开场景 案例展示: 案例视频: 垂直菜单折叠与展开效果 正文内容: 关于垂直菜单的折叠与…...

docker 中跑faster-whisper 教程(1050显卡)

之前我本地机器运行faster-whisper 会报错类似 Could not load library libcudnn_ops_infer.so.8github 上也有类似的情况 &#xff1a;https://github.com/SYSTRAN/faster-whisper/issues/516#issuecomment-2785038635 缺少.so.8 文件&#xff0c;我通过以下方式&#xff0c;…...

MySQL 在 CentOS 7 环境安装完整步骤

1. 卸载已有环境&#xff08;MariaDB/旧版MySQL&#xff09; 1.停止 MariaDB 服务 systemctl stop mariadb.service 2.检查并卸载 MariaDB/MySQL 安装包 rpm -qa | grep mariadb # 检查 MariaDB 相关包 rpm -qa | grep mysql # 检查 MySQL 相关包 sudo yum remo…...

下一代智能爬虫框架:ScrapeGraphAI 详解

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、ScrapeGraphAI 概述1.1 ScrapeGraphAI介绍1.2 核心特点1.3 工作流程1.4 关键模块1.5 对比传统爬虫框架1.6 安装二、基础操作2.1 自定义解析规则2.2 数据后处理2.3 分布式爬取三、高级功能3.1 多步骤交互采集3.2 动态…...

C++-ffmpeg-2-3-工厂模式封装SDL-9-7

1.接口设计 2.窗口渲染器和材质初始化 3.渲染Draw并测试渲染YUV 4.渲染画面随窗口大小自动缩放并抗锯齿 5.清理接口和接收窗口退出事件 1.接口设计&#xff1a;原则 主要的实现步骤&#xff1a; main的流程&#xff1a; 1打开文件 yuv_file.open("400_300_25.yuv&quo…...

下载极客漫画——Beautiful Soup实用案例

文章目录 一、背景介绍 二、实现思路 三、效果图 四、构思 五、实现细节 1. 第一步下载网页 2. 寻找和下载漫画图像 3. 保存图像&#xff0c;找到前⼀张漫画 六、完整代码 七、程序输出 八、附录 九、总结 一、背景介绍 XKCD网站是一个关于浪漫、隐喻、数字、以及…...

【大模型理论篇】SWIFT: 可扩展轻量级的大模型微调基础设施

1. 背景 大模型(LLM)和多模态大模型(MLLM)利用基于Transformer的架构获得了很迅速的发展。为满足对这些模型的训练和轻量级微调需求&#xff0c;目前已有一些开源框架&#xff0c;如LLaMA-Factory、Firefly、FastChat、Axolotl和LMFlow等。但这些框架在支持的模型、技术和功能上…...

利用 schedule 模块在每日上午每 3 秒执行任务

一、schedule 模块基础原理与功能概述 schedule 模块维护了一个任务队列,每个任务都关联着一个特定的时间触发器和对应的执行函数。当系统时间到达任务设定的触发时间时,模块会从队列中取出相应的任务并执行其关联的函数。这种设 计模式使得开发者无需过多关注底层的时间处理…...

ruby超高级语法

以下是 Ruby 中一些 极度硬核 的语法和底层特性&#xff0c;涉及元编程的深渊、虚拟机原理、语法黑魔法等&#xff0c;适用于追求极限的 Ruby 开发者&#xff1a; 一、语法核弹级操作 1. 动态修改继承链 class A; def foo; "A"; end end class B; def foo; "B…...

Java Stream API:现代化集合处理的艺术

Java Stream API:现代化集合处理的艺术 引言 在Java 8中引入的Stream API彻底改变了我们处理集合数据的方式。它不仅仅是一个新的工具集,更代表了一种声明式、函数式的编程范式。本文将深入探讨Java Stream的核心概念、使用场景和最佳实践。 一、什么是Stream? Stream(…...

ruby高级语法

以下是 Ruby 高级语法的详细总结&#xff0c;涵盖元编程、模式匹配、闭包、并发模型等核心主题&#xff1a; 一、元编程&#xff08;Metaprogramming&#xff09; 1. 动态定义方法 class DynamicClass# 使用 define_method 动态定义方法["foo", "bar"].e…...

特权FPGA之UART串口

0.简介 通用异步收发器(Universal Asynchronous Receiver&#xff0f;Transmitter&#xff0c;UART)可以和各种标准串行接口&#xff0c;如RS 232和RS 485等进行全双工异步通信&#xff0c;具有传输距离远、成本低、可靠性高等优点。一般UART由专用芯片如8250&#xff0c;1645…...

oracle 索引失效

在 Oracle 11g 中&#xff0c;索引失效的常见原因包括函数修改列、隐式类型转换、统计信息过时等&#xff0c;解决方法需结合版本特性&#xff08;如虚拟列、索引跳跃扫描&#xff09;。通过执行计划分析、统计信息维护和合理使用提示&#xff08;Hints&#xff09;&#xff0c…...

MySQL查看binlog执行情况

因数据丢失&#xff0c;被要求使用binlog查看 执行SQL的具体情况。 拿到数据库压缩包&#xff0c;解压缩获得文件和文件夹若干。 如图&#xff0c;有17个binlog文件&#xff0c;目标数据库名应为corr。 已知这个数据库是安装在windows下&#xff0c;版本8.0. 先下载一个mysq…...

kotlin,Android,jetpack compose,日期时间设置

AI生成&#xff0c;调试出来学习&#xff0c;这些小组件会用了&#xff0c;就可以组合一个大点的程序了。 package com.example.mydatetimeimport android.app.AlertDialog import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.co…...

sql-labs靶场 less-2

文章目录 sqli-labs靶场less 2 联合注入 sqli-labs靶场 每道题都从以下模板讲解&#xff0c;并且每个步骤都有图片&#xff0c;清晰明了&#xff0c;便于复盘。 sql注入的基本步骤 注入点注入类型 字符型&#xff1a;判断闭合方式 &#xff08;‘、"、’、“”&#xf…...

ssh密钥连接远程服务器并用scp传输文件

ssh密钥连接远程服务器 私钥的权限必须是600chmod 600 id_rsa连接时在命令中加上私钥的地址ssh -i PATH_to_id_rsa usernameip -p port scp -P port -i PATH_to_id_rsa file usernameip:PATH...

【锂电池SOH预测】PSO-BP锂电池健康状态预测,锂电池SOH预测(Matlab完整源码和数据)

预测效果 基于PSO-BP算法的锂电池健康状态预测研究 一、引言 1.1 研究背景与意义 在当今社会&#xff0c;锂电池凭借其高能量密度、长寿命及环境友好等特性&#xff0c;在现代能源系统中占据着举足轻重的地位。从消费电子领域如智能手机、笔记本电脑&#xff0c;到动力领域中…...

找搭子系统 搭子经济新风口 基于精准匹配的社交新生态探索

一、市场前景&#xff1a;为什么现在需要"找搭子"&#xff1f; 孤独经济爆发 超60%年轻人存在"精准陪伴"需求&#xff08;2024社交报告&#xff09; 传统社交App无法满足"非婚恋、非熟人"的中间态需求 线下活动复苏 剧本杀/飞盘等兴趣活动年增…...

《Spring Boot+策略模式:企业级度假订单Excel导入系统的架构演进与技术实现》

前言 在数字化时代背景下&#xff0c;订单管理系统的高效性与灵活性成为企业竞争力的核心要素。本文档详细剖析了一个基于 策略模式 的度假订单导入系统&#xff0c;通过分层架构设计实现了多源异构数据的标准化处理。系统以 Spring Boot 为核心框架&#xff0c;结合 MyBatis …...

软件需求分析习题汇编

需求工程练习题 一、选择题 1. 软件需求规格说明书的内容不应包括对&#xff08; &#xff09;的描述。 A. 主要功能B. 算法的详细过程C. 用户界面及运行环境D. 软件的性能 *正确答案:*B:算法的详细过程; 2. 需求分析最终结果是产生&#xff08; &#xff09; A. 项目开发…...

51单片机的汇编伪指令

目录 一、ORG 汇编起始地址命令 1、功能 2、语法格式 3、使用示例 4、注意事项 二、END 汇编终止命令 1、功能 2、语法格式 3、使用示例 4、注意事项 三、EQU 赋值命令 1、功能 2、语法格式 3、使用示例 4、注意事项 四、BIT 位定义命令 1、功能 2、语法格式…...

深入探究Python的re模块及其在爬虫中的应用

深入探究Python的re模块及其在爬虫中的应用 一、引言 在Python的强大生态系统中&#xff0c;re模块作为处理正则表达式的核心工具&#xff0c;发挥着举足轻重的作用。正则表达式是一种描述文本模式的强大语言&#xff0c;能够高效地进行字符串的匹配、查找、替换等操作。无论…...

aosp13增加摄像头控制功能实现

A13中&#xff0c;可以要求做一个开关来控制摄像头是否可用&#xff0c;约束所有使用摄像头的应用。思路&#xff1a;设置中增加开关设置一个属性值&#xff0c;在摄像头调用实现层增该值判断即可 一 开关的开发&#xff1a; 设置-安全中增加开关选项 代码部分&#x…...

Kotlin 之 vararg 参数向下传递的几种方式

在 Kotlin 中&#xff0c;vararg 参数是一种特殊的参数类型&#xff0c;用于表示可变数量的参数。vararg 参数是否可以向下传递&#xff0c;需要根据目标方法的参数类型来决定。 1、目标方法参数也是vararg类型&#xff0c;可直接向下传递 在 method 方法中&#xff0c;通过 …...

Kotlin学习

kotlin android 开源,Kotlin开源项目集合_晚安 呼-华为开发者空间 干货来袭&#xff0c;推荐几款开源的Kotlin的Android项目...

AI写程序:图片批量重命名工具 - 自动化整理您的图片库

图片批量重命名工具 - 自动化整理您的图片库 GitHub项目地址: https://github.com/dependon/renameImage 项目介绍 这是一个基于Python开发的图形界面工具&#xff0c;用于批量重命名文件夹中的图片文件。它能够递归处理选定文件夹及其所有子文件夹中的图片&#xff0c;按照…...

git命令自动拉去远程分支到本地

是的&#xff0c;可以通过 Git 命令自动拉取远程分支并在本地创建同名分支。以下是几种方法&#xff1a; 方法 1&#xff1a;直接拉取远程分支并创建同名本地分支 git fetch origin <远程分支名> # 拉取远程分支 git checkout -b <本地分支名> origin/<远程分…...

[创业之路-364]:穿透表象:企业投资的深层逻辑与误区规避

前言&#xff1a; 透过现象看本质 企业一生与人生相似 看企业如同看人 三岁看大&#xff0c;七岁看老 三十年河东&#xff0c;三十年河西 企业也有品行、文化、气质、性格、赚钱、生命周期与赚钱曲线 投资公司的目的是未来赚钱&#xff0c;赚未来赚钱。投资创业中的企业主要看…...

UE5 蓝图里的声音

文章目录 支持的格式设置循环播放在场景中放置音频设置音频的衰减与不衰减在UI动画中播放声音使用蓝图节点播放声音按钮本身就可以播放声音 支持的格式 支持&#xff1a;WAV 不支持&#xff1a;MP3 设置循环播放 双击音频&#xff0c;打开音频设置&#xff0c;勾选Looping …...

Spring Boot 邮件发送配置遇到的坑:解决 JavaMailSenderImpl 未找到的错误

在使用 Spring Boot 开发时&#xff0c;我们经常需要通过邮件发送功能来实现某些业务逻辑。为了方便快速集成&#xff0c;Spring Boot 提供了一个非常好用的 spring-boot-starter-mail 模块&#xff0c;帮助我们轻松发送邮件。但是&#xff0c;在配置过程中可能会遇到一些问题&…...

路由表的最终地址 root 路由跟踪,最终到哪里去

在路由跟踪(如tranceroute或trancert命令)中,最终地址是目标主机或服务器的IP地址或域名所对应的实际网络地址; 路由跟踪的目的是显示数据包从本地主机到目标主机所经过的每一跳路由器或网络节点,而最终地址是数据包要到达的目标设备。 1.路由跟踪的最终地址 目标主机:路…...

Docker面试全攻略(一):镜像打包、容器运行与高频问题解析

引言 在云原生和微服务架构盛行的今天,Docker 已成为后端开发的必备技能。本文从 面试高频考点 出发,系统梳理 Docker 镜像构建、容器运行的核心知识点,助你轻松应对 Docker 相关的技术面试! 一、Docker 镜像构建核心(面试必问) 1. Dockerfile 核心命令与参数 FROM:基…...

方法的重写

目录 1、重写的概念2、方法重写的规则3、重写 VS 重载 1、重写的概念 重写&#xff0c;也称为覆盖&#xff0c;是子类对父类的非构造、非静态、非 final 修饰、非 private 修饰的方法的实现过程的重新编写。重写可以让子类根据需要来实现父类的方法。方法重写是子类与父类的一…...

搭建hadoop集群模式并运行

3.1 Hadoop的运行模式 先去官方看一看Apache Hadoop 3.3.6 – Hadoop: Setting up a Single Node Cluster. 本地模式&#xff1a;数据直接存放在Linux的磁盘上&#xff0c;测试时偶尔用一下 伪分布式&#xff1a;数据存放在HDFS&#xff0c;公司资金不足的时候用 完全分布式&a…...

【学Rust写CAD】39 over_in_in 函数(alpha256补充方法)

源码 // Similar to over_in but includes an additional clip alpha value#[inline]pub fn over_in_in(self,src: Argb, dst: Argb, clip: Alpha) -> Argb {let src_alpha self * clip;let dst_alpha !(src_alpha*src.alpha_t());// we sum src and dst before reducing…...

Visual Studio 2022 UI机器学习训练模块

VS你还是太超标了&#xff0c;现在机器学习都不用写代码了吗&#xff01;&#xff01; 右键项目解决方案&#xff0c;选择机器学习模型...

c# 企业级ADB通信示例

using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks;namespace ADB_Demo {/// <summary>/// ADB管理器&#xff0c;提供与Android设备的通信功能/// </summary>publ…...

linux上todesk无法使用问题

安装 x11 查看是否已安装 X11 dpkg -l | grep xserver-xorg-core 安装 X11&#xff0c;大概需要 17 - 222 MB&#xff08;前者是在服务器上装&#xff0c;后者在自己的 WSL 装&#xff0c;具体视情况而定&#xff09; sudo apt-get install xorg sudo apt-get install xauth s…...

数字IC后端项目典型问题之后端实战项目问题记录

Q1:为了更高效过掉Calibre LVS&#xff0c;我们要求学员在chipfinish阶段需要先做Online LVS检查。该学员在做verifyConnectivity检查发现设计中存在大量的M1 VDD_CORE Un-Connect Pin的violation&#xff0c;具体violation如下图所示。 数字后端培训实战项目六大典型后端实现…...

【机器学习】决策树

一、什么是决策树&#xff1f; 想象一下你玩“二十个问题”游戏的场景&#xff0c;你通过问一系列“是”或“否”的问题来猜测对方心中的物体。决策树的工作方式与此非常相似。它本质上是一个流程图结构&#xff0c;其中&#xff1a; 每个内部节点&#xff08;Internal Node&…...

大模型格式化输出的几种方法

大模型格式化输出的几种方法 在开发一些和LLM相关的应用的时候,如何从大模型的反馈中拿到结构化的输出数据是非常重要的,那么本文就记录几种常用的方法。 OpenAI提供的新方法 在 OpenAI 的 Python 库中,client.beta.chat.completions.parse 是一个用于生成结构化输出的方法…...

250408_解决加载大量数据集速度过慢,耗时过长的问题

250408_解决加载Cifar10等大量数据集速度过慢&#xff0c;耗时过长的问题&#xff08;加载数据时多线程的坑&#xff09; 在做Cifar10图像分类任务时&#xff0c;发现每个step时间过长&#xff0c;且在资源管理器中查看显卡资源调用异常&#xff0c;主要表现为&#xff0c;显卡…...

Linux网络多进程并发服务器和多线程并发服务器

多进程 还是以大小写转换为例子 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <sys/socket.h> #include <arpa/inet.h> #include "wrap.h" #include…...

LLMs基础学习(五)模型微调专题(中)

文章目录 LLMs基础学习&#xff08;五&#xff09;模型微调专题&#xff08;中&#xff09;Adapter 类的微调1 背景2 技术原理3 具体细节4 Adapter 类其他方法的微调 Prefix 类的微调1 Prefix Tuning2 Prompt Tuning3 P - tuning4 P - Tuning v25 总结 LoRA 篇a. 什么是 LoRA?…...