10. Spring MVC 程序开发

本文源码位置: Spring-MVC

1. Spring MVC 概要

摘自Spring官方: Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, “Spring Web MVC,” comes from the name of its source module (spring-webmvc), but it is more commonly known as “Spring MVC”.

上面一大段话主要讲的是:Spring Web MVC是基于Servlet API构建的原始web框架,从它一诞生就包含在Spring Framework中,现在更多地被概括为Spring MVC

【经典问题】Spring/Spring Boot/Spring MVC 有什么区别?

发布的先后顺序:Spring -> Spring MVC -> Spring Boot

  • Spring指的是Spring Framework,是一个完整的框架,核心是IoC和AOP
  • Spring Boot是Spring Framework的一个子项目,它是作为Spring Framework的脚手架而存在的,旨在简化 Spring 应用和服务的创建、开发与部署,并且简化了配置,使得 Java 开发者可以快速启动、运行和测试一个简单的 Spring 应用;
  • Spring MVC 属于 Spring 的一个模块,在 Spring MVC 诞生前,开发Java web项目主流还是使用原生的Servlet框架,而 Spring MVC 则是基于Servlet API的 web 框架,目的是简化web开发的操作。

1.1 MVC的定义

MVC是软件工程中的一种架构模式,MVC三个字母分别代表Model(模型)、View(视图)、Controller(控制器),也就是说MVC是由这三个部分构成的。

以下单的业务为例:用户在页面点击下单,下单成功后在页面上通知用户下单成功以及显示相应的订单的信息

  1. 用户通过页面下单触发点击事件,发送HTTP请求到其路由映射的Controller(控制器),也就是后端的第一层,它相当于是一个安检口,用来检查前端传来的参数是否合法等;
  2. 通过Controller的检查后,将请求信息分发给Model(业务模型)对请求进行相应业务的处理,如将新的订单入库,以及查询订单的操作;
  3. Model层处理完相应业务后,将该订单的信息数据,从数据库中取出给Model(数据模型),以Model为原型分装成响应信息返回给Controller;
  4. Controller得到对应数据后,将返回数据View(视图)
  5. View(视图)通过返回的数据,通过JSP等方式将数据可视化在页面上,作为HTTP响应展现给用户,此时用户就能在页面中看到下单的信息了。

Untitled Diagram.drawio-2.png

该架构模式出现的很早,缺点是通过JSP将数据可视化的方式将前后端的代码混在一起了,不利于前后端工程师的分工(前端工程师不怎么熟悉java语法,java工程师不熟悉前端语法),已经不适用于如今的项目,为了弥补这方面带来的不便,才出现了老生常谈的前后端分离。

1.2 MVC 和 Spring MVC的关系

MVC是一种思想,而 Spring MVC 是对其的具体实现。

但是由于前后端的分离,把View(视图)的部分给淡化了,现在更多将Spring MVC 叫做 Spring Web。

从讲Spring Boot的第一篇文章开始,其实就在使用Spring Web,当时是在starter中引入的依赖:

image.png

项目启动后,就能在pom.xml中找到Spring Boot项目中对Spring Web的依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2. Spring MVC 主要功能

无论学习 Spring MVC 还是 Servlet 无非就是主要围绕下面三个功能学习:

  1. 连接的功能: 将用户(浏览器)和 Spring 程序通过路由关系连接起来,也就是访问一个地址能够调用 Spring 程序中的路由方法
  2. 获取参数: 用户访问的时候会携带一些参数,在程序中要想办法获取参数
  3. 返回数据: 执行业务逻辑之后,程序要将用户需要的数据返回给浏览器

2.1 Spring MVC 创建和连接

2.1.1 项目创建

Spring MVC 的项目创建这里就不赘述了,在starter中选择Spring Web 就相当于创建了Spring MVC的项目。

image.png

2.1.2 建立连接

接下来,创建一个UserController类,通过@RequestMapping注解配置路由,实现用户到Spring程序的连接:

@Controller// 让 Spring 框架启动时,加载
@ResponseBody//返回非页面数据
@RequestMapping("user")//路由注册
public class UserController {
    @RequestMapping("/hi")//路由注册
    public String doController() {
        return "<h1> Hello Spring MVC !<h1>";
    }
}

