面向切面思想实现项目全局异常处理(简单切面+Spring提供的封装)

以前写项目的时候所谓,异常直接在各个层里面捕捉处理后向外返回错误信息,但是实际上有些运行时异常很容易被忽略,这样就会导致这些异常出现后会直接返回给调用方。当时的解决方案就是在Controller层的每个方法加try-catch块,捕捉所有的异常并处理后返回给调用方,但是这样处理起来比较麻烦,因为每个方法上都要加try-catch,显得代码很不优雅。后来随着项目接触的多,有使用拦截器或者过滤器的,当然现在使用最多的还是切面,并且在Spring boot中还提供了封装好的切面,实现即可。

使用切面思想统一处理

使用切面的思想实现,主要的就两步,第一步是是编写切面的逻辑方法,第二步只要将切面作用到对应的控制层的方法上即可。当然在Spring中已经提供了对应实现封装,只要使用注解和配置即可,但是为了循序渐进的了解这个实现过程,还是先看一下原始的写法是什么样的。

原始写法

编写全局异常处理的实现类:

/*全局异常处理切面类*/
@Aspect
@Service
public class ExceptionAspectHandler {

    @Pointcut(value = "@annotation(com.itcrud.jpa.annotaion.MyExceptionHandler)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object exceptionHandle(ProceedingJoinPoint joinPoint) throws Throwable {
        //TODO 可以获取请求参数进行打印操作
        try {
            Object object = joinPoint.proceed();//执行业务方法
            //可以打印响应结果信息
            System.out.println("响应信息:"+ object);
            return object;
        } catch (ServiceException e) {
            //TODO 自定义异常处理逻辑
            return new Object();//模拟异常处理结果返回
        } catch (Exception e) {
            //TODO 系统异常处理逻辑
            return new Object();//模拟异常处理结果返回
        }
    }
}

上面的代码注释很清楚,注释写的也很详细,就是没有具体的业务处理代码,偷懒一下,有兴趣的自己补充。另外这里使用的注解方式实现,需要在控制层内的方法上加@MyExceptionHandler注解。但是这种实现是不友好的,可以考虑将注解加到类上,也可以考虑使用表达式,在这里感觉表达式是最给力的。

自定义注解代码:

@Target({ElementType.METHOD}) //只能作用在方法上的注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExceptionHandler {

}

表达式的写法:

execution(* com.itcrud.blog.*.controller..*.*(..))

大概说一下这个表达式,在blog和controller中间加了一个*表示的是功能模块,如在一个大的maven项目下有web、api等,因为分类不同,这里的*可能就不同,所以使用通配符更好,如果你的项目没有涉及到这种分类,那就可以不同考虑这个*,直接去掉就好。

到这里一个原始的写法基本就是这样。

Spring提供的方法

Spring对于异常的处理切面给出了一套注解,以@ControllerAdvice@RestControllerAdvice为主。下面来介绍一下。

  • @ControllerAdvice@RestControllerAdvice是直接写在异常处理类上的,类似于上面的切面类,这两个注解的区别就是在于有没有@ResponseBody。可以类比@Controller@RestController之间的区别。
  • @ExceptionHandler是用在具体处理异常的方法上,可以通过value属性来指定此方法处理的异常类型。
  • @ResponseStatus是用在具体处理异常的方法上,指定响应给调用方的状态吗,如404、500等,状态码的指定使用HttpStatus枚举类。
  • @ResponseBody可以用在处理异常的方法上,对响应的结果进行json的转换,如果在类上使用了@RestControllerAdvice注解,方法上就不用加@ResponseBody啦。(但是这里还是建议分开写,细化)

示例代码:

@ControllerAdvice
public class WebExceptionHandler {
    
    /*自定义异常处理*/
    @ExceptionHandler(value = ServiceException.class)
    @ResponseBody
    public Map<String,String> localHandle(){
        return new HashMap<String, String>();
    }
    
    /*空指针异常处理*/
    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public String NullPointHandle(){
        return "";
    }
    
    /*最终异常处理*/
    @ExceptionHandler(Exception.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public String exceptionHandle(){
     return "";   
    }
}

上面的一段简洁代码就能实现,不需要在控制层内加任何注解,这个类会对被@RequestMapping作用的方法自动拦截,实现异常的统一处理。

这种处理方式让我想起了一个问题,如果现在控制层里面定义了一个方法,这个方法是控制层中的一个公用方法,但是不处理外部的请求,使用之前原始写法的表达式方式就会出现问题,这个公用方法也会被切面切到,是不符合设计效果的,因此还是要在切面中去判断被切到的方法是否被@RequestMapping注解修饰。

总的来说,Spring这个叫春的东西真的很好用,提供的功能都很强大。

总结

在用原始写法的时候,一定要注意加上必要的判断,防止出现一些预想不到的问题。合理使用第三方提供的功能封装能够大大的提高开发的效率。

其实这个原始写法还是很有用的,正如我注释中写的,可以将请求的出参入参打印出来,另外的就是可以做日志编号的统一管理,实现日志链,方便在日志中查找。当然了这里也可以通过实现Spring的HandlerInterceptor接口,然后重写preHandlepostHandle以及afterCompletion方法来实现。总感觉Spring什么地方都可以插一腿。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序猿洞晓

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值