SpringBoot 统⼀功能处理

目录

前言

1.⽤户登录权限效验

1.1、最初⽤户登录效验

1.2、Spring AOP ⽤户统⼀登录验证的问题

1.3、Spring 拦截器

了解 创建一个 Spring 拦截器 的流程

1、 创建自定义拦截器,实现 HandlerInterceptor 接⼝的preHandle(执⾏具体⽅法之前的预处理)⽅法。

2、将⾃定义拦截器加⼊到框架的配置中,并且设置拦截规则。

实践:实现一个自定义的拦截器,使其在项目中生效。

预备工作:创建一个 Spring AOP 的项目。

 1、 创建⾃定义拦截器,实现 HandlerInterceptor 接⼝的preHandle(执⾏具体⽅法之前的预处理)⽅法。

2、将⾃定义拦截器加⼊到框架的配置中,并且设置拦截规则。​编辑​

3、验收成果

拦截器实现原理

实现原理源码分析

拦截器小结

扩展:统⼀访问前缀添加

2.统⼀异常处理

3.统⼀数据返回格式

为什么需要统⼀数据返回格式?

统⼀数据返回格式的实现

总结

最后,补充一点:@ControllerAdvice 源码分析 - 了解


前言

接下来是 Spring Boot 统⼀功能处理模块了,也是 AOP 的实战环节,要实现的⽬标有以下 3 个:

1、统⼀⽤户登录权限验证;
2、统⼀数据格式返回;
3、统⼀异常处理。

接下我们⼀个⼀个来看。

1.⽤户登录权限效验

        ⽤户登录权限的发展从之前每个⽅法中⾃⼰验证⽤户登录权限,到现在统⼀的⽤户登录验证处理,它是⼀个逐渐完善和逐渐优化的过程。

1、最初的用户登录效验:在每个方法里面获取 session 和 session 中的 用户信息,如果用户信息存在,那么就登录成功了,否则就登录失败了。

2、第二版用户登录效验:提供了统一的方法,在每个需要验证的方法中调用统一的用户登录身份效验方法来判断。

3、第三版用户登录效验: 使用 Spring AOP 来使用统一的用户登录效验。
就是说:不再需要我们敲代码去调用统一方法了,Spring AOP 会帮我们自动调用。

1.1、最初⽤户登录效验

@RestController
@RequestMapping("/user")
public class UserController {//某⽅法 1@RequestMapping("/m1")public Object method(HttpServletRequest request) {
// 有 session 就获取,没有不会创建HttpSession session = request.getSession(false);if (session != null && session.getAttribute("userinfo") != null){
// 说明已经登录,业务处理return true;} else {
// 未登录return false;}}//某⽅法 2 @RequestMapping("/m2")public Object method2(HttpServletRequest request) {
// 有 session 就获取,没有不会创建HttpSession session = request.getSession(false);if (session != null && session.getAttribute("userinfo") != null){
// 说明已经登录,业务处理return true;} else {
// 未登录return false;}}
// 其他⽅法...
}

从上述代码可以看出,每个⽅法中都有相同的⽤户登录验证权限,它的缺点是:

1、 每个⽅法中都要单独写⽤户登录验证的⽅法,即使封装成公共⽅法,也⼀样要传参调⽤和在⽅法中进⾏判断。
2、 添加控制器越多,调⽤⽤户登录验证的⽅法也越多,这样就增加了后期的修改成本和维护成本。
3、 这些⽤户登录验证的⽅法和接下来要实现的业务几乎没有任何关联,但每个⽅法中都要写⼀遍。所以提供⼀个公共的 AOP ⽅法来进⾏统⼀的⽤户登录权限验证迫在眉睫。

1.2、Spring AOP ⽤户统⼀登录验证的问题

说到统⼀的⽤户登录验证,我们想到的第⼀个实现⽅案是 Spring AOP 前置通知或环绕通知来实现,实现模板代码如下:

@Aspect
@Component
public class UserAspect {// 定义切点⽅法 controller 包下、⼦孙包下所有类的所有⽅法@Pointcut("execution(* com.example.demo.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 throwable) {throwable.printStackTrace();}System.out.println("Around ⽅法结束执⾏");return obj;}
}

