您的位置:首页 > 理论基础 > 计算机网络

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

2016-12-10 03:25 405 查看

需求业务场景:

在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());
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