JavaEE|SpringMVC

Spring Web MVC

Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从一开始就包含在 Spring 框,Spring

Web MVC 是一个 Web 框架.下面咱们简称之为: Spring MVC

MVC 定义

MVC 是 Model View Controller 的缩写,它是软件工程中的一种软件架构设计模式,它把软件系统分 为模型、视图和控制器三个基本部分.

|-----------------|-------------------------------------------------------------------|
| View(视图) | 指在应用程序中专门用来与浏览器进行交互,展示数据的资源. |
| Model(模型) | 是应用程序的主体部分,用来处理程序中数据逻辑的部分. |
| Controller(控制器) | 可以理解为一个分发器,用来决定对于视图发来的请求,需要用哪一个模型 来处理,以及处理完后需要跳回到哪一个视图。即用来连接视图和模型 |

了解Spring MVC

Spring MVC 是 Spring 框架中的 Web 模块,是一个基于 MVC 设计模式的轻量级 Web 框架。

简化理解,它主要做三件事:

  1. 核心思想(MVC):把代码拆分成三层
  • Controller(控制器):接收请求,协调业务。
  • Model(模型):处理业务逻辑,封装数据。
  • View(视图):展示数据(如网页、JSON)。
  1. 核心机制:通过一个叫 DispatcherServlet(前端控制器) 的核心组件统一接收所有请求,然后自动分发(路由)给对应的 Controller 去处理。
  2. 核心作用:解耦(让代码各司其职)、简化开发(通过注解即可轻松写接口),主要用于构建 Web 应用和 RESTful API。

关于Spring Boot项目的补充

我们创建的Spring Boot项目时,勾选的Spring Web选项就是运用到了,Spring Boot是实现Spring

MVC的其中一种方式。

Spring Boot 可以添加很多依赖,借助这些依赖实现不同的功能。Spring Boot 通过添加Spring

Web MVC框架来实现web功能.

学习Spring MVC

学习Spring MVC就是学习如何通过浏览器与用户程序进行交互,可以通过以下三个方面来学习:

  1. 建立连接:将用户(浏览器)和Java程序连接起来,也就是访问一个地址能够调用到我们的Spring程序
  2. 请求:用户请求的时候会带一些参数,在程序中要想办法获取到这些参数,所以请求这块主要是获取参数的功能
  3. 响应:执行业务逻辑之后要把程序执行的结果返回给用户,也就是响应

项目准备

关于项目的创建,上一篇已经讲解如何进行项目创建,不清楚的可以去看下上一篇blog

建立连接

在 Spring MVC 中使用 @RequestMapping 来实现 URL 路由映射

创建一个UserController类,实现用户通过浏览器和程序的交互,具体实现代码如下:

java 复制代码
package com.chen.spring.demo;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/user")
@RestController
public class UserController {
    @RequestMapping("/m1")
    public String m1(){
        return "hello world";
    }
}

接下来访问: http://127.0.0.1:8080/m1, 就可以看到程序返回的数据了

@RequestMapping

@RequestMapping 注解介绍

@RequestMapping是Spring Web MVC应用程序中最常被用到的注解之一,它是用来注册接口的 路由映射的

路由映射: 当用户访问一个 URL 时, 将用户的请求对应到程序中某个类的某个方法的过程就叫路由映射.

@RequestMapping 使用

@RequestMapping 既可修饰类也可以修饰方法 ,当修饰类和方法时,访问的地址是类路径 + 方 法路径.

|-----------------------|------------------|
| @RequestMapping标识一个类 | 设置映射请求的请求路径的初始信息 |
| @RequestMapping标识一个方法 | 设置映射请求请求路径的具体信息 |

注意: @RequestMapping 的URL 路径最前面加不加 / (斜杠)都可以, Spring程序启动时, 会进行判断,如果前面没有加 / , Spring会拼接上一个 /

请求

访问不同的路径,就是发送不同的请求. 在发送请求时可能会带一些参数, 所以学习Spring的请求, 主要是学习如何传递参数到后端以及后端如何接收

传递参数, 咱们主要是使用浏览器和Apifox来模拟

传递单个参数

接收单个参数, 在 Spring MVC 中直接用方法中的参数就可以,比如以下代码:

java 复制代码
@RequestMapping("/request")
@RestController
public class RequestController {
    @RequestMapping("/r1")
    public String r1(String keyword) {
        return "接收参数" + keyword;
    }
}

状态码显示200,表示返回请求成功

注意事项