如果要在以上 Spring AOP 的切⾯中实现⽤户登录权限效验的功能,有以下两个问题:

1、没办法获取到 HttpSession 对象。
2、我们要对⼀部分⽅法进⾏拦截,⽽另⼀部分⽅法不拦截,
如用户的注册⽅法和登录⽅法是不拦截的,这样的话切点方法的拦截规则很难定义,甚⾄没办法定义。

那这样如何解决呢?
接下来,我们就使用 Spring 提供的方案来解决 原始 AOP 所带来的问题。
这个解决方案,也就是 第四版用户登录效验,也是下面要讲的内容:Spring 拦截器。


1.3、Spring 拦截器

对于以上问题 Spring 中提供了具体的实现拦截器:HandlerInterceptor,拦截器的实现分为以下两个步骤:

1、 创建⾃定义拦截器,实现 HandlerInterceptor 接⼝的 preHandle(执⾏具体⽅法之前的预处理)⽅法。
2、 将⾃定义拦截器加⼊ WebMvcConfigurer 的 addInterceptors ⽅法中。

有的人可能会有疑问:Spring 拦截器 和 AOP 有什么关系呢?

Spring 拦截器是基于 AOP 实现的。

因为 原生 AOP 没有办法获取 httpSession 和 Request 对象。而且,拦截规则也非常难以定义。
所以,官方就做了一个拦截器。
然后,把我们之前最基础的 原生 AOP 做了一个封装
得到了一个新东西 “ Spring拦截器 ”。

我们也可以将 Spring 拦截器 称为是 Spring AOP 的一种实现。

Spring 拦截器中,封装了很多对象,对象里面就提供了专门的方法 来解决 原生 AOP 所带来的两个问题。

了解 创建一个 Spring 拦截器 的流程

1、 创建自定义拦截器,实现 HandlerInterceptor 接⼝的preHandle(执⾏具体⽅法之前的预处理)⽅法。

HandlerInterceptor 的 中文意思 就是 拦截器。

重写 的 preHeadle 方法,返回值的类型是 布尔类型。

返回是 true,则表示通过了 拦截器的验证,可以继续 执行,调用 目标方法了。
反之,验证没有通过,直接返回一个错误信息。

总的来说:拦截器 运行的模式 和 Spring AOP 是一样的。
起着一个 代理的作用。

现在是 前端先访问 拦截器,执行里面的 preHandle 方法,如果方法返回一个 true,则继续执行后面的层序。
反之如果返回的是 false,直接返回一个 错误信息,后面的代码就不用执行了。

下面我们来看个具体的代码

public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {HttpSession session = request.getSession(false);if (session != null && session.getAttribute("userinfo") != null){return true;}response.setStatus(401);return false;}
}

我们可以发现: preHandle方法中提供了 HttpServletRequest 和HttpServletResponse 的 对象!!!

有了请求对象,我们就可以获取到 Session 对象,从而获取到里面的用户信息。
也就意味着我们可以进行用户登录状态的检测了!
写法 和 Servlet 是 一样的,这个你看上面的代码就知道了。

而且,我们可以通过响应对象,直接向前端返回一个 JSON 格式(或者 HTML)的数据。
甚至,我还可以实现一些业务。
假设 用户处于未登录 状态,我们可以通过 HttpServletResponse 发送重定向。
让用户直接跳转到 登录界面,让他先去登录。

需要注意的是:
虽然我们实现了拦截器,但是 preHandle 方法 是一个普通方法。
没有程序去调用它。
因为我们没有给它加上任何注解。
没有加注解,意味着 框架的 启动 和 这个没有任何关系。
另外,这个自定义拦截器的方法,只是具有拦截器的业务逻辑,但是没有拦截的规则!

所以,才会有第二步:将⾃定义拦截器加⼊到系统配置。
或者说:配置到当前项目中。并且设置拦截的规则


2、将⾃定义拦截器加⼊到框架的配置中,并且设置拦截规则。

@Configuration
public class AppConfig implements WebMvcConfigurer {// 添加拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**") // 拦截所有接⼝.excludePathPatterns("/art/param11"); // 排除接⼝}
}

大家需要注意一点:

不管是 Spring,还是 Spring Boot。
它们默认的配置文件都是叫做 Spring MVC Configurer 的一个文件。

我们要设置当前的一些配置信息的时候,我们是一定要去实现WebMvcConfigurer 接口的。
然后,去重写里面的 addInterceptors(添加多个拦截器)方法。
只有这样,系统才会认为 当前设置的东西 是给 当前项目去设置的配置文件。

所以说:光加上 @Configuration 注解是不行,还必须实现 WebMvcConfigurer 接口,重写里面的方法,当前这个类才是一个配置文件。
这是一种固定写法,注解 和 实现接口,都不能少。
所有的当前项目的配置文件,都是 来自于实现 WebMvcConfigurer 接口 。
所以,这个接口是一定要实现的。

实现它之后,
在当前类上, 加上@Configuration 注解,是为了让 当前这个类 作为 系统的一个配置类。
此时,类里面设置的东西(在这里指的是拦截器),才能生效。

而 addInterceptors - 添加多个拦截器方法,表示 拦截器,可以添加(支持)多个自定义拦截器。
即:一个项目中可以设置多个拦截器。
比如:
1、一个拦截器 去做登录的效验
2、一个拦截器 去验证 前端数据 的正确性 合法性。
等等。。。

并且,不同的拦截器可以配置 不同的路由规则。
这个先暂且方法,后面会细讲

⾃定义拦截器加⼊到框架的配置中,并且设置拦截规则。
实现步骤:
1、在类上,添加 Configuration 注解,使其成系统的配置类
2、当前类实现 WebMvcConfigurer 接口
3、重写 WebMvcConfigurer 接口中的 addInterceptors 方法。
上面讲述了 实现 Spring 拦截器的流程。
下面,我们可以开始实践了!

实践:实现一个自定义的拦截器,使其在项目中生效。

预备工作:创建一个 Spring AOP 的项目。

这个 Spring 拦截器,Spring Boot 项目 本身就支持!
所以,就不用引入 AOP 框架的支持了。

 1、 创建⾃定义拦截器,实现 HandlerInterceptor 接⼝的preHandle(执⾏具体⽅法之前的预处理)⽅法。

先在根目录下 创建一个 config 子包,毕竟拦截类使用的是 @configuration 注解。属于它的一部分。
并且,在里面创建一个拦截类(拦截器)LoginIntercept。

因为我们要做的是 登录验证嘛,就取名 Login,后面的 Intercept 是拦截器的意思。

 接下来开始实现 拦截器。

 此时,我们就实现了一个最简单的 验证用户登录状态的 拦截器(自定义)。

2、将⾃定义拦截器加⼊到框架的配置中,并且设置拦截规则。

 你们说难吗?
其实也不难,就是制定拦截规则需要过细一点。
不要把不该拦截的url,忘记写,就行了。

3、验收成果

我们先来给它加一个页面资源。

 页面有了,下面我们来创建 controller 层的代码。【负责与前端交互的代码】

下面,我们先在没有登录的情况,访问那些被拦截的资源 

 

还有一个 index 的 静态页面对吧,我们也来试一下,效果与 访问 index 方法是一样的效果。因为没有登录嘛再来访问那些 没有被拦截的资源。

 再来访问那些 没有被拦截的资源。

 

我们在这里在拓展一下业务。
在未登录的情况下访问其他访问方法,或者页面,让其跳转到登录页面。

 此时,我们先去访问 login方法,登录成功后,再去访问 index 方法

 效果就出来了,非常的nice!!!

拦截器实现原理

拦截器实现原理

 然⽽有了拦截器之后,会在调⽤ Controller 之前进⾏相应的业务处理,执⾏的流程如下图所示:

实现原理源码分析

所有的 Controller 执⾏都会通过⼀个调度器 DispatcherServlet 来实现,这⼀点可以从 Spring Boot 控制台的打印信息看出,如下图所示:

那么,问题来了:为什么执行每一条请求的时候,都会先去调用 DispatcherServlet 呢?
先从字面意思来看: Dispatcher 的 中文意思是 调度器(分发器)。
也就是说:所有的请求进来之后,会先进入调度器,由调度器进行分发。

