• 【Java开发】 Spring 06 :Spring MVC 实践详解(Spring Boot+MyBatis-Plus+JSP 项目)


    Spring MVC 是 Spring Framework 提供的 Web 组件,是目前主流的实现 MVC 设计模式的框架,提供前端路由映射、视图解析等功能,Java Web 是开发者必须要掌握的技术框架。请注意本文使用 Spring Boot 2.7.6 进行演示,直接使用 spring-boot-starter-web 依赖,同时也默认前后端分离,本文重点在于第5章的【Spring Boot + MyBatis-Plus + JSP】项目实战。

    目录

    1 Spring MVC 介绍

    1.1 MVC 是什么

    1.2 Spring MVC 核心组件

    1.3 Spring MVC 工作流程

    2 搭建项目

    2.1 Mysql 数据库初始化

    ① 新建数据库

    ② 数据库 Schema 脚本

    ③ 数据库 Data 脚本

    ④ 使用 SQL 编辑器运行脚本

    ⑤ id 主键设置自增

    2.2 Spring Boot 项目初始化

    ① 通过 IDEA Spring Initializr 创建项目

    ② 添加 Spring Web(最关键)等依赖

    ③ 导入 mybatis-plus 依赖

    ④ 连接数据库配置

    ⑤ 编写数据库对应的实体类

    3 mybatis-plus 编写 Dao层 + Service层 

    3.1 Dao层的 Mapper接口

    ① 编写实体类对应的mapper接口

    ② 在主启动类添加@MapperScan注解

    3.2 Service层的 Iservice 接口和实现类

    ① 编写实体类对应的 UserBaseService 接口

    ② Service 层的实现类

    4 定义 Controller

    4.1 使用 @RequestMapping

    ① 支持Restful风格

    ② 支持Ant风格

    4.2 接收参数

    ① 普通参数

    ② @RequestParam参数名绑定

    ③ @PathVariable路径参数

    ④ @RequestHeader绑定请求头属性

    ⑤ @CookieValue绑定请求的Cookie值

    ⑥ 绑定请求参数到实体类对象

    ⑦ @Requestbody自动解析 JSON 字符串封装到对象

    5 【Spring Boot + MyBatis-Plus + JSP】项目实战

    5.1 JSP项目初始化

    ① 导入 jasper、jstl、servlet 等依赖

    ② webapp 目录

    ③  配置视图解析器

    ④  测试配置是否成功

    5.2 UserController类及视图层编写

    ① /user/homePage 接口及 homePage 视图

    ② /user/allUser 接口及 allUser 视图

    ③ /user/toAddUser、/user/addUser 接口及 addUser 视图

    ④ /user/toUpdateUser、/user/updateUser 接口及 updateUser 视图

    ⑤ /user/del/{userId}接口

    5.3 项目展示

    5 拦截器

    5.1 自定义拦截器

    5.2 将拦截器注入到Spring容器中

    5.3 测试拦截器

    6 全局异常管理

    6.1 ResponseStatusExceptionResolver

    ①自定义一个异常类,并且使用 @ResponseStatus 注解修饰 👇

    ② 编写 Controller 接口进行测试

    6.2 ExceptionHandlerExceptionResolver

    ① 定义自定义异常BaseException

    ② 定义错误提示实体类ErrorInfo

    ③ 定义全局异常处理类 GlobalExceptionHandler

    ④ 编程测试接口进行测试


     项目源码:尹煜 / SpringMvcJsp · GitCode

    1 Spring MVC 介绍

    1.1 MVC 是什么

    MVC是一种软件架构思想,把软件按照模型--M,视图--V,控制器--C 来划分

    Model:模型层,指工程中的 JavaBean,用来处理数据 JavaBean 分成两类:

    • 一类称为实体类Bean:专门用来存储业务数据,比如Student,User
    • 一类称为业务处理Bean:指Servlet或Dao对象,专门用来处理业务逻辑和数据访问

    View:视图层,指工程中的html,jsp等页面,作用是和用户进行交互,展示数据

    Controler:控制层,指工程中的Servlet,作用是接收请求和响应浏览器

    MVC 简单流程:

    • 用户通过视图层发送请求到服务器,在服务器中请求被 Controller 接收
    • Controller 调用相应的 Model 层处理请求,处理完毕后结果返回到 Controller
    • Controller 再根据请求处理的结果找到对应的 View 视图,渲染数据后最终响应给浏览器

    在这里插入图片描述

    Spring MVC 对这套 MVC 流程进行封装,帮助开发者屏蔽底层细节,并且开放出相关接口供开发者调用,让 MVC 开发更简单方便

    在这里插入图片描述

    该流程及图片转载自 Spring MVC详解(学习总结) ,写的真的很不错!

    1.2 Spring MVC 核心组件

    • DispatcherServlet:前置控制器,负责调度其他组件的执行,可以降低不同组件之间的耦合性,是整个Spring MVC的核心模块
    • Handler:处理器,完成具体的业务逻辑,相当于Servlet
    • HandlerMapping:DispatcherServlet是通过 HandlerMapping把请求映射到不同的Handler
    • HandlerInterceptor:处理器拦截器,是一个接口,如果我们需要进行一些拦截处理,可以通过实现该接口完成
    • HandlerExecutionChain:处理器执行链,包括两部分内容:Handler和HandlerInterceptor(系统会有一个默认的HandlerInterceptor,如果有额外拦截处理,可以添加拦截器进行设置)
    • HandlerAdapter:处理器适配器,Handler执行业务方法之前,需要进行一系列的操作包括表单的数据验证、数据类型转换、把表单数据封装到POJO等,这些一系列的操作都是由HandlerAdapter完成,DispatcherServlet通过HandlerAdapter执行不同的Handler
    • ModelAndView:封装了模型数据和视图信息,作为Handler的处理结果,返回给DispatcherServlet,项目中会用到!
    • ViewResolver视图解析器,DispatcherServlet通过它把逻辑视图解析为物理视图,最终把渲染的结果响应给客户端

    1.3 Spring MVC 工作流程

    1. 客户端请求被 DispatcherServlet 接收
    2. 根据 HandlerMapping 映射到 Handler
    3. 生成 Handler 和 HandlerInterceptor
    4. Handler 和 HandlerInterceptor 以 HandlerExecutionChain 的形式一并返回给 DispatcherServlet
    5. DispatcherServlet 通过 HandlerAdapter 调用 Handler 的方法完成业务逻辑处理
    6. 返回一个 ModelAndView 对象给 DispatcherServlet
    7. DispatcherServlet 把获取的 ModelAndView 对象传给 ViewResolver 视图解析器,把逻辑视图解析成物理视图
    8. ViewResolver 返回一个View进行视图渲染(把模型填充到视图中)
    9. DispatcherServlet 把渲染后的视图响应给客户端

    在这里插入图片描述


    2 搭建项目

    2.1 Mysql 数据库初始化

    本人在云服务器上部署了 Mysql (部署教程链接:Docker 环境下安装 Mysql),同时已通过 DBeaver 数据库可视化软件,此时我需要通过 DBeaver 在 Mysql 中创建表和插入数据。

    ① 新建数据库

    通过 DBeaver 连接刚部署完的 Mysql 后发现空无一物 👇

    此时右键选择新建数据库并命名,选择 utf-8 (兼容性强)编码格式

    创建成功 👇

    ② 数据库 Schema 脚本

    简单来说就是创建数据库的 SQL 脚本代码 👇

    1. DROP TABLE IF EXISTS user;
    2. CREATE TABLE user
    3. (
    4. id BIGINT(20) NOT NULL COMMENT '主键ID',
    5. name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    6. age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    7. email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    8. PRIMARY KEY (id)
    9. );

    ③ 数据库 Data 脚本

    添加相应数据的 SQL 脚本代码 👇

    1. DELETE FROM user;
    2. INSERT INTO user (id, name, age, email) VALUES
    3. (1, 'yinyu', 18, 'yinyu@baomidou.com'),
    4. (2, 'Jack', 20, 'test2@baomidou.com'),
    5. (3, 'Tom', 28, 'test3@baomidou.com'),
    6. (4, 'Sandy', 21, 'test4@baomidou.com'),
    7. (5, 'Billie', 24, 'test5@baomidou.com');

    ④ 使用 SQL 编辑器运行脚本

    点击菜单【SQL编辑器】新建 SQL 编辑器,依次执行 Schema 脚本和 Data 脚本

     user 数据表创建成功 👇

    ⑤ id 主键设置自增

    通过 DBeaver 设置,Sql 语句和其他数据库可视化软件都可以的

    2.2 Spring Boot 项目初始化

    ① 通过 IDEA Spring Initializr 创建项目

    StringBoot 默认不支持使用 jar 包打包有 JSP 页面项目,所以如果包含有 JSP 页面的项目,需要使用 war 打包,由于本文使用 Jsp 项目,因此使用 war 打包。

    ② 添加 Spring Web(最关键)等依赖

    注意本文的 Spring Boot 版本是 2.7.6 ,建议将版本控制在 2-3 之间,超出范围的话会产生兼容问题。

    ③ 导入 mybatis-plus 依赖

    注意本项目用的 mybatis-plus 依赖是 3.5.2 版本,算是比较新的版本

    路径:pom.xml

    1. com.baomidou
    2. mybatis-plus-boot-starter
    3. 3.5.2

    ④ 连接数据库配置

    路径:src/main/resources/application.properties

    1. #数据库连接配置
    2. spring.datasource.username=root
    3. spring.datasource.password=root
    4. #mysql5~8 驱动不同driver-class-name 8需要增加时区的配置serverTimezone=UTC,放在url最后
    5. #useSSL=false 安全连接
    6. spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    7. spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

    ⑤ 编写数据库对应的实体类

    使用 lombok 和 mybatisplus 的实体类注释,加大开发效率

    注意:mybatis-plus 会通过实体类的名称自动匹配数据表,比如 User 实体类和 user 数据表,也可通过 @Ttable 指定,如 @Ttable("user"),本文采用第一种形式

    路径:src/main/java/com/yinyujsp/pojo/User.java

    1. package com.yinyujsp.pojo;
    2. import com.baomidou.mybatisplus.annotation.IdType;
    3. import com.baomidou.mybatisplus.annotation.TableId;
    4. import lombok.AllArgsConstructor;
    5. import lombok.Builder;
    6. import lombok.Data;
    7. import lombok.NoArgsConstructor;
    8. @Builder
    9. @Data
    10. @AllArgsConstructor
    11. @NoArgsConstructor
    12. public class User {
    13. @TableId(type = IdType.AUTO)//新增记录时未命名id时id自增
    14. private Long id;
    15. private String name;
    16. private Integer age;
    17. private String email;
    18. }

    3 mybatis-plus 编写 Dao层 + Service层 

    关于 mybatis-plus 的具体使用可查看【MyBatisPlus-3.5.2】专栏,因为本文目标是 Spring MVC,所以不会对 mybatis-plus 过多深入,本文编写 Mapper 和 IService 的增删改查等简单操作。

    3.1 Dao层的 Mapper接口

    ① 编写实体类对应的mapper接口

    路径:src/main/java/com/yinyujsp/mapper/UserMapper.java

    1. package com.yinyujsp.mapper;
    2. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    3. import com.yinyujsp.pojo.User;
    4. import org.apache.ibatis.annotations.Mapper;
    5. import org.springframework.stereotype.Repository;
    6. //在对应的接口上面继承一个基本的接口 BaseMapper
    7. @Mapper
    8. public interface UserMapper extends BaseMapper {
    9. //mybatisplus 将所有CRUD操作都编写完成了,不用像以前一样配置一大堆文件
    10. }

    ② 在主启动类添加@MapperScan注解

    路径:src/main/java/com/yinyujsp/YinyujspApplication.java

    1. package com.yinyujsp;
    2. import org.mybatis.spring.annotation.MapperScan;
    3. import org.springframework.boot.SpringApplication;
    4. import org.springframework.boot.autoconfigure.SpringBootApplication;
    5. @MapperScan("com.yinyujsp.mapper")
    6. @SpringBootApplication
    7. public class YinyujspApplication {
    8. public static void main(String[] args) {
    9. SpringApplication.run(YinyujspApplication.class, args);
    10. }
    11. }

    3.2 Service层的 Iservice 接口和实现类

    ① 编写实体类对应的 UserBaseService 接口

    路径:src/main/java/com/yinyujsp/service/UserBaseService.java

    1. package com.yinyujsp.service;
    2. import com.baomidou.mybatisplus.extension.service.IService;
    3. import com.yinyujsp.pojo.User;
    4. //如有需要用以重写IService里的抽象方法,如不需要重写也可去掉该文件,将IService写在UserServiceImpl文件
    5. public interface UserBaseService extends IService {
    6. }

    ② Service 层的实现类

    接下来就是在实现类写具体的增删改查操作了~

    我将使用 mapper 和 Iservice 接口的方法用 _ByMapper 后缀做了区分,未加后缀的方法调用的是Iservice 接口,以此方便大家理解使用,实际项目中不会区分得这么细,一个方法中这两接口都可能存在。

    路径:src/main/java/com/yinyujsp/service/impl/UserServiceImpl.java

    1. package com.yinyujsp.service.impl;
    2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    3. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    4. import com.yinyujsp.mapper.UserMapper;
    5. import com.yinyujsp.pojo.User;
    6. import com.yinyujsp.service.UserBaseService;
    7. import org.springframework.beans.factory.annotation.Autowired;
    8. import org.springframework.stereotype.Service;
    9. import java.util.List;
    10. @Service
    11. public class UserServiceImpl extends ServiceImpl implements UserBaseService {
    12. @Autowired
    13. private UserMapper userMapper;
    14. /*
    15. Iservice CRUD(增删改查)
    16. */
    17. //增加一个User
    18. public boolean addUser(User user){
    19. return save(user);
    20. }
    21. //根据id删除一个User
    22. public boolean deleteUserById(int id){
    23. return removeById(id);
    24. }
    25. //更新User
    26. public boolean updateUser(User user){
    27. return updateById(user);
    28. }
    29. //根据id查询,返回一个User
    30. public User queryUser(int id){
    31. return getById(id);
    32. }
    33. //查询全部User,返回list集合
    34. public List queryAllUser(){
    35. return list();
    36. }
    37. /*
    38. Mapper CRUD(增删改查)
    39. */
    40. //增加一个User
    41. public int addUser_ByMapper(User user){
    42. return userMapper.insert(user);
    43. }
    44. //根据id删除一个User
    45. public int deleteUserById_ByMapper(int id){
    46. return userMapper.deleteById(id);
    47. }
    48. //更新User
    49. public boolean updateUser_ByMapper(User user){
    50. userMapper.updateById(user);
    51. return true;
    52. }
    53. //根据id查询,返回一个User
    54. public User queryUser_ByMapper(int id){
    55. return userMapper.selectById(id);
    56. }
    57. //查询全部User,返回list集合
    58. public List queryAllUser_ByMapper(){
    59. return userMapper.selectList(new QueryWrapper<>());//QueryWrapper没有任何条件
    60. }
    61. }

    4 定义 Controller

    4.1 使用 @RequestMapping

    此种方式是最常用的,使用注解相对轻量级一些,至于远古方式就不介绍了~

    @RestController 是 @controller(注入容器) 和 @ResponseBody(用于返回数据,若返回实体类,会包装成 Json 格式) 的结合

    路径:src/main/java/com/yinyujsp/controller/demoController.java

    1. @RestController
    2. @RequestMapping("/demo")
    3. public class demoController {
    4. @RequestMapping("/RequestMapping")
    5. public String demo() {
    6. return "HelloWord";
    7. }
    8. }

    响应成功 👇

    ① 支持Restful风格

    支持 Restful 风格,使用 method 属性定义对资源的操作方式,

    1. @RequestMapping(value = "/restful", method = RequestMethod.GET)
    2. public String get() {
    3. //查询
    4. return "get";
    5. }
    6. @RequestMapping(value = "/restful", method = RequestMethod.POST)
    7. public String post() {
    8. //创建
    9. return "post";
    10. }
    11. @RequestMapping(value = "/restful", method = RequestMethod.PUT)
    12. public String put() {
    13. //更新
    14. return "put";
    15. }
    16. @RequestMapping(value = "/restful", method = RequestMethod.DELETE)
    17. public String del() {
    18. //删除
    19. return "post";
    20. }

    ② 支持Ant风格

    1. //匹配 /antA 或者 /antB 等URL
    2. @RequestMapping("/ant?")
    3. public String ant() {
    4. return "ant";
    5. }
    6. //匹配 /ant/a/create 或者 /ant/b/create 等URL
    7. @RequestMapping("/ant/*/create")
    8. public String antCreate() {
    9. return "antCreate";
    10. }
    11. //匹配 /ant/create 或者 /ant/a/b/create 等URL
    12. @RequestMapping("/ant/**/create")
    13. public String antAllCreate() {
    14. return "antAllCreate";
    15. }

    4.2 接收参数

    定义完Controller之后,需要接收前端传入的参数,主要有以下几种形式 ~

    ① 普通参数

    在 @RequestMapping 映射方法上直接写上接收参数名即可:

    1. @RequestMapping(value = "/queryUserById")
    2. public User queryUserById(int id) {
    3. return userService.queryUser(id);
    4. }

    请求成功 👇

    ② @RequestParam参数名绑定

    如果不想使用形参名称作为参数名称,可以使用 @RequestParam 进行参数名称绑定:

    1. @RequestMapping(value = "/queryNameById")
    2. public String queryNameById(@RequestParam(value = "userId", required = false, defaultValue = "0") int id) {
    3. return userService.queryUser(id).getName();
    4. }

    请求成功 👇

    ③ @PathVariable路径参数

    通过 @PathVariable 将 URL 中的占位符 {xxx} 参数映射到操作方法的入参,演示代码如下:

    1. @RequestMapping(value = "/queryEmailById/{id}")
    2. public String queryEmailById(@PathVariable("id") int id) {
    3. return userService.queryUser(id).getEmail();
    4. }

    请求成功 👇

    ④ @RequestHeader绑定请求头属性

    使用@RequestHeader注解,用法和@RequestParam类似:

    1. @RequestMapping("/queryHead")
    2. public String queryHead(@RequestHeader("Accept-Encoding") String acceptEncoding) {
    3. return acceptEncoding;
    4. }

    请求成功 👇

    查看接口请求头详情,匹配正确~

    ⑤ @CookieValue绑定请求的Cookie值

    获取 Request 请求中 Cookie 的值:

    1. @RequestMapping("/querycookie")
    2. public String querycookie(@CookieValue("JSESSIONID") String jSESSIONID) {
    3. return jSESSIONID;
    4. }

    返回 cookie 值 👇

    查看接口 cookie 详情,匹配正确~

    ⑥ 绑定请求参数到实体类对象

    1. @RequestMapping("/body")
    2. public boolean body(User user) {
    3. return userService.addUser(user);
    4. }

    此时,就需要用到辅助工具来测试了,我用的是 Apifox(类似Postman)👇,请求参数与属性名相同自动填充到 user 对象中

    ⑦ @Requestbody自动解析 JSON 字符串封装到对象

    这是前后端分离最常用的方式,前端传入一个json字符串,自动转换成pojo对象:

    1. @RequestMapping("/requestBody")
    2. public String requestBody(@RequestBody User user) {
    3. return user.toString();
    4. }

    使用POST请求,发送端的 media type 设置为 json,数据是 json 字符串,请求成功 👇

    5 【Spring Boot + MyBatis-Plus + JSP】项目实战

    项目源码:尹煜 / SpringMvcJsp · GitCode

    前后端未分离之前,页面跳转的工作都是由后端控制,采用JSP进行展示数据。虽然现在互联网项目几乎不会再使用JSP,但是我觉得还是需要学习一下,因为有些旧项目还是会用JSP,或者需要重构,有兴趣的可以看下~

    首先提前看下最终的框架图 👇

    5.1 JSP项目初始化

    以下内容建立在第2章【搭建项目】的前提下,下边是针对 JSP 进行的一些操作,包括Dao层 、Service层等内容已在第2章完成,而且本项目意在精简,没有繁多的 xml 文件,一次配置即可,可维护性不错。

    ① 导入 jasper、jstl、servlet 等依赖

    路径:pom.xml

    1. <dependency>
    2. <groupId>org.apache.tomcat.embedgroupId>
    3. <artifactId>tomcat-embed-jasperartifactId>
    4. <scope>providedscope>
    5. dependency>
    6. <dependency>
    7. <groupId>javax.servletgroupId>
    8. <artifactId>jstlartifactId>
    9. dependency>
    10. <dependency>
    11. <groupId>javax.servletgroupId>
    12. <artifactId>javax.servlet-apiartifactId>
    13. dependency>

    ② webapp 目录

    Jsp 资源存放在 webapp 目录,需要对其进行创建及相关操作

    Ⅰ首先创建如下目录

    Ⅱ 然后在 IDEA 中设置 webapp 路径

    点击进入到 Project Structure 页面

    指定配置目录,这样的话,框架就会将 webapp 目录识别为资源文件

    ③  配置视图解析器

    路径:src/main/resources/application.properties

    1. #项目名称
    2. spring.application.name=jsp
    3. #项目访问名称,如果不配置直接访问bean就可以
    4. #server.servlet.context-path=/index
    5. #端口
    6. server.port=8080
    7. #Spring boot视图配置
    8. spring.mvc.view.prefix=/jsp/
    9. spring.mvc.view.suffix=.jsp
    10. #静态文件访问配置
    11. spring.mvc.static-path-pattern=/static/**

    注:server.servlet.context-path:应用的上下文路径,也可以称为项目路径,是构成url地址的一部分。不配置时,默认为 / ,如:localhost:8080/xxxxxx;配置时,比如 /demo,此时的访问方式为localhost:8080/demo/xxxxxx。

    ④  测试配置是否成功

    Ⅰ新增 index.jsp

    index.jsp 默认为初始页,也就是说输入 localhost:8080 就会跳转到该页!

    路径:src/main/webapp/index.jsp

    1. <%--
    2. Created by IntelliJ IDEA.
    3. User: xiaowo
    4. Date: 2022/11/27
    5. Time: 9:45
    6. To change this template use File | Settings | File Templates.
    7. --%>
    8. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    9. <html>
    10. <head>
    11. <title>Titletitle>
    12. head>
    13. <body>
    14. <h1>yinyu!!!!!h1>
    15. body>
    16. html>

    Ⅱ 启动项目测试

    输入网址:http://localhost:8080/ ,测试成功 👇 

    5.2 UserController类及视图层编写

    首先展示下 webapp 的最终目录 👇,index.jsp 可以忽略(只用作测试)

    ① /user/homePage 接口及 homePage 视图

    Ⅰ/user/homePage 接口

    主要用到 ModelAndView ,将 homePage.jsp 注入,最后返回 homePage 视图

    1. @Slf4j
    2. @RestController
    3. @RequestMapping("/user")
    4. public class UserController {
    5. @Autowired
    6. private UserServiceImpl userService;
    7. @RequestMapping(value="/homePage")
    8. public ModelAndView homePage(){
    9. //设置首页
    10. ModelAndView mv = new ModelAndView();
    11. mv.setViewName("homePage");
    12. return mv;
    13. }
    14. //继续加接口
    15. }

    Ⅱ 编写首页 homePage .jsp

    1. <%--
    2. Created by IntelliJ IDEA.
    3. User: xiaowo
    4. Date: 2022/11/26
    5. Time: 17:47
    6. To change this template use File | Settings | File Templates.
    7. --%>
    8. <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
    9. HTML>
    10. <html>
    11. <head>
    12. <title>首页title>
    13. <style type="text/css">
    14. a {
    15. text-decoration: none;
    16. color: black;
    17. font-size: 18px;
    18. }
    19. h3 {
    20. width: 180px;
    21. height: 38px;
    22. margin: 100px auto;
    23. text-align: center;
    24. line-height: 38px;
    25. background: deepskyblue;
    26. border-radius: 4px;
    27. }
    28. style>
    29. head>
    30. <body>
    31. <h3>
    32. <%--调用 /user/allUser 接口--%>
    33. <a href="${pageContext.request.contextPath}/user/allUser">点击进入列表页a>
    34. h3>
    35. body>
    36. html>

    ② /user/allUser 接口及 allUser 视图

    Ⅰ/user/allUser 接口

    此处用到了查询操作,将返回的 List 注入到 ModelAndView ,从而 allUser 视图能够取到。

    1. @RequestMapping("/allUser")
    2. public ModelAndView allUser(ModelAndView mv) {
    3. //① 查询操作,//modelAndView 注入 list,以便页面调用
    4. List list = userService.queryAllUser();
    5. mv.addObject("list", list);
    6. //② modelAndView 注入 /jsp/allUser.jsp ,跳转至 /jsp/allUser.jsp页面
    7. mv.setViewName("allUser");
    8. return mv;
    9. }

    Ⅱ 编写用户详情页 allUser.jsp

    1. <%--
    2. Created by IntelliJ IDEA.
    3. User: xiaowo
    4. Date: 2022/11/26
    5. Time: 20:26
    6. To change this template use File | Settings | File Templates.
    7. --%>
    8. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    9. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    10. <html>
    11. <head>
    12. <title>用户列表title>
    13. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    14. <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    15. head>
    16. <body>
    17. <div class="container">
    18. <div class="row clearfix">
    19. <div class="col-md-12 column">
    20. <div class="page-header">
    21. <h1>
    22. <small>用户列表 —— 显示所有用户small>
    23. h1>
    24. div>
    25. div>
    26. div>
    27. <div class="row">
    28. <div class="col-md-4 column">
    29. <a class="btn btn-primary" href="${pageContext.request.contextPath}/user/toAddUser">新增a>
    30. div>
    31. div>
    32. <div class="row clearfix">
    33. <div class="col-md-12 column">
    34. <table class="table table-hover table-striped">
    35. <thead>
    36. <tr>
    37. <th>用户IDth>
    38. <th>用户姓名th>
    39. <th>用户年龄th>
    40. <th>用户邮箱th>
    41. <th>操作th>
    42. tr>
    43. thead>
    44. <tbody>
    45. <c:forEach var="user" items="${requestScope.get('list')}">
    46. <tr>
    47. <td>${user.getId()}td>
    48. <td>${user.getName()}td>
    49. <td>${user.getAge()}td>
    50. <td>${user.getEmail()}td>
    51. <td>
    52. <a href="${pageContext.request.contextPath}/user/toUpdateUser?id=${user.getId()}">更改a>|
    53. <a href="javascript:void(0)" onclick="confirmDel(${user.getId()})">删除a>
    54. <script type="text/javascript">
    55. function confirmDel(param)
    56. {
    57. if(window.confirm("确定删除?")){
    58. document.location="${pageContext.request.contextPath}/user/del/"+param
    59. }
    60. }
    61. script>
    62. <%-- onclick的响应函数中还传递了一个参数param,我使用的是$标签,数据传到函数中还进行了一个字符串的拼接。--%>
    63. td>
    64. tr>
    65. c:forEach>
    66. tbody>
    67. table>
    68. div>
    69. div>
    70. div>

    ③ /user/toAddUser、/user/addUser 接口及 addUser 视图

    Ⅰ/user/toAddUser、/user/addUser 接口

    1. @RequestMapping("/toAddUser")
    2. public ModelAndView toAddPaper() {
    3. //跳转至 addUser 页面
    4. ModelAndView mv = new ModelAndView();
    5. mv.setViewName("addUser");
    6. return mv;
    7. }
    8. @RequestMapping("/addUser")
    9. public ModelAndView addUser(User user) {
    10. boolean b = userService.addUser(user);
    11. log.info("The outcome of adding User:{}",b);
    12. //新增成功后跳转至 allUser 接口
    13. ModelAndView mv = new ModelAndView();
    14. mv.setViewName("redirect:/user/allUser");//controller接口跳转到另一个controller接口
    15. return mv;
    16. }

    Ⅱ 编写用户新增页 addUser.jsp

    1. <%--
    2. Created by IntelliJ IDEA.
    3. User: xiaowo
    4. Date: 2022/11/26
    5. Time: 21:37
    6. To change this template use File | Settings | File Templates.
    7. --%>
    8. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    9. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    10. <html>
    11. <head>
    12. <title>新增用户title>
    13. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    14. <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    15. head>
    16. <body>
    17. <div class="container">
    18. <div class="row clearfix">
    19. <div class="col-md-12 column">
    20. <div class="page-header">
    21. <h1>
    22. <small>新增用户small>
    23. h1>
    24. div>
    25. div>
    26. div>
    27. <form action="${pageContext.request.contextPath}/user/addUser" method="post">
    28. 用户姓名:<input type="text" name="name"><br><br><br>
    29. 用户年龄:<input type="number" name="age"><br><br><br>
    30. 用户邮箱:<input type="text" name="email"><br><br><br>
    31. <input type="submit" value="添加">
    32. form>
    33. div>

    ④ /user/toUpdateUser、/user/updateUser 接口及 updateUser 视图

    Ⅰ/user/toUpdateUser、/user/updateUser 接口

    1. @RequestMapping("/toUpdateUser")
    2. public ModelAndView toUpdateUser(int id, ModelAndView mv) {
    3. User user = userService.queryUser(id);
    4. log.info("The updating user -- {}",user);
    5. mv.addObject("user",user);
    6. //跳转至 uodateUser 页面
    7. mv.setViewName("updateUser");
    8. return mv;
    9. }
    10. @RequestMapping("/updateUser")
    11. public ModelAndView updateUser(User user, ModelAndView mv) {
    12. boolean b = userService.updateUser(user);
    13. log.info("The outcome of updated User:{}",b);
    14. //更新成功后跳转至 allUser 接口
    15. mv.setViewName("redirect:/user/allUser");
    16. return mv;
    17. }

    Ⅱ 编写用户跟新页 updateUser.jsp

    1. <%--
    2. Created by IntelliJ IDEA.
    3. User: xiaowo
    4. Date: 2022/11/26
    5. Time: 22:10
    6. To change this template use File | Settings | File Templates.
    7. --%>
    8. <jsp:useBean id="user" scope="request" class="com.yinyujsp.pojo.User"/>
    9. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    10. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    11. <html>
    12. <head>
    13. <title>修改信息title>
    14. <meta name="viewport" content="width=device-width, initial-scale=1.0">
    15. <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    16. head>
    17. <body>
    18. <div class="container">
    19. <div class="row clearfix">
    20. <div class="col-md-12 column">
    21. <div class="page-header">
    22. <h1>
    23. <small>修改信息small>
    24. h1>
    25. div>
    26. div>
    27. div>
    28. <form action="${pageContext.request.contextPath}/user/updateUser" method="post" >
    29. <%-- 在最上边取到上一个接口传回的 user --%>
    30. <input type="hidden" name="id" value="${user.getId()}"/>
    31. 用户姓名:<input type="text" name="name" value="${user.getName()}"/>
    32. 用户年龄:<input type="number" name="age" value="${user.getAge()}"/>
    33. 用户邮箱:<input type="text" name="email" value="${user.getEmail()}"/>
    34. <input type="submit" value="提交"/>
    35. form>
    36. div>

    ⑤ /user/del/{userId}接口

    使用了路径参数,该接口的实现在 allUser.jsp 里边,我还给它加了个删除确认弹窗~

    1. @RequestMapping("/del/{userId}")
    2. public ModelAndView deleteUser(@PathVariable("userId") int id) {
    3. boolean b = userService.deleteUserById(id);
    4. log.info("The outcome of deleted User:{}",b);
    5. //删除成功后跳转至 allUser 接口
    6. ModelAndView mv = new ModelAndView();
    7. mv.setViewName("redirect:/user/allUser");
    8. return mv;
    9. }

    5.3 项目展示

    首页 👇

    详情页 👇

     用户新增页 👇

    用户修改页 👇

    删除确认弹窗 👇

    5 拦截器

    拦截器是重点内容了,很多时候都要用拦截器,比如登录校验,权限校验等等。

    实现HandlerInterceptor接口,接口有三个方法需要重写。

    • preHandle():在业务处理器处理请求之前被调用。预处理。
    • postHandle():在业务处理器处理请求执行完成后,生成视图之前执行。后处理。
    • afterCompletion():在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面);

    5.1 自定义拦截器

    路径:src/main/java/com/yinyujsp/config/DemoInterceptor.java

    1. package com.yinyujsp.config;
    2. import org.springframework.web.servlet.HandlerInterceptor;
    3. import org.springframework.web.servlet.ModelAndView;
    4. import javax.servlet.http.HttpServletRequest;
    5. import javax.servlet.http.HttpServletResponse;
    6. public class DemoInterceptor implements HandlerInterceptor {
    7. @Override
    8. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    9. //预处理,返回true则继续执行。如果需要登录校验,校验不通过返回false即可,通过则返回true。
    10. System.out.println("执行preHandle()方法");
    11. return true;
    12. }
    13. @Override
    14. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    15. //后处理
    16. System.out.println("执行postHandle()方法");
    17. }
    18. @Override
    19. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    20. //在DispatcherServlet完全处理完请求后被调用
    21. System.out.println("执行afterCompletion()方法");
    22. }
    23. }

    5.2 将拦截器注入到Spring容器中

    路径:src/main/java/com/yinyujsp/config/ConverterConfig.java

    1. package com.yinyujsp.config;
    2. import org.springframework.context.annotation.Configuration;
    3. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    4. import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
    5. @Configuration
    6. public class ConverterConfig extends WebMvcConfigurationSupport {
    7. @Override
    8. public void addInterceptors(InterceptorRegistry registry) {
    9. registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/**");///**代表所有路径
    10. }
    11. }

    5.3 测试拦截器

    run 启动类,请求如下接口 👇

    控制台输出成功 👇 

    6 全局异常管理

    SpringMVC 本身就对一些异常进行了全局处理,请看 HandlerExceptionResolver 接口的类图👇

    从类图可以看出有四种异常处理器:

    • DefaultHandlerExceptionResolver,默认的异常处理器。根据各个不同类型的异常,返回不同的异常视图。
    • SimpleMappingExceptionResolver,简单映射异常处理器。通过配置异常类和view的关系来解析异常。
    • ResponseStatusExceptionResolver,状态码异常处理器。解析带有@ResponseStatus注释类型的异常。
    • ExceptionHandlerExceptionResolver,注解形式的异常处理器。对@ExceptionHandler注解的方法进行异常解析。

    第一个默认的异常处理器是内置的异常处理器,是对一些常见的异常处理,一般来说不用管它,SimpleMappingExceptionResolver 的异常处理器,在如今前后端分离的环境下已经看不到了,所以本文针对扩展最后两个。

    6.1 ResponseStatusExceptionResolver

    这种异常处理器主要用于处理带有 @ResponseStatus 注释的异常。

    ①自定义一个异常类,并且使用 @ResponseStatus 注解修饰 👇

    路径:src/main/java/com/yinyujsp/config/DefinedException.java

    1. package com.yinyujsp.config;
    2. import org.springframework.http.HttpStatus;
    3. import org.springframework.web.bind.annotation.ResponseStatus;
    4. //HttpStatus枚举有所有的状态码,这里返回一个400的响应码
    5. @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    6. public class DefinedException extends Exception{
    7. }

    ② 编写 Controller 接口进行测试

    路径:src/main/java/com/yinyujsp/controller/demoController.java

    1. @RequestMapping("/defined")
    2. public String defined(String msg) throws Exception {
    3. if ("defined".equals(msg)) {
    4. throw new DefinedException();
    5. }
    6. return "index";
    7. }

    启动项目,请求接口后响应成功 👇

    6.2 ExceptionHandlerExceptionResolver

    注解形式的异常处理器,目前来说最为常见,也十分方便。

    ① 定义自定义异常BaseException

    路径:src/main/java/com/yinyujsp/config/BaseException.java

    1. package com.yinyujsp.config;
    2. public class BaseException extends Exception {
    3. public BaseException(String message) {
    4. super(message);
    5. }
    6. }

    ② 定义错误提示实体类ErrorInfo

    路径:src/main/java/com/yinyujsp/pojo/ErrorInfo.java

    1. package com.yinyujsp.pojo;
    2. import lombok.AllArgsConstructor;
    3. import lombok.Data;
    4. import lombok.NoArgsConstructor;
    5. @Data
    6. @AllArgsConstructor
    7. @NoArgsConstructor
    8. public class ErrorInfo {
    9. public static final Integer OK = 0;
    10. public static final Integer ERROR = -1;
    11. private Integer code;
    12. private String message;
    13. private String url;
    14. }

    ③ 定义全局异常处理类 GlobalExceptionHandler

    路径:src/main/java/com/yinyujsp/config/GlobalExceptionHandler.java

    1. package com.yinyujsp.config;
    2. import com.yinyujsp.pojo.ErrorInfo;
    3. import org.springframework.web.bind.annotation.ExceptionHandler;
    4. import org.springframework.web.bind.annotation.RestControllerAdvice;
    5. import javax.servlet.http.HttpServletRequest;
    6. //这里使用了RestControllerAdvice,是@ResponseBody和@ControllerAdvice的结合
    7. //会把实体类转成JSON格式的提示返回,符合前后端分离的架构
    8. @RestControllerAdvice
    9. public class GlobalExceptionHandler {
    10. //这里自定义了一个BaseException,当抛出BaseException异常就会被此方法处理
    11. @ExceptionHandler(BaseException.class)
    12. public ErrorInfo errorHandler(HttpServletRequest req, BaseException e) throws Exception {
    13. ErrorInfo r = new ErrorInfo();
    14. r.setMessage(e.getMessage());
    15. r.setCode(ErrorInfo.ERROR);
    16. r.setUrl(req.getRequestURL().toString());
    17. return r;
    18. }
    19. }

    ④ 编程测试接口进行测试

    路径:src/main/java/com/yinyujsp/controller/demoController.java

    1. @RequestMapping("/base")
    2. public String base(String msg) throws Exception {
    3. if ("base".equals(msg)) {
    4. throw new BaseException("测试抛出BaseException异常~");
    5. }
    6. return "index";
    7. }

    启动项目,请求该接口响应成功 👇


    参考文章

    狂神说SpringMVC05:整合SSM框架

    5千字的SpringMVC总结,我觉得你会需要! - 知乎

    springBoot+JSP搭建项目_那清澈的漓江的博客-CSDN博客_springboot+jsp

  • 相关阅读:
    Spring 中使用MyBatis
    mybatis bean属性识别丢失【NoSuchPropertyException】
    Debian11 安装 OpenJDK8
    图像去雾开源数据集资源汇总
    本人碰到的RN项目的坑
    vue3加axios配合element-plus实现图片等文件本地上传,并获取服务器返回的真实地址数据,前端写法
    多目标优化算法matlab代码大合集
    21【JDBC操作数据库元数据】
    『现学现忘』Git基础 — 22、Git中文件重命名
    一个文件管理系统的软硬件配置清单
  • 原文地址:https://blog.csdn.net/weixin_51407397/article/details/128027376