前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Sprinboot中使用AOP实现异常处理

Sprinboot中使用AOP实现异常处理

作者头像
用户1483438
修改2021-11-16 21:19:12
1.3K0
修改2021-11-16 21:19:12
举报
文章被收录于专栏:大数据共享大数据共享

一、异常处理的乱象例举

乱象一:捕获异常后只输出到控制台

前端js-ajax代码

代码语言:javascript
复制
$.ajax({
    type: "GET",
    url: "/user/add",
    dataType: "json",
    success: function(data){
        alert("添加成功");
    }
});

后端业务代码

代码语言:javascript
复制
try {
    // do something
} catch (XyyyyException e) {
    e.printStackTrace();
}

问题:

  1. 后端直接将异常捕获,而且只做了日志打印。用户体验非常差,一旦后台出错,用户没有任何感知,页面无状态,。
  2. 如果没有人去经常关注日志,不会有人发现系统出现异常

乱象二:混乱的返回方式

前端代码

代码语言:javascript
复制
$.ajax({
    type: "GET",
    url: "/goods/add",
    dataType: "json",
    success: function(data) {
        if (data.flag) {
            alert("添加成功");
        } else {
            alert(data.message);
        }
    },
    error: function(data){
        alert("添加失败");
    }
});

后端代码

代码语言:javascript
复制
@RequestMapping("/goods/add")
@ResponseBody
public Map add(Goods goods) {
    Map map = new HashMap();
    try {
        // do something
        map.put(flag, true);
    } catch (Exception e) {
        e.printStackTrace();
        map.put("flag", false);
        map.put("message", e.getMessage());
    }
    reutrn map;
}

问题:

  1. 每个人返回的数据有每个人自己的规范,你叫flag他叫isOK,你的成功code是0,它的成功code是0000。这样导致后端书写了大量的异常返回逻辑代码,前端也随之每一个请求一套异常处理逻辑。很多重复代码。
  2. 如果是前端后端一个人开发还勉强能用,如果前后端分离,就是系统灾难。

参考:juejin.im/post/5c3ea9…

二、该如何设计异常处理

面向相关方友好

  1. 后端开发人员职责单一,只需要将异常捕获并转换为自定义异常一直对外抛出。不需要去想页面跳转404,以及异常响应的数据结构的设计。
  2. 面向前端人员友好,后端返回给前端的数据应该有统一的数据结构,统一的规范。不能一个人一个响应的数据结构。而在此过程中不需要后端开发人员做更多的工作,交给全局异常处理器去处理“异常”到“响应数据结构”的转换。
  3. 面向用户友好,用户能够清楚的知道异常产生的原因。这就要求自定义异常,全局统一处理,ajax接口请求响应统一的异常数据结构,页面模板请求统一跳转到404页面。
  4. 面向运维友好,将异常信息合理规范的持久化,以便查询。

为什么要将系统运行时异常捕获,转换为自定义异常抛出? 答:因为用户不认识 ConnectionTimeOutException类似这种异常是什么东西,但是转换为自定义异常就要求程序员对运行时异常进行一个翻译,比如:自定义异常里面应该有message字段,后端程序员应该明确的在message字段里面用面向用户的友好语言,说明发生了什么。

三、开发规范

  1. Controller、Service、DAO层拦截异常转换为自定义异常,不允许将异常私自截留。必须对外抛出。
  2. 统一数据响应代码,使用httpstatusode,不要自定义。自定义不方便记忆。200请求成功,400用户输入错误导致的异常,500系统内部异常,999未知异常。
  3. 自定义异常里面有message属性,一定用友好的语言描述异常,并赋值给message.
  4. 不允许对父类Excetion统一catch,要分小类catch,这样能够清楚地将异常转换为自定义异常传递给前端。

四、页面类异常处理

我们做页面模板时,Controller发生异常我们该怎么办?应该统一跳转到404页面。

面临的问题:程序员抛出自定义异常CustomException,全局异常处理截获之后返回@ResponseBody AjaxResponse,不是ModelAndView,所以我们无法跳转到error.html页面,那我们该如何做页面的全局的异常处理? 答:

  1. 用面向切面的方式,将CustomException转换为ModelAndViewException。
  2. 全局异常处理器拦截ModelAndViewException,返回ModelAndView,即error.html页面
  3. 切入点是带@ModelView注解的Controller层方法

使用这种方法处理页面类异常,程序员只需要在页面跳转的Controller上加@ModelView注解即可

错误的写法

代码语言:javascript
复制
@GetMapping("/freemarker")
public String index(Model model) {
    try{
        List<ArticleVO> articles = articleRestService.getAll();
        model.addAttribute("articles", articles);
    }catch (Exception e){
        return "error";
    }
    return "fremarkertemp";
}

正确的写法

代码语言:javascript
复制
@ModelView
@GetMapping("/freemarker")
public String index(Model model) {
    List<ArticleVO> articles = articleRestService.getAll();
    model.addAttribute("articles", articles);
    return "fremarkertemp";
}

五、用面向切面的方法处理页面全局异常

因为用到了面向切面编程,所以引入maven依赖包

代码语言:javascript
复制
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

新定义一个异常类ModelViewException

代码语言:javascript
复制
public class ModelViewException extends RuntimeException{

    //异常错误编码
    private int code ;
    //异常信息
    private String message;

    public static ModelViewException transfer(CustomException e) {
        return new ModelViewException(e.getCode(),e.getMessage());
    }

    private ModelViewException(int code, String message){
        this.code = code;
        this.message = message;
    }

    int getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }

}

ModelView 注解,只起到标注的作用

代码语言:javascript
复制
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})//只能在方法上使用此注解
public @interface ModelView {

}

以@ModelView注解为切入点,面向切面编程,将CustomException转换为ModelViewException抛出。

代码语言:javascript
复制
@Aspect
@Component
@Slf4j
public class ModelViewAspect {

    //设置切入点:这里直接拦截被@ModelView注解的方法
    @Pointcut("@annotation(club.krislin.exception.ModelView)")
    public void pointcut() { }

    /**
     * 当有ModelView的注解的方法抛出异常的时候,做如下的处理
     */
    @AfterThrowing(pointcut="pointcut()",throwing="e")
    public void afterThrowable(Throwable e) {
        log.error("切面发生了异常:", e);
        if(e instanceof  CustomException){
            throw ModelViewException.transfer((CustomException) e);
        }
    }
}

全局异常处理器:

代码语言:javascript
复制
@ExceptionHandler(ModelViewException.class)
    public ModelAndView viewExceptionHandler(HttpServletRequest req, ModelViewException e) {
        ModelAndView modelAndView = new ModelAndView();

        //将异常信息设置如modelAndView
        modelAndView.addObject("exception", e);
        modelAndView.addObject("url", req.getRequestURL());
        modelAndView.setViewName("error");

        //返回ModelAndView
        return modelAndView;
    }

本文系转载,前往查看

如有侵权,请联系?cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系?cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 乱象二:混乱的返回方式
  • 二、该如何设计异常处理
  • 面向相关方友好
  • 三、开发规范
  • 四、页面类异常处理
  • 错误的写法
  • 正确的写法
  • 五、用面向切面的方法处理页面全局异常
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com