项目中设计一个简洁优雅高效的全局异常处理(只需增加两个类)

你们有没有见过一个项目整个controller层每一个方法写一个try..catch来处理异常情况的,例如下面这种:

不但如此,在业务层的操作也全都是 try...catch,异常全靠打印异常堆栈;这是我待过的第一家公司的代码。

后来其实一直想重构一下,做一个全局的异常处理,但是害怕一改全是问题,心生恐惧,犹如下图(直到离职都没敢动手):

下面写一个简洁优雅的全局异常捕获处理的方法:

 需要用到两个注解:

  • @ControllerAdvice:是Spring3.2提供的新注解,它是一个Controller增强器,可对controller中被 @RequestMapping注解的方法加一些逻辑处理。最常用的就是异常处理
  • @ExceptionHandler: 异常处理,Exception的匹配 符合" 就近原则 ", 一次声明,全接口生效

实现

一、首先添加一个业务异常类 ServiceException.java 继承自RuntimeException,用于项目中业务异常。

package com.pn.ebs.util;

public class ServiceException extends RuntimeException {

    public ServiceException(String message) {
        super(message);
    }
}

说明一下:

  • 如果希望写一个检查性异常类,则需要继承 Exception 类。
  • 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

这里我是需要写一个运行时异常,所以继承自 RuntimeException

二、写一个 ExceptionTranslator.java ,包含两个方法,errorHandler和handleBusinessException ,一个处理全局(未知)异常 ,一个处理业务异常。

package com.pn.ebs.util;

import com.pn.ebs.dto.BaseResp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class ExceptionTranslator {
    private final Logger log = LoggerFactory.getLogger(ExceptionTranslator.class);

    // 全局异常
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public BaseResp errorHandler(Exception ex) {
        log.error(ex.getLocalizedMessage(), ex);
        return BaseResp.getFailInstance();
    }

    // 业务异常
    @ResponseBody
    @ExceptionHandler(value = ServiceException.class)
    public BaseResp handleBusinessException(ServiceException ex) {
        log.error(ex.getLocalizedMessage(), ex);
        return BaseResp.getFailInstance(ex.getMessage());
    }
}

其中BaseResp.java 是定义的一个返回结果类型类。

package com.pn.ebs.dto;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;

import java.io.Serializable;

//保证序列化json的时候,如果是null的对象,key也会消失
@Data
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY, getterVisibility=JsonAutoDetect.Visibility.NONE)
public class BaseResp implements Serializable {

    public final static Integer HTTP_OK = 200;
    public final static String MESSAGE_OK = "SUCCESS";

    public final static Integer HTTP_ERROR_500 = 500;
    public final static String MESSAGE_ERROR = "The system error, please wait.";

    public final static Integer HTTP_ERROR_501 = 501;

    @JSONField(name = "retCode")
    private Integer retCode;

    @JSONField(name = "message")
    private String message;

    @JSONField(name = "data")
    private Object data;

    public BaseResp() { }

    public BaseResp(int retCode, String message, Object data) {
        this.retCode = retCode;
        this.message = message;
        this.data = data;
    }

    public static BaseResp getSuccessInstance() {
        return new BaseResp(HTTP_OK, MESSAGE_OK, null);
    }

    public static BaseResp getSuccessInstance(Object data) {
        return new BaseResp(HTTP_OK, MESSAGE_OK, data);
    }

    // 系统异常
    public static BaseResp getFailInstance() {
        return new BaseResp(HTTP_ERROR_500, MESSAGE_ERROR, null);
    }

    //  业务异常
    public static BaseResp getFailInstance(String errorMsg) {
        return new BaseResp(HTTP_ERROR_501, errorMsg, null);
    }

    public Integer getRetCode() {
        return retCode;
    }

    public void setRetCode(Integer retCode) {
        this.retCode = retCode;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

}

三、使用

  • 全局异常:

在controller层写一个Get方法如下:

    @GetMapping("/dispatcher/test")
    public BaseResp test() {
        int s = 3 / 0;
        return BaseResp.getSuccessInstance();
    }

当调用此接口时肯定会抛出异常,而不会运行到test中最后一行 return BaseResp.getSuccessInstance(); 这一行。此异常 会被ExceptionTranslator 的errorHandler方法捕获,然后 log.error(ex.getLocalizedMessage(), ex); 打印错误日志,最后return BaseResp.getFailInstance(); 返回 错误信息 500, The system error, please wait.  可以打断电调试验证一下过程。

控制台日志:

  • 业务异常

改造一下刚刚的test方法,throw 一个异常 ServiceException。

    @GetMapping("/dispatcher/test")
    public BaseResp test() {
//        int s = 3 / 0;
        if (true) {
            throw new ServiceException("catch example2 exception");
        }
        return BaseResp.getSuccessInstance();
    }

当接口被调用时,会抛出一个 ServiceException,然后被ExceptionTranslator类中  handleBusinessException 方法捕获,然后执行log.error(ex.getLocalizedMessage(), ex); 输出错误日志,之后执行 return BaseResp.getFailInstance(ex.getMessage());返回错误信息:

控制台:

这样 ,每个接口方法的异常都会被 errorHandler 捕获,不需要 在每个接口方法中处理异常了,而业务上已知的异常直接throw new ServiceExption(msg) 就可以了。是不是简洁了不少。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这个问题可以通过前端技术实现,可以使用 JavaScript 和 CSS 来实现。可以在同一个页面上设置两个按钮,一个是登录按钮,一个是注册按钮。当用户点击登录按钮时,显示登录表单,同时隐藏注册表单;当用户点击注册按钮时,显示注册表单,同时隐藏登录表单。可以使用 CSS 的 display 属性来实现表单的显示和隐藏,使用 JavaScript 的事件监听来实现按钮的点击事件。 ### 回答2: 在同一个界面实现登录和注册翻转的实现方法可以通过使用按钮来切换两者之间的状态。 首先,设计一个包含登录和注册表单的界面。在页面上放置两个按钮,一个用于登录,一个用于注册。 当用户进入界面时,默认显示登录表单。用户可以通过点击"注册"按钮来翻转到注册表单界面。 在用户点击"注册"按钮后,通过JavaScript或其他交互方式,将登录表单隐藏,同时显示注册表单。这样,用户就可以在同一个界面上填写注册所的信息。 同样地,用户在注册表单界面也可以通过点击"登录"按钮翻转回到登录表单。 通过以上方式,实现了在同一个界面上实现登录和注册的翻转效果。用户只要通过点击不同的按钮即可切换到相应的表单界面,方便简洁。 ### 回答3: 在同一个界面实现登录和注册翻转可以通过以下步骤实现: 首先,设计一个初始界面,包括一个登录表单和一个注册表单。可以使用布局管理来放置这两个表单,使其处于同一个位置。并在界面顶部设置两个按钮,标识为“登录”和“注册”。 其次,设置两个全局变量,一个用于标识当前显示的是登录表单还是注册表单,另一个用于存储用户的登录状态。 然后,在每个按钮的点击事件处理函数,切换表单的显示和隐藏。当点击“登录”按钮时,如果当前显示的是注册表单,则隐藏注册表单,显示登录表单;当点击“注册”按钮时,如果当前显示的是登录表单,则隐藏登录表单,显示注册表单。可以通过修改布局管理表单的可见性实现这一切换。 接下来,在登录和注册的处理逻辑,通过调用相应的登录和注册函数实现功能。例如,登录处理逻辑,获取用户输入的用户名和密码,并调用登录函数进行验证。注册处理逻辑,获取用户输入的注册信息,并调用注册函数进行保存。 最后,在登录成功或注册成功后,设置全局变量来标识用户的登录状态,并显示相应的提示信息。 通过以上步骤,就可以在同一个界面实现登录和注册翻转。用户可以通过点击“登录”和“注册”按钮来切换表单的显示,同时在登录和注册的处理逻辑实现相应的功能。这样既方便用户在同一个界面进行登录和注册,也简化了界面设计和交互逻辑的实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值