Spring MVC 是 Spring Framework 提供的 Web 组件,是目前主流的实现 MVC 设计模式的框架,提供前端路由映射、视图解析等功能,Java Web 是开发者必须要掌握的技术框架。请注意本文使用 Spring Boot 2.7.6 进行演示,直接使用 spring-boot-starter-web 依赖,同时也默认前后端分离,本文重点在于第5章的【Spring Boot + MyBatis-Plus + JSP】项目实战。
目录
① 通过 IDEA Spring Initializr 创建项目
3 mybatis-plus 编写 Dao层 + Service层
⑦ @Requestbody自动解析 JSON 字符串封装到对象
5 【Spring Boot + MyBatis-Plus + JSP】项目实战
① /user/homePage 接口及 homePage 视图
② /user/allUser 接口及 allUser 视图
③ /user/toAddUser、/user/addUser 接口及 addUser 视图
④ /user/toUpdateUser、/user/updateUser 接口及 updateUser 视图
6.1 ResponseStatusExceptionResolver
①自定义一个异常类,并且使用 @ResponseStatus 注解修饰 👇
6.2 ExceptionHandlerExceptionResolver
③ 定义全局异常处理类 GlobalExceptionHandler
项目源码:尹煜 / SpringMvcJsp · GitCode
MVC是一种软件架构思想,把软件按照模型--M,视图--V,控制器--C 来划分
Model:模型层,指工程中的 JavaBean,用来处理数据 JavaBean 分成两类:
View:视图层,指工程中的html,jsp等页面,作用是和用户进行交互,展示数据
Controler:控制层,指工程中的Servlet,作用是接收请求和响应浏览器
MVC 简单流程:

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

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

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

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

创建成功 👇

简单来说就是创建数据库的 SQL 脚本代码 👇
- DROP TABLE IF EXISTS user;
-
- CREATE TABLE user
- (
- id BIGINT(20) NOT NULL COMMENT '主键ID',
- name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
- age INT(11) NULL DEFAULT NULL COMMENT '年龄',
- email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
- PRIMARY KEY (id)
- );
添加相应数据的 SQL 脚本代码 👇
- DELETE FROM user;
-
- INSERT INTO user (id, name, age, email) VALUES
- (1, 'yinyu', 18, 'yinyu@baomidou.com'),
- (2, 'Jack', 20, 'test2@baomidou.com'),
- (3, 'Tom', 28, 'test3@baomidou.com'),
- (4, 'Sandy', 21, 'test4@baomidou.com'),
- (5, 'Billie', 24, 'test5@baomidou.com');
点击菜单【SQL编辑器】新建 SQL 编辑器,依次执行 Schema 脚本和 Data 脚本

user 数据表创建成功 👇

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

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

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

注意本项目用的 mybatis-plus 依赖是 3.5.2 版本,算是比较新的版本
路径:pom.xml
-
-
-
com.baomidou -
mybatis-plus-boot-starter -
3.5.2 -