使用基本类型来接收参数时, 参数必须传(除boolean类型), 否则会报500错误

类型不匹配时, 会报400错误

对于包装类型,如果不传对应参数,Spring 接收到的数据则为null,所以企业开发中,对于参数可能为空的数据,建议使用包装类型

传递多个参数

如何接收多个参数呢?

和接收单个参数一样, 直接使用方法的参数接收即可. 使用多个形参.

java 复制代码
 @RequestMapping("/r2")
    public String r2(String userName, String password){
        return "接收参数:userName"+userName+", password"+password;
    }

当有多个参数时,前后端进行参数匹配时,是以参数的名称进行匹配的,因此参数的位置是不影响

后端获取参数的结果.

传递对象

如果参数比较多时,方法声明就需要有很多形参. 并且后续每次新增一个参数, 也需要修改方法声

明. 我们不妨把这些参数封装为一个对象.

java 复制代码
public class UserInfo {
    private String name;
    private int gender;
    private int age;
    public UserInfo(){

    }
    public UserInfo(String name,int gender,int age){
        this.name=name;
        this.gender=gender;
        this.age=age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getGender() {
        return gender;
    }

    public void setGender(int gender) {
        this.gender = gender;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "name='" + name + '\'' +
                ", gender=" + gender +
                ", age=" + age +
                '}';
    }
}
java 复制代码
 @RequestMapping("r5")
    public String r5(UserInfo userInfo){
        return "接收参数:userInfo"+userInfo.toString();
    }

Spring 会根据参数名称自动绑定到对象的各个属性上, 如果某个属性未传递, 则赋值为null(基本类

型则 赋值为默认初识值, 比如int类型的属性, 会被赋值为0)

后端参数重命名

后端参数重命名也叫做后端参数映射。某些特殊的情况下前端传递的参数和我们后端接收的可以不

一致,比如前端传递了一个A给后端,而后端是使用B字段来接收的,这样就会出现参数接收不到

的情况,如果出现这种情况,我们就可以使用 @RequestParam 来重命名前后端的参数值.

java 复制代码
    @RequestMapping("/r6")
    public String r6(@RequestParam(value = "q") String keyword){
        return "接收参数:keyword ="+keyword;
    }

此时, 如果浏览器使用keyword进行参数传递呢?

显然访问结果时null,表示无法找到

可以得出结论

  1. 使用 @RequestParam 进行参数重命名时, 请求参数只能和 @RequestParam 声明的名称一 致, 才能进行参数绑定和赋值.
  2. 使用 @RequestParam 进行参数重命名时, 参数就变成了必传参数.
非必传参数设置

如果存在一个非必传参数,我们可以通过设置 @RequestParam 中的 required=false 来避免不传

递时报错

java 复制代码
@RequestMapping("/r6")
    public String r6(@RequestParam(value = "q",required = false) String keyword){
        return "接收参数:keyword ="+keyword;
    }

可以看到添加required=false之后,time前面也加了key,变成了 value = "time" 注解属性赋值时, 没

有指明key的话, 默认为value属性. 如果需要有多个属性进行赋值时, 需要写上key

传递数组

Spring MVC 可以自动绑定数组参数的赋值

java 复制代码
  @RequestMapping("/r7")
    public String r7(String[] arr){
        return "接收参数:arr="+ Arrays.toString(arr);
    }

传递集合

集合参数:和数组类似, 同一个请求参数名有为多个, 且需要使用 @RequestParam 绑定参数关系

java 复制代码
@RequestMapping("/r8")
    public String r8(@RequestParam List<Integer> list){
        return "接收参数:list"+list;
    }

传递JSON数据

JSON是一种数据格式, 有自己的格式和语法, 使用文本表示一个对象或数组的信息, JSON本质是字

符串. 主要负责在不同的语言中数据传递和交换

JSON与Javascript的关系

二者没有关系, 只是语法相似, js开发者能更快的上手而已, 但是他的语法本身比较简单, 所以也很好

JSON语法

JSON 是一个字符串,其格式非常类似于 JavaScript 对象字面量的格式

