解决Springboot全局异常处理与AOP日志处理中@AfterThrowing失效问题

 更新时间:2023年05月15日 10:32:08   作者:Janson666  
这篇文章主要介绍了解决Springboot全局异常处理与AOP日志处理中@AfterThrowing失效问题,文中介绍了两种失效场景,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧

一、前言

  • 在实际业务场景中,我们通常会使用全局异常处理机制,也就是在业务代码发生异常的时候,拦截异常并进行统一的处理,然后以Json格式返回给前端。
  • 同时我们也会使用AOP进行操作日志记录,在不发生异常时,可以使用四种advice方式记录操作日志:@Before(“”),@After(“”)、 @AfterReturning(“”)、 @Around(“”)。当发生异常时,使用@AfterThrowing(value = “”,throwing = “e”)进行日志记录。

二、问题

同时使用上述两种方式,可能出现某一种失效的场景。

三、失效场景

失效场景一: 如果采用前后端不分离架构,采用下属代码返回前端响应结果,如果统一异常处理执行顺序在@AfterThrowing之前,就会出现@AfterThrowing中不执行情况。前后端分离架构不会出现此问题。

String xRequestedWith = request.getHeader("x-requested-with");
if ("XMLHttpRequest".equals(xRequestedWith)) {
    response.setContentType("application/plain;charset=utf-8");
    PrintWriter writer = response.getWriter();
    writer.write(CommunityUtil.getJSONString(1, "服务器异常!"));
} else {
    response.sendRedirect(request.getContextPath() + "/error");
}

解决方案:让AOP日志处理类实现Ordered 接口,并重写getOrder()方法,使其返回值为1,返回值越小,执行的顺序越靠前,使其执行顺序优先于全部异常处理类。

@Component
@Aspect
public class LogAspectTest implements Ordered {
    @Override
    public int getOrder() {
        return 1;
    }
        @AfterThrowing(value = "pointcut()",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e){
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        System.out.println("className is : " + className + ". methodName is : " + methodName);
        System.out.println("afterThrowing excute········" + e.getMessage());
        e.printStackTrace();
    }
}    

失效场景二:如果使用了 @Around(“”),在执行 joinPoint.proceed()方法时,捕获了异常,会导致全局异常处理无法收到异常,因此失效。

@Around("pointcut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
    String className = joinPoint.getTarget().getClass().getName();
    String methodName = joinPoint.getSignature().getName();
    System.out.println("className is : " + className + ". methodName is : " + methodName);
    System.out.println("around excute before ········");
    Object obj = null;
    try {
        obj = joinPoint.proceed();
    } catch (Throwable e) {
        System.out.println("我捕获了异常");
    }
    System.out.println("obj 对象: " + obj);
    System.out.println("around excute after ········");
}

解决方案:不要进行异常捕获,或者捕获后重新抛出异常。

@Around("pointcut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
    String className = joinPoint.getTarget().getClass().getName();
    String methodName = joinPoint.getSignature().getName();
    System.out.println("className is : " + className + ". methodName is : " + methodName);
    System.out.println("around excute before ········");
    Object obj = null;
    try {
        obj = joinPoint.proceed();
    } catch (Throwable e) {
        System.out.println("我捕获了异常");
        throw new RuntimeException("执行失败",e);
    }
    System.out.println("obj 对象: " + obj);
    System.out.println("around excute after ········");
}

4、测试全部代码

package com.nowcoder.community.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
 * @Description aop 日志记录
 */