路径:src/main/resources/application.properties
- #数据库连接配置
- spring.datasource.username=root
- spring.datasource.password=root
- #mysql5~8 驱动不同driver-class-name 8需要增加时区的配置serverTimezone=UTC,放在url最后
- #useSSL=false 安全连接
- spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
- 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
- package com.yinyujsp.pojo;
-
- import com.baomidou.mybatisplus.annotation.IdType;
- import com.baomidou.mybatisplus.annotation.TableId;
- import lombok.AllArgsConstructor;
- import lombok.Builder;
- import lombok.Data;
- import lombok.NoArgsConstructor;
-
- @Builder
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public class User {
-
- @TableId(type = IdType.AUTO)//新增记录时未命名id时id自增
- private Long id;
-
- private String name;
- private Integer age;
- private String email;
- }
关于 mybatis-plus 的具体使用可查看【MyBatisPlus-3.5.2】专栏,因为本文目标是 Spring MVC,所以不会对 mybatis-plus 过多深入,本文编写 Mapper 和 IService 的增删改查等简单操作。
路径:src/main/java/com/yinyujsp/mapper/UserMapper.java
- package com.yinyujsp.mapper;
-
- import com.baomidou.mybatisplus.core.mapper.BaseMapper;
- import com.yinyujsp.pojo.User;
- import org.apache.ibatis.annotations.Mapper;
- import org.springframework.stereotype.Repository;
-
- //在对应的接口上面继承一个基本的接口 BaseMapper
- @Mapper
- public interface UserMapper extends BaseMapper
{ - //mybatisplus 将所有CRUD操作都编写完成了,不用像以前一样配置一大堆文件
-
- }
路径:src/main/java/com/yinyujsp/YinyujspApplication.java
- package com.yinyujsp;
-
- import org.mybatis.spring.annotation.MapperScan;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
-
- @MapperScan("com.yinyujsp.mapper")
- @SpringBootApplication
- public class YinyujspApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(YinyujspApplication.class, args);
- }
-
- }
路径:src/main/java/com/yinyujsp/service/UserBaseService.java
- package com.yinyujsp.service;
-
- import com.baomidou.mybatisplus.extension.service.IService;
- import com.yinyujsp.pojo.User;
-
- //如有需要用以重写IService里的抽象方法,如不需要重写也可去掉该文件,将IService
写在UserServiceImpl文件 - public interface UserBaseService extends IService
{ -
- }
接下来就是在实现类写具体的增删改查操作了~
我将使用 mapper 和 Iservice 接口的方法用 _ByMapper 后缀做了区分,未加后缀的方法调用的是Iservice 接口,以此方便大家理解使用,实际项目中不会区分得这么细,一个方法中这两接口都可能存在。
路径:src/main/java/com/yinyujsp/service/impl/UserServiceImpl.java
- package com.yinyujsp.service.impl;
-
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.yinyujsp.mapper.UserMapper;
- import com.yinyujsp.pojo.User;
- import com.yinyujsp.service.UserBaseService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
-
- import java.util.List;
-
- @Service
- public class UserServiceImpl extends ServiceImpl
implements UserBaseService { -
- @Autowired
- private UserMapper userMapper;
-
- /*
- Iservice CRUD(增删改查)
- */
-
- //增加一个User
- public boolean addUser(User user){
- return save(user);
- }
-
- //根据id删除一个User
- public boolean deleteUserById(int id){
- return removeById(id);
- }
-
- //更新User
- public boolean updateUser(User user){
- return updateById(user);
- }
-
- //根据id查询,返回一个User
- public User queryUser(int id){
- return getById(id);
- }
-
- //查询全部User,返回list集合
- public List
queryAllUser(){ - return list();
- }
-
-
- /*
- Mapper CRUD(增删改查)
- */
-
- //增加一个User
- public int addUser_ByMapper(User user){
- return userMapper.insert(user);
- }
-
- //根据id删除一个User
- public int deleteUserById_ByMapper(int id){
- return userMapper.deleteById(id);
- }
-
- //更新User
- public boolean updateUser_ByMapper(User user){
- userMapper.updateById(user);
- return true;
- }
-
- //根据id查询,返回一个User
- public User queryUser_ByMapper(int id){
- return userMapper.selectById(id);
- }
-
- //查询全部User,返回list集合
- public List
queryAllUser_ByMapper(){ - return userMapper.selectList(new QueryWrapper<>());//QueryWrapper没有任何条件
- }
-
-
- }
此种方式是最常用的,使用注解相对轻量级一些,至于远古方式就不介绍了~
@RestController 是 @controller(注入容器) 和 @ResponseBody(用于返回数据,若返回实体类,会包装成 Json 格式) 的结合
路径:src/main/java/com/yinyujsp/controller/demoController.java
- @RestController
- @RequestMapping("/demo")
- public class demoController {
-
- @RequestMapping("/RequestMapping")
- public String demo() {
- return "HelloWord";
- }
- }
响应成功 👇

