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 {
    @Override
    public 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 {
    // 添加拦截器
    @Override
    public 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.mfbz.cn/a/55919.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

day17 | 654.最大的二叉树 617.合并二叉树 700.二叉搜索树中的搜索 98.验证二叉搜索树

文章目录 一、最大的二叉树二、合并二叉树三、二叉搜索树中的搜索四、验证二叉搜索树 一、最大的二叉树 654.最大的二叉树 构建二叉树的题目,都用前序遍历。 因为我们一定要先构建根节点,才能继续向后构建。 递归函数的参数和返回值: Tree…

【MyBatis】MyBatis把空字符串转换成0的问题处理方案(96)

先看问题: Postman入参: MyBatis采用map循环插入: // Mapper接口层void addPar(Param(value "question") Map<String, Object> paramMap);<!-- 新增&#xff1a;参数 --><insert id"addPar" parameterType"map">INSERT IGNO…

小研究 - JVM 垃圾回收方式性能研究(一)

本文从几种JVM垃圾回收方式及原理出发&#xff0c;研究了在 SPEC jbb2015基准测试中不同垃圾回收方式对于JVM 性能的影响&#xff0c;并通过最终测试数据对比&#xff0c;给出了不同应用场景下如何选择垃圾回收策略的方法。 目录 1 引言 2 垃圾回收算法 2.1 标记清除法 2.2…

构建语言模型:BERT 分步实施指南

学习目标 了解 BERT 的架构和组件。了解 BERT 输入所需的预处理步骤以及如何处理不同的输入序列长度。获得使用 TensorFlow 或 PyTorch 等流行机器学习框架实施 BERT 的实践知识。了解如何针对特定下游任务(例如文本分类或命名实体识别)微调 BERT。为什么我们需要 BERT? 正…

使用docker部署Wordpress

文章目录 1.创建网络2.创建volume存储3.拉取镜像4.创建mysql容器mysql修改密码 5.创建wordpress容器6.访问localhost:80就可以直接使用啦 1.创建网络 docker network create --subnet172.18.0.0/24 pro-net2.创建volume存储 # mysql 存储 docker volume create volume_mysql…

怎么才能远程控制笔记本电脑?

为什么选择AnyViewer远程控制软件&#xff1f; 为什么AnyViewer是远程控制笔记本电脑软件的首选&#xff1f;以下是选择AnyViewer成为笔记本电脑远程控制软件的主要因素。 跨平台能力 AnyViewer作为一款跨平台远程控制软件&#xff0c;不仅可以用于从一台Windows电…

如何制作VR全景地图,VR全景地图可以用在哪些领域?

引言&#xff1a; 随着科技的迅速进步&#xff0c;虚拟现实&#xff08;VR&#xff09;技术正逐渐渗透到各个领域。VR全景地图作为其中的重要应用之一&#xff0c;为人们提供了身临其境的全新体验。 一.什么是VR全景地图&#xff1f; VR全景地图是一种利用虚拟现实技术&…

PHP8的数据类型-PHP8知识详解

在PHP8中&#xff0c;变量不需要事先声明&#xff0c;赋值即声明。 不同的数据类型其实就是所储存数据的不同种类。在PHP8.0、8.1中都有所增加。以下是PHP8的15种数据类型&#xff1a; 1、字符串&#xff08;String&#xff09;&#xff1a;用于存储文本数据&#xff0c;可以使…

【LeetCode每日一题】——1572.矩阵对角线元素的和

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 矩阵 二【题目难度】 简单 三【题目编号】 1572.矩阵对角线元素的和 四【题目描述】 给你一…

HTML5网页设计小案例:网页导航栏的设计

什么是导航栏&#xff0c;按我的理解就是位于网页顶部或者侧边一组链接或者按钮&#xff0c;用来指导大家找到网页的不同板块&#xff0c;大家可以一目了然的找到自己想看的板块内容。今天我们设计一个位于网页顶部的的导航栏。按我的生活经验来说&#xff0c;网页的顶部导航栏…

Django学习记录:使用ORM操作MySQL数据库并完成数据的增删改查

Django学习记录&#xff1a;使用ORM操作MySQL数据库并完成数据的增删改查 数据库操作 MySQL数据库pymysql Django开发操作数据库更简单&#xff0c;内部提供了ORM框架。 安装第三方模块 pip install mysqlclientORM可以做的事&#xff1a; 1、创建、修改、删除数据库中的…

网络安全进阶学习第八课——信息收集

文章目录 一、什么是信息收集&#xff1f;二、信息收集的原则三、信息收集的分类1.主动信息收集2.被动信息收集 四、资产探测1、Whois查询#常用网站&#xff1a; 2、备案信息查询#常用网站&#xff1a; 3、DNS查询#常用网站&#xff1a; 4、子域名收集#常用网站&#xff1a;#常…

Linux编辑器 - vim使用

1.vim的基本概念 Vim是一个广泛使用的文本编辑器&#xff0c;它是在Unix和Linux系统中常用的命令行文本编辑器之一。 vim的主要三种模式 ( 其实有好多模式&#xff0c;目前掌握这 3 种即可 ), 分别是 命令模式 &#xff08; command mode &#xff09;、 插入模式 &#xff0…

html学习5(表单)

1、表单是一个包含表单元素的区域&#xff0c;用于收集用户的输入信息。 2、表单元素是允许用户在表单中输入内容&#xff0c;比如&#xff1a;文本域&#xff08;textarea&#xff09;、下拉列表&#xff08;select&#xff09;、单选框&#xff08;radio-buttons&#xff09…

MySQL篇

文章目录 一、MySQL-优化1、在MySQL中&#xff0c;如何定位慢查询?2、SQL语句执行很慢, 如何分析呢&#xff1f;3、了解过索引吗&#xff1f;&#xff08;什么是索引&#xff09;4、索引的底层数据结构了解过嘛 ?5、什么是聚簇索引什么是非聚簇索引 ?6、知道什么是回表查询嘛…

go初识iris框架(三) - 路由功能处理方式

继了解get,post后 package mainimport "github.com/kataras/iris/v12"func main(){app : iris.New()//app.Handle(请求方式,url,请求方法)app.Handle("GET","/userinfo",func(ctx iris.Context){path : ctx.Path()app.Logger().Info(path) //获…

CEC2022:CEC2022测试函数及多种智能优化算法求解CEC2022对比

目录 一、CEC2022测试函数 二、多种智能优化算法求解CEC2022 2.1 本文参与求解CEC2022的智能优化算法 2.2 部分测试函数运行结果与收敛曲线 三、带标记收敛曲线代码(获得代码后可自行更改&#xff09; 一、CEC2022测试函数 CEC2022测试集共有12个单目标测试函数&#x…

SpringBoot使用JKS或PKCS12证书实现https

SpringBoot使用JKS或PKCS12证书实现https 生成JKS类型的证书 可以利用jdk自带的keytool工具来生成证书文件&#xff0c; 默认生成的是JKS证书 cmd命令如下: 执行如下命令&#xff0c;并按提示填写证书内容&#xff0c;最后会生成server.keystore文件 keytool -genkey tomcat…

VMware Linux Centos 配置网络并设置为静态ip

在root用户下进行以下操作 1. 查看子网ip和网关 &#xff08;1&#xff09;进入虚拟网络编辑器 &#xff08;2&#xff09;进入NAT设置 &#xff08;3&#xff09;记录子网IP和子网掩码 2. 修改网络配置文件 &#xff08;1&#xff09;cd到网络配置文件路径下 [rootlo…

【element-ui】form表单初始化页面如何取消自动校验rules

问题描述&#xff1a;elementUI表单提交页面&#xff0c;初始化页面是获取接口数据&#xff0c;给form赋值&#xff0c;但是有时候这些会是空值情况&#xff0c;如果是空值&#xff0c;再给form表单赋值的话&#xff0c;页面初始化时候进行rules校验会不通过&#xff0c;此时前…