SpringMVC的三大功能

目录

一、初识SpringMVC

1.1 MVC的定义

1.2 MVC和SpringMVC的关系是什么? 

1.3 SpringMVC的重要性

二、Spring MVC的三大功能

2.1 连接功能

2.1.1 @RequestMapping 注解介绍

2.1.2 @GetMapping 和 PostMapping

2.2 获取参数功能

2.2.1 传递普通参数

2.2.2 传递对象

2.2.3 表单参数传递

2.2.4 后端参数重命名(后端参数映射)——@RequestParam

2.2.5 使用@RequestBody来接受JSON对象

2.2.6 使用@PathVariable获取基础URL中的参数(不是从URL的参数部分获取参数)

2.2.7@PathVariable和@RequestParam的区别

2.2.8 上传文件@RequestPart

2.2.9 获取Cookie/Session/header

2.3 返回功能

2.3.1 请求转发VS请求重定向


一、初识SpringMVC

Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从一开始就包含在 Spring 框架中。它的正式名称“Spring Web MVC”来自其源模块的名称(Spring-WebMVC),但它通常被称为“Spring MVC“。

从上述定义我们可以得出两个关键信息:

  • Spring MVC 是⼀个 Web 框架。
  • Spring MVC 是基于 Servlet API 构建的。

然⽽要真正的理解什么是 Spring MVC?我们⾸先要搞清楚什么是 MVC?

1.1 MVC的定义

MVC(Model-View-Controller)是一种常见的设计模式,用于构建用户界面和应用程序逻辑的分离。MVC模式将应用程序分为三个部分:

  • 模型(Model):负责维护应用程序的状态和数据。
  • 视图(View):负责呈现模型数据,通常是用户界面。
  • 控制器(Controller):接收和处理用户输入,并将请求委派给模型或视图进行处理。

在MVC框架中,应用程序逻辑由控制器处理,控制器从视图接收用户输入,然后使用模型进行状态更改和数据更新,最后将结果传递回视图进行呈现。

1.2 MVC和SpringMVC的关系是什么? 

MVC 是⼀种思想,⽽ Spring MVC 是对 MVC 思想的具体实现。
总结来说,Spring MVC 是⼀个实现了 MVC 模式,并继承了 Servlet API 的 Web 框架。既然是 Web框架,那么当⽤户在浏览器中输⼊了 url 之后,我们的 Spring MVC 项⽬就可以感知到⽤户的请求。

1.3 SpringMVC的重要性

现在绝⼤部分的 Java 项⽬都是基于 Spring(或 Spring Boot)的,⽽ Spring 的核⼼就是 Spring
MVC。也就是说 Spring MVC 是 Spring 框架的核⼼模块,⽽ Spring Boot 是 Spring 的脚⼿架,因此我们可以推断出,现在市⾯上绝⼤部分的 Java 项⽬约等于 Spring MVC 项⽬,这是我们要学 SpringMVC 的原因。

在创建SpringBoot项目时,我们所选择的Spring Web 框架其实就是Spring MVC框架,如下图所示:

这里给大家简单的讲讲RESTful,其实实际的应用场景并不是基于这一套,很多都是用Get表示获取资源,其他都是使用post方法。

RESTful介绍

RESTful是一种设计风格或者说是一种架构风格,用于构建Web服务。它是一种轻量级的风格,可以通过HTTP协议进行通信,适合分布式超媒体系统。

RESTful的核心思想是将所有的Web资源抽象成一些列的URI(Uniform Resource Identifier),并通过HTTP方法(GET、POST、PUT、DELETE等)对资源进行操作,实现客户端和服务端之间的无状态通信。RESTful强调的是资源的表现层状态转换(Representational State Transfer,简称REST),它需要满足一定的约束条件,包括:客户端-服务器模型、无状态、缓存、统一接口、分层系统。