注意事项:

  1. 只能在@Controller的类中配置路由,否则路由不生效;
  2. @ResponseBody可以修饰类也可以修饰方法,作用是通过适当的转换器转换为指定的格式之后,写入到response对象的body区,使用此注解之后不会再走视图处理器,如果没有这个注解只能返回一个页面;
  3. 前面的篇章使用的@RestController是组合注解,注解中包含@Controller@ResponseBody
  4. 与Servlet的路由配置不同,@RequestMapping注解是可以精确到方法上的。

访问配置文件中配置的默认端口号8080,并通过相应路由访问到程序中的路由方法:
image.png

2.1.3 指定请求类型

可以通过Postman测试一下,当前的Spring版本下,@RequestMapping是否支持接收所有类型的请求,这里测试下POST和PATCH:
image.png

image.png

我当前的Spring版本为2.7.3,通过测试得知@RequestMapping是可以接收所有请求的。


但是如果有一个需求,要求是该路由只能接收Post请求。

1) 使用@RequestMapping

如果还是使用@RequestMapping注解就需要设置method参数了,参数的值需要结合枚举类RequestMethod进行设置:

@RestController
@RequestMapping("user")//路由注册
public class UserController {
    @RequestMapping(value = "/hi", method = RequestMethod.POST)//路由注册
    public String doController() {
        return "<h1> Hello Spring MVC !<h1>";
    }
}

观察@RequestMapping(value = "/hi", method = RequestMethod.POST):当添加了method参数后,我们发现"/hi"也需要添加value参数,当只有一个参数的时候,可以只设置"/hi",此时"/hi"为value的值。

这时再使用其他类型的请求,就会发现报了 “405” 的错误,表示请求方法不被允许:

image.png

2) 使用@PostMapping

除了设置method参数的方式,还可以使用请求类型相应的注解来制定请求类型(@PostMapping只支持post请求、@GetMapping只支持get请求):

image.png

@RestController
@RequestMapping("user")//路由注册
public class UserController {
    @PostMapping("/hi")
    public String doController() {
        return "<h1> Hello Spring MVC !<h1>";
    }
}

2.1.4 设置多个请求类型

此时@PostMapping等注解就不管用了,只能通过@RequestMappingmethod属性设置多个值:

@RestController
@RequestMapping("user")//路由注册
public class UserController {
    @RequestMapping(value = "/hi", method = {RequestMethod.POST, RequestMethod.GET})//路由注册
    public String doController() {
        return "<h1> Hello Spring MVC !<h1>";
    }
}

2.2 获取参数

Spring MVC是基于Servlet的框架,内置了两个隐藏的参数,HttpServletRequest和HttpServletResponse,如果想要使用这两个对象,只要在方法的参数中加入这两个类型,因此围绕HttpServletRequest对象获取参数的写法在这里也是适用的。

2.2.1 获取单个参数

方式一:使用request对象接收
@RequestMapping("/getOneParam1")
public String getOneParam1(HttpServletRequest request) {
    return "Hi," + request.getParameter("name");
}

image.png

这种方式既麻烦又不安全:通过这种方式获取的参数只能是String类型的,如果此时程序中需要的是一个Interger类型的年龄,那么只能通过Integer.valueOf()实现

@RequestMapping("/getOneParam1")
public String getOneParam1(HttpServletRequest request) {
    //return "Hi," + request.getParameter("name");
    Integer age = Integer.valueOf(request.getParameter("age"));
    return "value: " + age;
}

但如果前端没有传这个参数,在不额外做判断空指针的处理的话,就会抛java.lang.NumberFormatException的异常:

image.png


方式二:使用单个参数接收

注意:参数名必须和前端匹配

@RequestMapping("/getOneParam2")
public String getOneParam2(String name) {
    return "Hi," + name;
}

image.png
通过方式二,获取Integer类型的年龄

@RequestMapping("/getOneParam2")
public String getOneParam2(Integer age) {
    //return "Hi," + name;
    return "value: " + age;
}

即使前端不传参数,也不会出现异常:

image.png