举个例子:
上学的时候,每个老师都有一个课代表,对不对?
没次发作业本的时候,不是直接由老师发到你手里的。
一般都是把作业全部交给课代表,有课代表分法作业。
原因很简单!
1、方便,不用自己动手
2、老师不止带一个班,所以对每个学生坐的位置不是了解。分发的效率低。
3、课代表也班级中一员。对每个人的位置很熟悉,发作业效率要高一些。
放在程序里,也是一样的理由。
DispatcherServlet 就是 “课代表”,对方每个映射方法都“轻车熟路”,执行的效率极高。

⽽所有⽅法都会执⾏ DispatcherServlet 中的 doDispatch 调度⽅法.

另外,为什么Spring 封装的拦截器的 preHandle 方法中会封装了 HttpServletRequest 和HttpServletResponse 呢?
也是因为 它执行目标方法之前,回调用 DispatcherServlet 中 doDispatch 方法。
进行预处理的时候,就会使用 这两参数,
顺水推舟,你要用,我就给你传嘛。

拦截器小结

通过上⾯的源码分析,我们可以看出,Spring 中的拦截器也是通过动态代理和环绕通知的思想实现的,⼤体的调⽤流程如下:

 这个时候,原本 Spring AOP 的 切面类(代理对象) 换成了 DispatcherServlet 。
一个是我们自定义的,一个 Spring 框架 自带的。

不过执行原理并没有变。
用户 想要与 目标对象 直接交互,必须要通过 代理对象(拦截器)的验证。
验证通过,才有资格访问目标对象

扩展:统⼀访问前缀添加

所有请求地址添加 api 前缀:

 index 方法也是一样的

2.统⼀异常处理

对于 统一异常处理,提出一个问题:如果不统一处理异常会是什么效果?

本来按道理来说:是返回一个 JSON 格式的信息,因为前后端都是通过JSON 来交流的。
但是!我 “ 不小心 ” 写出了一个 bug

 解决的办法:我们就需要对所有的异常做一个拦截。

有的人可能会说:我可以给那些可能会出现异常的带啊,使用 tryCatch 包裹。但是这些导致代码太长了!阅读性还低!
尤其是涉及到 事务,如果你乱使用 tryCatch 会导致 事务 无法正常进行回滚。
从而出现 预料之外的错误。

为了解决这个问题,我们在 Spring 中 使用的是 统一异常处理的方式来解决。

对所有的异常进行一个拦截。
拦截之后,你 “ 想干嘛就干嘛 ”.
你想返回一个什么形式的代码给前端,都可以。

统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实现的.

@ControllerAdvice 表示:控制器通知类

@ExceptionHandler 是异常处理器

两个结合表示:
当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件。

统⼀异常处理 的实现有两步:

1、给当前统一处理的类上加上 @ControllerAdvice 注解。
@ControllerAdvice 注解,你可以认为该类是 controller 的通知类,或者 说:这是对 Controller 里面方法的增强。
Controller的通知,指的是 controller 里面出现什么问题之后,我能走到这个通知的方法里执行相应的处理。

2、给方法上添加一个 @ExceptionHandler 注解。
ExceptionHandler : Exception(异常) + Handler (处理器 / 管理器)
然后呢, @ExceptionHandler 注解后面 需要带有参数,表明处理异常的类型。

 进一步认证一下:我们使用 fiddler 抓个包。

 现在放心了吧!
前端在拿到这个响应,知道它是一个算术异常,他就可以对其进行“包装”,然后返回给用户,比如:非法操作,此接口异常,稍后再试、

也就是说:后端一般返回的错误信息,是给 前端程序员去使用的。
前端程序员会根据错误信息,比如:对错误码,进行“包装”,让用户看的懂。
比如 404 未找到对应的资源。
我们来访问一个不存在的资源,以b站为例。因为它的页面做的确实很好!

 这里需要注意的是 没有定义拦截的异常。
是不会被拦截的。
来看下面的例子

 此时,我们丢出一个想法:
上面写的异常拦截方法,是具有很强的针对性的,一个方法针只能针对一种错误类型。
那么,存不存在一种 “ 灵活 ” 写法。
简单来说:对于 异常类型,我们不写死。
我们一个方法,就能拦截多种类型的异常,甚至所有的异常,都可以拦截?