RESTful架构的好处包括:

  • 轻量级,传输数据格式简单,易于理解。
  • 易于扩展,由于符合HTTP协议标准,因此支持多种语言开发和多种平台交互。
  • 易于缓存,由于每个请求都包含足够的信息来理解请求本身,因此可以缓存结果以提高性能。
  • 代码可读性好,因为每个请求都清晰地指定了要执行的操作,因此代码易于理解和调试。

二、Spring MVC的三大功能

在实际应用中,RESTful通常用于Web API设计,可以通过HTTP请求方式进行数据交互。

下面我们主要介绍SpringMV的三大功能,掌握了以下 3 个功能就相当于掌握了 Spring MVC

  • 连接的功能:将⽤户(浏览器)和 Java 程序连接起来,也就是访问⼀个地址能够调⽤到我们的Spring 程序。
  • 获取参数的功能:⽤户访问的时候会带⼀些参数,在程序中要想办法获取到参数。
  • 输出数据的功能:执⾏了业务逻辑之后,要把程序执⾏的结果返回给⽤户。

2.1 连接功能

在SpringMVC项目中,创建⼀个 UserController 类,实现⽤户到 Spring 程序的互联互通,具体实现代码如下:

package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

//@Controller
//@ResponseBody
// 使用以上两个注解或者直接使用RestController去返回一个数据,
@RestController
public class UserController {
    @RequestMapping(value = "/sayHi")//可以是一级路由也可以是n级路由
    public String sayHi() {
        return "say Hi";
    }

}

分析:注意这里我们并没有在类上加入@RequestMapping,而是在方法上加入这个注解,这种做法是可以的,因为在SpringMVC中方法是最终访问的最小的单位和单元了。

在地址栏输入:localhost:8080/sayHi   后出现如下界面,代表访问成功。

当然,如果把@RestController给注解了就会出现以下界面:

分析:这是因为没加注解后,是无法让这个方法随着Spring容器启动而启动的。只有加了这个注解才能让这个url地址,随着Spring的启动而注册到Spring当中。

2.1.1 @RequestMapping 注解介绍

@RequestMapping 是 Spring Web 应⽤程序中最常被⽤到的注解之⼀,它是⽤来注册接⼝的路
由映射的。

路由映射

路由映射:所谓的路由映射指的是,当⽤户访问⼀个 url 时,将⽤户的请求对应到程序中某个类
的某个⽅法的过程就叫路由映射

c 基础使用:

可以像上述代码一样,直接修饰方法。

也可以修饰类,当修饰类和方法的时候,访问的地址是类+方法,如下所示:

 @RequestMapping支持get、post等请求吗?

其实,了解HTTP协议的同学都知道,当我们在浏览器输入url的时候,其实就是通过浏览器向服务器发送Get请求,我们可以通过浏览器的开发者模式查看:

我们这里使用PostMan进行模拟POST请求:发现是可以实现的,PUT、DELETE等就不再一一模拟了,均可实现。

在很多时候,可能需要指定GET/POST方法

那么应该如何指定呢?我们来看看 @RequestMapping注解的源码:

分析:

具体来说,该注解定义了如下属性:

  • name :名称,默认为空字符串。
  • value:请求的路径,可以是一个字符串或字符串数组。这是@AliasFor(“path”)的别名属性。
  • path:请求的路径,可以是一个字符串或字符串数组。这是@AliasFor(“value”) 的别名属性。
  • method:请求的 HTTP 方法,可以是一个 RequestMethod 类型的枚举值或枚举值数组。
  • params:请求中必须包含的参数,可以是一个字符串或字符串数组。
  • headers:请求中必须包含的头信息,可以是一个字符串或字符串数组。
  • consumes:接受请求的内容类型,可以是一个字符串或字符串数组。
  • produces:响应的内容类型,可以是一个字符串或字符串数组。

需要注意的是:注解中的属性都有默认值,使用时可以根据需要设置。其中,value和path二者是等价的,如果同时设置了这两个属性,则它们的值必须相同。如果没有设置任何属性,则默认将映射到处理程序方法的路径上。

进入RequestMetho[] 中可以看到有以下方法:

但是很多人可能会有疑问,可是刚刚源码上面不是默认为空吗?那为什么之前在没有设置方法的时候,我们可以通过post、get等方式来进行访问呢?

首先我们需要明确,在Java中,使用关键字default来表示默认值。在这段源码中RequestMethod[]()default{} 确实表示的是在没有设置path属性值时,该属性的默认值为空数组。

换句话说,如果使用@RequestMapping注解时没有指定path属性的值,则该属性的默认值为一个空数组。

但是@RequestMapping注解在默认情况下会被视为一个模糊匹配的方式来处理请求,也就是说,只要请求的 URL 能够匹配到该注解的value或path属性指定的路径,那么该注解就会被触发,不管这个请求是使用什么 HTTP 请求方法发送的。

 分析之后,可以得知需要通过method方式指定访问的方式:

当指定之后,通过其他方式就不能访问这个url地址了,可以通过PostMan来发送请求进一步进行验证: 

2.1.2 @GetMapping 和 PostMapping

这两种方法就是用来专门指定通过get/post方式来访问url的,简化了上述通过参数指定的方式:

这里由于GetMapping和PostMapping用法差不多,只演示一个的用法:

 由于只支持post请求,通过其他方式访问会抛出405:

2.2 获取参数功能

2.2.1 传递普通参数

在 Spring MVC 中可以直接⽤⽅法中的参数来实现传参,⽐如以下代码:

 ​​​​​​展示效果:

如果是传递多个参数的时候:那么参数的顺序其实是无需固定的,只需要键值对匹配即可

 来看一组小练习:观察以下三个方法返回的内容是什么?

当我们分别访问这三个方法时候,不传入任何值时:

可以发现String,Integer这两个类,会有默认值null,而int这样的基本类型在没有传递给它值的时候会直接出现报错的行为。

因此,平常开发的过程中,建议传递参数的时候使用类,而不是基本类型,这样做的目的是可以让前端的开发人员明白快速的哪里出错,代码可以修改为如下所示:

我们知道,SpringMVC是基于Servlet API而搭建的,因此也可以使用HttpServletRequest/HttpServletResponse来完成接受参数/做出响应的任务.

不过这种方式比较少见,在SpringMVC中,通常是使用HttpServletRequest的getRemoteAddr()方法获取客户端的IP地址(例如用于将某些用户加入黑名单中以此来拒绝其访问):

@RequestMapping("/getIpAddress")
public String getIpAddress(HttpServletRequest request) {
    String ipAddress = request.getRemoteAddr();
    return ipAddress;
}

需要注意的是,使用该方法获取IP地址可能会有一定的误差,例如当客户端使用了代理服务器时,获取到的IP地址可能是代理服务器的IP地址而不是真实客户端的IP地址。如果需要更准确的IP地址,可以考虑通过X-Forwarded-For等HTTP头部信息来获取。

当然,也可以使用HttpServletResponse的sendRedirect方法来进行重定向操作:

    @RequestMapping("/Hi4")//可以是一级路由也可以是n级路由
    public String sayHi4(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.sendRedirect("https://www.baidu.com/");//当然这里跳转了,就不会触发后续的return了。
        return "say Hi "+request.getParameter("name");
    }

 聊到重定向,那么我们就顺便聊聊请求转发和请求重定向的区别吧

1. 概念不同;

重定向是指:当服务器接收到客户端发送的请求时,服务器返回一个重定向的响应,告诉客户端请求的资源已经被移动到另一个位置,客户端需要重新发送请求到新的地址。重定向是在浏览器端完成的,服务器只需要在响应中返回新的URL地址即可(URL改变了)。

请求转发是指:服务器接收到客户端发送的请求后,将请求转发给另一个资源进行处理,然后将处理结果返回给客户端。重转发是在服务器端完成的,客户端并不知道所请求的资源被转发了。因此整个过程请求转发的URL是不会变的。

2. 实现方式不同:

重定向通过修改HTTP响应头中的Location字段,让客户端重新发送请求到新的URL地址。因为是两次请求,所以在浏览器中会出现两个请求记录,因此重定向会比重转发慢一些。