2.2.2 获取多个参数

方式一:使用多个参数接收

Spring MVC 传递多个参数的时候,只要在方法中添加多个参数即可,获取参数时只需要保证参数名对应,获取参数的结果只和参数的名称有关,和顺序无关

@RequestMapping("/getMoreParam")
public String getMoreParam(String name, Integer age) {
    return "name: " + name + " | " + "age: " + age;
}

前端传参数的时候顺序与程序中获取参数的顺序不同,但还是顺利拿到结果:
image.png

如果参数少的时候还可以这样做,但如果有极端情况需要传成百上千个参数的话,用这种做法就显得太抽象了。


方式二:使用对象接收

编写一个实体类:

@Data
public class User {
    private Integer id;
    private String name;
    private Integer age;
    private String gender;
}

使用对象接收:

@RequestMapping("/getMoreParam2")
public String getMoreParam(User user) {
    return user.toString();
}

前端传参数给后端后,Spring MVC 检测到你是用一个对象来接收的,就会通过setter方法来将对应的参数注入到对象中,如果前端传的参数实体类中没定义该属性就不处理,如果对象中某些属性前端没有传参数,属性就为默认值null
image.png

2.2.3 获取JSON对象 —— @RequestBody

大部分情况下前面提到的获取参数的方式都能解决,但如果参数是一个JSON对象就得用特殊的方式获取了。

编写一个login的路由方法用于测试:

@RequestMapping("/login")
public String login(String name, String password) {
    return "name: " + name + " | " + "password: " + password;
}

前端传参方式介绍(简单了解)

前端将数据传递给后端大概有三种方式:

  • Url中携带QueryString直接访问路由
  • Form表单提交
  • Ajax提交

URL传参:前面所使用的都是通过URL直接携带QueryString访问的,这里就不过多赘述了。

From表单传参(下面的method参数可以填get/post):

<body>
    <form method="xxx" action="/user/login">
        <h1>登录</h1>
        <div>用户:<input id="name" name="name"/></div>
        <div>密码:<input id="password" name="password"/></div>
        <input type="submit" value="提交">
    </form>
</body>

页面的显示如下:

image.png

1. method=“get”

点击提交后抓包我们可以观察到From表单的get方法穿参其实就跟URL传参是一样的,因此我们的后端可以顺利的获取到数据

image.png


2. method=“post”

点击提交后抓包我们发现这次的QueryString不在url中了,而是在body(请求体)中,并且body中的参数的content-typeapplication/x-www-form-urlencoded,抓包数据如下:

image.png

image.png
此时虽然QueryString到了body中,但是格式还是和URL中的一样,因此我们的后端还是能顺利的拿到数据:
image.png


Ajax传参

1. 通过Ajax传一个js对象

<body>
    <h1>登录</h1>
    <div>用户:<input id="name" name="name"/></div>
    <div>密码:<input id="password" name="password"/></div>
    <input type="button" onclick="submit()" value="提交">
</body>
<script>
    function submit() {
        jQuery.ajax({
            url:"/user/login",
            type:"POST",
            data:{
                "name":jQuery("#name").val(),
                "password":jQuery("#password").val(),
            },
            success:function(body) {
                alert(body);
                console.dir(body);
            }
        })
    }
</script>

点击提交后抓包我们发现虽然此时是通过Ajax传参,但本质还是和Form表单的Post方法传参是一样的,body中的参数的content-typeapplication/x-www-form-urlencoded,抓包数据如下:

image.png

image.png

因此后端还是可以获取到参数并返回给前端:

image.png


2. 通过Ajax传一个JSON对象

当我尝试用Postman传一个JSON对象给前端时,奇怪的事发生了,这时候我们的后端无法再顺利拿到数据了

image.png

使用@RequestBody+对象接收

@RequestBody主要用来接收前端传递给后端的JSON对象的数据的,需要搭配实体类来使用,由于我们需要传password的参数,因此我们在实体类中加上password字段:

@Data
public class User {
    private Integer id;
    private String name;
    private Integer age;
    private String gender;
    private String password;
}

接下来我们就可以使用@RequestBody来获取JSON对象了,具体代码如下:

@RequestMapping("/login2")
public String loginAjax(@RequestBody User user) {
    return "name: " + user.getName() + " | " + "password" + user.getPassword();
}

通过Postman重新发送请求,这次我们终于成功获取到了JSON对象。
image.png

2.2.4 获取文件 —— @RequestPart

引入需求:如用户修改头像的业务,用户想要通过前端页面的上传头像功能将桌面上的一个可爱小猫图片上传给后端。

image.png

使用@RequestPart+MultipartFile对象接收

跟JSON一样,文件也是一个比较特殊的对象,Spring MVC 提供了一个简化上传操作的工具类 —— MultipartFile,当前端上传一个文件给后端时,后端需要使用@RequestPart注解来修饰MultipartFile对象来接收前端发来的文件。

注意事项:

  1. @RequestPart注解需要设置一个参数,与前端的参数的key值相对应;

  2. 服务器在用MultipartFile对象接收之后,应该保存到服务器本地上,不然程序执行完该对象就会被GC回收掉。MultipartFile对象有一个transferTo()方法,可以将该文件复制到本地的路径中

具体代码如下:

@RequestMapping("/upload")
public String upload(@RequestPart("myfile")MultipartFile file, ServletRequest request) throws IOException {
    String path = "/Users/chenshu/Code/classcode_java/blog-demo/Spring-MVC/src/main/resources/static/img";
    file.transferTo(new File(path+"/cat.jpg"));
    return "success";
}

使用Postman来模拟用户上传头像的HTTP请求:

image.png

成功保存到相应路径下:

image.png

2.2.5 获取Cookie/Session/Header

获取Header —— @RequestHeader

传统方法:

@RequestMapping("/header1")
public String getHeader1(HttpServletRequest request) {
    String header = request.getHeader("User-Agent");
    return "User-Agent: " + header;
}

image.png

通过@RequestHeader获取:

@RequestMapping("/header2")
public String getHeader2(@RequestHeader("User-Agent") String header) {
    return "User-Agent: " + header;
}

image.png


获取Cookie —— @CookieValue

通过该路由方法给浏览器设置一个Cookie值用于测试:

@RequestMapping("/setCookie")
public void setCookie(HttpServletResponse response) {
    Cookie cookie = new Cookie("status", "OK");
    response.addCookie(cookie);
}

传统方法:

@RequestMapping("/cookie1")
public String getCookie1(HttpServletRequest request) {
    String ret = "status: ";
    Cookie[] cookies = request.getCookies();
    for (Cookie cookie : cookies) {
        if (cookie.getName().equals("status")) {
            ret += cookie.getValue();
        }
    }
    return ret;
}

image.png

通过@CookieValue获取:

@CookieValue中的参数设置的是Cookie的键,该注解的作用是在所有Cookie中找对应键的Cookie,并将该Cookie的值并赋给一个String

@RequestMapping("/cookie2")
public String cookie(@CookieValue("status") String status) {
    return "status: " + status;
}

image.png


获取Session —— @SessionAttribute

通过该路由方法给浏览器设置一个Session用于测试:

@RequestMapping("/setSession")
public String setSession(HttpServletRequest request) {
    HttpSession session = request.getSession(true);
    if (session!=null) {
        session.setAttribute("username", "zhangsan");
    }
    return "session 存储成功.";
}

传统方法:

@RequestMapping("/session1")
public String getSession1(HttpServletRequest request) {
    HttpSession session = request.getSession(false);
    String username = null;
    if (session != null && session.getAttribute("username") != null) {
        username = (String) session.getAttribute("username");
    }
    return  "username: " + username;
}

image.png

通过@SessionAttribute获取:

  • value参数代表的是Session里的键,作用是在Session的所有键值对中找到对应的键,并将该键所对应的值赋给一个String;
  • required参数默认为true,表示没有找到value参数指定的键就直接报错,设置为false的话则不报错。
@RequestMapping("/session2")
public String getSession2(@SessionAttribute(value = "username", required = false) String username) {
    return "username: " + username;
}

image.png

2.2.6 后端参数重命名 —— @RequestParam

