SpringMVC Day 09 : 异常处理

news2025/7/25 9:01:45

前言

在Web应用程序开发中,我们经常会遇到各种各样的错误和异常情况。那么如何有效地捕获和处理这些异常呢?本文将介绍Spring MVC中的异常处理机制,帮助您构建更稳定、可靠的Web应用程序。

一、前期准备

1、新建项目,结构如下

2、导入依赖
    <dependencies>
    
        <!-- springmvc 依赖,会将spring的核心包一并添加进来 -->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.23</version>
        </dependency>
     
 
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
        </dependency>
 
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.4.5</version>
        </dependency>
 
 
 
    </dependencies>
 3、配置 web.xml 
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>


</web-app>

用于配置 Servlet 的映射和加载。在 Spring MVC 中,它用于配置 DispatcherServlet 的初始化和请求映射。

具体来说,这段配置的作用如下:

  1. 定义了一个名为 "dispatcher" 的 Servlet,并指定了 org.springframework.web.servlet.DispatcherServlet 作为其处理类。
  2. 设置了 load-on-startup 属性为 1,表示在应用启动时就加载该 Servlet。
  3. 使用 <servlet-mapping> 元素将 "dispatcher" Servlet 映射到所有的请求路径上(即 <url-pattern>/</url-pattern>),意味着所有的请求都会经过该 Servlet 进行处理。

 这段配置的作用是将所有的请求交给 DispatcherServlet 处理,并让它成为应用的核心控制器。DispatcherServlet 将根据请求的 URL 和其他配置信息,将请求分发给相应的处理器方法进行处理,然后返回响应结果。
 

4、新建一个 User 类
@Data
public class User {

    private String userName;
    private String password;

}
5、新建一个 UserDao 接口
public interface UserDao {


    User getUser(String userName);

}
 6、新建一个 UserDaoImpl 实现类
@Repository
@Slf4j
public class UserDaoImpl implements UserDao {
    @Override
    public User getUser(String userName) {
        log.info("select * from user ");
        User user = new User();
        user.setUserName("qiu");
        user.setPassword("88888888");
        return user;
    }
}

 使用日志简单的输出,并封装数据到 user 实体中。

7、新建一个 LoginService
public interface LoginService {

    /**
     * 验证用户
     * @param userName
     * @param password
     * @return
     */
    User auth(String userName,String password);

}

二、传统的 try catch 处理异常

1、编写 controller 类
@RestController
@RequiredArgsConstructor
@Slf4j
public class UserController {

    private final LoginService loginService;


    @PostMapping("/login")
    public ResultVO login(String userName, String password, HttpSession session){

        try{
            User user = loginService.auth(userName, password);
            // 将 user 保存到会话中
            user.setUserName(userName);
            user.setPassword(password);
            session.setAttribute("user",user);

            return new ResultVO();
        } catch (AuthException e){
            // 验证未通过则创建提示信息
            ResultVO resultVO = new ResultVO();
            resultVO.setCode(e.getErroeCode());
            resultVO.setMessage(e.getMessage());
            return resultVO;
            // 捕获其他非业务异常(也就是服务器内部错误异常)
        } catch (RuntimeException e){
            // 记录异常日志
            log.error(e.getMessage());
            // 验证未通过则创建提示信息
            ResultVO resultVO = new ResultVO();
            resultVO.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
            resultVO.setMessage("服务器内部错误,请稍候重试");
            return resultVO;
        }

    }
}

我们使用之前的方法是不是需要有很多个 catch 语句块去抛出多个异常,这还是异常少的例子,如果还有更多的异常,还要很多的 catch 语句块,这样就会显得我们的 Controler 很臃肿。我们的控制器不能有这么多的多余的代码,那么有没有办法把异常信息抽取出来呢?

三、局部异常

1、自定义一个异常类来处理业务类的异常(登录)

/**
 * @Date 2023-10-27
 * @Author qiu
 * 自定义异常
 */
public class AuthException extends RuntimeException{

    /**
     * 异常状态码
     */
    private Integer erroeCode;

    public AuthException(Integer erroeCode,String message){
        super(message);
        this.erroeCode =erroeCode;
    }

    public Integer getErroeCode() {
        return erroeCode;
    }
}

