Spring 中的 AOP——在 Advice 方法中获取目标方法的参数
Signature getSignature:返回目标方法的签名
Object getTarget:返回被织入增强处理的目标对象
Object getThis:返回 AOP 框架为目标对象生成的代理对象
注意:当使用 @Around 处理时,我们需要将第一个参数定义为 ProceedingJoinPoint 类型,该类是 JoinPoint 的子类。
下面的切面类(依然放在 com.abc.advice 包中)中定义了 Before、Around、AfterReturning 和 After 4 中增强处理,并分别在 4 种增强处理中访问被织入增强处理的目标方法、目标方法的参数和被织入增强处理的目标对象等:
package com.abc.advice;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class AdviceTest {
@Around("execution(* com.abc.service..many(..))")
public Object process(ProceedingJoinPoint point) throws Throwable {
System.out.println("@Around:执行目标方法之前...");
//访问目标方法的参数:
Object[] args = point.getArgs();
if (args != null && args.length > 0 && args[0].getClass() == String.class) {
args[0] = "改变后的参数 1";
}
//用改变后的参数执行目标方法
Object returnValue = point.proceed(args);
System.out.println("@Around:执行目标方法之后...");
System.out.println("@Around:被织入的目标对象为:" + point.getTarget());
return "原返回值:" + returnValue + ",这是返回结果的后缀";
}
@Before("execution(* com.abc.service..many(..))")
public void permissionCheck(JoinPoint point) {
System.out.println("@Before:模拟权限检查...");
System.out.println("@Before:目标方法为:" +
point.getSignature().getDeclaringTypeName() +
"." + point.getSignature().getName());
System.out.println("@Before:参数为:" + Arrays.toString(point.getArgs()));
System.out.println("@Before:被织入的目标对象为:" + point.getTarget());
}
@AfterReturning(pointcut="execution(* com.abc.service..many(..))",
returning="returnValue")
public void log(JoinPoint point, Object returnValue) {
System.out.println("@AfterReturning:模拟日志记录功能...");
System.out.println("@AfterReturning:目标方法为:" +
point.getSignature().getDeclaringTypeName() +
"." + point.getSignature().getName());
System.out.println("@AfterReturning:参数为:" +
Arrays.t
oString(point.getArgs()));
System.out.println("@AfterReturning:返回值为:" + returnValue);
System.out.println("@AfterReturning:被织入的目标对象为:" + point.getTarget());
}
@After("execution(* com.abc.service..many(..))")
public void releaseResource(JoinPoint point) {
System.out.println("@After:模拟释放资源...");
System.out.println("@After:目标方法为:" +
point.getSignature().getDeclaringTypeName() +
"." + point.getSignature().getName());
System.out.println("@After:参数为:" + Arrays.toString(point.getArgs()));
System.out.println("@After:被织入的目标对象为:" + point.getTarget());
}
}
在 AdviceManager 类中增加以下内容:
//将被 AdviceTest 的各种方法匹配
public String manyAdvices(String param1, String param2) {
System.out.println("方法:manyAdvices");
return param1 + " 、" + param2;
}
在 com.abc.main.AOPTest 中加入方法的调用,触发切点:
String result = manager.manyAdvices("aa", "bb");
System.out.println("Test 方法中调用切点方法的返回值:" + result);
下面是执行结果:
@Around:执行目标方法之前...
@Before:模拟权限检查...
@Before:目标方法为:com.abc.service.AdviceManager.manyAdvices
@Before:参数为:[改变后的参数 1, bb]
@Before:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e
方法:manyAdvices
@Around:执行目标方法之后...
@Around:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e
@After:模拟释放资源...
@After:目标方法为:com.abc.service.AdviceManager.manyAdvices
@After:参数为:[改变后的参数 1, bb]
@After:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e
@AfterReturning:模拟日志记录功能...
@AfterReturning:目标方法为:com.abc.service.AdviceManager.manyAdvices
@AfterReturning:参数为:[改变后的参数 1, bb]
@AfterReturning:返回值为:原返回值:改变后的参数 1 、 bb,这是返回结果的后缀
@AfterReturning:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e
Test 方法中调用切点方法的返回值:原返回值:改变后的参数 1 、bb,这是返回结果的后缀
从结果中可以看出:在任何一个织入的增强处理中,都可以获取目标方法的信息。另外,Spring AOP 采用和 AspectJ 一样的有限顺序来织入增强处理:在“进入”连接点时,最高优先级的增强处理将先被织入(所以给定的两个 Before 增强处理中,优先级高的那个会先执行);在“退出”连接点时,最高优先级的增强处理会最后被织入(所以给定的两个 After 增强处理中,优先级高的那个会后执行)。当不同的切面中的多个增强处理需要在同一个连接点被织入时,Spring AOP 将以随机的顺序来织入这些增强处理。如果应用需要指定不同切面类里的增强处理的优先级,Spring 提供了如下两种解决方案:
让切面类实现 org.springframework.core.Ordered 接口:实现该接口只需要实现一个 int getOrder()方法,该方法返回值越小,优先级越高
直接使用 @Order 注解来修饰一个切面类:使用这个注解时可以配置一个 int 类型的 value 属性,该属性值越小,优先级越高
优先级高的切面类里的增强处理的优先级总是比优先级低的切面类中的增强处理的优先级高。例如:优先级为 1 的切面类 Bean1 包含了 @Before,优先级为 2 的切面类 Bean2 包含了 @Around,虽然 @Around 优先级高于 @Before,但由于 Bean1 的优先级高于 Bean2 的优先级,因此 Bean1 中的 @Before 先被织入。
同一个切面类里的两个相同类型的增强处理在同一个连接点被织入时,Spring AOP 将以随机的顺序来织入这两个增强处理,没有办法指定它们的织入顺序。如果确实需要保证它们以固有的顺序被织入,则可以考虑将多个增强处理压缩为一个增强处理;或者将不同增强处理重构到不同切面中,通过在切面级别上定义顺序。
如果只要访问目标方法的参数,Spring 还提供了一种更加简洁的方法:我们可以在程序中使用 args 来绑定目标方法的参数。如果在一个 args 表达式中指定了一个或多个参数,该切入点将只匹配具有对应形参的方法,且目标方法的参数值将被传入增强处理方法。下面辅以例子说明:
package com.abc.advice;
import java.util.Date;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class AccessArgAdviceTest {
@AfterReturning(
pointcut="execution(* com.abc.service..access(..)) && args(time, name)",
returning="returnValue")
评论