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

一天急速通关SpringMVC

一天急速通关SpringMVC

  • 0 文章介绍
    • 1 介绍
    • 1.1 MVC架构与三层架构
    • 1.2 Spring MVC介绍
    • 1.3 入门程序
  • 2 请求的映射
  • 3 请求数据的接收
    • 3.1 @RequestParam接收
    • 3.2 POJO/JavaBean接收
    • 3.3 RequestHeader和CookieValue接收
  • 4 请求数据的传递
  • 5 视图
    • 5.1 视图的理解
    • 5.2 请求转发和响应重定向的使用
    • 5.3 特定资源映射和静态资源映射
  • 6 技巧
    • 6.1 RESTful编程风格
    • 6.2 HTTP消息转换器
    • 6.3 文件上传与下载
    • 6.4 异常处理器
    • 6.5 拦截器
  • 7 全注解开发

0 文章介绍

在倍速观看动力节点杜老师的Spring6MVC教程之后,根据视频内容以及课程笔记进行实践,经过自己的理解并总结后形成这篇学习笔记。文章总共分为六个章节,包括了原教程的十四个章节的大部分知识,学习本文的前置知识需要:JavaSEJDBCMySQLXMLMyBatisSpring6AjaxThymeleaf。本文所提供的信息和内容仅供参考,作者和发布者不保证其准确性和完整性。

1 介绍

1.1 MVC架构与三层架构

MVC架构本质是一种用户界面设计模式,主要用于分离用户界面的展示、数据管理和用户交互逻辑,将应用分为三个核心组件:

  • 模型(Model):应用程序的数据结构和业务逻辑,负责与数据库、文件系统和其他数据源进行交互,获取和处理数据。
  • 视图(View):用户界面的展示部分,负责向用户呈现数据并接收用户的输入数据,不涉及业务逻辑。
  • 控制器(Controller):充当模型和视图之间的中介,处理用户输入并调用模型和视图去完成用户的需求。

理解: 前端浏览器发送请求给Web服务器,Web服务器中的Controller接收到用户的请求,Controller负责将前端提交的数据进行封装,然后Controller调用Model来处理业务,当Model处理完业务后会返回处理之后的数据给ControllerController再调用View来完成数据的展示,最终将结果响应给浏览器,浏览器进行渲染展示页面。

"Uses"
"Updates"
"Used by"
"Used by"
Model
+ data: any
+getData()
+updateData(data: any)
View
+display(data: any)
Controller
+ model: Model
+ view: View
+request(request: any)