支持 Restful 风格,使用 method 属性定义对资源的操作方式,
- @RequestMapping(value = "/restful", method = RequestMethod.GET)
- public String get() {
- //查询
- return "get";
- }
-
- @RequestMapping(value = "/restful", method = RequestMethod.POST)
- public String post() {
- //创建
- return "post";
- }
-
- @RequestMapping(value = "/restful", method = RequestMethod.PUT)
- public String put() {
- //更新
- return "put";
- }
-
- @RequestMapping(value = "/restful", method = RequestMethod.DELETE)
- public String del() {
- //删除
- return "post";
- }
- //匹配 /antA 或者 /antB 等URL
- @RequestMapping("/ant?")
- public String ant() {
- return "ant";
- }
-
- //匹配 /ant/a/create 或者 /ant/b/create 等URL
- @RequestMapping("/ant/*/create")
- public String antCreate() {
- return "antCreate";
- }
-
- //匹配 /ant/create 或者 /ant/a/b/create 等URL
- @RequestMapping("/ant/**/create")
- public String antAllCreate() {
- return "antAllCreate";
- }
定义完Controller之后,需要接收前端传入的参数,主要有以下几种形式 ~
在 @RequestMapping 映射方法上直接写上接收参数名即可:
- @RequestMapping(value = "/queryUserById")
- public User queryUserById(int id) {
- return userService.queryUser(id);
- }
请求成功 👇

如果不想使用形参名称作为参数名称,可以使用 @RequestParam 进行参数名称绑定:
- @RequestMapping(value = "/queryNameById")
- public String queryNameById(@RequestParam(value = "userId", required = false, defaultValue = "0") int id) {
- return userService.queryUser(id).getName();
- }
请求成功 👇

通过 @PathVariable 将 URL 中的占位符 {xxx} 参数映射到操作方法的入参,演示代码如下:
- @RequestMapping(value = "/queryEmailById/{id}")
- public String queryEmailById(@PathVariable("id") int id) {
- return userService.queryUser(id).getEmail();
- }
请求成功 👇

使用@RequestHeader注解,用法和@RequestParam类似:
- @RequestMapping("/queryHead")
- public String queryHead(@RequestHeader("Accept-Encoding") String acceptEncoding) {
- return acceptEncoding;
- }
请求成功 👇

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

获取 Request 请求中 Cookie 的值:
- @RequestMapping("/querycookie")
- public String querycookie(@CookieValue("JSESSIONID") String jSESSIONID) {
- return jSESSIONID;
- }
返回 cookie 值 👇

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

- @RequestMapping("/body")
- public boolean body(User user) {
- return userService.addUser(user);
- }
此时,就需要用到辅助工具来测试了,我用的是 Apifox(类似Postman)👇,请求参数与属性名相同自动填充到 user 对象中

这是前后端分离最常用的方式,前端传入一个json字符串,自动转换成pojo对象:
- @RequestMapping("/requestBody")
- public String requestBody(@RequestBody User user) {
- return user.toString();
- }
使用POST请求,发送端的 media type 设置为 json,数据是 json 字符串,请求成功 👇

项目源码:尹煜 / SpringMvcJsp · GitCode
前后端未分离之前,页面跳转的工作都是由后端控制,采用JSP进行展示数据。虽然现在互联网项目几乎不会再使用JSP,但是我觉得还是需要学习一下,因为有些旧项目还是会用JSP,或者需要重构,有兴趣的可以看下~
首先提前看下最终的框架图 👇

以下内容建立在第2章【搭建项目】的前提下,下边是针对 JSP 进行的一些操作,包括Dao层 、Service层等内容已在第2章完成,而且本项目意在精简,没有繁多的 xml 文件,一次配置即可,可维护性不错。
路径:pom.xml
-
- <dependency>
- <groupId>org.apache.tomcat.embedgroupId>
- <artifactId>tomcat-embed-jasperartifactId>
- <scope>providedscope>
- dependency>
-
- <dependency>
- <groupId>javax.servletgroupId>
- <artifactId>jstlartifactId>
- dependency>
-
- <dependency>
- <groupId>javax.servletgroupId>
- <artifactId>javax.servlet-apiartifactId>
- dependency>
Jsp 资源存放在 webapp 目录,需要对其进行创建及相关操作
Ⅰ首先创建如下目录

Ⅱ 然后在 IDEA 中设置 webapp 路径
点击进入到 Project Structure 页面

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

路径:src/main/resources/application.properties
- #项目名称
- spring.application.name=jsp
- #项目访问名称,如果不配置直接访问bean就可以
- #server.servlet.context-path=/index
- #端口
- server.port=8080
- #Spring boot视图配置
- spring.mvc.view.prefix=/jsp/
- spring.mvc.view.suffix=.jsp
- #静态文件访问配置
- 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
- <%--
- Created by IntelliJ IDEA.
- User: xiaowo
- Date: 2022/11/27
- Time: 9:45
- To change this template use File | Settings | File Templates.
- --%>
- <%@ page contentType="text/html;charset=UTF-8" language="java" %>
- <html>
- <head>
- <title>Titletitle>
- head>
- <body>
- <h1>yinyu!!!!!h1>
- body>
- html>
Ⅱ 启动项目测试

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

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