当然自定义的异常类型,另说。不过差别也不大。

我们来一个暴力解法,直接 异常的源头 Exception 作为 @ExceptionHandler 参数对象。
这样我们不就可以拦截所有的异常类型了嘛!

3.统⼀数据返回格式

为什么需要统⼀数据返回格式?

统⼀数据返回格式的优点有很多,⽐如以下⼏个:

1、 ⽅便前端程序员更好的接收和解析后端数据接⼝返回的数据。

2、 降低前端程序员和后端程序员的沟通成本,按照某个格式实现就⾏了,因为所有接⼝都是这样返回的。

3、 有利于项⽬统⼀数据的维护和修改。

4、 有利于后端技术部⻔的统⼀规范的标准制定,不会出现稀奇古怪的返回内容。

我们之前是使用一个对象,来作为 返回格式的标准。
但是!还是有那么一点丁麻烦的!
我们还有更简单的做法!
举个例子:
就是开惯手动挡的车,觉得还行。
但是,开过自动挡车,很容易就回不去了。
因为 自动挡,实在是香!
再往后看,N年以后,出现自动驾驶的汽车,我们也就该彻底 “堕落” 了。。。。

程序也是一样的,它也在不断的进化!
之前。我们刚学的时候,比较菜嘛。
老老实实的每个方法都去自己实现一个 HashMap,搞得自己很累,很憔悴。
然后呢,后面能力得到的提升,把 它给封装起来,只需要去new一个对象就行了。不再需要我们去手动实现了。
但是!依然觉得不是那么爽。
学到当前这一块,之前是怎么做的,现在还是这么做的。【“ 大道至简!”】
就比如说:登录的时候,本来就只需要返回 true,或者 false。
那么,我就只返回 true / false,就行了。
我呢,会拦截这个 true 和 false,并将这个 true / false 填充到 data 里面。
再去封装好,让前端能够识别 的 统一的对象,这是不是很爽?
这样做,开发者就不需要关注,到底返回的是一个什么格式的数据给前端。
对于前端来说,都一样。我按到的数据格式是不变的。
中间的处理,就是由统一功能去处理的。


统⼀数据返回格式的实现

统⼀的数据返回格式可以使⽤ @ControllerAdvice + ResponseBodyAdvice 的⽅式实现。

类上加 @ControllerAdvice 。
并且,该类实现 ResponseBodyAdvice 接口。
必须重写 里面的 supports 和 beforeBodyWrite 方法。
supports(支持):当它返回 true,就表示要对 返回的数据,进行 格式的重写。反之,当它返回 false,就不需要重写了。

当需要 到 重写,就会进入 beforeBodyWrite 方法。
beforeBodyWrite 方法的意义:就是将数据返回之前,要不要进行重写 响应数据。

 下面来验证一下效果

 抓包的结果,显示 返回的响应是 JSON 类型的

我们再来试一下,用户名密码 不匹配的情况

我们再来写一个极其简单的伪代码。等等!我好像还没有演示 supports 方法 返回 false ,是否还会触发重写。
搞起!

总结

 经过上述的练习。
我们可以发现 统一功能处理 是存在缺陷的,就是 返回数据的信息描述被写死了。
因为都是统一的格式,所以返回的键值对 的 键值 是无法改变的。

 换句话来说:统一带来的问题就是:牺牲了 “ 灵活 ”!

所以,关于统一功能的使用,还是需要根据实际情况来决定!

最后,补充一点:@ControllerAdvice 源码分析 - 了解

大家其实回想一下 第二个功能(异常) 和 第三个功能(返回数据的格式),其实都是事件啊!
我们代码出现一个异常,这就是一个异常事件啊!
事件发生了,我们是可以感知到的。
感知到了之后,就可以根据感知到的事件的处理方法,敲自己的业务代码。
这样的话,如果感知到事件,我们写代码,就能起到作用了。
异常是这样,返回统一的数据格式,也是这样的!
每一次返回一个数据给前端的时候,它也是一个事件(数据传输事件)。
事件发生了,我们的程序是能感知到的。所以才能做统一额处理。

