在spring mvc项目中,使用@Aspect截取所有contoller中的exception,并且通过HttpServletResponse输出的一些问题

需求业务场景:

在spring 4.3 的mvc项目中,需要把所有controller层中的exception全部捕获,把错误信息进行处理后,通过HttpServletResponse 输出到浏览器

出现的问题:

2个项目都需要完成这个场景,但是一个项目实现了。另外一个项目通过同样的配置,却无法实现。表现在aop虽然截获了所有的controller中的exception,通过aop中的@autowired注入的HttpServletResponse却无法替换controller中的输出。浏览器界面依然显示的是标准的web 500 出错界面。

解决过程:

通过2个项目比对,发现实现这个功能需要注意以下几点:
1. 对controller中所有的方法进行切面的定义后,如果只是用@AfterThrowing 来通知处理出错内容,并且需要用HttpServletResponse来代替controller输出到浏览器,需要对同样的切面,进行@Around通知处理。
2. @Around 通知,必须优先于 @AfterThrowing通知.
3. 在同一个@Aspect类中,无法做到第二点,所以需要拆分成2个@Aspect类,并且通过@Order来指定顺序。
相关文档可以查看:
http://sishuok.com/forum/blogPost/list/0/2474.html
4. 另外一个项目碰巧可以实现这个功能,是因为@Around通知所在的aop类的包名排名在@AfterThrowing通知所在的aop类的签名。应该是优先载入了,所以在没有显式指定的时候,恰好就完成了这个功能,并且没有错误。
5. 相关AOP代码如下代码:2个代码都是定义同样的切面(切入所有的controller中的方法)
a. 处理@Around 的AOP类代码

/**
 * @Title: SystemLogAspect.java
 * @Package com.ninelephas.meerkat.aspect
 * @Description: 系统日志的AOP类
 *               Copyright: Copyright (c) 2016
 *               Company:九象网络科技(上海)有限公司
 * 
 * @author "徐泽宇"
 * @date 2016年8月1日 下午5:25:15
 * @version V1.0.0
 */

package com.ninelephas.terrier.aspect.controller;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 
 * @ClassName: ArroundControllerAspect
 * @Description: 处理所有controller类中方法的环绕通知,什么事情都不做,就是执行方法,为了和CatchControllerExceptionAspect配合
 *               切面定义和 CatchControllerExceptionAspect 一致,并且优先级要高于CatchControllerExceptionAspect
 * @author 徐泽宇
 * @date 2016年12月10日 上午3:11:39
 *
 */
@Aspect
@Order(1)
@Component("com.ninelephas.terrier.aspect.controller.ArroundControllerAspect")
public class ArroundControllerAspect {

    private static final Logger logger = LogManager.getLogger(ArroundControllerAspect.class);

    /**
     * 
     * runControllerMethod
     * 
     * @Auther 徐泽宇
     * @Date 2016年12月10日 上午3:15:59
     * @Title: logTheController
     * @Description: 定义一个切面,指向所有的controller类中的所有方法
     */
    @Pointcut("execution(* com.ninelephas.terrier.controller..*.*(..)) ")
    public void runControllerMethod() {
        // Nothing to clean up
    }



    /**
     * 
     * runControllerMethod
     * 
     * @Auther 徐泽宇
     * @Date 2016年12月10日 上午3:17:12
     * @Title: runControllerMethod
     * @Description: 什么都不处理,仅仅是增加一个环绕通知.
     * 这里必须返回一个object,把处理过程交回给controller。否则controller中的@ResponseBody不会起作用了
     * @param joinPoint
     */
    @Around("runControllerMethod()")
    private Object runControllerMethod(ProceedingJoinPoint joinPoint) {
        if (logger.isDebugEnabled()) {
            logger.debug("runControllerMethod(ProceedingJoinPoint joinPoint={}) - start", joinPoint); //$NON-NLS-1$
        }

        Object rtnObject = null;
        try {
            rtnObject = joinPoint.proceed();
        } catch (Throwable e) {
            logger.error(e);
        }

        if (logger.isDebugEnabled()) {
            logger.debug("runControllerMethod(ProceedingJoinPoint joinPoint={}) - end", joinPoint); //$NON-NLS-1$
        }
        return rtnObject;
    }
}

b. 处理@AfterThrowing的AOP类代码(核心功能),处理controller中的exception,并且用HttpServletResponse 输出

package com.ninelephas.terrier.aspect.catchcontroller;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * @ClassName: CatchControllerExceptionAspect
 * @Description: 捕获所有Controller中的方法的Exception
 *               通过HttpServletResponse输出到浏览器
 * @author 徐泽宇
 * @date 2016年11月1日 下午10:05:29
 *
 */
@Aspect
@Order(2)
@Component("com.ninelephas.terrier.aspect.catchcontroller.CatchControllerExceptionAspect")
public class CatchControllerExceptionAspect {
    /**
     * Logger for this class
     */
    private static final Logger logger = LogManager.getLogger(CatchControllerExceptionAspect.class.getName());

    @Autowired
    private HttpServletResponse response;

    /**
     * catchController
     * 
     * @Auther 徐泽宇
     * @Date 2016年11月1日 下午10:08:50
     * @Title: catchControllerMethod
     * @Description: 切入所有controller中方法的切面
     */
    @Pointcut("execution(* com.ninelephas.terrier.controller..*.*(..)) ")
    public void catchControllerMethod() {
        // Nothing to clean up
    }

    /**
     * writeToHttpResponse
     * 
     * @throws IOException
     * 
     * @throws ServletException
     * 
     * @Auther 徐泽宇
     * @Date 2016年11月1日 下午10:11:24
     * @Title: writeToHttpResponse
     * @Description: 捕捉到切面产生的Exception后,写入HttpServletResponse
     */
    @AfterThrowing(throwing = "ex", pointcut = "catchControllerMethod()")
    private void writeToHttpResponse(Throwable ex) throws IOException {
        response.setStatus(500);
        response.getWriter().write(ex.getMessage());
        logger.debug("浏览器输出完成!");
        logger.error(ex, ex.fillInStackTrace());
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值