Ⅰ/user/homePage 接口
主要用到 ModelAndView ,将 homePage.jsp 注入,最后返回 homePage 视图
- @Slf4j
- @RestController
- @RequestMapping("/user")
- public class UserController {
-
- @Autowired
- private UserServiceImpl userService;
-
- @RequestMapping(value="/homePage")
- public ModelAndView homePage(){
- //设置首页
- ModelAndView mv = new ModelAndView();
- mv.setViewName("homePage");
- return mv;
- }
-
- //继续加接口
- }
Ⅱ 编写首页 homePage .jsp
- <%--
- Created by IntelliJ IDEA.
- User: xiaowo
- Date: 2022/11/26
- Time: 17:47
- To change this template use File | Settings | File Templates.
- --%>
- <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
- HTML>
- <html>
- <head>
- <title>首页title>
- <style type="text/css">
- a {
- text-decoration: none;
- color: black;
- font-size: 18px;
- }
- h3 {
- width: 180px;
- height: 38px;
- margin: 100px auto;
- text-align: center;
- line-height: 38px;
- background: deepskyblue;
- border-radius: 4px;
- }
- style>
- head>
- <body>
-
- <h3>
- <%--调用 /user/allUser 接口--%>
- <a href="${pageContext.request.contextPath}/user/allUser">点击进入列表页a>
- h3>
- body>
- html>
Ⅰ/user/allUser 接口
此处用到了查询操作,将返回的 List
- @RequestMapping("/allUser")
- public ModelAndView allUser(ModelAndView mv) {
- //① 查询操作,//modelAndView 注入 list,以便页面调用
- List
list = userService.queryAllUser(); - mv.addObject("list", list);
- //② modelAndView 注入 /jsp/allUser.jsp ,跳转至 /jsp/allUser.jsp页面
- mv.setViewName("allUser");
- return mv;
- }
Ⅱ 编写用户详情页 allUser.jsp
- <%--
- Created by IntelliJ IDEA.
- User: xiaowo
- Date: 2022/11/26
- Time: 20:26
- To change this template use File | Settings | File Templates.
- --%>
- <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- <%@ page contentType="text/html;charset=UTF-8" language="java" %>
- <html>
- <head>
- <title>用户列表title>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
-
- <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
- head>
- <body>
-
- <div class="container">
-
- <div class="row clearfix">
- <div class="col-md-12 column">
- <div class="page-header">
- <h1>
- <small>用户列表 —— 显示所有用户small>
- h1>
- div>
- div>
- div>
-
- <div class="row">
- <div class="col-md-4 column">
- <a class="btn btn-primary" href="${pageContext.request.contextPath}/user/toAddUser">新增a>
- div>
- div>
-
- <div class="row clearfix">
- <div class="col-md-12 column">
- <table class="table table-hover table-striped">
- <thead>
- <tr>
- <th>用户IDth>
- <th>用户姓名th>
- <th>用户年龄th>
- <th>用户邮箱th>
- <th>操作th>
- tr>
- thead>
-
- <tbody>
- <c:forEach var="user" items="${requestScope.get('list')}">
- <tr>
- <td>${user.getId()}td>
- <td>${user.getName()}td>
- <td>${user.getAge()}td>
- <td>${user.getEmail()}td>
- <td>
- <a href="${pageContext.request.contextPath}/user/toUpdateUser?id=${user.getId()}">更改a>|
- <a href="javascript:void(0)" onclick="confirmDel(${user.getId()})">删除a>
- <script type="text/javascript">
- function confirmDel(param)
- {
- if(window.confirm("确定删除?")){
- document.location="${pageContext.request.contextPath}/user/del/"+param
- }
- }
- script>
- <%-- onclick的响应函数中还传递了一个参数param,我使用的是$标签,数据传到函数中还进行了一个字符串的拼接。--%>
- td>
- tr>
- c:forEach>
- tbody>
- table>
- div>
- div>
- div>
Ⅰ/user/toAddUser、/user/addUser 接口
- @RequestMapping("/toAddUser")
- public ModelAndView toAddPaper() {
- //跳转至 addUser 页面
- ModelAndView mv = new ModelAndView();
- mv.setViewName("addUser");
- return mv;
- }
-
- @RequestMapping("/addUser")
- public ModelAndView addUser(User user) {
- boolean b = userService.addUser(user);
- log.info("The outcome of adding User:{}",b);
- //新增成功后跳转至 allUser 接口
- ModelAndView mv = new ModelAndView();
- mv.setViewName("redirect:/user/allUser");//controller接口跳转到另一个controller接口
- return mv;
-
- }
Ⅱ 编写用户新增页 addUser.jsp
- <%--
- Created by IntelliJ IDEA.
- User: xiaowo
- Date: 2022/11/26
- Time: 21:37
- To change this template use File | Settings | File Templates.
- --%>
- <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- <%@ page contentType="text/html;charset=UTF-8" language="java" %>
-
- <html>
- <head>
- <title>新增用户title>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
-
- <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
- head>
- <body>
- <div class="container">
-
- <div class="row clearfix">
- <div class="col-md-12 column">
- <div class="page-header">
- <h1>
- <small>新增用户small>
- h1>
- div>
- div>
- div>
- <form action="${pageContext.request.contextPath}/user/addUser" method="post">
- 用户姓名:<input type="text" name="name"><br><br><br>
- 用户年龄:<input type="number" name="age"><br><br><br>
- 用户邮箱:<input type="text" name="email"><br><br><br>
- <input type="submit" value="添加">
- form>
-
- div>
Ⅰ/user/toUpdateUser、/user/updateUser 接口
- @RequestMapping("/toUpdateUser")
- public ModelAndView toUpdateUser(int id, ModelAndView mv) {
- User user = userService.queryUser(id);
- log.info("The updating user -- {}",user);
- mv.addObject("user",user);
- //跳转至 uodateUser 页面
- mv.setViewName("updateUser");
- return mv;
- }
-
- @RequestMapping("/updateUser")
- public ModelAndView updateUser(User user, ModelAndView mv) {
- boolean b = userService.updateUser(user);
- log.info("The outcome of updated User:{}",b);
- //更新成功后跳转至 allUser 接口
- mv.setViewName("redirect:/user/allUser");
- return mv;
- }
Ⅱ 编写用户跟新页 updateUser.jsp
- <%--
- Created by IntelliJ IDEA.
- User: xiaowo
- Date: 2022/11/26
- Time: 22:10
- To change this template use File | Settings | File Templates.
- --%>
- <jsp:useBean id="user" scope="request" class="com.yinyujsp.pojo.User"/>
- <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
- <%@ page contentType="text/html;charset=UTF-8" language="java" %>
- <html>
- <head>
- <title>修改信息title>
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
-
- <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
- head>
- <body>
- <div class="container">
-
- <div class="row clearfix">
- <div class="col-md-12 column">
- <div class="page-header">
- <h1>
- <small>修改信息small>
- h1>
- div>
- div>
- div>
-
- <form action="${pageContext.request.contextPath}/user/updateUser" method="post" >
- <%-- 在最上边取到上一个接口传回的 user --%>
- <input type="hidden" name="id" value="${user.getId()}"/>
- 用户姓名:<input type="text" name="name" value="${user.getName()}"/>
- 用户年龄:<input type="number" name="age" value="${user.getAge()}"/>
- 用户邮箱:<input type="text" name="email" value="${user.getEmail()}"/>
- <input type="submit" value="提交"/>
- form>
-
- div>
使用了路径参数,该接口的实现在 allUser.jsp 里边,我还给它加了个删除确认弹窗~
- @RequestMapping("/del/{userId}")
- public ModelAndView deleteUser(@PathVariable("userId") int id) {
- boolean b = userService.deleteUserById(id);
- log.info("The outcome of deleted User:{}",b);
- //删除成功后跳转至 allUser 接口
- ModelAndView mv = new ModelAndView();
- mv.setViewName("redirect:/user/allUser");
- return mv;
- }
首页 👇