某些特殊的情况下,前端传递的参数的key不满足后端的参数命名规范,比如前端传递了一个money表示用户的金额,你认为这个参数的命名模棱两可,想使用balance来接收,出现这种情况,我们就可以使用@RequestParam来重命名前端的参数。

@RequestMapping("/showBalance")
public String renameParam(@RequestParam("money") String balance){
     return "账户余额为: " + balance;
}

image.png

注意事项:当使用了这个注解后,前端一定得传键为"money"的参数,否则就会报400错误

image.png

2.2.7 设置参数必传 —— required参数

在Session中就经常使用这个参数,前面也提到过,这里还是单独拿出来一谈,required参数默认是true的,如果不想报错的话,需要设置required参数为false

@RequestMapping("/showBalance")
public String renameParam(@RequestParam(value = "money", required = false) String balance){
     return "账户余额为: " + balance;
}

这样就不会报错了:

image.png

2.2.8 获取URL中参数 —— @PathVariable

有些特殊的场景,需要将参数直接放在URL里,与放在"?"后面的QueryString中不同,它是直接将参数放在路径上的:

image.png

对于上面那种情况,我们需要在@RequestMapping中告知程序路由上的哪些玩意是参数,如"/login2/{username}/{password}",表示login2后面紧跟的两个子路径是作为参数传递的

然后使用@PathVariable注解修饰变量来接收参数

@RequestMapping("/login2/{username}/{password}")
public String login2(@PathVariable String username, @PathVariable String password) {
    return "username: " + username + " | " + "password: " + password;
}

成功取到URL中的参数:

image.png

2.3 返回数据

Spring MVC 中可以通过 Controller 返回数据给前端

如果没加@RequestBody注解的话,Spring MVC就会根据return的值去找view(视图)的路径,在HTTP响应报文中返回text/html类型的HTML源码从而展示在页面上。

如果加了@RequestBody注解,SpringSpring MVC会根据你在Java中返回的类型来自动设置HTTP响应包中的的Content-Type(包含数据类型和编码方式),并且根据Content-Type中的数据类型和编码方案,自动转化为相应的数据并返回。

2.3.1 添加@RequestBody返回String

前面我们所用的方式都是返回一个String给前端,通过抓包我们可以看到,响应的Content-Typetext/html类型,浏览器会将返回的数据当作HTML页面解析,本质上就跟没有添加@RequestBody返回视图是一样的。

但是有一点不一样,并且由于Idea中的默认编码方式是UTF-8,因此 Spring MVC 设置HTTP响应报文中的Content-Type会携带charset=UTF-8,浏览器也会以UTF-8的格式来解码,看2.3.2 到返回静态页面的时候会更直观地看出差别。
image.png

2.3.2 返回JSON对象

一个json对象其实就是大括号中包含一个个键值对,原生的Servlet想要返回JSON对象,通常是使用一个外部的JSON框架如jackson,将一个对象或者对象列表转化成Json格式的字符串,并通过设置HTTP响应报文中的Content-Type字段,从而告知浏览器要把数据以Json的格式解析。

String jsonString = objectMapper.writeValueAsString(obj);
resp.setContentType("application/json;charset=utf8");
resp.getWriter().write(jsonString);

在Spring MVC中内置了jackson框架,如果你在类上或者方法上添加了@RequestBody注解,当你返回一个对象、Map、列表时,会自动将它们转化成json格式发送给前端

@RequestMapping("/userinfo1")
public User getUserInfo1() {
    User user = new User();
    user.setId(1);
    user.setName("zhangsan");
    user.setPassword("123");
    user.setGender("male");
    user.setAge(20);
    return user;
}

返回对象:
一个普通对象可以映射一个Json对象,对象中有哪些属性,Json对象就对应拥有哪些键。

@RequestMapping("/userinfo1")
public User getUserInfo1() {
    User user = new User();
    user.setId(1);
    user.setName("zhangsan");
    user.setPassword("123");
    user.setGender("male");
    user.setAge(20);
    return user;
}

image.png

返回Map: 由于Map是键值对的形式,一个Map对象也可以映射一个Json对象,Map中有哪些键,Json对象就对应拥有哪些键。