说白了:@ControllerAdvice注解,就是针对项目中发生的事件,做拦截的。

通过对 @ControllerAdvice 源码的分析我们可以知道上⾯统⼀异常和统⼀数据返回的执⾏流程,我们先从 @ControllerAdvice 的源码看起,点击 @ControllerAdvice 实现源码如下:

 从上述源码可以看出 @ControllerAdvice 派⽣于 @Component 组件,⽽所有组件初始化都会调⽤ InitializingBean 接⼝。
所以接下来我们来看 InitializingBean 有哪些实现类?
在查询的过程中我们发现了,
其中 Spring MVC 中的实现⼦类是 RequestMappingHandlerAdapter,
它⾥⾯有⼀个⽅法 afterPropertiesSet() ⽅法,表示所有的参数设置完成之后执⾏的⽅法。
如下图所示:

 ⽽这个⽅法中有⼀个 initControllerAdviceCache ⽅法,查询此⽅法的源码如下:

 我们发现这个⽅法在执⾏是会查找使⽤所有的 @ControllerAdvice 类,这些类会被容器中,但发⽣某个事件时,调⽤相应的 Advice ⽅法。
⽐如
返回数据前调⽤统⼀数据封装,⽐如发⽣异常是调⽤异常的Advice ⽅法实现。

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

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

相关文章

win10日程怎么同步到安卓手机?电脑日程同步到手机方法

在如今快节奏的生活中,高效地管理时间变得至关重要。而对于那些经常在电脑上安排日程的人来说,将这些重要的事务同步到手机上成为了一个迫切的需求。因为目前国内使用win10系统电脑、安卓手机的用户较多,所以越来越多的职场人士想要知道&…

Jenkins 安装构建

一、CentOS 安装 1. 使用该存储库 sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key 2. 安装 Java yum install fontconfig java-11-openjdk配…

解决eclipse 打开报错 An error has occurred. See the log file null.

解决eclipse 打开报错an error has ocurred. See the log file null 出现原因:安装了高版本的jdk,更换 jdk 版本,版本太高了。 解决方案:更改环境变量 改成 jkd 1.8

【深度学习实践】垃圾检测

简介 本项目使用深度学习目标检测开源框架PaddleDetection中的yolox算法实现了垃圾检测,本文包含了从头训练yolox模型和直接使用训练好的模型进行推理的代码及相关权重。 一、数据集准备 本次训练的数据集为coco格式,共包含150张垃圾的照片&#xff0…

利用小波分解信号,再重构

function [ output_args ] example4_5( input_args ) %EXAMPLE4_5 Summary of this function goes here % Detailed explanation goes here clc; clear; load leleccum; s leleccum(1:3920); % 进行3层小波分解,小波基函数为db2 [c,l] wavedec(s,3,db2); %进行…

hcip——路由策略

要求: 基础配置 AR1 [R1]int g 0/0/0 [R1-GigabitEthernet0/0/0]ip add 12.0.0.1 24[R1-GigabitEthernet0/0/0]int g 0/0/1 [R1-GigabitEthernet0/0/1]ip add 14.0.0.1 24[R1]int loop0 [R1-LoopBack0]ip add 1.1.1.1 24[R1]rip 1 [R1-rip-1]vers 2 [R1-rip-1]net…

Unity 性能优化五:渲染模块压力

CPU压力 Batching 在GPU渲染前,CPU会把数据按batch发送给GPU,每发送一次,都是一个drawcall,GPU在渲染每个batch的时候,会切换渲染状态,这里的渲染状态指的是:影响对象在屏幕上的外观的渲染属性…

深入学习java虚拟机||JVM内存结构五大模型

目录 程序计数器 栈 虚拟机栈 垃圾回收是否涉及栈内存? 栈内存分配越大越好吗? 方法内的局部变量是否线程安全? 栈内存溢出 本地方法栈 堆 方法区 先看内存图总览 程序计数器 定义:全称P r o g r a m C o u n t e r R e …

windows下配置vue开发环境

安装nodejs,配置npm 1.下载安装包:下载地址:https://nodejs.org/en/download 2.安装node:下载完成后进行安装,记住安装的文件夹。本人安装路径为 D:\Program Files\nodejs 3.配置环境变量: ①安装完成后…