这是一个自定义异常类AuthException的示例代码。

该异常类继承自RuntimeException,因此是一个运行时异常。它的目的是在程序中处理身份验证相关的异常情况。

在该类中,有两个成员变量:

  • erroeCode:表示异常状态码,用于标识不同的异常情况。
  • message:异常的详细信息。

构造方法AuthException(Integer erroeCode, String message)用于创建AuthException对象,并接受一个异常状态码和异常信息作为参数。它调用父类的构造方法super(message)来设置异常信息,并将异常状态码赋值给成员变量erroeCode

同时,类中提供了getErroeCode()方法用于获取异常状态码。

通过自定义异常类,可以在程序中抛出和捕获AuthException对象,以便对身份验证过程中的异常进行更细粒度的处理和控制。

2、完成 LoginImpl 实现类

@Service
@Slf4j
@RequiredArgsConstructor
public class LoginServiceImpl implements LoginService {

    private final UserDao userDao;

    @Override
    public User auth(String userName, String password) {

        User user = userDao.getUser(userName);
        // 用户不为 Null 则校验密码
        if ( user != null ){
            if ( password.equals(user.getPassword())){
                return user;
            }
        }
        // 抛出业务异常
        throw new AuthException(10001,"账号密码错误");
    }

}

在类中,定义了一个userDao成员变量,并通过构造方法注入UserDao对象。

实现了LoginService接口中的auth()方法,用于进行用户名和密码的校验。首先通过userDao.getUser(userName)方法获取对应用户名的用户对象,然后判断用户是否存在以及密码是否匹配。如果匹配成功,则返回用户对象;否则,抛出一个业务异常AuthException,并设置异常状态码为10001,异常信息为"账号密码错误"。

通过LoginServiceImpl类,可以实现用户登录认证的功能,并且在校验失败时抛出自定义的业务异常,以便后续处理和提供详细的错误提示。

3、使用 @ExceptionHandler 注解
    /**
     * 局部异常处理,处理登录业务异常
     * @ExceptionHandler:注解标注的方法专门用于处理请求方式产生的异常。
     * value:属性指定要处理的异常类型
     * 注意:这个局部异常的范围只在当前的 Controller 中有效,也就是说
     * 每个 controller 都有自己专门的 handlerException
     * @param e
     * @return
     */
    @ExceptionHandler(AuthException.class)
    public ResultVO handlerAuthException(AuthException e){
        // 验证未通过则创建提示信息
        ResultVO resultVO = new ResultVO();
        resultVO.setCode(e.getErroeCode());
        resultVO.setMessage(e.getMessage());
        return resultVO;
    }

这是一个局部异常处理的示例代码,用于处理登录业务异常。

在该代码中,使用@ExceptionHandler注解标记了一个方法,该方法专门用于处理AuthException类型的异常。value属性指定要处理的异常类型,即AuthException.class

当控制器(Controller)中的请求处理方法抛出AuthException异常时,Spring框架会自动调用该方法进行异常处理。在该方法中,首先创建一个ResultVO对象作为返回结果,并将异常状态码和异常信息设置到ResultVO对象中。然后将ResultVO对象作为方法的返回值返回。最终,将返回结果以JSON格式返回给客户端。

通过局部异常处理,可以提高代码的可读性和可维护性,将异常处理逻辑从业务逻辑中分离出来,并且能够对不同类型的异常做出不同的响应。

 四、全局异常

1、新建 ExceptionAdvice 类

/**
 * @Date 2023-10-27
 * @Author qiu
 * 定义一个全局异常处理类(类似一个切面)
 * 这个类中定义所有的异常处理方法,也可以
 * 理解为全局异常通知
 */
//@ControllerAdvice (对应 @Controller 注解的类)

// (对应 @RestController 注解的类)
// value 屬性 controller 包下所有类都需要捕获异常
@RestControllerAdvice("edu.nf.ch09.controller")

@Slf4j
public class ExceptionAdvice {