请求转发则是在服务器端完成的,不需要修改HTTP响应头中的内容,只需要将请求转发到目标资源进行处理即可。因为是在服务器端完成的,所以重转发的效率要比重定向高。

3.数据共享不同:

重定向是两次请求,所以请求的上下文信息是不能够共享的,每次请求都是独立的,因此不能直接共享请求中的参数和属性。

重转发是在服务器端完成的,所以可以共享请求中的参数和属性,因为它们都在服务器内部处理的。

综上所述,重定向和重转发虽然都是页面跳转的方式,但是它们的实现方式和特点有很大的不同,应该根据实际情况选择使用哪种方式。

2.2.2 传递对象

待传递对象的代码:

 获取对象的代码实现:

演示如下:

我们发现SpringMVC框架是很智能的,可以根据实际返回的类型,灵活的转换:比如上面代码所展示的,本来是放回Object类型,实际返回的是JSON格式。

还有下面所展示的:返回的本来是Object类,但是根据实际情况,却返回了一个HTML界面:

2.2.3 表单参数传递

表单参数的获取可以将多个数据封装成一个对象来获取,或者可以使用像获取多个参数一样的方法都可,由于上面已经演示过了,这里就不再详细演示。

2.2.4 后端参数重命名(后端参数映射)——@RequestParam

为什么需要给后端参数重命名呢?其实同学们可能已经知晓一二了,因为在后端捕获数据的时候,一般情况下,如果想获取成功,那么是需要保证参数的一致性的,如下所示: 

但是在实际开发的过程中:可能前后端约定的参数不一定完全相同,这时为了保证能够正常的进行前后端交互,那么就需要将后端参数进行重命名:

对参数进行重命名免不了使用@RequestParam,我们先来看看其源码:

@RequestParam有三个主要的属性:

  1. value:请求参数的名称。
  2. required:指定该参数是否必须存在。默认值为true。
  3. defaultValue:指定请求参数的默认值。

接下来使用@RequestParam来演示下如何实现参数的重命名:

可能有人注意到@RequestParam中的属性required()是默认为true的,也就是说,如果没有传递正确的参数,或者没有传递参数,就会抛出异常:

总结:其实就是要让username获取到参数即可,如果这个参数是非必传的参数,那么可以将require设置为false,如下图所示:

2.2.5 使用@RequestBody来接受JSON对象

如果直接使用对象来接受JSON对象,那么是不被允许的:

    @RequestMapping("/reg3")
    public Object reg3(Userinfo userinfo) {
        System.out.println(userinfo);
        return userinfo;
    }

使用PostMan模拟发送POST请求进行验证: 

使用@RequestBody,代码如下:

    @RequestMapping("/reg3")
    public Object reg3(@RequestBody Userinfo userinfo) {
        System.out.println(userinfo);
        return userinfo;
    }

需要注意的是,@RequestBody注解只能用于处理请求体中的数据,如果需要处理URL参数,那么需要使用@RequestParam注解,同时,由于@RequestBody需要反序列化请求体,因此请求体中的数据格式需要符合指定类型的Java对象的格式要求,否则反序列化会失效。

什么是反序列化?

序列化是将对象转换为可传输或可存储的格式的过程。那么反序列化就是将序列化后的数据恢复成原始数据的过程,通常在网络传输、数据存储等场景中使用。在Java中,反序列化的过程是通过将序列化后的字节流转换成Java对象来实现的。反序列化的目的是为了将数据从一种形式转换成另一种形式,方便数据在不同系统或场景中的传输、存储和处理。常见的反序列化库包括Java原生的序列化机制、Jackson、Gson等。

2.2.6 使用@PathVariable获取基础URL中的参数(不是从URL的参数部分获取参数)

@PathVariable获取基础URL中的参数:

    /**
     * 获取URL参数(?之前的参数)
     * @param name
     * @param password
     * @return
     */
    @RequestMapping("/reg4/{name}/{password}")
    public Object reg4(@PathVariable String name,@PathVariable String password) {
        return "say Hi "+name+" 密码是: "+password;
    }