基于传统检测算法hog+svm实现图像多分类

直接上效果图: 代码仓库和视频演示b站视频005期: 到此一游7758258的个人空间-到此一游7758258个人主页-哔哩哔哩视频 代码展示: 数据集在datasets文件夹下 运行01train.py即可训练 训练结束后会保存模型在本地 运行02pyqt.py会有一个可视化…

某信用中心之加速乐实战分析

某信用中心之加速乐实战分析 某信用中心之加速乐实战分析声明逆向目标逆向分析第一层cookie获取第二层cookie获取调试分析JS文件 模拟执行致谢 某信用中心之加速乐实战分析 声明 本文章中所有内容仅供学习交流,抓包内容、敏感网址、数据接口均已做脱敏处理&#x…

QTday2信号和槽

点击登录按钮,关闭Widget登录窗口,打开QQList窗口 widget.cpp #include "widget.h"void my_setupUI(Widget *w);Widget::Widget(QWidget *parent): QWidget(parent) {my_setupUI(this); }Widget::~Widget() { }void Widget::login_slots() {//fixemit jump_signal(…

CAN学习笔记1:计算机网络

计算机网络 1 概述 计算机网络就是把多种形式的计算机用通信线路连接起来,并使其能够互相进行交换的系统。实际上,计算机网络包括了计算机、各种硬件、各种软件、组成网络的体系结构、网络传输介质和网络通信计数。因此,计算机网络是计算机…

SpringCloudAlibaba之Ribbon

Ribbon是nacos自带的负载均衡器,属于客户端的负载均衡 但是在Spring高级版本中让LoadBalancer替代了 本人用的是2.1.0的nacos,ribbon还没有被替换。 使用: 在配置类中:LoadBalanced BeanLoadBalancedpublic RestTemplate restT…

【点云处理教程】05-Python 中的点云分割

一、说明 这是我的“点云处理”教程的第 5 篇文章。“点云处理”教程对初学者友好,我们将在其中简单地介绍从数据准备到数据分割和分类的点云处理管道。 在上一教程中,我们看到了如何过滤点云以减少噪声或其密度。在本教程中,我们将应用一些聚…

Redis(五)—— Redis进阶部分

一、Redis配置文件详解 注意这是Redis服务本身的配置文件&#xff0c;相当于maven的settings.xml&#xff0c;而不是我们在springboot去配置Redis的那个application.yml。 核心部分include 引入其他redis配置文件&#xff0c;相当于spring的<import>bind 设置IP&#xf…

Elasticsearch:通过动态修剪实现更快的基数聚合

作者&#xff1a;Adrien Grand Elasticsearch 8.9 通过支持动态修剪&#xff08;dynamic pruning&#xff09;引入了基数聚合加速。 这种优化需要满足特定的条件才能生效&#xff0c;但一旦实现&#xff0c;通常会产生惊人的结果。 我们观察到&#xff0c;通过此更改&#xff0…

C语言指针应该这么学?

数组名的意义&#xff1a; 1. sizeof(数组名)&#xff0c;这里的数组名表示整个数组&#xff0c;计算的是整个数组的大小。 2. &数组名&#xff0c;这里的数组名表示整个数组&#xff0c;取出的是整个数组的地址。 3. 除此之外所有的数组名都表示首元素的地址。 根据以上数…

C++代码格式化工具clang-format详细介绍

文章目录 clang-format思考代码风格指南生成您的配置运行 clang-format禁用一段代码的格式设置clang-format的设置预览 clang-format 我曾在许多编程团队工作过&#xff0c;这些团队名义上都有“编程风格指南”。该指南经常被写下来并放置在开发人员很少查看的地方。几乎在每种…

网络编程 IO多路复用 [select版] (TCP网络聊天室)

//head.h 头文件 //TcpGrpSer.c 服务器端 //TcpGrpUsr.c 客户端 select函数 功能&#xff1a;阻塞函数&#xff0c;让内核去监测集合中的文件描述符是否准备就绪&#xff0c;若准备就绪则解除阻塞。 原型&#xff1a; #include <sys/select.…