Spring Boot 统一功能处理(用户登录权限效验-拦截器、异常处理、数据格式返回)
文章目录
- 1. 统一用户登录权限效验
- 1.1 最初用户登录权限效验
- 1.2 Spring AOP 统一用户登录验证
- 1.3 Spring 拦截器
- 1.4 练习:登录拦截器
- 1.5 拦截器实现原理
- 1.6 统一访问前缀添加
- 2. 统一异常处理
- 3. 统一数据格式返回
- 3.1 统一数据格式返回的实现
- 3.2 @ControllerAdvice 源码分析
本篇将要学习 Spring Boot 统一功能处理模块,这也是 AOP 的实战环节
- 统一用户登录权限的效验实现接口 HandlerInterceptor + WebMvcConfigurer
- 统一异常处理使用注解 @RestControllerAdvice + @ExceptionHandler
- 统一数据格式返回使用注解 @ControllerAdvice 并且实现接口 @ResponseBodyAdvice
1. 统一用户登录权限效验
用户登录权限的发展完善过程
- 最初用户登录效验:在每个方法中获取 Session 和 Session 中的用户信息,如果存在用户,那么就认为登录成功了,否则就登录失败了
- 第二版用户登录效验:提供统一的方法,在每个需要验证的方法中调用统一的用户登录身份效验方法来判断
- 第三版用户登录效验:使用 Spring AOP 来统一进行用户登录效验
- 第四版用户登录效验:使用 Spring 拦截器来实现用户的统一登录验证
1.1 最初用户登录权限效验
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/a1")public Boolean login (HttpServletRequest request) {// 有 Session 就获取,没有就不创建HttpSession session = request.getSession(false);if (session != null && session.getAttribute("userinfo") != null) {// 说明已经登录,进行业务处理return true;} else {// 未登录return false;}}@RequestMapping("/a2")public Boolean login2 (HttpServletRequest request) {// 有 Session 就获取,没有就不创建HttpSession session = request.getSession(false);if (session != null && session.getAttribute("userinfo") != null) {// 说明已经登录,进行业务处理return true;} else {// 未登录return false;}}
}
这种方式写的代码,每个方法中都有相同的用户登录验证权限,缺点是:
- 每个方法中都要单独写用户登录验证的方法,即使封装成公共方法,也一样要传参调用和在方法中进行判断
- 添加控制器越多,调用用户登录验证的方法也越多,这样就增加了后期的修改成功和维护成功
- 这些用户登录验证的方法和现在要实现的业务几乎没有任何关联,但还是要在每个方法中都要写一遍,所以提供一个公共的 AOP 方法来进行统一的用户登录权限验证是非常好的解决办法。
1.2 Spring AOP 统一用户登录验证
统一用户登录验证,首先想到的实现方法是使用 Spring AOP 前置通知或环绕通知来实现
@Aspect // 当前类是一个切面
@Component
public class UserAspect {// 定义切点方法 Controller 包下、子孙包下所有类的所有方法@Pointcut("execution(* com.example.springaop.controller..*.*(..))")public void pointcut(){}// 前置通知@Before("pointcut()")public void doBefore() {}// 环绕通知@Around("pointcut()")public Object doAround(ProceedingJoinPoint joinPoint) {Object obj = null;System.out.println("Around 方法开始执行");try {obj = joinPoint.proceed();} catch (Throwable e) {e.printStackTrace();}System.out.println("Around 方法结束执行");return obj;}
}
但如果只在以上代码 Spring AOP 的切面中实现用户登录权限效验的功能,有这样两个问题:
- 没有办法得到 HttpSession 和 Request 对象
- 我们要对一部分方法进行拦截,而另一部分方法不拦截,比如注册方法和登录方法是不拦截的,也就是实际的拦截规则很复杂,使用简单的 aspectJ 表达式无法满足拦截的需求
1.3 Spring 拦截器
针对上面代码 Spring AOP 的问题,Spring 中提供了具体的实现拦截器:HandlerInterceptor,拦截器的实现有两步:
-
创建自定义拦截器,实现 Spring 中的 HandlerInterceptor 接口中的 preHandle方法
-
将自定义拦截器加入到框架的配置中,并且设置拦截规则
1) 给当前的类添加 @Configuration 注解
2)实现 WebMvcConfigurer 接口
3)重写 addInterceptors 方法
注意:一个项目中可以同时配置多个拦截器
(1)创建自定义拦截器
/*** @Description: 自定义用户登录的拦截器* @Date 2023/2/13 13:06*/
@Component
public class LoginIntercept implements HandlerInterceptor {// 返回 true 表示拦截判断通过,可以访问后面的接口// 返回 false 表示拦截未通过,直接返回结果给前端@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception {// 1.得到 HttpSession 对象HttpSession session = request.getSession(false);if (session != null && session.getAttribute("userinfo") != null) {// 表示已经登录return true;}// 执行到此代码表示未登录,未登录就跳转到登录页面response.sendRedirect("/login.html");return false;}
}
(2)将自定义拦截器添加到系统配置中,并设置拦截的规则
- addPathPatterns:表示需要拦截的 URL,**表示拦截所有⽅法
- excludePathPatterns:表示需要排除的 URL
说明:拦截规则可以拦截此项⽬中的使⽤ URL,包括静态⽂件(图⽚⽂件、JS 和 CSS 等⽂件)。
/*** @Description: 将自定义拦截器添加到系统配置中,并设置拦截的规则* @Date 2023/2/13 13:13*/
@Configuration
public class AppConfig implements WebMvcConfigurer {@Resourceprivate LoginIntercept loginIntercept;@Overridepublic void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor(new LoginIntercept());//可以直接new 也可以属性注入registry.addInterceptor(loginIntercept).addPathPatterns("/**"). // 拦截所有 urlexcludePathPatterns("/user/login"). //不拦截登录注册接口excludePathPatterns("/user/reg").excludePathPatterns("/login.html").excludePathPatterns("/reg.html").excludePathPatterns("/**/*.js").excludePathPatterns("/**/*.css").excludePathPatterns("/**/*.png").excludePathPatterns("/**/*.jpg");}
}
1.4 练习:登录拦截器
要求
- 登录、注册页面不拦截,其他页面都拦截
- 当登录成功写入 session 之后,拦截的页面可正常访问
在 1.3 中已经创建了自定义拦截器 和 将自定义拦截器添加到系统配置中,并设置拦截的规则
(1)下面创建登录和首页的 html
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q9Gqxzj7-1676278749702)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1676269833666.png)]](https://img-blog.csdnimg.cn/22bda352587047e0b21b15d417afb25a.png)
(2)创建 controller 包,在包中创建 UserController,写登录页面和首页的业务代码
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/login")public boolean login(HttpServletRequest request,String username, String password) {boolean result = false;if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) {if(username.equals("admin") && password.equals("admin")) {HttpSession session = request.getSession();session.setAttribute("userinfo","userinfo");return true;}}return result;}@RequestMapping("/index")public String index() {return "Hello Index";}
}
(3)运行程序,访问页面,对比登录前和登录后的效果
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jRWMgoSw-1676278749704)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1676268463235.png)]](https://img-blog.csdnimg.cn/edd80530665c40f4a266378e0748350c.png)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GHnJ7FuR-1676278749704)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1676269389910.png)]](https://img-blog.csdnimg.cn/0c45d4d2da924e1bac8f932f38d649d5.png)
1.5 拦截器实现原理
有了拦截器之后,会在调⽤ Controller 之前进⾏相应的业务处理,执⾏的流程如下图所示
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cuSCpU20-1676278749705)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1676270248519.png)]](https://img-blog.csdnimg.cn/418b210fddde4e769919b06d11218851.png)
实现原理源码分析
- 所有的 Controller 执行都会通过一个调度器 DispatcherServlet 来实现
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ektZVF7w-1676278749705)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1676270414355.png)]](https://img-blog.csdnimg.cn/f5c7e361a5df451eaf3db61693b3ebf0.png)
- 而所有方法都会执行 DispatcherServlet 中的 doDispatch 调度⽅法,doDispatch 源码分析如下:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TlRlduCp-1676278749706)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1676271200316.png)]](https://img-blog.csdnimg.cn/e8172c479900490b8d1aab23b8f75c1c.png)
通过源码分析,可以看出,Sping 中的拦截器也是通过动态代理和环绕通知的思想实现的
1.6 统一访问前缀添加
所有请求地址添加 api 前缀,c 表示所有
@Configuration
public class AppConfig implements WebMvcConfigurer {// 所有的接口添加 api 前缀@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {configurer.addPathPrefix("api", c -> true);}
}
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UGNUF3ox-1676278749706)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1676271883098.png)]](https://img-blog.csdnimg.cn/314477db88cc438d889d986ea886b8be.png)
2. 统一异常处理
- 给当前的类上加 @ControllerAdvice 表示控制器通知类
- 给方法上添加 @ExceptionHandler(xxx.class),表示异常处理器,添加异常返回的业务代码
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/index")public String index() {int num = 10/0;return "Hello Index";}
}
在 config 包中,创建 MyExceptionAdvice 类
@RestControllerAdvice // 当前是针对 Controller 的通知类(增强类)
public class MyExceptionAdvice {@ExceptionHandler(ArithmeticException.class)public HashMap<String,Object> arithmeticExceptionAdvice(ArithmeticException e) {HashMap<String, Object> result = new HashMap<>();result.put("state",-1);result.put("data",null);result.put("msg" , "算出异常:"+ e.getMessage());return result;}
}
也可以这样写,效果是一样的
@ControllerAdvice
public class MyExceptionAdvice {@ExceptionHandler(ArithmeticException.class)@ResponseBodypublic HashMap<String,Object> arithmeticExceptionAdvice(ArithmeticException e) {HashMap<String, Object> result = new HashMap<>();result.put("state",-1);result.put("data",null);result.put("msg" , "算数异常:"+ e.getMessage());return result;}
}
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pOxAmSTF-1676278749707)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1676273087133.png)]](https://img-blog.csdnimg.cn/e431310924d941d7994546bc19cbf893.png)
如果再有一个空指针异常,那么上面的代码是不行的,还要写一个针对空指针异常处理器
@ExceptionHandler(NullPointerException.class)
public HashMap<String,Object> nullPointerExceptionAdvice(NullPointerException e) {HashMap<String, Object> result = new HashMap<>();result.put("state",-1);result.put("data",null);result.put("msg" , "空指针异常异常:"+ e.getMessage());return result;
}
@RequestMapping("/index")public String index(HttpServletRequest request,String username, String password) {Object obj = null;System.out.println(obj.hashCode());return "Hello Index";}
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1bjJWAtS-1676278749707)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1676273765910.png)]](https://img-blog.csdnimg.cn/893342200aa94875b08f225ad625203c.png)
但是需要考虑的一点是,如果每个异常都这样写,那么工作量是非常大的,并且还有自定义异常,所以上面这样写肯定是不好的,既然是异常直接写 Exception 就好了,它是所有异常的父类,如果遇到不是前面写的两种异常,那么就会直接匹配到 Exception
当有多个异常通知时,匹配顺序为当前类及其⼦类向上依次匹配
@ExceptionHandler(Exception.class)
public HashMap<String,Object> exceptionAdvice(Exception e) {HashMap<String, Object> result = new HashMap<>();result.put("state",-1);result.put("data",null);result.put("msg" , "异常:"+ e.getMessage());return result;
}
可以看到优先匹配的还是前面写的 空指针异常
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-awVsPmiQ-1676278749708)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1676274382111.png)]](https://img-blog.csdnimg.cn/4f185b000be64461bfa9a11426ced9fc.png)
3. 统一数据格式返回
3.1 统一数据格式返回的实现
-
给当前类添加 @ControllerAdvice
-
实现 ResponseBodyAdvice 重写其方法
supports 方法,此方法表示内容是否需要重写(通过此⽅法可以选择性部分控制器和方法进行重写),如果要重写返回 true
beforeBodyWrite 方法,方法返回之前调用此方法
@ControllerAdvice
public class MyResponseAdvice implements ResponseBodyAdvice {// 返回一个 boolean 值,true 表示返回数据之前对数据进行重写,也就是会进入 beforeBodyWrite 方法// 返回 false 表示对结果不进行任何处理,直接返回@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {return true;}// 方法返回之前调用此方法@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {HashMap<String,Object> result = new HashMap<>();result.put("state",1);result.put("data",body);result.put("msg","");return result;}
}
@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/login")public boolean login(HttpServletRequest request,String username, String password) {boolean result = false;if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) {if(username.equals("admin") && password.equals("admin")) {HttpSession session = request.getSession();session.setAttribute("userinfo","userinfo");return true;}}return result;}@RequestMapping("/reg")public int reg() {return 1;}
}
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U6hvLVd8-1676278749708)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1676275787822.png)]](https://img-blog.csdnimg.cn/fcd97dcd5ad1469e93966a26eec52089.png)
3.2 @ControllerAdvice 源码分析
通过对 @ControllerAdvice 源码的分析我们可以知道上面统一异常和统一数据返回的执行流程
(1)先看 @ControllerAdvice 源码
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tHA3bOkT-1676278749708)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1676277580298.png)]](https://img-blog.csdnimg.cn/ed83ce6afa4a42a397b6ebebcc1001de.png)
可以看到 @ControllerAdvice 派生于 @Component 组件而所有组件初始化都会调用 InitializingBean 接口
(2)下面查看 initializingBean 有哪些实现类
在查询过程中发现,其中 Spring MVC 中的实现子类是 RequestMappingHandlerAdapter,它里面有一个方法 afterPropertiesSet()方法,表示所有的参数设置完成之后执行的方法
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1GfmMAY6-1676278749709)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1676277786403.png)]](https://img-blog.csdnimg.cn/5e5b9aac0a854f28b5e53c44550a687a.png)
(3)而这个方法中有一个 initControllerAdviceCache 方法,查询此方法
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y39ZLEbu-1676278749709)(C:\Users\28463\AppData\Roaming\Typora\typora-user-images\1676277853411.png)]](https://img-blog.csdnimg.cn/12ba5bce5641413f96d94e26547c42d8.png)
发现这个方法在执行时会查找使用所有的 @ControllerAdvice 类,发送某个事件时,调用相应的 Advice 方法,比如返回数据前调用统一数据封装,比如发生异常是调用异常的 Advice 方法实现的
相关文章:
Spring Boot 统一功能处理(用户登录权限效验-拦截器、异常处理、数据格式返回)
文章目录1. 统一用户登录权限效验1.1 最初用户登录权限效验1.2 Spring AOP 统一用户登录验证1.3 Spring 拦截器1.4 练习:登录拦截器1.5 拦截器实现原理1.6 统一访问前缀添加2. 统一异常处理3. 统一数据格式返回3.1 统一数据格式返回的实现3.2 ControllerAdvice 源码…...
oracle存储过程的使用
文章目录oracle存储过程的使用基本结构管理存储过程调用存储过程的方法存储过程参数关键词: IN 和outin/out测试案例调用in/out测试案例存储过程语法DECLARE声明关键词赋值使用in/out将值作为子程序的参数分配给变量,看上面的案例为布尔变量赋值表达式串…...
一些无线通信系统模型的概念
一些无线通信系统模型的概念 扩频通信,指的是系统的带宽WWW远大于其信息传输速率R(bits/s)R(bits/s)R(bits/s), 定义展频带因子BeWRB_e \frac{W}{R}BeRW, 易知在扩频通信系统中,BeB_eBe远大于1. 在频率上产生如此大的冗余度,主要是为了减轻无线通信或卫星通信中经常产生…...
GAIDC 2023盛会迎来大模型论坛“主场”,百度飞桨护航大模型产业发展
2月25日-26日,2023全球人工智能开发者先锋大会(GAIDC)在上海临港举行,大会以“向光而行的AI开发者”为主题,汇聚了当前科技和产业革命中的开发者先锋力量。百度深度参与本次大会,飞桨联合上海市人工…...
Python编写GUI界面案例:实现免费下载器
前言 嗨喽~大家好呀,这里是魔王呐 ❤ ~! 本次网站: 本文所有模块\环境\源码\教程皆可点击文章下方名片获取此处跳转 开发环境: python 3.8 运行代码 pycharm 2022.3 辅助敲代码 模块使用: import parsel >>> pip install parsel…...
我的 System Verilog 学习记录(6)
引言 本文简单介绍 SystemVerilog 语言的 线程。 前文链接: 我的 System Verilog 学习记录(1) 我的 System Verilog 学习记录(2) 我的 System Verilog 学习记录(3) 我的 System Verilog 学…...
SAP 常见问题大全及问题解决大全
1.A:在公司代码分配折旧表时报错? 在公司代码分配折旧表时报错,提示是“3000 的公司代码分录不完全-参见长文本” 希望各位大侠帮我看看。 3000 的公司代码分录不完全-参见长文本 R: a.你把零进项税的代码分配给这个公司代码就可以了 …...
10.Quartz实现定时打分 热帖排行
1.Spring Quartz(1)简介核心组件scheduler 接口:核心调度工具,所有任务由这一接口调用job:定义任务,重写execute方法JobDetail接口:配置描述Trigger接口:什么时候运行,以什么样的频率运行(2)Spr…...
pandas 读取Excel 批量转换时间戳
一、安装 pip install pandas 如果出报错,不能运行,可以安装 pip install xlrd 二、 代码如下 import pandas as pd import time,datetimefile_path rC:\Users\Administrator\Desktop\携号转网测试\admin_log.xls df pd.read_excel(file_path, sheet_n…...
绕过检测之Executor内存马浅析(内存马系列篇五)
写在前面 前面已经从代码层面讲解了Tomcat的架构,这是内存马系列文章的第五篇,带来的是Tomcat Executor类型的内存马实现。有了前面第四篇中的了解,才能更好的看懂内存马的构造。 前置 什么是Executor Executor是一种可以在Tomcat组件之间…...
《C++模板进阶》
致前行的人: 要努力,但不要着急,繁花锦簇,硕果累累都需要过程! 目录 前言: 1.非类型模板参数 1.1.概念: 1.2.使用注意事项 2.模板特化 2.1函数模板特化 2.2类模板特化 3.模板的分离编译 3.1什么…...
【项目管理】项目进度管理中的逻辑关系
项目的进度管理是项目核心管理之一,通过合理的进度安排,制定出科学可行的分项工期表,并条理清晰的显示出项目进度之间的逻辑关系。 1、目标是计划的灵魂 进度计划必须按照确定的项目总进度要求进行编制,了解项目总目标和整体安…...
ARM的汇编指令集
一、汇编指令 1.1 指令与伪指令 汇编的指令 指令是CPU机器指令的助记符,编译后会得到一串二进制机器码,由CPU执行 汇编的伪指令 伪指令本质上不是指令,它是编译器环境提供用来指导编译过程,编译后伪指令不会生成机器码 伪指令…...
@font-face用法超详细讲解
文章目录font-face是什么font-face基本语法urlTTFOTFEOTWOFFSVGformatfont-face用法示例font字体下载ttf-to-eot 字体转换器https://blog.csdn.net/qq_37417446/article/details/106728725 https://developer.mozilla.org/zh-CN/docs/Web/CSS/font-face font-face是什么 font-…...
[oeasy]python0095_乔布斯求职_雅达利_atari_breakout_打砖块_布什内尔_游戏机_Jobs
编码进化 回忆上次内容 上次 我们回顾了 电子游戏的历史 从 电子游戏鼻祖 双人网球到 视频游戏 PingPong再到 街机游戏 Pong 雅达利 公司 来了 嬉皮士 捣乱?🤔 布什内尔 会如何 应对 呢?🤔 布什内尔 布什内尔 本身就有点 …...
全景极简印度史
转自:印度简史 - 知乎 (zhihu.com)印度是世界上最早出现文明的地区之一,印度河是其文明的发源地。古印度文明的疆域曾包括今印度共和国、巴基斯坦、孟加拉国、阿富汗斯坦南部部分地区和尼泊尔。史前时代200万年前,巴基斯坦北部的希瓦利克遗址…...
《设计模式》模板方法
《设计模式》模板方法 模板方法是一种行为型设计模式,用于定义一个算法的框架,而将一些步骤的实现留给子类来完成。模板方法在基类中定义了一个模板方法,该方法确定了算法的基本结构,然后将一些步骤的实现交给子类去完成。这个模…...
Linux环境内存管理——链表
我是荔园微风,作为一名在IT界整整25年的老兵,今天我们来重新审视一下Windows程序员如何学习Linux环境内存管理。由于很多程序在Windows环境下开发好后,还要部署到Linux服务器上去,所以作为Windows程序员有必要学习Linux环境的内存…...
String、StringBuffer、StringBuilder类
String类 由多个字符组成的一串数据,值一旦创建不可改变 private final char value[]; 一旦值改变,就会创建新的对象 String s "abc"; //char[] c {a,b,c}s"def"; // 并不是String的值改变,而是创建了一个新的对象s"gh";s"aaa"…...
在VScode中添加Linux中的Docker容器中的Python解释器
VScode编辑器在安装好Python插件之后会自动选择环境变量中排序最高的那一个解释器作为默认解释器,而想要额外添加新的Python解释器就需要自己设置。 VScode编辑器安装在本地电脑 支持Python的docker安装在远程服务器 第一步,在/usr/local/下新建pytho…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...
保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...
游戏开发中常见的战斗数值英文缩写对照表
游戏开发中常见的战斗数值英文缩写对照表 基础属性(Basic Attributes) 缩写英文全称中文释义常见使用场景HPHit Points / Health Points生命值角色生存状态MPMana Points / Magic Points魔法值技能释放资源SPStamina Points体力值动作消耗资源APAction…...
大数据驱动企业决策智能化的路径与实践
📝个人主页🌹:慌ZHANG-CSDN博客 🌹🌹期待您的关注 🌹🌹 一、引言:数据驱动的企业竞争力重构 在这个瞬息万变的商业时代,“快者胜”的竞争逻辑愈发明显。企业如何在复杂环…...
raid存储技术
1. 存储技术概念 数据存储架构是对数据存储方式、存储设备及相关组件的组织和规划,涵盖存储系统的布局、数据存储策略等,它明确数据如何存储、管理与访问,为数据的安全、高效使用提供支撑。 由计算机中一组存储设备、控制部件和管理信息调度的…...
