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
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, '[email protected]'),
(2, 'Jack', 20, '[email protected]'),
(3, 'Tom', 28, '[email protected]'),
(4, 'Sandy', 21, '[email protected]'),
(5, 'Billie', 24, '[email protected]');
点击菜单【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
org.apache.tomcat.embed
tomcat-embed-jasper
provided
javax.servlet
jstl
javax.servlet
javax.servlet-api
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" %>
Title
yinyu!!!!!
Ⅱ 启动项目测试
输入网址: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" %>
首页
<%--调用 /user/allUser 接口--%>
点击进入列表页
Ⅰ/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" %>
用户列表
Ⅰ/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" %>
新增用户
新增用户
Ⅰ/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.
--%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
修改信息
修改信息
使用了路径参数,该接口的实现在 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";
}
启动项目,请求该接口响应成功
狂神说SpringMVC05:整合SSM框架
5千字的SpringMVC总结,我觉得你会需要! - 知乎
springBoot+JSP搭建项目_那清澈的漓江的博客-CSDN博客_springboot+jsp