    /**
     * 全局异常处理,处理登录业务异常
     * @ExceptionHandler:注解标注的方法专门用于处理请求方式产生的异常。
     * value:属性指定要处理的异常类型
     * 注意:这个局部异常的范围只在当前的 Controller 中有效,也就是说
     * 每个 controller 都有自己专门的 handlerException
     * @param e
     * @return
     */
    @ExceptionHandler(AuthException.class)
    public ResultVO handlerAuthException(AuthException e){
        // 验证未通过则创建提示信息
        ResultVO resultVO = new ResultVO();
        resultVO.setCode(e.getErroeCode());
        resultVO.setMessage(e.getMessage());
        return resultVO;
    }


    /**
     * 全局的异常处理(处理非业务异常,如:数据库异常)
     * @return
     */
    @ExceptionHandler(RuntimeException.class)
    public ResultVO handlerRunTimeException(RuntimeException e ){
        // 捕获其他非业务异常(也就是服务器内部错误异常)
        // 记录异常日志
        log.error(e.getMessage());
        // 验证未通过则创建提示信息
        ResultVO resultVO = new ResultVO();
        resultVO.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
        resultVO.setMessage("服务器内部错误,请稍候重试");
        return resultVO;
    }

}

这是一个全局异常处理类,用于统一处理控制器中抛出的异常。

在该代码中,使用@RestControllerAdvice注解标记了一个类,该类是一个切面,用于处理全局异常。通过value属性指定了需要捕获异常的控制器层(Controller)所在的包路径。

在该类中,定义了两个异常处理方法。

  1. handlerAuthException方法用于处理AuthException类型的异常。当控制器中的请求处理方法抛出AuthException异常时,Spring框架会自动调用该方法进行异常处理。在该方法中,首先创建一个ResultVO对象作为返回结果,并将异常状态码和异常信息设置到ResultVO对象中。然后将ResultVO对象作为方法的返回值返回。

  2. handlerRunTimeException方法用于处理RuntimeException类型的异常(即非业务异常,如数据库异常等)。当控制器中的请求处理方法抛出RuntimeException异常时,Spring框架会自动调用该方法进行异常处理。在该方法中,首先记录异常日志,然后创建一个ResultVO对象作为返回结果,并设置一个服务器内部错误的提示信息和状态码。最终将ResultVO对象作为方法的返回值返回。

通过全局异常处理类,可以集中处理控制器中抛出的异常,并返回统一的响应结果,提高代码的可读性和可维护性。

我们把异常都放在这个类来处理,就不用在 controller 写那么多处理异常代码了。

 2、编写 controller 类
@RestController
@RequiredArgsConstructor
@Slf4j
public class UserController {

    private final LoginService loginService;

    @PostMapping("/login")
    public ResultVO login(String userName, String password, HttpSession session){

        User user = loginService.auth(userName, password);
        // 将 user 保存到会话中
        user.setUserName(userName);
        user.setPassword(password);
        session.setAttribute("user",user);

        return new ResultVO();

    }
}

 在控制器中只需要完成对业务类的方法调用即可,不用去考虑异常怎么处理。

3、运行效果

五、总结

本次案例,介绍了三种处理异常的方式,我们学习了 SpringMVC 后就要使用全局的处理异常。比起原始的 try catch 语句块处理异常是不是简洁很多了,我们把同样的事情单独放在一个类中去完成,就不会像刚刚那样把所有代码都写在控制器中那么臃肿了。

六、使用 Springmvc 异常处理的好处 

使用Spring MVC异常处理的好处有以下几个方面:

  1. 统一异常处理:通过在全局配置中定义统一的异常处理类,可以集中处理应用程序中所有控制器抛出的异常。这样可以减少代码重复,提高代码的可维护性和可读性。

  2. 简化异常处理逻辑:在应用程序的控制器中,通常需要编写大量的异常处理代码来处理各种可能发生的异常情况。使用Spring MVC的异常处理功能,可以将这些异常处理逻辑从控制器中抽离出来,使得控制器代码更加简洁和清晰。

  3. 统一错误信息:通过自定义异常处理类,可以将不同类型的异常映射到统一的错误信息,并返回给客户端。这样可以提供一致的错误信息格式,便于前端进行处理和展示。

  4. 异常日志记录:在异常处理类中,可以对异常进行日志记录,包括异常的详细信息、发生异常的时间等。这样可以帮助开发人员快速定位和解决问题,提高系统的可靠性和稳定性。

  5. 提升用户体验:通过合理地处理异常,可以向用户提供更友好的错误提示信息,避免出现系统默认的错误页面或者错误信息。这样可以提升用户体验,增加系统的可用性。