@RequestMapping("/userinfo2")
public Map<String, Object> getUserInfo2() {
    HashMap<String, Object> user = new HashMap<>();
    user.put("name", "zhangsan");
    user.put("password", "123");
    return user;
}

image.png

返回List: 一个List对应多个Json对象,多个Json对象用[ ]来包裹起来,List里面可以有普通对象,也可以有Map对象。

@RequestMapping("/userinfo3")
public List<User> getUserInfo3() {
    List<User> userList = new ArrayList<>();
    User user1 = new User();
    user1.setName("zhangsan");
    User user2 = new User();
    user2.setName("lisi");
    userList.add(user1);
    userList.add(user2);
    return userList;
}

image.png

2.3.2 返回静态页面

去掉@ResponseBody注解,Spring MVC就知道你要返回的是一个静态页面:

@Controller
@RequestMapping("/test")
public class TestController {
    @RequestMapping("/index")
    public String index() {
        return "/index.html";
    }
}

index.html如下:

<html>
<header>
    <meta charset="UTF-8">
</header>
<body>
<h1>hello word!!!</h1>
<p>this is a html page</p>
<p>返回静态页面的测试</p>
</body>
</html>

访问对应路由后确实展示出了我们所编写的页面:

image.png

注意事项: 由于浏览器是按HTML中的<header>标签中设置的charset进行解码,因此抓包数据中的Content-Type没有设置charset类型:

image.png

我们必须在<header>设置默认的字符编码,否则浏览器会以默认的解码方案来解码,如果我把<header>中配置的UTF-8的字符编码删除,显示的页面出现的中文就是乱码:

image.png

2.3.4 请求转发和请求重定向

除了返回一个静态页面,还可以实现跳转,跳转的方式有两种

  • redirect:请求重定向
  • forward:请求转发

请求重定向: 你发送一个请求给后端,请求成功到达路由后,HTTP请求报文中的状态码为302请求重定向,后端在HTTP响应报文中添加Location字段中告诉你重定向的位置,然后浏览器会自动完成页面的跳转,也就相当于重新发起了一次HTTP请求

@RequestMapping("/redirect")
public String index2() {
    return "redirect:/index.html";
}

下面我通过抓包演示请求重定向

在浏览器中输入重定向的路由:

image.png

在用户的视角下:输入上面的URL后,直接跳转到下面的页面:
image.png

抓包看到HTTP请求的状态码为302 Found

image.png

HTTP响应中多了一个Location字段,浏览器就是通过该字段进行重新跳转到新页面:
image.png


请求转发: 当你发送一个请求给后端,后端会携带者你的HTTP请求,转发到新的路由,你会发现浏览器上的地址不会变,然后将新的路由返回的信息展现在你的页面上,HTTP请求报文的状态码为200,对用户来说是透明的。

@RequestMapping("/forward")
public String index3() {
    return "forward:/index.html";
}

下面我通过抓包演示请求转发

用户视角:输入URL按下回车键后即使路由没发生变化,还是将index.html页面展现给你
image.png

抓包看到HTTP请求的状态码为200 OK:

image.png

通过在HTTP响应报文中返回页面的text/html,将页面显示在用户视角中:

image.png

在index.html中引入了外部css样式如下:

image.png

再次访问test/forward路由,发现由于请求转发给后端是将页面通过HTTP响应报文以test/html的形式返回给前端从而显示到页面上,因此访问不到外部的css样式:

image.png

forward和redirect具体区别总结:

  1. 请求重定向(redirect)将请求重新定位到资源;请求转发(forward)服务器端转发
  2. 请求重定向地址发生变化,请求转发地址不发生变化。
  3. 请求重定向相当于前端发送了两次HTTP请求,而请求重定向只发送一次HTTP请求
  4. 由于请求转发给后端是将页面通过HTTP响应报文以test/html的形式返回给前端从而显示到页面上,因此如果引用了外部css/js等,可能造成外部资源访问不到

3. 总结

所谓 Spring MVC 就是基于 Servlet API 的框架,所谓 Spring MVC 程序开发讲的就是三件事情:建立连接、获取参数以及返回数据,本文对这三件事进行了详细的讲解,足以在开发中应对绝大多数需求。

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

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