三层架构(Three-Tier Architecture)是一种分层架构,为了实现高内聚低耦合,提高系统的可维护性和可扩展性,将应用分为三个逻辑层:

  • 表示层(Presentation Layer:负责用户界面的展示,接收用户输入并展示处理结果。
  • 业务逻辑层(Business Logic Layer:处理业务规则,封装业务逻辑,组合数据访问层的基本功能,形成复杂的业务逻辑功能。
  • 数据访问层(Data Access Layer:负责与数据库进行交互,执行数据的增删改查操作。

理解: 关注点和MVC不一样,关注点在于系统逻辑分离。

"Sends request to"
"Sends request to"
"Returns data to"
"Returns data to"
PresentationLayer
+handleRequest(request: any)
BusinessLogicLayer
+processRequest(request: any)
DataAccessLayer
+saveData(data: any)
+getData()

MVC 架构在实际应用中,模型部分可能会包含复杂的业务逻辑和数据访问操作,为了更好地组织和解耦代码,将 MVC 中的模型层进一步细分为三层架构中的业务逻辑层(Service)和数据访问层(DAO),三层架构中的表示层负责接收用户请求并展示处理结果,其实就对应MVC架构的视图层(View)和控制层(Controller)。

"Updates"
"Uses"
"Includes"
"Uses"
"Returns data to"
"Returns data to"
Model
+ data: any
+getData()
+updateData(data: any)
View
+display(data: any)
Controller
+ model: Model
+ view: View
+request(request: any)
Service
+processRequest(request: any)
DAO
+saveData(data: any)
+getData()

理解:MVC 架构本质上是一种用户界面设计模式,核心目标是分离用户界面的展示、数据和交互逻辑,本身并不涉及应用程序的底层架构设计,如数据存储、业务逻辑处理等,而这些通常是通过其他设计模式或架构(如三层架构、微服务架构等)来实现。通过结合两者,MVC 架构专注于用户界面的设计和交互,而三层架构则提供了更清晰的系统逻辑分离和模块化设计,使得应用程序更加易于维护和扩展。

1.2 Spring MVC介绍

SpringMVC是一个实现了MVC架构模式的Web框架,底层基于Servlet实现,之前学习JavaWeb的时候学了Servlet的一些操作,现在这个框架能够更方便的实现这些操作,同时还支持Spring6IoCAOP,具体的点有:

  • 通过DispatcherServlet作为入口控制器,负责接收请求和分发请求。Servlet需要手动编写Servlet程序。
  • 表单提交时可以通过简单的操作就能自动将表单数据绑定到相应的JavaBean对象中。Servlet需要手动获取。
  • 通过IoC容器管理对象,只需要在配置文件中进行相应的配置即可获取实例对象。Servlet需要手动创建。
  • 提供了拦截器、异常处理器等统一处理请求的机制,并且可以灵活地配置这些处理器。Servlet需要手动过滤器、异常处理器等。
  • 提供了多种视图模板,如JSPFreemarkerVelocity等,并且支持国际化、主题等特性。Servlet需要手动处理视图层。

总结:简化了很多操作的同时提供了更多的功能和扩展性。

1.3 入门程序

0 创建Maven Java Web项目 -> 1 配置pom.xml依赖 -> 2 配置web.xml -> 3 配置springmvc-servlet.xml -> 4 编写Java代码与视图。

  • 环境准备

    • IntelliJ IDEA:2024.1.7

    • Navicat for MySQL:17.1.2

    • MySQL:8.0.26

    • JDK:17.0.2

    • Maven:3.9.1

  • 设置pom.xml的打包方式以及添加依赖,打包方式设置为war

    <groupId>com.cut</groupId>
    <artifactId>spring-mvc-demo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!-- 打包方式设置为war方式 -->
    <packaging>war</packaging>
    <dependencies><!-- Spring MVC依赖 --><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>6.1.14</version></dependency><!--日志框架Logback依赖--><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.5.3</version></dependency><!--Servlet依赖--><dependency><groupId>jakarta.servlet</groupId><artifactId>jakarta.servlet-api</artifactId><version>6.0.0</version><scope>provided</scope></dependency><!--Spring6和Thymeleaf整合依赖--><dependency><groupId>org.thymeleaf</groupId><artifactId>thymeleaf-spring6</artifactId><version>3.1.2.RELEASE</version></dependency>
    </dependencies>
    
  • 配置web.xml文件,主要配置SpringMVC的前端控制器DispatcherServlet,让除了JSP的所有请求都走这个控制器。

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"version="6.0"><!--SpringMVC提供的前端控制器--><servlet><servlet-name>springmvc</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--手动设置springmvc配置文件的路径及名字--><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc.xml</param-value></init-param><!--为了提高用户的第一次访问效率,建议在web服务器启动时初始化前端控制器--><load-on-startup>6</load-on-startup></servlet><servlet-mapping><servlet-name>springmvc</servlet-name><!-- /* 表示任何一个请求都交给DispatcherServlet来处理 --><!-- / 表示当请求不是xx.jsp的时候,DispatcherServlet来负责处理本次请求--><url-pattern>/</url-pattern></servlet-mapping>
    </web-app>
    
  • 配置springmvc-servlet.xml,SpringMVC框架的配置文件,名字默认为:<servlet-name>-servlet.xml,默认存放的位置是WEB-INF根目录。

    如果web.xmlDispatcherServletservlet-namespringmvc,并且WEB-INF根目录下有springmvc-servlet.xml(短横线前面为servlet-name)的配置文件,就不用设置contextConfigLocation,自动加载,如果没有,则需要手动加载,这边在resources目录下也创建了springmvc.xml,内容一样,手动加载这个文件,没有使用默认路径下默认名称的springmvc-servlet.xml,作为演示。

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><!--组件扫描, 扫描注解--><context:component-scan base-package="com.cut.springmvc"/><!--视图解析器--><bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver"><property name="characterEncoding" value="UTF-8"/><!--如果配置多个视图解析器,它来决定优先使用哪个视图解析器,它的值越小优先级越高--><property name="order" value="1"/><property name="templateEngine"><bean class="org.thymeleaf.spring6.SpringTemplateEngine"><property name="templateResolver"><bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver"><property name="prefix" value="/WEB-INF/templates/"/><property name="suffix" value=".html"/><!--设置模板类型,例如:HTML,TEXT,JAVASCRIPT,CSS等--><property name="templateMode" value="HTML"/><property name="characterEncoding" value="UTF-8"/></bean></property></bean></property></bean>
    </beans>
    
  • 编写控制器com.cut.springmvc.controller.IndexController.java

    package com.cut.springmvc.controller;import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;/*** @author tang* @version 1.0.0* @since 1.0.0*/
    @Controller
    public class IndexController {@RequestMapping("/")public String toIndex() {return "index";}@RequestMapping("/hello")public String toHello() {// 返回逻辑视图名称(决定跳转到哪个页面)return "hello";}
    }
    
  • 编写视图WEB-INF/templates/index.html,WEB-INF/templates/hello.html

    <!DOCTYPE html>
    <!--指定 th 命名空间,让 Thymeleaf 标准表达式可以被解析和执行-->
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head><meta charset="UTF-8"><title>Index</title>
    </head>
    <body>
    <!-- Thymeleaf检测到以 / 开始,表示绝对路径,自动会将webapp的上下文路径加上去 -->
    <!-- 最终的效果是:href="/springmvc/hello" -->
    <a th:href="@{/hello}">点击进入hello页面</a>
    </body>
    </html>
    
    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head><meta charset="UTF-8"><title>Hello</title>
    </head>
    <body>
    <h1>Hello Spring MVC</h1>
    </body>
    </html>
    
  • 配置Tomcat并访问即可。

2 请求的映射

SpringMVC中,@RequestMapping 注解是控制器映射注解,用于将请求映射到相应的处理方法上,即将指定URL的请求绑定到一个特定的方法或类上,从而实现对请求的处理和响应,可以用在类或者方法上。

  • 类属性和方法属性结合使用

    同一个webapp中,RequestMapping必须具有唯一性,即value不能重复,同一个Controller或者不同Controller中不能出现相同的映射。

    @Controller
    public class UserController {@RequestMapping("/detail")public String toDetail(){return "detail";}
    }
    
    @Controller
    public class ProductController {@RequestMapping("/detail")public String toDetail(){return "detail";}
    }
    

    这种情况应该要加上前缀,区分两个detail映射,如何加,两种方式,一是方法上直接加前缀。

    @RequestMapping("/user/detail")
    public String toDetail(){return "/user/detail";
    }
    
    @RequestMapping("/product/detail")
    public String toDetail(){return "/product/detail";
    }
    

    二是在类上面加。

    @Controller
    @RequestMapping("/user")
    public class UserController {@RequestMapping("/detail")public String toDetail(){return "/user/detail";}
    }
    
    @Controller
    @RequestMapping("/product")
    public class ProductController {@RequestMapping("/detail")public String toDetail(){return "/product/detail";}
    }
  • RequestMappingvalue属性

    value属性是请求路径,通过该请求路径与对应的控制器的方法绑定,是一个字符串数组,可以映射多个路径。还支持模糊匹配路径,称之为Ant风格

    • ?,代表任意一个字符
    • *,代表0N个任意字符
    • **,代表0N个任意字符,路径中可以有分隔符 /,只能出现在路径的末尾。

    请求路径中还可以使用占位符,配合@PathVariable注解接收请求传递的参数

    @RequestMapping(value="/testRESTful/{id}/{username}/{age}")
    public String testRESTful(@PathVariable("id")int id,@PathVariable("username")String username,@PathVariable("age")int age){System.out.println(id + "," + username + "," + age);return "testRESTful";
    }
    
    <!--测试RequestMapping注解的value属性支持占位符-->
    <a th:href="@{/testRESTful/1/zhangsan/20}">测试value属性使用占位符</a>
    

    之前学习的请求路径应该是下面这样的

    http://localhost:8080/springmvc/login?username=admin&password=123&age=20
    

    上面代码的请求路径是RESTful风格的,在现代的开发中使用较多,因此请求路径变为

    http://localhost:8080/springmvc/login/admin/123/20
    
  • RequestMappingmethod属性

    这个属性用于限定请求方式,如果请求方式和method值不一致会出现405错误,下面演示了要求前端必须得是POST请求访问/login路径。两种方式,一种是RequestMapping,另一种直接使用PostMapping,其他请求方式用法一致。

    //@RequestMapping(value="/login", method = RequestMethod.POST)
    @PostMapping("/login")
    public String testMethod(){return "testMethod";
    }
    

    请求方式主要有下面的九种:

    • GETURL 中或请求头中传递参数,获取资源,只允许读取数据,不影响数据的状态和功能。
    • POST:通过表单等方式提交请求体提交资源。
    • PUT:通过请求体发送,更新指定的资源上所有可编辑内容。
    • DELETE:将要被删除的资源标识符放在 URL 中或请求体中用于删除指定的资源。
    • HEAD:类似 GET 命令,但是所有返回的信息都是头部信息,不能包含数据体。
    • PATCH:当被请求的资源是可被更改的资源时,每次更新一部分。
    • OPTIONS:请求获得服务器支持的请求方法类型,以及支持的请求头标志。
    • TRACE:服务器响应输出客户端的 HTTP 请求,主要用于调试和测试。
    • CONNECT:建立网络连接,通常用于加密 SSL/TLS 连接。

    注意:超链接发送的请求是get请求,form表单只能提交getpost请求,putdeletehead请求可以使用发送ajax请求的方式来实现,form表单的method如果不是get或者post,按照get请求处理。

    比较常用的getpost请求的区别:

    • get请求适合从服务器端获取数据,只能发送普通字符串,且长度有限制,请求的数据会回显到地址栏,支持缓存,如果存在缓存,就不会再发送到服务器,如果要避免,建议请求路径后加时间戳。

    • post请求适合向服务器端传递数据,可以发送任何数据,数据在请求体中,不会回显,不支持缓存,每次请求都走服务器。

  • RequestMappingparams属性

    用于限定请求传递的方式必须和设置的一致,否则发生400错误,对于@RequestMapping(value="/login", params={}) ,匹配规则如下:

    • params={**"username"**, "password"},必须包含 usernamepassword
    • params={**"!username"**, "password"},不能包含username,但必须包含password
    • params={**"username=admin"**, "password"},必须包含值是adminusername参数,必须包含password
    • params={**"username!=admin"**, "password"},不能包含值是adminusername参数,必须包含password
  • RequestMappingheaders属性

    params原理相同,用法也相同。请求头信息后端要求的不一致,则出现404错误

  • 状态码

    400 Bad Request:请求无效,请求格式错误或缺少必要的参数。

    404 Not Found:请求的资源未找到。

    405 Method Not Allowed:请求方法不被允许。

3 请求数据的接收

请求的数据接收主要用到@RequestParamPOJO/JavaBean来进行接收,还有RequestHeader@CookieValue用于接收请求头和Cookie

3.1 @RequestParam接收

对于@RequestParam来说,主要有三个属性vauenamerequireddefaultValue,其中namevalue本质一样。

@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {@AliasFor("name")String value() default "";@AliasFor("value")String name() default "";boolean required() default true;String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}

注册页面和注册成功页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>用户注册</title>
</head>
<body>
<div class="container"><h3>用户注册</h3><hr><form th:action="@{/success}" method="post"><label for="username">用户名:</label><input type="text" id="username" name="username" required><label for="password">密码:</label><input type="password" id="password" name="password" required><label>性别:</label><label><input type="radio" name="sex" value="1"></label><label><input type="radio" name="sex" value="0"></label><label>爱好:</label><label><input type="checkbox" name="hobby" value="smoke"> 抽烟</label><label><input type="checkbox" name="hobby" value="drink"> 喝酒</label><label><input type="checkbox" name="hobby" value="perm"> 烫头</label><label for="intro">简介:</label><textarea id="intro" rows="10" cols="60" name="intro"></textarea><input type="submit" value="注册"></form>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Register Success</title>
</head>
<body>
<h1>注册成功!</h1>
</body>
</html>

RegisterController

package com.cut.springmvc.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;/*** @author tang* @version 1.0.0* @since 1.0.0*/
@Controller
public class RegisterController {@RequestMapping("/success")public String toSuccess(@RequestParam(value = "username", required = true)String username,@RequestParam(value = "password", required = false)String password,@RequestParam(value = "sex")String sex,@RequestParam(value = "hobby")String[] hobby,@RequestParam(name = "intro")String intro,@RequestParam(value = "otherParam", required = false, defaultValue = "default value")String otherParam) {return "success";}@RequestMapping("/register")public String toRegister() {return "register";}
}

关于几个属性的说明:

  • 混用namevalue或者省略,都可以成功接收参数。
  • required默认情况为 true,表示请求参数必需,如果缺少会抛出异常。设置为false如果请求中缺少则方法的参数值为null
  • defaultValue设置形参的默认值,当没有提供对应的请求参数或者请求参数的**值是空字符串""**的时候,方法的形参会采用默认值。

注意,@RequestParam 可以省略的,请求参数和方法形参的名字相同,则可以省略,如果形参有请求参数没有的,为null

3.2 POJO/JavaBean接收

当提交的数据非常多时,方法的形参个数会非常多,能直接使用POJO/JavaBean来接收吗,可以的,但是有个要求,实体类得含有对应请求参数名一样的set方法,属性名可以不一致,但是set方法得一致。

private Long ID;
public void setId(Long id) {this.ID = id;
}
// 请求参数中有id
// 那么实体类这样也是可以的,只要setId对应id即可,属性ID名称没关系
@PostMapping("/register")
public String register(User user){System.out.println(user);return "success";
}

3.3 RequestHeader和CookieValue接收

@GetMapping("/register")
public String register(User user,@RequestHeader(value="Referer", required = false, defaultValue = "")String referer,@CookieValue(value="id", required = false, defaultValue = "1")String id){return "success";
}

4 请求数据的传递

之前在学习Servlet的时候,通过setAttribute + getAttribute方法来完成在域中数据的传递和共享。回顾Servlet的三个域对象:

域对象作用范围生命周期使用场景
应用域ServletContext全局范围Web应用启动到停止全局变量、配置信息
会话域HttpSession会话范围用户会话开始到结束用户登录信息、购物车
请求域HttpServletRequest请求范围请求开始到结束请求参数、临时数据
  • 请求域对象中共享数据方式有四种,前三者本质都是一样的BindingAwareModelMap

    • Model接口,model.addAttribute(key,value)

      @RequestMapping("/testModel")
      public String testModel(Model model){model.addAttribute("testRequestScope", "在SpringMVC中使用Model接口实现request域数据共享");return "view";
      }
      
    • Map接口,map.put(key,value)

      @RequestMapping("/testMap")
      public String testMap(Map<String, Object> map){map.put("testRequestScope", "在SpringMVC中使用Map接口实现request域数据共享");return "view";
      }
      
    • ModelMap接口,modelMap.addAttribute(key,value),直接添加对应类型的形参使用即可。

      @RequestMapping("/testModelMap")
      public String testModelMap(ModelMap modelMap){modelMap.addAttribute("testRequestScope", "在SpringMVC中使用ModelMap实现request域数据共享");return "view";
      }
      
    • ModelAndView接口

      这个类的实例封装了Model和View也就是说这个类既封装业务处理之后的数据,也体现了跳转到哪个视图。不是添加形参,而是在方法里new

      @RequestMapping("/testModelAndView")
      public ModelAndView testModelAndView(){ModelAndView modelAndView = new ModelAndView();// 绑定数据modelAndView.addObject("testRequestScope", "在SpringMVC中使用ModelAndView实现request域数据共享");// 绑定视图modelAndView.setViewName("view");return modelAndView;
      }
      
  • 会话域对象中共享数据方式

    直接在Controller类上使用@SessionAttributes注解,标注了当keyx 或者 y 时,数据将被存储到会话session中,如果没有 SessionAttributes注解,默认存储到request域中。

    @Controller
    @SessionAttributes(value = {"x", "y"})
    public class SessionScopeTestController {@RequestMapping("/testSessionScope2")public String testSessionAttributes(ModelMap modelMap){// 向session域中存储数据modelMap.addAttribute("x", "我是埃克斯");modelMap.addAttribute("y", "我是歪");return "view";}
    }
    
  • 应用域对象中共享数据方式

    直接使用Servlet API即可

    @Controller
    public class ApplicationScopeTestController {@RequestMapping("/testApplicationScope")public String testApplicationScope(HttpServletRequest request){// 获取ServletContext对象ServletContext application = request.getServletContext();// 向应用域中存储数据application.setAttribute("applicationScope", "我是应用域当中的一条数据");return "view";}
    }
    

5 视图

5.1 视图的理解

视图(View)是MVC架构中的表示层组件,负责将模型(Model)中的数据呈现给用户,并处理用户交互,之前在 springmvc.xml配置的,就是Thymeleaf视图解析器ThymeleafViewResolver

<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
...
</bean>

除了Thymeleaf以外,还有其他的视图:

  • InternalResourceView:内部资源视图(Spring MVC框架内置的,专门为JSP模板语法准备的)
  • RedirectView:重定向视图(Spring MVC框架内置的,用来完成重定向效果)
  • ThymeleafViewThymeleaf视图(第三方的,为Thymeleaf模板语法准备的)
  • FreeMarkerViewFreeMarker视图(第三方的,为FreeMarker模板语法准备的)
  • VelocityViewVelocity视图(第三方的,为Velocity模板语法准备的)
  • PDFViewPDF视图(第三方的,专门用来生成pdf文件视图)
  • ExcelViewExcel视图(第三方的,专门用来生成excel文件视图)

SpringMVC如何实现视图机制的呢,主要有下面几个核心接口

  • 负责中央调度的前端控制器DispatcherServlet,核心方法为doDispatch
  • 将逻辑视图名转换为物理视图名的视图解析器ViewResolver,核心方法为resolveViewName,返回视图实现类对象。
  • 将模板语言转换成HTML代码的视图View,核心代码为render
  • 将视图解析器注册到web容器中的视图解析器注册器ViewResolverRegistry,按order顺序放入到List集合中。

理解: 浏览器发送请求给web服务器,DispatcherServlet接收到请求并根据请求路径分发到对应的Controller并调用,Controller返回逻辑视图名给DispatcherServletDispatcherServlet再调用ThymeleafViewResolverresolveViewName方法,将逻辑视图名转换为物理视图名,并获取ThymeleafView对象,最后DispatcherServlet调用ThymeleafViewrender方法,render方法将模板语言转换为HTML代码,响应给浏览器,完成最终的渲染。

5.2 请求转发和响应重定向的使用

关于请求转发和响应重定向在SpringMVC中的使用,回顾之前Servlet中的使用

request.getRequestDispatcher("/index").forward(request, response);
response.sendRedirect("/webapproot/index");

两个的区别:

  • 转发是一次请求,服务器端内部的行为,地址栏不变化,不能实现跨域,但是可以访问WEB-INF下受保护的目录。
  • 重定向是两次请求,既可以完成服务端内部资源跳转也可以完成跨域跳转,地址栏会发生变化,无法访问WEB-INF下受保护的目录。注意第一次发送请求,服务端会将重定向的地址返回给浏览器,浏览器再对这个地址发送请求。

Spring MVC中控制器的返回值解析有下面三种情况:

  • 返回值以 "forward:" 开头,Spring MVC 会执行请求转发到指定的路径(/path)。

    @RequestMapping("/a")
    public String toA(){return "forward:/b";
    }
    

    一共创建了两个视图对象,转发源:InternalResourceView,转发目的地:ThymeleafView

  • 返回值以 "redirect:" 开头,Spring MVC 会执行 HTTP 重定向到指定的路径(/path)。

    @RequestMapping("/a")
    public String toA(){return "redirect:/b";
    }
    

    一共创建了两个视图对象,重定向源:RedirectView,重定向目的地:ThymeleafView

  • 返回值是除了前两种情况的字符串,Spring MVC 会将其视为视图名,通过视图解析器来解析对应的视图资源。

5.3 特定资源映射和静态资源映射

<mvc:view-controller> 配置用于将某个请求映射到特定的视图上,即指定某一个 URL 请求到一个视图资源的映射,使得这个视图资源可以被访问。当Controller中只是简单返回视图名的话,可以直接使用这个配置,例如首页之类的资源映射。

<mvc:view-controller path="/" view-name="index" />
<!-- 使用了上面的需要再手动开启SpringMVC的注解,不然会发生404错误 -->
<mvc:annotation-driven/>

关于静态资源的映射,比较简单的方式就是:

<!-- 开启注解驱动 -->
<mvc:annotation-driven />
<!-- 配置静态资源处理,请求路径是"/static/"开始的,都会去"/static/"目录下找该资源。 -->
<mvc:resources mapping="/static/**" location="/static/" />

也可以使用默认Servlet处理,直接开启默认Servlet处理静态资源功能,同一个请求路径,先走DispatcherServlet,如果找不到则走默认的Servlet

<!-- 开启注解驱动 -->
<mvc:annotation-driven />
<!--开启默认Servlet处理-->
<mvc:default-servlet-handler>

6 技巧

主要包括了RESTful编程风格,HTTP消息转换器,文件上传下载,异常处理器,拦截器等方面的知识。

6.1 RESTful编程风格

RESTfulRepresentational State Transfer,表述性状态转移)是WEB服务接口的一种设计风格,定义了一组约束条件和规范用于让WEB服务接口更加简洁、易于理解、易于扩展、安全可靠。其中:

  • 表述性(Representational)是:URI + 请求方式。
  • 状态(State)是:服务器端的数据。
  • 转移(Transfer)是:变化。
  • 表述性状态转移是指:通过URI + 请求方式来控制服务器端数据的变化。
传统的 URLRESTful URL
GET /getUserById?id=1GET /user/1
GET /getAllUserGET /user
POST /addUserPOST /user
POST /modifyUserPUT /user
GET /deleteUserById?id=1DELETE /user/1

如何通过表单来提交这些请求呢,之前说过表单只能提交get或者post请求,这里就得需要针对请求做一个处理,例如put请求:

<!--修改用户-->
<hr>
<form th:action="@{/api/user}" method="post"><!--隐藏域的方式提交 _method=put --><input type="hidden" name="_method" value="put">用户名:<input type="text" name="username"><br><input type="submit" th:value="修改">
</form>

表面上是post请求,服务端接收到后通过HiddenHttpMethodFilter判断是否为post请求,如果是post请求,调用request.getParameter(this.methodParam)this.methodParam的值是"_method",对应名称为"_method"input标签,只有请求方式是put/delete/patch的时候会创建HttpMethodRequestWrapper对象将methodpost变成put/delete/patch

<!--隐藏的HTTP请求方式过滤器-->
<filter><filter-name>hiddenHttpMethodFilter</filter-name><filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping><filter-name>hiddenHttpMethodFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>

注意:在web.xml文件中,应该先配置CharacterEncodingFilter,然后再配置HiddenHttpMethodFilter,因为HiddenHttpMethodFilter使用了request.getParameter(),避免乱码问题。

6.2 HTTP消息转换器

HTTP消息转换器HttpMessageConverter的主要作用是完成HTTP协议Java程序中的对象之间的互相转换,主要学习下面几个注解和类的使用:

  • @ResponseBody:将控制器方法的返回值直接写入 HTTP 响应体中。

    @Controller
    public class MyController {// StringHttpMessageConverter@GetMapping("/hello")@ResponseBodypublic String hello() {return "Hello, World!";}//MappingJackson2HttpMessageConverter@PostMapping("/user")@ResponseBodypublic User createUser() {// 处理用户数据return new User(); // 返回 JSON 格式的用户数据}
    }
    

    这里只要有@ResponseBody注解,那么返回的就不是视图的逻辑名称了,而是响应体,上面演示了两个消息转换器,一个是StringHttpMessageConverter,另一个是MappingJackson2HttpMessageConverterPOJO对象转换成JSON格式的字符串,响应给前端,使用MappingJackson2HttpMessageConverter得添加依赖

    <dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.17.0</version>
    </dependency>
    

    并在配置文件中开启注解

    <mvc:annotation-driven/>
    

    如果这个Controller类中全是响应体,那么可以在类上使用RestController,就不用每个方法都添加@ResponseBody

    @RestController
    public class MyController {// StringHttpMessageConverter@GetMapping("/hello")public String hello() {return "Hello, World!";}//MappingJackson2HttpMessageConverter@PostMapping("/user")public User createUser() {// 处理用户数据return new User(); // 返回 JSON 格式的用户数据}
    }
    
  • @RequestBody:将 HTTP 请求体中的数据绑定到控制器方法的参数上。

    @RestController
    public class MyController {// FormHttpMessageConverter将请求体转换成user对象@PostMapping("/user")public String hello(User user) {System.out.println(user)return "success";}// FormHttpMessageConverter将请求体转换成user字符串@PostMapping("/userStr")public User createUser(@RequestBody String userStr) {System.out.println("请求体:" + userStr);return "success";}
    }
    

    那还有一种组合呢

    // FormHttpMessageConverter将请求体的JSON字符串转换成POJO对象
    @PostMapping("/user")
    public User createUser(@RequestBody User user) {System.out.println("请求体:" + userStr);return "success";
    }
    

    总结:

    • User user:Form表单请求体用POJO接收
    • @RequestBody User user:Json请求体转换为POJO
    • @RequestBody String user:Form表单请求体转换为字符串
  • RequestEntity:用于封装 HTTP 响应的完整信息,包括状态码、响应头和响应体。直接在形参中添加即可使用。

    @RequestMapping("/send")
    @ResponseBody
    public String send(RequestEntity<User> requestEntity){System.out.println("请求方式:" + requestEntity.getMethod());System.out.println("请求URL:" + requestEntity.getUrl());HttpHeaders headers = requestEntity.getHeaders();System.out.println("请求的内容类型:" + headers.getContentType());System.out.println("请求头:" + headers);User user = requestEntity.getBody();System.out.println(user);System.out.println(user.getUsername());System.out.println(user.getPassword());return "success";
    }
    
  • RequestEntity:用于封装 HTTP 请求的完整信息,包括请求方法、请求头和请求体,将返回值类型变为对应的ResponseEntity对象即可,有啥用呢:前端提交一个id,后端根据id进行查询,如果返回null,请在前端显示404错误。如果返回不是null,则输出返回的user

    @Controller
    public class UserController {@GetMapping("/users/{id}")public ResponseEntity<User> getUserById(@PathVariable Long id) {User user = userService.getUserById(id);if (user == null) {return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);} else {return ResponseEntity.ok(user);}}
    }
    

6.3 文件上传与下载

  • 文件的上传

    html文件上传表单,enctype="multipart/form-data"

    <!--文件上传表单-->
    <form th:action="@{/file/up}" method="post" enctype="multipart/form-data">文件:<input type="file" name="fileName"/><br><input type="submit" value="上传">
    </form>
    

    前端控制器配置multipart-config

    <!--前端控制器-->
    <servlet><servlet-name>dispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:springmvc.xml</param-value></init-param><load-on-startup>1</load-on-startup><multipart-config><!--设置单个支持最大文件的大小--><max-file-size>102400</max-file-size><!--设置整个表单所有文件上传的最大值--><max-request-size>102400</max-request-size><!--设置最小上传文件大小--><file-size-threshold>0</file-size-threshold></multipart-config>
    </servlet>
    <servlet-mapping><servlet-name>dispatcherServlet</servlet-name><url-pattern>/</url-pattern>
    </servlet-mapping>
    

    Controller,主要就是文件的写入操作,注意@RequestParam("fileName") MultipartFile multipartFile的理解

    @RequestMapping(value = "/file/up", method = RequestMethod.POST)
    public String fileUp(@RequestParam("fileName") MultipartFile multipartFile, HttpServletRequest request) throws IOException {String name = multipartFile.getName();System.out.println(name);// 获取文件名String originalFilename = multipartFile.getOriginalFilename();System.out.println(originalFilename);// 将文件存储到服务器中// 获取输入流InputStream in = multipartFile.getInputStream();// 获取上传之后的存放目录File file = new File(request.getServletContext().getRealPath("/upload"));// 如果服务器目录不存在则新建if(!file.exists()){file.mkdirs();}// 开始写//BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file.getAbsolutePath() + "/" + originalFilename));// 可以采用UUID来生成文件名,防止服务器上传文件时产生覆盖BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file.getAbsolutePath() + "/" + UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."))));byte[] bytes = new byte[1024 * 100];int readCount = 0;while((readCount = in.read(bytes)) != -1){out.write(bytes,0,readCount);}// 刷新缓冲流out.flush();// 关闭流in.close();out.close();return "ok";
    }
    
  • 文件的下载即可

    <!--文件下载-->
    <a th:href="@{/download}">文件下载</a>
    

    注意返回类型

    @GetMapping("/download")
    public ResponseEntity<byte[]> downloadFile(HttpServletResponse response, HttpServletRequest request) throws IOException {File file = new File(request.getServletContext().getRealPath("/upload") + "/1.jpeg");// 创建响应头对象HttpHeaders headers = new HttpHeaders();// 设置响应内容类型headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);// 设置下载文件的名称headers.setContentDispositionFormData("attachment", file.getName());// 下载文件ResponseEntity<byte[]> entity = new ResponseEntity<byte[]>(Files.readAllBytes(file.toPath()), headers, HttpStatus.OK);return entity;
    }
    

6.4 异常处理器

Spring MVC 中,异常处理是一个非常重要的功能,用于捕获和处理控制器方法中抛出的异常,从而向客户端返回友好的错误信息。实际就是当异常发生了,决定返回什么的操作。

主要有两个异常处理的方式,一个是方法级别的@ExceptionHandler,一个是类级别的@ControllerAdvice ,这里结合之前学习的ResponseEntity使用。

  • @ExceptionHandler,方法上捕获特定异常或者所有异常。
@RestController
public class MyController {@GetMapping("/test")public String test() {throw new IllegalArgumentException("测试异常");}// 捕获特定异常@ExceptionHandler(IllegalArgumentException.class)public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("发生非法参数异常:" + ex.getMessage());}// 捕获所有异常@ExceptionHandler(Exception.class)public ResponseEntity<String> handleException(Exception ex) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("发生未知错误:" + ex.getMessage());}
}
  • @ControllerAdvice,统一管理异常,Advice通知,之前AOP的术语之一应该就明白什么意思了,面向切面编程,业务Controller就只关注核心业务。

    @ControllerAdvice
    public class GlobalExceptionHandler {// 捕获特定异常@ExceptionHandler(IllegalArgumentException.class)public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("发生非法参数异常:" + ex.getMessage());}// 捕获所有异常@ExceptionHandler(Exception.class)public ResponseEntity<String> handleException(Exception ex) {return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("发生未知错误:" + ex.getMessage());}
    }
    

6.5 拦截器

Spring MVC的拦截器作用是在请求到达控制器之前或之后进行拦截,可以对请求和响应进行一些特定的处理。下面是拦截器在整个请求到返回过程中的运行位置。

Filter 链完成
返回阶段
Spring 框架
Servlet 容器
客户端
FilterN doFilter完成
返回响应到客户端
Filter2 doFilter完成
Filter1 doFilter完成
请求完成
渲染页面
Interceptor2 afterCompletion
Interceptor1 afterCompletion
Servlet分发到Spring
请求到达Servlet
Interceptor1 preHandle
Interceptor2 preHandle
Controller处理请求
返回ModelAndView
Interceptor2 postHandle
Interceptor1 postHandle
执行Filter链
Servlet容器接收请求
Filter1 doFilter
Filter2 doFilter
FilterN doFilter
客户端请求
视图解析器解析视图
客户端接收响应

可以看到有三个核心方法,preHandle,postHandle,afterCompletion,Interceptor是后于Filer的执行的。

客户端发请求,Servlet容器接收,然后执行FiltersdoFilter方法,之后进入Spring,执行InterceptorspreHandle后进入Controller并返回ModelAndView,之后可以执行InterceptorspostHandle,再进行视图解析,渲染,完成后执行InterceptorsafterCompletion方法,最后返回响应到客户端并完成Filters链。

SpringMVC中配置拦截器的方式

<mvc:interceptors><bean class="com.powernode.springmvc.interceptors.MyInterceptor"/>
</mvc:interceptors>
<!-- 下面这种需要配置包扫描并使用 @Component 注解进行标注 -->
<mvc:interceptors><ref bean="myInterceptor"/>
</mvc:interceptors>

这种简单的配置会拦截所有请求,如何自定义呢,下面表示除 /test 请求路径之外,剩下的路径全部拦截。

<mvc:interceptors><mvc:interceptor><!--拦截所有路径--><mvc:mapping path="/**"/><!--除 /test 路径之外--><mvc:exclude-mapping path="/test"/><!--拦截器--><ref bean="myInterceptor"/></mvc:interceptor>
</mvc:interceptors>

如果有多个拦截器,他们的三个方法的执行顺序呢

<mvc:interceptors><ref bean="interceptor1"/><ref bean="interceptor2"/>
</mvc:interceptors>

preHandles顺序执行,postHandle逆序执行,afterCompletion逆序执行

Interceptor1.preHandle()  // 请求阶段,按顺序执行
Interceptor2.preHandle()  // 请求阶段,按顺序执行
Controller处理请求       // 请求到达Controller
Interceptor2.postHandle() // 返回阶段,逆序执行
Interceptor1.postHandle() // 返回阶段,逆序执行
视图解析与渲染           // 视图解析器渲染页面
Interceptor2.afterCompletion() // 完成阶段,逆序执行
Interceptor1.afterCompletion() // 完成阶段,逆序执行

注意: 只要有一个拦截器preHandle返回false,任何postHandle都不执行。但返回false的拦截器的前面(不包括当前)的拦截器按照逆序执行afterCompletion

Interceptor1.preHandle()  // 请求阶段,按顺序执行
Interceptor2.preHandle()  // 请求阶段,按顺序执行,返回false,阻止后续处理
Interceptor1.afterCompletion() // 完成阶段,逆序执行

7 全注解开发

其实就web.xmlspringmvc.xml怎么通过类配置

Spring3.1后提供的API
Servlet3.0新特性
Tomcat服务器
SpringServletContainerInitializer 类
WebApplicationInitializer 接口
AbstractAnnotationConfigDispatcherServletInitializer 类
ServletContainerInitializer 接口
Tomcat服务器
自定义web.xml配置类
  • web.xml的配置文件需要继承AbstractAnnotationConfigDispatcherServletInitializer
import jakarta.servlet.Filter;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[0];}// 指定Spring MVC配置类@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[]{SpringmvcConfig.class}; }// 指定DispatcherServlet的映射路径@Overrideprotected String[] getServletMappings() {return new String[]{"/"}; }// 配置过滤器,characterEncodingFilter先于hiddenHttpMethodFilter@Overrideprotected Filter[] getServletFilters() {CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter("UTF-8");characterEncodingFilter.setForceRequestEncoding(true);characterEncodingFilter.setForceResponseEncoding(true);HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};}
}
  • springmvc.xml配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.view.InternalResourceViewResolver;// 指定该类是一个配置类,可以当配置文件使用
@Configuration
// 指定扫描的包路径
@ComponentScan("com.example.controller") 
// 开启Spring MVC注解支持
@EnableWebMvc 
public class SpringmvcConfig implements WebMvcConfigurer {// 配置视图解析器@Beanpublic ThymeleafViewResolver getViewResolver(SpringTemplateEngine springTemplateEngine) {ThymeleafViewResolver resolver = new ThymeleafViewResolver();resolver.setTemplateEngine(springTemplateEngine);resolver.setCharacterEncoding("UTF-8");resolver.setOrder(1);return resolver;}@Beanpublic SpringTemplateEngine templateEngine(ITemplateResolver iTemplateResolver) {SpringTemplateEngine templateEngine = new SpringTemplateEngine();templateEngine.setTemplateResolver(iTemplateResolver);return templateEngine;}@Beanpublic ITemplateResolver templateResolver(ApplicationContext applicationContext) {SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();resolver.setApplicationContext(applicationContext);resolver.setPrefix("/WEB-INF/thymeleaf/");resolver.setSuffix(".html");resolver.setTemplateMode(TemplateMode.HTML);resolver.setCharacterEncoding("UTF-8");resolver.setCacheable(false);//开发时关闭缓存,改动即可生效return resolver;}// 配置静态资源处理@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();}// view-controller特殊映射@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/test").setViewName("test");}// 配置拦截器,这里的MyInterceptor需要自己写@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/test");}// 配置异常处理器@Overridepublic void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();Properties properties = new Properties();properties.setProperty("java.lang.Exception", "error");exceptionResolver.setExceptionMappings(properties);exceptionResolver.setExceptionAttribute("ex");resolvers.add(exceptionResolver);}
}

相关文章:

一天急速通关SpringMVC

一天急速通关SpringMVC 0 文章介绍1 介绍1.1 MVC架构与三层架构1.2 Spring MVC介绍1.3 入门程序 2 请求的映射3 请求数据的接收3.1 RequestParam接收3.2 POJO/JavaBean接收3.3 RequestHeader和CookieValue接收 4 请求数据的传递5 视图5.1 视图的理解5.2 请求转发和响应重定向的…...

MongoDB 7 分片副本集升级方案详解(下)

#作者&#xff1a;任少近 文章目录 1.4 分片升级1.5 升级shard11.6 升级shard2,shard31.7 升级mongos1.8重新启用负载均衡器1.9 推荐MongoDB Compass来验证数据 2 注意事项&#xff1a; 1.4 分片升级 使用“滚动”升级从 MongoDB 7.0 升级到 8.0&#xff0c;即在其他成员可用…...

如何在 MySQL 5.6 中实现按季度分组并找到销量最高的书籍

如何在 MySQL 5.6 中实现按季度分组并找到销量最高的书籍 引言问题描述实现步骤1. 计算每本书在每个季度的累计销量2. 找到每个季度的最高累计销量3. 匹配最高销量的书籍 总结扩展练习 引言 在数据分析和业务报表中&#xff0c;经常需要对数据进行分组统计&#xff0c;并找到每…...

JAVA生产环境(IDEA)排查死锁

使用 IntelliJ IDEA 排查死锁 IntelliJ IDEA 提供了强大的工具来帮助开发者排查死锁问题。以下是具体的排查步骤&#xff1a; 1. 编写并运行代码 首先&#xff0c;我们编写一个可能导致死锁的示例代码&#xff1a; public class DeadlockExample {private static final Obj…...

群体智能优化:粒子群算法(PSO)详解与实战

一、引言&#xff1a;从鸟群行为到优化算法 1995年&#xff0c;社会心理学家James Kennedy和电气工程师Russell Eberhart通过观察鸟群觅食行为&#xff0c;提出了著名的粒子群优化算法&#xff08;Particle Swarm Optimization, PSO&#xff09;。这一算法仅用不到30年时间&am…...

k8s集群搭建参考(by lqw)

文章目录 声明配置yum源安装docker安装 kubeadm&#xff0c;kubelet 和 kubectl部署主节点其他节点加入集群安装网络插件 声明 由于看了几个k8s的教程&#xff0c;都存在各种问题&#xff0c;自己搭建的时候&#xff0c;踩了不少坑&#xff0c;最后还是靠百度csdnchatGPT才搭建…...

vue3+vite项目引入electron运行为桌面项目

一、安装electron npm install --save-dev electron二、项目根目录添加electron文件 在此文件夹中添加两个js文件&#xff1a;main.js、preload.js main.js: // Modules to control application life and create native browser window const { app, BrowserWindow } requ…...

教育小程序+AI出题:如何通过自然语言处理技术提升题目质量

随着教育科技的飞速发展&#xff0c;教育小程序已经成为学生与教师之间互动的重要平台之一。与此同时&#xff0c;人工智能&#xff08;AI&#xff09;和自然语言处理&#xff08;NLP&#xff09;技术的应用正在不断推动教育内容的智能化。特别是在AI出题系统中&#xff0c;如何…...

使用 Flask 构建流式返回服务

使用 Flask 构建流式返回服务是一个很常见的应用场景&#xff0c;特别是在需要逐步传输大数据或进行长时间操作的场景下&#xff08;比如下载大文件、实时日志等&#xff09;。Flask 中可以通过 Response 对象来实现流式响应。以下是一个简单的例子&#xff0c;展示了如何在 Fl…...

Redis 集群相关知识介绍

Redis 集群详解&#xff1a;从入门到实战 Redis 是一个高性能的开源数据库&#xff0c;支持多种数据结构&#xff0c;广泛应用于缓存、消息队列、实时分析等领域。随着业务规模的增长&#xff0c;单机 Redis 的性能和容量往往无法满足需求&#xff0c;因此 Redis 集群&#xf…...

宏基传奇swift edge偶尔开机BIOS重置

电脑是acer swift edge&#xff0c; SFA16-41&#xff0c;出厂是Win11系统&#xff0c; BIOS版本出厂1.04&#xff0c;更新到了目前最新1.10。 问题是 会偶尔开机ACER图标变小跑到屏幕左上方&#xff0c;下次开机BIOS就会被重置&#xff0c;开机等待很长时间。 因为是偶尔现象的…...

DeepSeek是如何通过“蒸馏”技术打造自己的AI模型

1 引言&#xff1a; 最近&#xff0c;外媒对中国公司——DeepSeek进行了猛烈抨击&#xff0c;指控其采用了所谓的“蒸馏”&#xff08;Distillation&#xff09;技术&#xff0c;涉嫌抄袭甚至作弊。那么&#xff0c;什么是“蒸馏”技术&#xff1f; 在人工智能领域&#xff0c;…...

你如何利用SIMD(如SSE/AVX)优化图像处理的性能?

SIMD优化问题 1. SIMD 在图像处理中的优化方式2. 典型应用场景3. SIMD 的常见优化技巧4. 总结 利用 SIMD&#xff08;Single Instruction, Multiple Data&#xff09; 指令集&#xff08;如 SSE/AVX/AVX2/AVX-512&#xff09;优化图像处理的性能&#xff0c;可以极大地提升计算…...

支付宝 IoT 设备入门宝典(上)设备管理篇

相信不少朋友最近都被支付宝“碰一下”广告刷屏&#xff0c;“不用打开 APP 支付就碰一下”几个字一出简直自带BGM……其实“碰一下”就是支付宝 IoT 设备的一种&#xff0c;趁着热度还在&#xff0c;我会分为设备管理和设备经营上下两篇&#xff0c;简单介绍一下支付宝 IoT&am…...

Go语言 Web框架Gin

Go语言 Web框架Gin 参考 https://docs.fengfengzhidao.com https://www.liwenzhou.com/posts/Go/gin/#c-0-7-2 返回各种值 返回字符串 package mainimport ("net/http""github.com/gin-gonic/gin")func main() {router : gin.Default()router.GET("…...

蓝桥杯-洛谷刷题-day5(C++)(为未完成)

1.P1328 [NOIP2014 提高组] 生活大爆炸版石头剪刀布 i.题目 ii.代码 #include <iostream> #include <string> using namespace std;int N, Na, Nb; //0-"剪刀", 1-"石头", 2-"布", 3-"蜥", 4-"斯"&#xff1…...

【Unity3D优化】使用ASTC压缩格式优化内存

在Unity3D手游开发中&#xff0c;合理选择纹理压缩格式对于优化内存占用、提高渲染效率至关重要。本文将记录近期在项目内进行的图片压缩格式优化过程&#xff0c;重点介绍从ETC2到ASTC 5x5的优化方案及其带来的收益。 1. 现状分析&#xff1a;从ETC2到ASTC 6x6 block 在项目…...

NO.13十六届蓝桥杯备战|条件操作符|三目操作符|逻辑操作符|!||||(C++)

条件操作符 条件操作符介绍 条件操作符也叫三⽬操作符&#xff0c;需要接受三个操作数的&#xff0c;形式如下&#xff1a; exp1 ? exp2 : exp3条件操作符的计算逻辑是&#xff1a;如果 exp1 为真&#xff0c; exp2 计算&#xff0c; exp2 计算的结果是整个表达式的结果&am…...

【uniapp-小程序】实现方法调用的全局tips弹窗

【uniapp-小程序】实现方法调用的全局tips弹窗 开发背景弹窗组件全局调用封装配置项入参全局注入使用 附带&#xff1a;如何在uniapp-H5项目中实现全局自定义弹窗组件定义定义vue插件引入 笑死&#xff0c;只有在想找工作的时候才会想更新博客。 开发背景 本来是个uniapp开发…...

springboot如何将lib和jar分离

遇到一个问题&#xff0c;就是每次maven package或者maven install后target中的jar很大&#xff0c;少的50几MB&#xff0c;大的100多兆 优化前&#xff1a; 优化后&#xff1a; 优化前 优化后压缩率77.2MB4.65MB93% 具体方案&#xff1a; pom.xml中 <build><…...

深入探索C语言中的字符串处理函数:strstr与strtok

在C语言的字符串处理领域&#xff0c; strstr 和 strtok 是两个非常重要的函数&#xff0c;它们各自承担着独特的功能&#xff0c;为开发者处理字符串提供了强大的支持。 一、strstr函数&#xff1a;字符串查找的利器 strstr 函数用于在一个字符串中查找另一个字符串的首次出现…...

Django学习笔记(第一天:Django基本知识简介与启动)

博主毕业已经工作一年多了&#xff0c;最基本的测试工作已经完全掌握。一方面为了解决当前公司没有自动化测试平台的痛点&#xff0c;另一方面为了向更高级的测试架构师转型&#xff0c;于是重温Django的知识&#xff0c;用于后期搭建测试自动化平台。 为什么不选择Java&#x…...

npm版本号标记

在 npm 中,版本号的标记遵循 语义化版本控制(Semantic Versioning, SemVer) 的规则,版本号通常由 主版本号(major)、次版本号(minor) 和 修订版本号(patch) 组成,格式为: <major>.<minor>.<patch>1. 版本号格式 主版本号(major):当你做了不兼…...

无人机雨季应急救灾技术详解

无人机在雨季应急救灾中发挥着至关重要的作用&#xff0c;其凭借机动灵活、反应迅速、高效安全等特点&#xff0c;为救灾工作提供了强有力的技术支撑。以下是对无人机雨季应急救灾技术的详细解析&#xff1a; 一、无人机在雨季应急救灾中的应用场景 1. 灾情侦查与监测 无人机…...

算法与数据结构(多数元素)

题目 思路 方法一&#xff1a;哈希表 因为要求出现次数最多的元素&#xff0c;所以我们可以使用哈希映射存储每个元素及其出现的次数。每次记录出现的次数若比最大次数大&#xff0c;则替换。 方法二&#xff1a;摩尔算法 摩尔的核心算法就是对抗&#xff0c;因为存在次数多…...

详解如何使用Pytest内置Fixture tmp_path 管理临时文件

关注开源优测不迷路 大数据测试过程、策略及挑战 测试框架原理&#xff0c;构建成功的基石 在自动化测试工作之前&#xff0c;你应该知道的10条建议 在自动化测试中&#xff0c;重要的不是工具 临时目录在测试中起着至关重要的作用&#xff0c;它为执行和验证代码提供了一个可控…...

量子计算的五大优势

量子计算的优势有哪些&#xff1f; 量子计算是一个快速发展的领域&#xff0c;有望彻底改变我们处理复杂计算问题的方式。那么&#xff0c;量子计算的优势是什么&#xff1f;与经典计算相比&#xff0c;量子计算又有哪些优势呢&#xff1f;当我们探索量子力学的世界以及量子系…...

行内元素和块级元素

行内元素和块级元素 1.行内元素1.1什么是行内元素1.2行内元素的特点1.3常见的行内元素 2.块级元素2.1什么是块级元素2.2块级元素的特点2.3常见的块级元素 3.行内元素和块级元素的区别 1.行内元素 1.1什么是行内元素 行内元素是指在网页中不会独占一行,而是与其他行内元素在同…...

java面试题-集合篇

Collection 1.Collection有哪些类&#xff1f; Java集合框架中的Collection接口是所有集合类的基础接口&#xff0c;定义了一些基本的集合操作&#xff0c;如添加元素、删除元素、判断是否包含某个元素等。常见的集合类包括List、Set和Queue。 List List接口定义了按照索引…...

二十九、vite项目集成webpack+vue2项目

一、开发 基座应用: 1、安装依赖 npm i @micro-zoe/micro-app@0.8.6 --save 2、在入口处引入(main.ts) import microApp from @micro-zoe/micro-appmicroApp.start()...

小程序之间实现互相跳转的逻辑

1:小程序之间可以实现互相跳转吗 可以实现互相跳转! 2:小程序跳转是否有限制 有限制!限制如下 2.1:需要用户触发跳转 从 2.3.0 版本开始,若用户未点击小程序页面任意位置,则开发者将无法调用此接口自动跳转至其他小程序。 2.2:需要用户确认跳转 从 2.3.0 版本开始…...

算法——数学建模的十大常用算法

数学建模的十大常用算法在数学建模竞赛和实际问题解决中起着至关重要的作用。以下是这些算法的具体信息、应用场景以及部分算法的C语言代码示例&#xff08;由于篇幅限制&#xff0c;这里只给出部分算法的简要代码或思路&#xff0c;实际应用中可能需要根据具体问题进行调整和扩…...

cookie、session、jwt、Oauth2.0、sso 分别有什么用

cookie、session、jwt都是 web 应用中的认证方式&#xff0c;最早只有 cookie&#xff0c;后面觉得所有数据存储在客户端不安全&#xff0c;就出现了 cookie-session&#xff0c;再后面有了 jwt。 cookie工作原理 cookie 数据存储在用户的本地。服务器完全根据 cookie 确定访…...

maven使用默认settings.xml配置时,Idea基于pom.xml更新依赖时报错,有些组件下载时连接超时

1、问题背景&#xff1a;maven使用默认settings.xml配置时&#xff0c;Idea基于pom.xml更新依赖时报错&#xff0c;有些组件下载时连接超时&#xff0c; 通过日志发下&#xff0c;去连接maven.org网站下载依赖&#xff0c;有时候肯定会超时。 2、解决办法&#xff1a;使用国外…...

信息收集-Web应用搭建架构指纹识别WAF判断蜜罐排除开发框架组件应用

知识点&#xff1a; 1、信息收集-Web应用-架构分析&指纹识别 2、信息收集-Web应用-架构分析&WAF&蜜罐 3、信息收集-Web应用-架构分析&框架组件识别 指纹识别 EHole_magic https://github.com/lemonlove7/EHole_magic 指纹识别 Wappalyzer https://github.com…...

蓝桥杯之图

图&#xff1a; 对于图来说&#xff0c;重点在于之后的最短路径算法&#xff0c;这边简单做一下了解即可...

ProxySQL构建PolarDB-X标准版高可用路由服务三节点集群

ProxySQL构建PolarDB-X标准版高可用路由服务三节点集群 一、PolarDB-X标准版主备集群搭建 三台机器上传 polardbx 包&#xff0c;包可以从官网https://openpolardb.com/download获取&#xff0c;这里提供离线rpm。 1、上传 polardbx 安装包 到 /opt目录下 rpm -ivh t-pol…...

【leetcode】双指针:移动零 and 复写零

文章目录 1.移动零2.复写零 1.移动零 class Solution { public:void moveZeroes(vector<int>& nums) {for (int cur 0, dest -1; cur < nums.size(); cur)if (nums[cur] ! 0)swap(nums[dest], nums[cur]);} };class Solution { public:void moveZeroes(vector&l…...

正则化(Regularization)和正则表达式(Regular Expression)区别

文章目录 1. **正则化&#xff08;Regularization&#xff09;**2. **正则表达式&#xff08;Regular Expression&#xff09;**关键区别为什么名字相近&#xff1f; 正则化&#xff08;Regularization&#xff09;和正则表达式&#xff08;Regular Expression&#xff09;不是…...

【C++】C++-教师信息管理系统(含源码+数据文件)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;专__注&#x1f448;&#xff1a;专注主流机器人、人工智能等相关领域的开发、测试技术。 【C】C教师信息管理系统&#xff08;含源码&#x…...

MySql从入门到精通

第一部分 基础篇 1.概述 1.1 启动与停止MySql 启动 net start mysql80 停止 net stop mysql80 注意&#xff1a; mysql开机默认启动 1.2 客户端连接 方法一&#xff1a;使用MySQL提供的命令行客户端方法二&#xff1a;系统自带的命令行工具执行指令 mysql [-h 127.0.0.1] …...

27、深度学习-自学之路-NLP自然语言处理-做一个简单的项目识别一组电影评论,来判断电影评论是积极的,还是消极的。

一、如果我们要做这个项目&#xff0c;第一步我们要做的就是需要有对应的训练数据集。 这里提供两个数据集&#xff0c;一个是原始评论数据集《reviews.txt》&#xff0c;以及对应的评论是消极还是积极的数据集《labels.txt》&#xff0c;下面的程序就是找到这两个数据集&…...

微信小程序 - 组件和样式

组件和样式介绍 在开 Web 网站的时候&#xff1a; 页面的结构由 HTML 进行编写&#xff0c;例如&#xff1a;经常会用到 div、p、 span、img、a 等标签 页面的样式由 CSS 进行编写&#xff0c;例如&#xff1a;经常会采用 .class 、#id 、element 等选择器 但在小程序中不能…...

滤波总结 波形处理原理 如何对一个规律的波形进行滤波 显现出真正的波形 如何设计滤波

需要用到的软件:waveserialport vofa++ 1.波形想用MCU进行采集首先你要考虑到你的采集频率因为如果你的对象波形即你要采集的波形,他过于快速的话有一些MCU它是不能的比如说有一些它的主频才36兆72兆呢你如果遇到一个特别快的波形毫秒级别那他就检测不了 2.…...

【代码随想录】刷题记录(115)-岛屿数量(广搜)

题目描述&#xff1a; 题目描述 给定一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的矩阵&#xff0c;你需要计算岛屿的数量。岛屿由水平方向或垂直方向上相邻的陆地连接而成&#xff0c;并且四周都是水域。你可以假设矩阵外均被水包围。 输入描…...

Git指南-从入门到精通

代码提交和同步命令 流程图如下&#xff1a; 第零步: 工作区与仓库保持一致第一步: 文件增删改&#xff0c;变为已修改状态第二步: git add &#xff0c;变为已暂存状态 bash $ git status $ git add --all # 当前项目下的所有更改 $ git add . # 当前目录下的所有更改 $ g…...

uvm错误记录4

如下所示&#xff0c;奇怪的是penable莫名其妙的出X。可问题&#xff0c;我发送激励了。 仔细定位发现&#xff0c;39行用的是vif中的penable, 问题是都是赋值&#xff0c;却出现同时赋值多次&#xff0c;这是因为nonblocking和blocking同时触发导致的&#xff0c;因此&#xf…...

用大模型学大模型03-数学基础 概率论

https://metaso.cn/s/Jp0OU9w 提示词:我正在学习深度学习相关的数学知识&#xff0c;学到了概率论与统计学部分&#xff0c;给我一个入门级的完整教程和实例。 好的&#xff0c;我现在需要回答用户关于深度学习所需的概率论与统计学入门教程和实例的问题。用户希望得到一个完整…...

数字电路-基础逻辑门实验

基础逻辑门是数字电路设计的核心元件&#xff0c;它们执行的是基本的逻辑运算。通过这些基本运算&#xff0c;可以构建出更为复杂的逻辑功能。常见的基础逻辑门包括与门&#xff08;AND&#xff09;、或门&#xff08;OR&#xff09;、非门&#xff08;NOT&#xff09;、异或门…...

基于springboot轨道交通安全评估系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 时代在飞速进步&#xff0c;每个行业都在努力发展现在先进技术&#xff0c;通过这些先进的技术来提高自己的水平和优势&#xff0c;轨道交通安全评估管理当然不能排除在外。轨道交通安全评估系统是在实际应用和软件工程的开发原理之上&#xff0c;运用Java语言以及Spring…...