实际效果:

 另外需要注意以下几点:

@PathVariable源码:

观察@PathVariable源代码发现,其默认值为true,那就是说,跟@RequestParam一样,如果参数获取失败,就会报错:

虽然我们可以将required设置为false,让其不报错,但是这样还是获取不到参数,显示为null。

在设置@PathVariable中设置value属性后:就可以正常获取参数了。

2.2.7@PathVariable和@RequestParam的区别

回顾:我们知道:使用@PathVariable和@RequestParam都可以获取请求的参数,但是它们的实现方式和使用场景有所不同。

@RequestParam主要用于获取请求参数(?之后的参数),它将请求参数映射到对应的控制器方法参数上,可以获取 GET 和 POST 请求中的请求参数。使用@RequestParam获取参数时,请求参数必须要传递,如果不传递则会报错。

而@PathVariable则是用于获取 URL 中的参数(?之前的参数),它可以将 URL 中的一部分作为参数传递到控制器方法中。相对于@RequestParam,它的优势在于可以实现更为友好的 URL,并且可以增强网站的 SEO,让搜索引擎更容易地识别和解析网站的 URL。

因此,如果需要实现更为友好的 URL,并且增强网站的 SEO,建议使用@PathVariable获取参数。

什么是SEO?

SEO(Search Engine Optimization)是指搜索引擎优化,是一种通过了解搜索引擎的运作规则,对网站进行内外部的优化,以提高网站在自然搜索结果中的排名,从而提高网站在搜索引擎中的曝光度、流量和转化率的过程。

2.2.8 上传文件@RequestPart

在介绍@RequestPart之前,我们先来看看几个预备知识:

MultipartFile是什么

MultipartFile 是 Spring框架提供的一个接口,用于表示接收到的上传文件。它是 Spring 对 Servlet 中的 Part 接口进行封装的结果,提供了一些方法用于获取文件名、文件类型、文件字节数组等信息,同时也支持流式读取文件内容。

在 Spring Web 应用程序中,通常会在控制器方法的参数列表中使用MultiFile参数来接收上传的文件。这样做的好处是可以轻松地对上传文件进行处理,例如保存到本地文件系统或者上传到云存储服务等。

MultipartFile的transferto方法的作用是什么?

MultipartFile的transferto方法是用于将MultipartFile的内容写入到磁盘文件中的方法,它的语法如下:

    void transferTo(File dest) throws IOException, IllegalStateException;

该方法将MultipartFile的内容写入指定的文件dest中。如果目标文件已经存在,则会覆盖原有的文件。当MultipartFile的内容已经被写入到文件中时,它就不再可用了。

需要注意的是,transferTo 方法需要指定一个File对象作为参数,该File对象表示目标文件的路径和文件名。同时,调用该方法的前提是目标文件所在的目录必须存在,并且具有写入权限。如果目录不存在,则会抛出IOException异常。

