全局异常处理

title: 全局异常处理
cover: 'https://i.loli.net/2021/10/14/921G8ZSv4nHAXti.png'
secret: false
categories:

  • code
    tags:
  • 异常
  • AOP
  • '@ControllerAdvice'
    abbrlink: 33238
    date: 2021-10-14 01:15:03
    password:
    message:

前言

在Java web 项目中,我们经常需要自己去实现一些异常处理的代码。随着Spring框架的不断升级,也给我们带来了更多的选择。下面说一下可行性比较好的两个方式吧:

  • 采用 Spring AOP 方式实现
  • 采用 Spring3.2之后提供的 @ControllerAdvice 注解 和 @ExceptionHandler 实现

关于 Controller 层异常的处理

如果你是想处理关于Controller层的异常(不仅限于是controller层自身主动抛出/catch的异常,只要是最终由controller层抛出的异常都可以使用 @ControllerAdvice 方式),那么以上两种方式均可由你选择。

@ControllerAdvice + @ExceptionHandler 方式

见名知意,@ControllerAdvice 是对于 使用@Controller的建议,那么凡是使用了@RequestMapping的地方的异常均可该种方式处理。
下面我以一个demo来说明一下,如何使用吧。
controller

@RestController
public class UserController {
  @RequestMapping(value = "/update", method = RequestMethod.POST)
    public R update(@RequestBody User user){
        userService.update(user);
        return R.success(null);
    }
}

注意:在调用的service中,我手动模拟抛出了一个异常,如下所示:

@Service
public class UserServiceImpl implements UserService {
  @Override
    public void update(User user) {
        userMapper.update(user);
        throw new RuntimeException("运行时易出错");
    }
}

关于类似这种controller层的异常我们就可以构造一个全局controller异常的处理工具,如下:

// 使用 @ControllerAdvice 方式来实现对 @RequestMapping 的异常拦截 // 缺点很明显,是对于 controller 的建议,只能对controller起作用
@ControllerAdvice
public class ControllerException {

    private static final Logger log = LoggerFactory.getLogger(ControllerException.class);

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Map<String, String> getMsg(HttpServletRequest request, HttpServletResponse response, Exception e){

        log.error("该服务可能出错了");
        return Collections.singletonMap("msg", "服务出错");
    }
}

这样当调用 /update 接口时,service层会报错,从而传递到controller层,从而被 ControllerException 异常处理类拦截,就可以在这里解决了,可以down下代码,自行测试一下,这里不做演示了。


这种方式的缺点很明显:只能处理 controller 层抛出的异常,对例如 Interceptor(拦截器)层的异常、定时任务中的异常、异步方法中的异常,不会进行处理。

Spring AOP 动态代理方式

上面的controller层和service代码不用作任何修改。只需要再新增一个异常处理类即可。

package com.example.study_source.transaction.exception;

// 使用 aop 来全局拦截异常,可以作用于你自定义的任何包路径下 // 相比 @ControllerAdvice 有优势

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Slf4j
public class AOPGlobalException {

    @Pointcut("execution(* com.example.study_source.transaction..*.*(..))")
    public void point1(){

    }

    @AfterReturning(pointcut = "point1()")
    public void afterReturn(JoinPoint joinPoint){
        log.error("全局捕获到异常了..............");
        //纪录错误信息
        // todo 想要执行的操作
    }
}

如上方式不仅可以实现controller层的异常处理,只要符合切点包的定义的方法,都可以拦截。

本文参考于segmentfault,特此表示感谢!

posted @ 2022-06-21 23:49  LoremMoon  阅读(119)  评论(0编辑  收藏  举报