使用spring aop统一处理异常和打印日志方式

网友投稿 324 2022-10-20


使用spring aop统一处理异常和打印日志方式

我们很容易写出的代码

我们很容易写出带有很多try catch 和 logger.warn(),logger.error()的代码,这样一个方法本来的业务逻辑只有5行,有了这些,代码就变成了10行或者更多行,如:

public ResultDTO queryUserByCardId(String cardId) {

ResultDTO result = new ResultDTO();

StringBuilder log = new StringBuilder();

log.append("queryUserByCardId:" + cardId);

try {

checkCardIdNotNull(cardId);

StationUserDO userDO = userDAO.queryUserByCardId(cardId);

UserDTO stationUserDTO = DataTypeConvertUtils.DOToDTO(userDO);

result.setData(stationUserDTO);

logger.warn(log.append(" result:").toString() + result);

} catch (StationErrorCodeException e) {

//logger.error(log.append("catch StationErrorCodeException!").toString(), e);

result.setSuccess(false);

result.setErrorCode(e.getErrorCode().getErrorCode());

result.setErrorMessage(e.getErrorCode().getErrorMessage());

} catch (Exception e) {

logger.error(log.append("catch Exception!").toString(), e);

result.setSuccess(false);

result.setErrorCode(StationErrorCodeConstants.STA10001.getErrorCode());

result.setErrorMessage(StationErrorCodeConstants.STA10001.getErrorMessage());

}

return result;

}

实际上,我们的业务逻辑就几行而已,中间却夹杂着那么多的异常处理代码及日志信息代码。

如何改进代码

我们可以使用springaop,做一个切面,这个切面专门做记录日志和异常处理的工作,这样就能减少重复代码。

代码如下:

@Override

public ResultDTOqueryUserByCardId(String cardId) {

ResultDTO result = new ResultDTO();

checkCardIdNotNull(cardId);

StationUserDO userDO = stationUserDAO.queryStationUserByCardId(cardId);

StationUserDTO stationUserDTO = DataTypeConvertUtils.DOToDTO(userDO);

result.setData(stationUserDTO);

return result;

}

我们在切面中做异常处理和记录日志:

@Aspect

public class CardServiceAspect {

private final Logger logger = LoggerFactory.getLogger("card");

// 切入点表达式按需配置

@Pointcut("execution(* *.*(..)))")

private void myPointcut() {

}

@Before("execution(* *.*(..)))")

public void before(JoinPoint joinPoint) {

String className = joinPoint.getTarget().getClass().getName();

String methodName = joinPoint.getSignature().getName();

logger.warn(className + "的" + methodName + "执行了");

Object[] args = joinPoint.getArgs();

StringBuilder log = new StringBuilder("入参为");

for (Object arg : args) {

log.append(arg + " ");

}

logger.warn(log.toString());

}

@AfterReturning(value = "execution(* *.*(..)))", returning = "returnVal")

public void afterReturin(Object returnVal) {

logger.warn("方法正常结束了,方法的返回值:" + returnVal);

}

@AfterThrowing(value = "StationCardServiceAspect.myPointcut()", throwing = "e")

public void afterThrowing(Throwable e) {

if (e instanceof StationErrorCodeException) {

logger.http://error("通知中发现异常StationErrorCodeException", e);

} else {

logger.error("通知中发现未知异常", e);

}

}

@Around(value = "StationCardServiceAspect.myPointcut()")

public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

logger.warn("前置增强...");

Object result = null;

try {

result = proceedingJoinPoint.proceed();

} catch (Exception e) {

ResultDTO resultDTO = new ResultDTO();

if (e instanceof StationErrorCodeException) {

StationErrorCodeException errorCodeException = (StationErrorCodeException) e;

resultDTO.setSuccess(false);

resultDTO.setErrorCode(errorCodeException.getErrorCode().getErrorCode());

resultDTO.setErrorMessage(errorCodeException.getErrorCode().getErrorMessage());

} else http://{

resultDTO.setSuccess(false);

resultDTO.setErrorCode(StationErrorCodeConstants.STA10001.getErrorCode());

resultDTO.setErrorMessage(StationErrorCodeConstants.STA10001.getErrorMessage());

}

return resultDTO;

}