  1. 数据在键值对(Key/Value) 中
  2. 数据由逗号 , 分隔
  3. 对象用 {} 表示
  4. 数组用 \[\] 表示
  5. 值可以为对象, 也可以为数组, 数组中可以包含多个对象
JSON的两种结构

对象: 大括号 {} 保存的对象是一个无序的 键值对 集合. 一个对象以左括号 { 开始, 右括号 } 结束。每个"键"后跟一个冒号 : ,键值对使用逗号 , 分隔

数组: 中括号 \[\] 保存的数组是值(value)的有序集合. 一个数组以左中括号 开始, 右中括 号 结束,值之间使用逗号 , 分隔。

JSON字符串和Java对象互转

Spring MVC框架也集成了JSON的转换工具用来来完成JSON字符串和Java对象的互转

java 复制代码
public class Person {
    private String Name;
    private int Id;
    private String Password;

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public int getId() {
        return Id;
    }

    public void setId(int Id) {
        this.Id = Id;
    }

    public String getPassword() {
        return Password;
    }

    public void setPassword(String password) {
        Password = password;
    }
}
java 复制代码
public class Json {
    private static ObjectMapper objectMapper = new ObjectMapper();
    public static void main(String[] args) throws JsonProcessingException {
        Person person = new Person();
        person.setId(5);
        person.setName("zhangsan");
        person.setPassword("123456");
        //对象转为JSON字符串
        String jsonStr = objectMapper.writeValueAsString(person);
        System.out.println("JSON字符串为:"+jsonStr);
        //JSON字符串转为对象
        Person p = objectMapper.readValue(jsonStr,Person.class);
        System.out.println("转换的对象 id:"+p.getId()+",name:"+p.getName()+",password:"+p.getPassword());
    }
}

使用ObjectMapper 对象提供的两个方法, 可以完成对象和JSON字符串的互转

|--------------------|--------------|
| writeValueAsString | 把对象转为JSON字符串 |
| readValue | 把字符串转为对象 |

JSON优点
  1. 简单易用:语法简单,读写快
  2. 跨平台:多语言通用,互通好
  3. 轻量级:体积小,传输快
  4. 易扩展:结构灵活,支持嵌套
  5. 安全性:纯文本,无可执行代码

传递JSON对象

接收JSON对象, 需要使用 @RequestBody 注解

@RequestBody

RequestBody: 请求正文,意思是这个注解作用在请求正文的数据绑定,请求参数必须在写在请求正 文中

后端实现

java 复制代码
@RequestMapping("/r9")
    public String r9(@RequestBody UserInfo userInfo){
        return userInfo.toString();
    }

通过观察响应报头,返回的响应格式也为json格式

当去除掉 @RequestBody

后端未能成功给Person对象赋值

@PathVariable

@PathVariable用来获取URL中参数

path variable: 路径变量。和字面表达的意思一样, 这个注解主要作用在请求URL路径上的数据绑定,以获取一篇文章编号为例:

当我们阅览文章时,会发现每一篇文章都有一段对应的编号

我们通过代码来实现对编号的获取(这里的文章编号是我们自己模拟的)

java 复制代码
@RequestMapping("/article/{articleId}")
      public String r10(@PathVariable Integer articleId){
          return "获取文章ID:"+articleId;
      }

注意:

java 复制代码
 @RequestMapping("/article/{TYPE}/{articleId}")
         public String r11(@PathVariable Integer articleId,@PathVariable("TYPE") String type){
            return "获取文章ID:"+articleId+", type:"+type;
        }

如果方法参数名称和需要绑定的URL中的变量名称一致时, 可以简写, 不用给@PathVariable的属性赋值, 如上述例子中的article变量

如果方法参数名称和需要绑定的URL中的变量名称不一致时, 需要@PathVariable的属性value赋值, 如上述例子中的type变量

@RequestPart

@RequestPart用来上传指定名称的文件

java 复制代码
 @RequestMapping("/r12")
        public String r12(@RequestPart("file11") MultipartFile file) throws IOException {
            System.out.println(file.getOriginalFilename());
            file.transferTo(new File("D:\\code\\"+file.getOriginalFilename()));
            return "文件获取成功";
        }

获取Cookie/Session

HTTP 协议自身是属于 "无状态" 协议.

无状态" 的含义指的是: 默认情况下 HTTP 协议的客户端和服务器之间的这次通信, 和下次通信之间没有直接的联系

但是实际开发中, 我们很多时候是需要知道请求之间的关联关系的。此时在服务器这边就需要记录客户端信息,这个就是 Session 机制所做的工作

Session

Session是服务器为了保存用户信息而创建的一个特殊的对象

Session的本质就是一个 "哈希表", 存储了一些键值对结构

关于登录
  1. 当用户登陆的时候, 服务器在 Session 中新增一个新记录, 并把 sessionId返回给客户端. (通过 HTTP 响应中的 Set-Cookie 字段返回).
  2. 客户端后续再给服务器发送请求的时候, 需要在请求中带上 sessionId. (通过 HTTP 请求中的 Cookie 字段带上).
  3. 服务器收到请求之后, 根据请求中的 sessionId在 Session 信息中获取到对应的用户信息, 再进行后 续操作.找不到则重新创建Session, 并把SessionID返回
  1. Cookie是客户端用来保存信息的机制,Session是服务器保存用户的一种机制
  2. Cookie 和 Session之间主要是通过 SessionId 关联起来的, SessionId 是 Cookie 和 Session 之间的桥梁
  3. Cookie和Session常配合但非必须。Cookie可存客户端非身份/非sessionId数据;sessionId也可通过URL传递,不依赖Cookie/Set - Cookie。
获取Cookie
java 复制代码
 @RequestMapping("/r14")
        public String r14(@CookieValue("java") String java){
            return "从Cookie中获取Java的值:"+java;
        }

此处是向Cookie传值,注意区分传值的区域

获取Session

获取Session有两种方式,一种是通过HttpSession来获取,另一种就是通过标@SessionAttribute来获取

java 复制代码
 @RequestMapping("/getSession1")
            public String getSession2(HttpSession session){
              String userName=(String) session.getAttribute("userName");
              int age=(int) session.getAttribute("age");
            return "登录用户名为:"+userName+",age:"+age;
          }
@RequestMapping("/getSession2")
            public String getSession3(@SessionAttribute("userName") String userName){
              return "登录用户为:"+userName;
          }

获取Session要提前设置,这里通过预先设置的setSession可以直接获取到

java 复制代码
 @RequestMapping("/setSession")
          public String setSesssion(HttpServletRequest request){
              HttpSession session=request.getSession();
              session.setAttribute("userName","zhangsan");
              session.setAttribute("age",17);
              return "设置session成功";
          }

获取Header

Header中具有以下几个参数

以获取Use-Agent为例:

java 复制代码
 @RequestMapping("/getHeader2")
            public String getHeader2(@RequestHeader("User-Agent") String userAgent){
              return "从header中获取userAgent"+userAgent;
          }

通过观察,结果正确

响应

返回静态页面

要想返回静态页面,得先设置一个静态页面为index.html,将其放在static目录下

java 复制代码
@RequestMapping("/resp")
@RestController
public class RespController {
    /**
     * 返回页面
     *
     * @return
     */
    @RequestMapping("/r1")
    public String returnPage() {
        return "/aa/index.html";
    }

}

页面未正确返回,http响应把 "/index.html" 当做了http响应正文的数据

那Spring MVC如何才能识别出来 index.html 是一个静态页面, 并进行返回呢?

当将@RestController 改为 @Controller

java 复制代码
@RequestMapping("/resp")
@Controller
public class RespController {
    @RequestMapping("/r1")
    public String returnPage(){
        return "/aa/index.html";
    }

此时返回结果为页面数据

关于@RestController与@Controller

前面使用的 @RestController 其实是返回的数据,而@Controller是定义一个控制器, Spring 框架启动时加载, 把这个对象交给Spring管理

可以理解为:@RestController = @Controller + @ResponseBody

此处的@ResponseBody是定义返回的数据格式为非视图, 返回一个 text/html 信息

查看@RestController源码

所以如果想返回视图的话, 只需要把 @ResponseBody 去掉就可以了, 也就是 @Controller

返回数据@ResponseBody

@ResponseBody 表示返回数据,加上 @ResponseBody 注解, 该方法就会把 "/index.html" 当做一个数据返回给前端.

java 复制代码
    @ResponseBody
    @RequestMapping("/r1")
    public String returnPage(){
        return "/aa/index.html";
    }

@ResponseBody 既是类注解, 又是方法注解

如果作用在类上, 表示该类的所有方法返回的都是数据;如果作用在方法上, 表示该方法返回的是数据

如果一个类的方法里, 既有返回数据的又有返回页面的, 就把 @ResponseBody 注解添加到对应的方法上即可

返回HTML代码片段

后端返回数据时, 如果数据中有HTML代码, 也会被浏览器解析

通过Fiddler观察响应结果, Content-Type 为 text/html

返回JSON

后端方法返回结果为对象

java 复制代码
    @ResponseBody
    @RequestMapping("/r5")
    public UserInfo returnJson(){
        UserInfo userInfo=new UserInfo("zhangs",10,1);
        return userInfo;
    }

Content-Type的响应格式

|------------------------|-----------------------|
| text/html | body 数据格式是 HTML |
| text/css | body 数据格式是 CSS |
| application/javascript | body 数据格式是 JavaScript |
| application/json | body 数据格式是 JSON |

注意:

  1. 如果请求的是js文件, Spring MVC会自动设置Content-Type为 application/javascript
  2. 如果请求的是css文件, Spring MVC会自动设置Content-Type为 text/css

设置状态码

Spring MVC会根据我们方法的返回结果自动设置响应状态码, 程序员也可以手动指定状态码

java 复制代码
    @ResponseBody
    @RequestMapping("/r6")
    public UserInfo setStatus(HttpServletResponse resp){
        resp.setStatus(401);
        UserInfo userInfo=new UserInfo("zhangs",10,1);
        return userInfo;
    }

设置Header

Http响应报头也会向客户端传递一些附加信息, 比如服务程序的名称,请求的资源已移动到新地址等, 如: Content-Type, Local等

java 复制代码
    @ResponseBody
    @RequestMapping("/r7")
    public String setHeader(HttpServletResponse resp){
        resp.setHeader("myHeader","myHeader");
        return "设置header成功";
    }

应用分层

应用分层 是一种软件开发设计思想, 它将应用程序分成N个层次, 这N个层次分别负责各自的职责

三层架构

MVC 主要把整体的系统分成了 Model(模型), View(视图)和Controller (控制器)三个层次

但随着前后端的分离,又将后端进行整体架构的分层。通常将整体架构分为表现层、业务逻辑层和数据层,这种分层方式也称之为"三层架构

|-------|----------------------------|
| 表现层 | 就是展示数据结果和接受用户指令的,是最靠近用户的一层 |
| 业务逻辑层 | 负责处理业务逻辑, 里面有复杂业务的具体实现 |
| 数据层 | 负责存储和管理与应用程序相关的数据 |

这三个部分, 在Spring的实现中, 均有体现:

|------------|------------------------------------|
| Controller | 控制层。接收前端发送的请求,对请求进行处理,并响应数据 |
| Service | 业务逻辑层。处理具体的业务逻辑 |
| Dao | 数据访问层,也称为持久层。负责数据访问操作,包括数据的增、删、改、查 |

0总结

1.学习Spring MVC, 其实就是学习各种Web开发需要用的到注解

|-------------------|---------------------------------------------------|
| 注解 | 含义 |
| @RequestMapping | 路由映射 |
| @RequestParam | 后端参数重命名 |
| @RequestBody | 接收JSON类型的参数 |
| @PathVariable | 接收路径参数 |
| @RequestPart | 上传文件 |
| @ResponseBody | 返回数据 |
| @CookieValue | 从Cookie中获取值 |
| @SessionAttribute | 从Session中获取值 |
| @RequestHeader | 从Header中获取值 |
| @Controller | 定义一个控制器, Spring 框架启动时加载, 把这个对象交给Spring管理. 默认返回 视图 |
| @RestController | @ResponseBody + @Controller 返回数据 |

2.关于Cookie和Session

