背景
AOP(面向切面编程)是Spring的核心功能之一,本位对AOP的基本配置做一总结。
内容
- 切点表达式
execution([可见性] 返回类型 [方法所在类的完全限定名称].方法名(参数) [异常])
*: 匹配所有字符
..: 一般用于匹配多个包,多个参数
+: 表示类及其子类
- 切面注解
@Aspect 将一个java类定义为切面类
@Pointcut 定义一个切入点(可以是一个正则表达式,例如某个package下的所有函数,也可以是一个注解)
根据需要在切入点的不同位置切入内容:
@Before 在切入点开始时,切入内容
@After 在切入点结尾处,切入内容
@Around 在切入点前后处,切入内容,并自己控制何时执行内容的切入
@AfterReturning 在切入点return内容之后切入内容,用来定义对返回值的处理逻辑
@AfterThrowing 在切入点抛出异常后切入内容,用来定义抛出异常之后的处理逻辑
- 切面类(注意:需用@Componet标注后,才能被注册到Spring容器中)
@Aspect
@Component
public class MyAspect {
private final String pointcut = "pointcut()";
// 切点
@Pointcut("execution(* com.rainlf.service.someService(..))")
private void pointcut(){
}
// 前置通知
@Before(pointcut)
public void before(){
System.out.println("前置通知....");
}
// 后置通知, returnVal: 切点方法执行后的返回值
@AfterReturning(value=pointcut, returning = "returnVal")
public void AfterReturning(Object returnVal){
System.out.println("后置通知...." + returnVal);
}
// 环绕通知
@Around(pointcut)
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取logger
Class clazz = joinPoint.getTarget().getClass();
Logger logger;
try {
Field field = clazz.getDeclaredField("logger");
if (!field.isAccessible()) {
field.setAccessible(true);
}
logger = (Logger) field.get(clazz);
} catch (IllegalAccessException | NoSuchFieldException e) {
logger = LoggerFactory.getLogger(clazz);
logger.warn("目标类中无默认logger,建议添加。");
}
// 获取方法名
String methodName = joinPoint.getSignature().getName();
// 记录函数参数
logger.debug("函数: {}, 参数: {}", methodName, Arrays.toString(joinPoint.getArgs()));
// 函数开始时间
long start = System.currentTimeMillis();
// 执行函数
Object obj;
try {
obj = joinPoint.proceed();
} catch (Exception e) {
logger.error("函数: {}, 发生异常: {}", methodName, e.getMessage());
throw e;
}
// 函数结束时间
long end = System.currentTimeMillis();
// 记录函数返回值
logger.debug("函数: {}, 返回值: {}", methodName, obj);
// 记录函数耗时
logger.debug("函数: {}, 耗时: {} ms", methodName, (end - start));
return obj;
}
// 异常通知
@AfterThrowing(value=pointcut, throwing = "e")
public void afterThrowable(Throwable e) {
System.out.println("出现异常:msg="+e.getMessage());
}
// 最终通知
@After(value=pointcut)
public void after(){
System.out.println("最终通知....");
}
}
-
切点通知顺序
-
切点定义相关规则图解