详情页 👇

用户新增页 👇

用户修改页 👇

删除确认弹窗 👇

拦截器是重点内容了,很多时候都要用拦截器,比如登录校验,权限校验等等。
实现HandlerInterceptor接口,接口有三个方法需要重写。
路径:src/main/java/com/yinyujsp/config/DemoInterceptor.java
- package com.yinyujsp.config;
-
- import org.springframework.web.servlet.HandlerInterceptor;
- import org.springframework.web.servlet.ModelAndView;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
-
- public class DemoInterceptor implements HandlerInterceptor {
-
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- //预处理,返回true则继续执行。如果需要登录校验,校验不通过返回false即可,通过则返回true。
- System.out.println("执行preHandle()方法");
- return true;
- }
-
- @Override
- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
- //后处理
- System.out.println("执行postHandle()方法");
- }
-
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
- //在DispatcherServlet完全处理完请求后被调用
- System.out.println("执行afterCompletion()方法");
- }
- }
路径:src/main/java/com/yinyujsp/config/ConverterConfig.java
- package com.yinyujsp.config;
-
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
- import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
-
- @Configuration
- public class ConverterConfig extends WebMvcConfigurationSupport {
-
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- registry.addInterceptor(new DemoInterceptor()).addPathPatterns("/**");///**代表所有路径
- }
- }
run 启动类,请求如下接口 👇