  • 会话机制:Cookie(客户端) 与 Session(服务端) 通过 SessionId 关联。
  • Spring MVC 用法 :直接在方法参数中注入 HttpServletRequest / Response
  • 核心操作:从 Request 获取 Cookie/Session,用 Response 设置状态码。
相关推荐
摇滚侠1 小时前
SpringMVC 入门到实战 SpringMVC 的执行流程 96
java·后端·spring·maven·intellij-idea
唐青枫1 小时前
Java Liquibase 实战指南:让数据库变更像代码一样可追踪
java
qq_422152572 小时前
PDF 解密工具怎么选?2026 年文档密码移除方案与注意事项
java·前端·pdf
布朗克1682 小时前
38 Spring Boot入门——自动配置、核心注解与Starter机制
java·spring boot·后端
沪漂阿龙2 小时前
LangChain 系列:Structured Output结构化输出与源码解析
java·人工智能·架构·langchain
半夜燃烧的香烟2 小时前
springboot3.0 集成minio上传文件,支持多个桶名
java·开发语言·spring boot
J2虾虾2 小时前
Android支持Java语言的标准
android·java·开发语言
Oo_行者_oO2 小时前
Spring Schedule + ShedLock + RabbitMQ 生产级落地方案 - 云楼(中国)
java·后端