实现代码如下:

    @RequestMapping("/upload")
    public Object upload(@RequestPart("img") MultipartFile file) {
        File saveFile = new File("C:\\Users\\86136\\Desktop\\img.png");
        try {
            file.transferTo(saveFile);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

使用PostMan发送请求:

 上传成功之后,便可以在目标路径中找到:

 

在上传文件的过程中,可能会碰到文件太大而上传失败的情况,这时候可以在配置文件中修改配置:

以properties为例:

限制单个文件最大大小,单位为字节,默认1MB

spring.servlet.multipart.max-file-size=2MB 

 限制整个请求的最大大小,包括所有文件和表单项的大小,单位为字节,默认为10MB

 spring.servlet.multipart.max-request-size=2MB

 使用UUID进行改进

但是这个方法是有缺陷的,因为使用 transferto 方法将 MultipartFile 的内容写入指定的文件dest中。如果目标文件已经存在,则会覆盖原有的文件。当MultipartFile的内容已经被写入到文件中时,它就不再可用了。

    @RequestMapping("/upload")
    public Object upload(@RequestPart("img") MultipartFile file) {
        String fileName = UUID.randomUUID() + //获取文件名
                file.getOriginalFilename().substring(  //获取文件后缀
                        file.getOriginalFilename().lastIndexOf("."));

        File saveFile = new File("C:\\Users\\86136\\Desktop\\"+fileName);
        try {
            file.transferTo(saveFile);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

2.2.9 获取Cookie/Session/header

获取Cookie

第一种获取Cookie的方式是使用HttpServletRequest的getCookie的方式获取:

    @RequestMapping("/getCookies")
    public String getCookies(HttpServletResponse response,HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        return cookies.toString();//无实际意义,只是为了不报错
    }

 第二种则是使用@CookieValue的方式来获取单个Cookie:

    @RequestMapping("getCookie")
    public Object getCookie(@CookieValue String a) {
        return a;
    }

分析该代码的含义:获取一个名为a的Cookie,注入到String类型的a中,但是由于源码中显示required默认为true,获取失败时候会报错:

 当然我们也可以把required的值设置为false,这样就不会报错,但是页面仍然获取不到:

 当然,如果手动添加了名称为 a 的Cookie,那么就可以获取成功了:

 获取header

 使用HttpServletRequest的getHeader方式获取header:

    @RequestMapping("/getHeader")
    public Object getHeader(HttpServletResponse response,HttpServletRequest request) {
        String userAgent = request.getHeader("User-Agent");
        return userAgent;
    }

实现效果如图:

 使用@RequestHeader方式获取header:

 其源码的required仍然为true,这里就不再做演示。

 实现代码如下:

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

 实现效果:

 当然,也可以进一步验证:发现完全匹配。

 获取Session

 在获取Session之前,我们需要手动的先存储一个Session进去。 

注意这里我们可以不用传参,因为默认是true。(千万不要传false,否则将无法完成存储session的任务):

 获取Session:

    /**
     * 获取Session
     * @param name
     * @return
     */
    @RequestMapping("/getSession")
    public Object getSession(@SessionAttribute(SESSION_KEY) String name) {
        return name;
    }

 获取成功:

2.3 返回功能

通过上⾯的学习我们知道,默认请求下⽆论是 Spring MVC 或者是 Spring Boot 返回的是视图
(xxx.html),⽽现在都是前后端分离的,后端只需要返给给前端数据即可,这个时候我们就需要使⽤@ResponseBody(或者使用@RestController代替@ResponseBody和@Controller)。

现在static的目录下创建前端页面 index.html:

发现无法访问该页面(这种方式默认是请求转发forward,如果当前路径找不到那么就报错): 

而我们将index.html文件前面加个 '/'  ,发现就可以正常访问了,这是为什么呢?

首先我们需要明白根路径的含义:域名(IP)+端口号。

因为如果按照正常逻辑,这个路由的查询地址的意思是:在localhost:8080/test/目录中查询这个静态网页:

也就是实际上URL是:localhost:8080/test/index。

可以通过fiddler抓包查看:

如果加上/的话表示:在根目录中查找,这是因为这个return是默认重定向的,如果带了根路径,那么就会直接去查询这个绝对路径下有没有这个html界面。

 

2.3.1 请求转发VS请求重定向

return 不但可以返回⼀个视图,还可以实现跳转,跳转的⽅式有两种:

  • forward: 是请求转发;
  • redirect:请求重定向。

请求转发和重定向的使⽤对⽐:

请求转发: 

 请求重定向(当然,也可以像上面 2.2.1 一样,使用sendRedirect来重定向):

我们会发现:

请求转发是服务器端代为请求,再将结果返回给客户端的,所以整个请求的过程中 URL 地址是不变的;而请求重定向是服务器端告诉客户端,“你去另一个地访问去”,所以浏览器会重新再发送一次请求,因此客户端最终显示的 URL 也为最终跳转的地址,而非刚开始请求的地址,所以 URL 地址发生了改变。

猜你喜欢

转载自blog.csdn.net/qq_63218110/article/details/130659168