控制台输出成功 👇

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

从类图可以看出有四种异常处理器:
第一个默认的异常处理器是内置的异常处理器,是对一些常见的异常处理,一般来说不用管它,SimpleMappingExceptionResolver 的异常处理器,在如今前后端分离的环境下已经看不到了,所以本文针对扩展最后两个。
这种异常处理器主要用于处理带有 @ResponseStatus 注释的异常。
路径:src/main/java/com/yinyujsp/config/DefinedException.java
- package com.yinyujsp.config;
-
- import org.springframework.http.HttpStatus;
- import org.springframework.web.bind.annotation.ResponseStatus;
-
- //HttpStatus枚举有所有的状态码,这里返回一个400的响应码
- @ResponseStatus(value = HttpStatus.BAD_REQUEST)
- public class DefinedException extends Exception{
- }
路径:src/main/java/com/yinyujsp/controller/demoController.java
- @RequestMapping("/defined")
- public String defined(String msg) throws Exception {
- if ("defined".equals(msg)) {
- throw new DefinedException();
- }
- return "index";
- }
启动项目,请求接口后响应成功 👇

注解形式的异常处理器,目前来说最为常见,也十分方便。
路径:src/main/java/com/yinyujsp/config/BaseException.java
- package com.yinyujsp.config;
-
- public class BaseException extends Exception {
- public BaseException(String message) {
- super(message);
- }
- }
路径:src/main/java/com/yinyujsp/pojo/ErrorInfo.java
- package com.yinyujsp.pojo;
-
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
-
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public class ErrorInfo {
- public static final Integer OK = 0;
- public static final Integer ERROR = -1;
- private Integer code;
- private String message;
- private String url;
- }
路径:src/main/java/com/yinyujsp/config/GlobalExceptionHandler.java
- package com.yinyujsp.config;
-
- import com.yinyujsp.pojo.ErrorInfo;
- import org.springframework.web.bind.annotation.ExceptionHandler;
- import org.springframework.web.bind.annotation.RestControllerAdvice;
-
- import javax.servlet.http.HttpServletRequest;
-
- //这里使用了RestControllerAdvice,是@ResponseBody和@ControllerAdvice的结合
- //会把实体类转成JSON格式的提示返回,符合前后端分离的架构
- @RestControllerAdvice
- public class GlobalExceptionHandler {
- //这里自定义了一个BaseException,当抛出BaseException异常就会被此方法处理
- @ExceptionHandler(BaseException.class)
- public ErrorInfo errorHandler(HttpServletRequest req, BaseException e) throws Exception {
- ErrorInfo r = new ErrorInfo();
- r.setMessage(e.getMessage());
- r.setCode(ErrorInfo.ERROR);
- r.setUrl(req.getRequestURL().toString());
- return r;
- }
- }
路径:src/main/java/com/yinyujsp/controller/demoController.java
- @RequestMapping("/base")
- public String base(String msg) throws Exception {
- if ("base".equals(msg)) {
- throw new BaseException("测试抛出BaseException异常~");
- }
- return "index";
- }
启动项目,请求该接口响应成功 👇
