接手同事的工作,由于很多模块都是刚写的没做测试,代码里也没有做错误日志记录,测试一直报bug,但测试环境又不能看出哪报错,无从下手。一个一个方法加日志处理加起来太麻烦,就想着能不能调用统一处理。
那这个概念就和aop 切面不谋而合。
AOP介绍:
spring aop英文文档
AOP:Aspect-Oriented Programming的缩写
JoinPoint:要切入的点,具体的方法。
Pointcut: 符合条件的Joinpoint集合。
Aspect:切面,即封装了切入面和切入点的模块,例如我们要做的日志处理,就是切面。
Advice:切点如何处理,什么时候处理(之前、之后、环绕等等)。
poincut放在controller中,开始打算使用 After throwing advice,可以对系统中没做处理的抛出进行增强处理,但项目中需要返回自定义响应数据实体类,以显示请求状态,joinpoint方法出现抛出异常后无法正常返回,显然无法满足需求。
那哪些advice 适合呢?下面同样是官方文档相关内容
Before advice: Advice that executes before a join point, but which does not have the ability to prevent execution flow proceeding to the join point (unless it throws an exception).
After returning advice: Advice to be executed after a join point completes normally: for example, if a method returns without throwing an exception.
After throwing advice: Advice to be executed if a method exits by throwing an exception.
After (finally) advice: Advice to be executed regardless of the means by which a join point exits (normal or exceptional return).
Around advice: Advice that surrounds a join point such as a method invocation. This is the most powerful kind of advice. Around advice can perform custom behavior before and after the method invocation. It is also responsible for choosing whether to proceed to the join point or to shortcut the advised method execution by returning its own return value or throwing an exception.
这是最有力的advice。可以在方法调用之前之后执行自定义行为。它还可以选择继续执行连接点或返回其自身的返回值或引发异常来快速建立方法执行。
如上,around可以很好的满足我们的需求。
下面是一些具体示例,主要展示一些大体框架,至于对于错误日志是入库还是怎么处理,看个人具体需求。
pring 配置
<aop:aspectj-autoproxy/>//开注解 @AspectJ Support
注解方式实现:
package com.test.test;
import com.common.util.ResponseDataUtil;
import org.apache.poi.ss.formula.functions.T;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
@Aspect
@Component
public class errorLogAspect {
private static final Logger logger = LoggerFactory.getLogger(logAspect.class);
//controller的包名,所有返回自定义响应的方法
@Pointcut("execution(com.ResponseData<*> com.test.test..* (..))")
public void pointCut() {
}
/**
* 环绕方式方便修改返回值
* @param jp
* @return
*/
@Around("pointCut()")
public ResponseData<T> handlerAround(ProceedingJoinPoint jp){
try {
return (ResponseData<T>)jp.proceed();//反射执行目标对象的连接点处的方法,可修改入参
} catch (Throwable e) {
Object[] args = jp.getArgs();//参数
String className = ((MethodSignature) jp.getSignature()).getMethod().getDeclaringClass().getName();//类名
String funcName = jp.getSignature().getName();//方法名
StringBuilder sb = new StringBuilder("调用"+ className +" 的"+ funcName+" 方法异常。方法入参:"+Arrays.toString(args));
logger.error(sb.toString(), e);
return ResponseDataUtil.getFailResponse("内部错误" + e.getMessage());
}
}
}
总的来说,学会面向文档编程吧,学会啃文档。另外业务上不要把异常处理等代码和业务代码混杂在一起,减少重复代码并且让业务主逻辑更清晰明了。