8.4.5 基于注解的 零配置 方式 6. Around增强处理

8.4.5 基于注解的 零配置 方式 6. Around增强处理

@Around注解用于修饰Around增强处理, Around增强处理是功能比较强大的增强处理,它近似等于Before增强处理和AfterReturning增强处理的总和, Around处理既可在执行目标方法之前织入增强动作,也可在执行目标方法之后织入增强动作

Around增强与Before增强和AfterReturning增强的不同

Before增强处理、 AfterReturning增强处理不同的是, Around增强处理可以决定目标方法在什么时候执行,如何执行,甚至可以完全阻止目标方法的执行。
Around增强处理可以改变执行目标方法的参数值,也可以改变执行目标方法之后的返回值。

什么时候适合使用Around增强

Around增强处理的功能虽然强大,但通常需要在线程安全的环境下使用。因此,如果使用普通的Before增强处理、Afterreturning增强处理就能解决的问题,则没有必要使用Around增强处理了。
如果需要目标方法执行之前和之后共享某种状态数据,则应该考虑使用Around增强处理;尤其是需要改变目标方法的返回值时,则只能使用Around增强处理了

使用Value属性指定被织入的切入点

Around增强处理方法应该使用@Around来标注,使用@Around注解时需要指定一个value属性,该属性指定该增强处理被织入的切入点。

第一个形参必须是ProceedingJoinPoint类型

当定义一个Around增强处理方法时,该方法至少包含一个形参,并且第一个形参必须是ProceedingJoinPoint类型

需要显示调用ProceedingJoinPoint参数的proceed方法来执行目标方法

在增强处理方法体内,调用ProceedingJoinPoint参数的proceed方法才会执行目标方法,这就是Around增强处理可以完全控制目标方法的执行时机、执行方式的关键;
如果程序没有调用ProceedingJoinPoint参数的proceed方法,则目标方法不会被执行。

执行目标方法时传入参数

调用ProceedingJoinPoint参数的proceed方法时,还可以传入一个Object数组作为参数,该数组中的值将被传入目标方法作为执行方法的实参

程序示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\Around
└─src\
├─beans.xml
├─lee\
│ └─BeanTest.java
└─org\
└─crazyit\
└─app\
├─aspect\
│ └─TxAspect.java
└─service\
├─Hello.java
├─impl\
│ ├─HelloImpl.java
│ └─WorldImpl.java
└─World.java

下面的程序定义了一个Around增强处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package org.crazyit.app.aspect;

import org.aspectj.lang.annotation.*;
import org.aspectj.lang.*;

// 定义一个切面
@Aspect
public class TxAspect
{
// 匹配org.crazyit.app.service.impl包下所有类的、
// 所有方法的执行作为切入点
@Around("execution(* org.crazyit.app.service.impl.*.*(..))")
public Object processTx(ProceedingJoinPoint jp) throws java.lang.Throwable
{
System.out.println("执行目标方法之前,模拟开始事务...");
// 获取目标方法原始的调用参数
Object[] args = jp.getArgs();
if (args != null && args.length > 1)
{
// 修改目标方法的第一个参数
args[0] = "【增加的前缀】" + args[0];
}
// 以改变后的参数去执行目标方法,并保存目标方法执行后的返回值
Object rvt = jp.proceed(args);
System.out.println("执行目标方法之后,模拟结束事务...");
// 如果目标方法的返回值rvt的类型是Integer,将rvt改为它的平方
if (rvt != null && rvt instanceof Integer)
rvt = (Integer) rvt * (Integer) rvt;
return rvt;
}
}

上面的程序定义了一个TxAspect切面,该切面里包含一个Around增强处理的processTx()方法,该方法中的代码:Object rvt = jp.proceed(args);用于回调目标方法,回调目标方法时传入了一个args数组,但这个args数组是执行目标方法的原始参数被修改后的结果,这样就实现了对调用参数的修改;rvt = (Integer) rvt * (Integer) rvt;这行代码则用于改变目标方法的返回值。
本示例程序中依然使用前面的Hellolmpl.javaWorld.java类,只是主程序增加了输出addUser()方法返回值的功能。执行主程序,将看到如下效果。

1
2
3
4
5
6
7
8
9
10
11
执行目标方法之前,模拟开始事务...
执行Hello组件的addUser添加用户:【增加的前缀】孙悟空
执行目标方法之后,模拟结束事务...
addUser()的返回值为:400
执行目标方法之前,模拟开始事务...
执行Hello组件的deleteUser删除用户:1
执行目标方法之后,模拟结束事务...
执行目标方法之前,模拟开始事务...
执行World组件的bar()方法
执行目标方法之后,模拟结束事务...

从运行结果可以看出,使用Around增强处理可以取得对目标方法最大的控制权,

  • 既可完全控制目标方法的执行,
  • 也可改变执行目标方法的参数,
  • 还可改变目标方法的返回值。

proceed方法调用目标方法时参数不匹配会出现异常

当调用ProceedingJoinPointproceed方法时,传入的Object数组参数值将作为目标方法的参数,如果传入的Object数组的长度与目标方法所需要参数的个数不相等,或者Object数组元素与目标方法所需参数的类型不匹配,程序就会出现异常。

为了能获取目标方法的参数的个数和类型,需要增强处理方法能访问执行目标方法的参数。