@Component
@Aspect
public class LogAspectTest implements Ordered {
    /**
     * 定义执行顺序的优先级,值越小,优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 1;
    }
    /**
     * 定义切点(织入点)
     *  execution(* com.nowcoder.community.controller.*.*(..))
     *      - 第一个 * 表示 支持任意类型返回值的方法
     *      - com.nowcoder.community.controller 表示这个包下的类
     *      - 第二个 * 表示 controller包下的任意类
     *      - 第三个 * 表示 类中的任意方法
     *      - (..) 表示方法可以拥有任意参数
     *   可以根据自己的需求替换。
     *
     */
    @Pointcut("execution(* com.nowcoder.community.controller.*.*(..))")
    public void pointcut(){
    }
    /**
     * 定义 advice 通知
     *    - 1. @Before 目标方法执行之前
     *    - 2. @After  目标方法执行之后
     *    - 3. @AfterReturning 目标方法返回执行结果之后
     *    - 4. @AfterThrowing 目标方法抛出异常后
     *    - 5. @Around 可以使用ProceedingJoinPoint joinPoint,获取目标对象,通过动态代理,代理目标类执行,在目标对象执行前后均可
     */
    @Before("pointcut()")
    public void before(JoinPoint joinPoint){
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        if (attributes == null){
            return;
        }
        // 请求ip
        String ip = attributes.getRequest().getRemoteHost();
        // 请求路径
        String requestURI = attributes.getRequest().getRequestURI();
        // 请求方法
        String requestMethod = attributes.getRequest().getMethod();
        System.out.println(String.format("用户: [%s] 的请求路径为:[%s], 请求方式为: [%s],请求类名为: [%s], 请求方法名为: [%s]", ip,requestURI,
                requestMethod,className,methodName));
        System.out.println("before excute········");
    }
    @After("pointcut()")
    public void after(JoinPoint joinPoint){
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        if (attributes == null){
            return;
        }
        // 请求ip
        String ip = attributes.getRequest().getRemoteHost();
        // 请求路径
        String requestURI = attributes.getRequest().getRequestURI();
        // 请求方法
        String requestMethod = attributes.getRequest().getMethod();
        System.out.println(String.format("用户: [%s] 的请求路径为:[%s], 请求方式为: [%s],请求类名为: [%s], 请求方法名为: [%s]", ip,requestURI,
                                         requestMethod,className,methodName));
        System.out.println("after excute········");
    }
    @AfterReturning("pointcut()")
    public void afterReturning(JoinPoint joinPoint){
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        if (attributes == null){
            return;
        }
        // 请求ip
        String ip = attributes.getRequest().getRemoteHost();
        // 请求路径
        String requestURI = attributes.getRequest().getRequestURI();
        // 请求方法
        String requestMethod = attributes.getRequest().getMethod();
        System.out.println(String.format("用户: [%s] 的请求路径为:[%s], 请求方式为: [%s],请求类名为: [%s], 请求方法名为: [%s]", ip,requestURI,
                                         requestMethod,className,methodName));
        System.out.println("afterReturning excute········");
    }
    @AfterThrowing(value = "pointcut()",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e){
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        if (attributes == null){
            return;
        }
        // 请求ip
        String ip = attributes.getRequest().getRemoteHost();
        // 请求路径
        String requestURI = attributes.getRequest().getRequestURI();
        // 请求方法
        String requestMethod = attributes.getRequest().getMethod();
        System.out.println(String.format("用户: [%s] 的请求路径为:[%s], 请求方式为: [%s],请求类名为: [%s], 请求方法名为: [%s],请求失败原因: [%s]", ip,
                                         requestURI, requestMethod,className,methodName,e.getMessage()+e.getCause()));
        System.out.println("afterThrowing excute········" + e.getMessage());
        e.printStackTrace();
    }
    @Around("pointcut()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        System.out.println("className is : " + className + ". methodName is : " + methodName);
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        if (attributes == null){
            return;
        }
        // 请求ip
        String ip = attributes.getRequest().getRemoteHost();
        // 请求路径
        String requestURI = attributes.getRequest().getRequestURI();
        // 请求方法
        String requestMethod = attributes.getRequest().getMethod();
        System.out.println(String.format("用户: [%s] 的请求路径为:[%s], 请求方式为: [%s],请求类名为: [%s], 请求方法名为: [%s]", ip,requestURI,
                                         requestMethod,className,methodName));
        Object obj = null;
        try {
            obj = joinPoint.proceed();
        } catch (Throwable e) {
            System.out.println("我捕获了异常");
            throw new RuntimeException("执行失败",e);
        }
        System.out.println(String.format("用户: [%s] 的请求路径为:[%s], 请求方式为: [%s],请求类名为: [%s], 请求方法名为: [%s]", ip,requestURI,
                                         requestMethod,className,methodName));
        System.out.println("around excute after ········");
    }
}

到此这篇关于解决Springboot全局异常处理与AOP日志处理中@AfterThrowing失效问题的文章就介绍到这了,更多相关Springboot @AfterThrowing失效内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Boot打包war jar 部署tomcat

    Spring Boot打包war jar 部署tomcat

    这篇文章主要介绍了Spring Boot打包war jar 部署tomcat的相关资料,需要的朋友可以参考下
    2017-10-10
  • SpringBoot打成jar包瘦身方法总结

    SpringBoot打成jar包瘦身方法总结

    springBoot打包的时候代码和jar包打包在同一个jar包里面,会导致jar包非常庞大,下面这篇文章主要给大家介绍了关于SpringBoot打的jar包瘦身方法的相关资料,需要的朋友可以参考下
    2022-12-12
  • 关于设置Mybatis打印调试sql的两种方式

    关于设置Mybatis打印调试sql的两种方式

    这篇文章主要介绍了关于设置Mybatis打印调试sql的两种方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08
  • 浅谈Java内存模型之happens-before

    浅谈Java内存模型之happens-before

    于存在线程本地内存和主内存的原因,再加上重排序,会导致多线程环境下存在可见性的问题。那么我们正确使用同步、锁的情况下,线程A修改了变量a何时对线程B可见?下面小编来简单介绍下
    2019-05-05
  • 每日几道java新手入门面试题,通往自由的道路

    每日几道java新手入门面试题,通往自由的道路

    这篇文章主要为大家分享了最有价值的是几道java面试题,涵盖内容全面,包括数据结构和算法相关的题目、经典面试编程题等,对hashCode方法的设计、垃圾收集的堆和代进行剖析,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • Java开发人员最常犯的10个错误

    Java开发人员最常犯的10个错误

    这篇文章主要介绍了Java开发人员最常犯的10个错误,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-07-07
  • Java中Spring技巧之扩展点的应用

    Java中Spring技巧之扩展点的应用

    这篇文章主要介绍了Java中Spring技巧之扩展点的应用,下文Spring容器的启动流程图展开其内容的相关资料,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-04-04
  • Java如何不解压读取.zip的文件内容

    Java如何不解压读取.zip的文件内容

    这篇文章主要给大家介绍了关于Java如何不解压读取.zip的文件内容的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • Java和C#下的参数验证方法

    Java和C#下的参数验证方法

    下面小编就为大家带来一篇Java和C#下的参数验证实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-09-09
  • Java使用Comparable解决排序问题

    Java使用Comparable解决排序问题

    这篇文章主要介绍了Java使用Comparable解决排序问题的方法,涉及java针对文件的相关操作及排序的技巧,需要的朋友可以参考下
    2015-05-05

最新评论