相关文章

Redis的Stream 和 实现队列的方式【List、SortedSet、发布订阅、Stream、Java】

Redis队列与Stream、Redis 6多线程详解 Redis队列与StreamStream总述常用操作命令生产端消费端单消费者消费组消息消费 Redis队列几种实现的总结基于List的 LPUSHBRPOP 的实现基于Sorted-Set的实现PUB/SUB&#xff0c;订阅/发布模式基于Stream类型的实现与Java的集成 消息队列问…

权限管理Ranger详解

文章目录 一、Ranger概述与安装1、Ranger概述1.1 Ranger介绍1.2 Ranger的目标1.3 Ranger支持的框架1.4 Ranger的架构1.5 Ranger的工作原理 2、Ranger安装2.1 创建系统用户和Kerberos主体2.2 数据库环境准备2.3 安装RangerAdmin2.4 启动RangerAdmin 二、Ranger简单使用1、安装 R…

使用代理IP实现Python爬虫中的随机IP请求

目录 前言 一、什么是代理IP&#xff1f; 二、 随机IP请求的实现步骤 1. 获取代理IP列表 2. 随机选择代理IP 3. 发送请求 4. 处理响应 总结 前言 在网络爬虫中&#xff0c;使用代理IP是一种常见的技术手段&#xff0c;它可以帮助我们隐藏真实IP地址&#xff0c;提高爬取…

python生成二维码

要在Python中生成二维码&#xff0c;可以使用第三方库qrcode。首先&#xff0c;确保已经安装了qrcode库&#xff1a; pip install qrcode然后&#xff0c;使用以下代码生成二维码&#xff1a; import qrcodedata "https://mp.csdn.net/mp_blog/creation/editor?spm100…

static+单例模式+类的复合继承

汇编语言 汇编语言是最靠谱的验证“编程语言相关知识点”正确性的方式 汇编语言与机器语言一一对应&#xff0c;每一条机器语言都有与之对应的汇编指令 机器语言是计算机使用的语言&#xff0c;它是一串二进制数字 汇编语言可以通过汇编得到机器语言机器语言可以通过反汇编得到…

LoRA:大模型的低阶自适用(使用BERT在IMDB数据集上运用LoRA微调)

文章目录 简介LoRA文章主要贡献LoRA技术模型图技术细节论文实验结果LoRA在bert的运用LoRA核心代码实战分析 简介 论文链接https://arxiv.org/pdf/2106.09685v2.pdf 本文将先介绍论文中的LoRA技术&#xff0c;然后以BERT为例在IMDB数据集上代码实现运用这项微调技术。 代码数…

Day 14 网络协议

常见网络设备&#xff1a;交换机 路由器 中继器 多协议网关&#xff08;路由器的前身&#xff09; 交换机&#xff1a;用于连接统一网络的设备&#xff0c;实现内网设备通信。 从广义上分为&#xff1a;局域网交换机&#xff0c;广域网交换机 从网络构成分为&#xff1a;接…

Prompt提示工程上手指南:基础原理及实践-思维树 (ToT)策略下的Prompt

前言 此篇文章已经是本系列的第五篇文章&#xff0c;之前我们已经将检索增强生成(RAG)策略&#xff0c;逐渐我们掌握的知识和技术都在不断提高&#xff0c;对于Prompt的技巧策略也不能只局限于局部运用而要适应LLM大模型的整体框架去进行改进休整。较为主流的LLM模型框架设计基…

通过adb 命令打印安装在第三方模拟器上的log

1&#xff0c;环境&#xff1a;Windows 11 &#xff0c;第三方模拟器 网易的MuMu 步骤&#xff1a; 1&#xff0c;打开cmd&#xff0c;输入 adb connect 172.0.0.1:7555 2&#xff0c;在cmd&#xff0c;再次输入adb logcat 回车

【Web】陇原战“疫“2021网络安全大赛 题解

目录 CheckIN eaaasyphp EasyJaba CheckIN 拿到附件&#xff0c;贴出关键代码 func getController(c *gin.Context) {cmd : exec.Command("/bin/wget", c.QueryArray("argv")[1:]...)err : cmd.Run()if err ! nil {fmt.Println("error: ", …