总之,使用Spring MVC的异常处理功能可以简化开发过程,提高代码的可维护性和可读性,提供统一的错误信息和日志记录,并提升用户体验。这些优势使得Spring MVC成为开发Web应用程序时的首选框架之一。

七、gitee 案例 

地址:ch09 · qiuqiu/SpringMVC - 码云 - 开源中国 (gitee.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1156907.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【问题解决】 avue dicUrl 动态参数加载字典数据(已解决)

事情是这样的&#xff0c;用了avue-crud组件&#xff0c;配置了一个option。     现在有一列source属性要展示为 多选的下拉框 &#xff0c;当然问题不在这而在于&#xff0c;选项是需要根据同级别属性id去拿的。也就是option.column.source 的配置中 需要该行的option.col…

V90PN总线伺服梯形加减速速度控制(标准报文1应用)

V90 PN总线伺服速度控制应用可以利用标准报文1和SinaSpeed功能块实现,具体代码介绍请查看下面相关文章链接,这里不再赘述。 速度随动控制 V90伺服PN总线速度随动控制(手摇轮功能)-CSDN博客文章浏览阅读40次。V90PN总线控制相关内容,请参考下面文章链接:博途1200/1500PLC …

我和云栖有个约会

开端&#xff0c;似曾相识的云栖 2023年阿里云云栖大会在云栖小镇举办&#xff0c;云栖小镇&#xff1f;在2020年的时候&#xff0c;曾经来过这里参加竞赛。时隔三年&#xff0c;身份变换&#xff0c;以开发者的身份&#xff0c;收到阿里云开发者社区的邀请&#xff0c;正好有…

简单易懂的讲解一下什么是自动化测试?

1.什么是自动化测试 以程序测试程序&#xff0c;以代码代替思维&#xff0c;以脚本的运行代替手工测试。自动化的测试涵盖了&#xff1a;功能&#xff08;黑盒&#xff09;自动化测试&#xff0c;功能&#xff08;白盒&#xff09;自动化测试&#xff0c;性能测试&#xff0c;…

如何在Linux上安装JDK、Tomcat和MySQL以及部署后端项目

目录 前言 一、JDK和Tomcat的安装 1.JDK安装 2.Tomcat安装 二、安装MySQL 三、后端接口部署 1.将ssh前后端分离项目进行部署 ​2.将单体项目进行部署 3.将ssm前后端分离项目进行部署并修改端口号 前言 随着现代软件开发的快速发展&#xff0c;越来越多的企业和个人开始…

【C语法学习】6 - gets()函数

文章目录 1 函数原型2 参数3 返回值4 读取机制5 示例 1 函数原型 gets()&#xff1a;从标准输入流stdin读取一个字符串存储到str指向的内存空间&#xff0c;函数原型如下&#xff1a; char *gets(char *str)2 参数 gets()函数的参数只有一个str&#xff1a; str是一个指向c…

如何借助知乎平台开展问答口碑营销?

知乎作为一个开放性的问答社区&#xff0c;把各行各业的用户链接在一起&#xff0c;用户可以在知乎平台分享自己的经验&#xff0c;也可以在知乎上搜索自己感兴趣的问题&#xff0c;因此就会有很多企业想要在知乎上进行问答营销推广自己的产品&#xff0c;但这些企业自己亲身试…

【C语法学习】7 - fgets()函数

文章目录 1 函数原型2 参数3 返回值4 读取机制5 比较6 示例6.1 示例16.2 示例26.3 示例3 1 函数原型 fgets()&#xff1a;从指定流stream读取一个字符串存储到str指向的内存空间&#xff0c;函数原型如下&#xff1a; char *fgets( char *str, int n, FILE *stream );2 参数 …

一篇文章认识【性能测试】

一、 性能测试术语解释 1. 响应时间 响应时间即从应用系统发出请求开始&#xff0c;到客户端接收到最后一个字节数据为止所消耗的时间。响应时间按软件的特点再可以细分&#xff0c;如对于一个 C/S 软件的响应时间可以细分为网络传输时间、应用服务器处理时间、数据库服务器…

一文带你了解自动化测试是什么?

本章主要讲解自动化测试的含义、分类、项目使用&#xff0c;以及自动化测试工具的优势。 一、自动化测试概述 1、什么是自动化测试&#xff1f; 自动化测试是软件测试活动中的一个重要分支和组成部分。随着软件产业的不断发展&#xff0c;市场对软件周期的要求越来越高&…

Android页面周期、页面跳转

1.什么是Activity&#xff1f; Activity是Android的四大组件之一&#xff0c;它是一种可以包含用户界面的组件&#xff0c;主要用于和用户进行交互。Activity用于显示用户界面&#xff0c;用户通过Activity交互完成相关操作&#xff0c;一个APP允许有多个Activity。 2.Activi…

CRM客户管理系统助力企业全流程精细化运营

效率是每一家企业都十分关注的重点。要想提高工作效率就要鄙弃粗放的管理模式&#xff0c;采用CRM客户管理系统赋能企业全流程精细化运营&#xff0c;让销售环节高速运转起来。 全流程精细化运营从哪些方面出发&#xff1f; 每一家企业的内部流程都不尽相同&#xff0c;客户管…

mfc140u.dll丢失怎么修复,mfc140u.dll文件有什么作用

今天我想和大家分享的是关于mfc140u.dll文件丢失的解决方法。在我们使用电脑的过程中&#xff0c;有时候会遇到一些错误提示&#xff0c;其中比较常见的就是“无法找到mfc140u.dll文件”。那么&#xff0c;这个文件是什么呢&#xff1f;它有什么作用呢&#xff1f; 首先&#…

双轮差速模型机器人通过线速度、角速度计算机器人位姿

已知上一时刻机器人位置P_OLD (x,y,),机器人当前时刻的线速度和角速度&#xff08;v,&#xff09;,短时间内t内&#xff0c;机器人在线性部分和非线性部分的增量为 线性部分&#xff1a; 非线性部分&#xff1a; 由于可能非常小&#xff0c;导致非线性部分数值不稳定&#xf…

关于preempt count的疑问

Linux中的preempt_count - 知乎 https://www.cnblogs.com/hellokitty2/p/15652312.html LWN&#xff1a;关于preempt_count()的四个小讨论&#xff01;-CSDN博客 主要是参考这些文章 之前一直认为只要是in_interrupt()返回非0值&#xff0c;那么就可以认为当前在中断上下文。即…

Spring Cloud Alibaba 之 Sentinel

系列文章目录 第一章 Java线程池技术应用 第二章 CountDownLatch和Semaphone的应用 第三章 Spring Cloud 简介 第四章 Spring Cloud Netflix 之 Eureka 第五章 Spring Cloud Netflix 之 Ribbon 第六章 Spring Cloud 之 OpenFeign 第七章 Spring Cloud 之 GateWay 第八章 Sprin…

小黑子—spring:第四章 事务控制与整合web环境

spring入门4.0 四 小黑子基于AOP的声明式事务控制1. Spring事务编程概述2. 搭建测试环境3. 基于XML声明式事务控制3.1 入门操作3.2 声明式事务控制详解 4. 基于注解的声明式事务控制 五 小黑子用spring进行web环境整合5. JavaWeb三大组件作用及其特点6. Spring整合web环境的思路…

Java作业一

编写程序实现如下界面效果&#xff1a; import java.util.Scanner;public class Test01 {public static void main(String[] args) {String name;Scanner input new Scanner(System.in);System.out.println("请输入您的姓名&#xff1a;");name input.nextLine();S…

算法学习打卡day39|动态规划:62.不同路径 、 63. 不同路径 II

62.不同路径 力扣题目链接 题目描述&#xff1a; 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。…

Python图像处理【15】基于非锐化掩码锐化图像

基于非锐化掩码锐化图像 0. 前言1. 使用 scikit-image filters 模块执行非锐化掩码2. 使用 PIL ImageFilter 模块执行非锐化掩码3. 使用 SimpleITK 执行拉普拉斯锐化4. 使用 OpenCV 实现非锐化掩码小结系列链接 0. 前言 非锐化滤波器是一个简单的锐化算子&#xff0c;通过从原…