return result;

}

}

然后我们在spring配置文件中配置切面

这样,我们就可以统一处理异常和日志了。

不足点

利用这种方式,只能打入参和出参,还有抛出异常时打异常日志,不能打方法运行中的中间值,目前我只能想到,方法中间值的日志,就是用原来的方式打出,不知道大家有没有什么好的方法。

spring aop的其他使用

推荐使用aspectJ来完成面向切面编程。我们还可以利用aop完成其他功能如记录程序运行时间等。

aop实现统一记录请求方法返回值日志及统一异常处理

接到将请求返回值写入到日志方便查问题需求,首先考虑的是用拦截器实现,无奈拦截器postHandle方法里获取不到返回值就此作罢。

继续寻找新的方法,网上查询一番找到一个便捷的方法,利用log4j2,在log4j2.xml配置文件里添加如下配置:

这样就能将方法返回值记录到日志里了,但是这样记录的日志和系统其它日志不一样不方便查看,此方法pass。最后只能用spring aop来实现此功能了,步骤如下:

1、引入aop依赖的jar包

org.springframework

spring-aop

4.0.0.RELEASE

org.springframework

spring-aspects

4.0.0.RELEASE

2、配置xml文件

引入aop命名空间

xmlns:aop="http://springframework.org/schema/aop"

http://springframework.org/schema/aop

http://springframework.org/schema/aop/spring-aop-4.0.xsd

3、编写切面类

@Aspect //指定当前类为切面类

@Component //把普通pojo实例化到spring容器中,相当于配置文件中的

public class MethodLogAndExceptionAop {

@Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")

public Object around(ProceedingJoinPoint jp) throws Throwable {

String targetName = jp.getTarget().getClass().getName();

String methodName = jp.getSignature().getName();

Object[] arguments = jp.getArgs();

Object[] args = new Object[arguments.length];

for (int i = 0; i < arguments.length; i++) {

if (arguments[i] instanceof ServletRequest || arguments[i] instanceof ServletResponse || arguments[i] instanceof MultipartFile) {

//ServletRequest不能序列化,从入参里排除,否则报异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)

//ServletResponse不能序列化 从入参里排除,否则报异常:java.lang.IllegalStateException: getOutputStream() has already been called for this response

continue;

}

args[i] = arguments[i];

}

Object result = null;

try {

//StopWatch 计时

StopWatch clock = new StopWatch();

clock.start();

result = jp.proceed();

clock.stop();

long executeTime = clock.getTime();

LoggerUtil.info(targetName, methodName, "调用Controller方法返回结果", result, executeTime, args);

} catch (Exception exception) {

LoggerUtil.error(targetName, methodName, "统一异常处理", exception, args);

ResultVo resultVo = new ResultVo(false);

// 为安全起见,只有业务异常我们对前端可见,否则统一归为系统异常

if (exception instanceof BusinessException) {

resultVo.setResultAndCode(false, ((BusinessException) exception).getErrorCode(), ((BusinessException) exception).getErrorMessage());

} else {

resultVo.setResultAndCode(false, ErrorCode.DEALER_ERR_100000.getCode(), "系统异常,请联系管理员");

}

result = resultVo;

}

return result;

}

}

系统本来的统一异常处理是通过实现HandlerExceptionResolver接口自定义异常处理,实现这个aop后发现,在这里也可以实现系统异常统一处理,于是就把自定义异常处理给干掉了。一举两得。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:2010年度中国十大布线品牌:泰科电子(上海)有限公司/安普布线
下一篇:数据中心意义和解决方案(如何管理数据中心)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~