【HCIP】OSPF的高级特性

OSPF的高级特性1 --- 不规则区域 一、OSPF不规则区域类型 产生原因&#xff1a;区域划分不合理&#xff0c;导致的问题 1、非骨干区域无法和骨干区域保持连通 2、骨干区域被分割 造成后果&#xff1a;非骨干区域没和骨干区域相连&#xff0c;导致ABR将不会帮忙转发区域间的路由…

element-ui设置弹窗等级最高

通过参数:appendToBody"true"设置弹窗等级最高 主要是 :appendToBody“true”&#xff0c;其他参数可根据自己需求配置 <el-dialog :title"title" :visible.sync"isShow" top"5vh" :appendToBody"true"><el-image…

Web前端开发——Ajax,Axios概述及在Vue框架中的使用

前言&#xff1a; 整理下学习笔记&#xff0c;打好基础&#xff0c;daydayup!!! Ajax Ajax是什么&#xff1f; Ajax全称Asynchromous JavaScript And Xml&#xff0c;是异步的JavaScript和Xml。 Ajax的作用&#xff1f; 1&#xff0c;数据交换&#xff1a;通过Ajax可以给服务器…

uniapp 当前系统没有安装苹果根证书,是否打开证书目录(打开后依次安装证书

当你遇到这类问题时&#xff0c;说明你也极其的困惑&#xff01;这就是为啥大抵国内这些货色搞的东西总是不尽人意&#xff01;连开发者生态都搞不好&#xff0c;就急着吹嘘。 这是官方给的技术说明方案&#xff1a; 恭喜你&#xff0c;当你按照这个搞之后&#xff0c;你的问题…

Map与Set的模拟实现封装

目录 一. 底层原理 二. 红黑树节点的定义 三. 仿函数封装 四. 基本函数的封装 五. 迭代器的封装 5.1 迭代器的基本定义 5.2 *与->操作 5.3 迭代器的操作 5.3.1 右子树不为空 5.3.2 右子树为空 5.4 迭代器的--操作 5.4.1 当前节点的父节点…

CSS基础:最详细 padding的 4 种用法解析

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃&#xff0c;大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 We…

Adobe Premiere Pro将加入AI生成式功能,以提高视频编辑的效率;OpenAI宣布在东京设立亚洲首个办事处

&#x1f989; AI新闻 &#x1f680; Adobe Premiere Pro将加入AI生成式功能&#xff0c;以提高视频编辑的效率 摘要&#xff1a;Adobe宣布&#xff0c;将为Premiere Pro引入由生成式AI驱动的新功能&#xff0c;以提高视频编辑的效率。这些功能包括“生成扩展”&#xff0c;能…

免费开源多客圈子婚恋社交校园跑腿线上线下陪玩 源码交付 可打包小程序 支持二开!

聊天软件作为一种现代化的通讯工具&#xff0c;其好处可以总结如下&#xff1a; 1.方便快捷&#xff1a;聊天软件只要有网络连接&#xff0c;就可以随时随地与他人进行交流&#xff0c;不受时间和地点的限制&#xff0c;可以随时随地进行沟通&#xff0c;大大方便了人们的日常…

【结构型模式】装饰器模式

​一、装饰器模式概述 装饰器模式&#xff08;装饰者模式&#xff09;定义&#xff1a;装饰器模式动态地将责任附加到对象上。若要拓展功能&#xff0c;装饰者提供了比继承更有弹性地替代方案。&#xff08;对象结构型模型&#xff09;通俗点来说&#xff1a;动态的给一个对象增…

适用于 Windows 的 10 个顶级 PDF 编辑器 [免费和付费]

曾经打开PDF文件&#xff0c;感觉自己被困在数字迷宫中吗&#xff1f;无法编辑的文本、无法调整大小的图像以及签署感觉像是一件苦差事的文档&#xff1f;好吧&#xff0c;不用再担心了&#xff01;本指南解开了在 Windows 上掌握 PDF 的秘密&#xff0c;其中包含 10 款适用于 …