8.4.6 基于XML配置文件的管理方式 2. 配置增强处理

8.4.6 基于XML配置文件的管理方式 2. 配置增强处理

与使用@AspectJ完全一样,使用XML一样可以配置BeforeAfterAfterReturningAfterThrowingAround五种增强处理,而且完全支持和@AspectJ完全一样的语义。
使用XML配置增强处理分别依赖于如下几个元素。

XML元素 描述
<aop:before> 配置 Before增强处理。
<aop:after> 配置Afer增强处理。
<aop:after-returning> 配置AfterReturning增强处理。
<aop:after-throwing> 配置AfterThrowing增强处理
<aop:around> 配置 Around增强处理。

上面这些元素都不支持使用子元素,但通常可指定如下属性。

属性 描述
pointcut或者pointcut-ref pointcut属性指定一个切入表达式, pointcut-ref属性指定已有的切入点名称, Spring将在匹配该表达式的连接点时织入该增强处理。通常pointcutpointcut-ref两个属性只需使用其中之一。
method 该属性指定一个方法名,指定将切面Bean的该方法转换为增强处理。
throwing 该属性只对<after-throwing>元素有效,用于指定一个形参名, AfteThrowing增强处理方法可通过该形参访问目标方法所抛出的异常。
returning 该属性只对< after-returning…>元素有效,用于指定一个形参名, Afterreturning增强处理方法可通过该形参访问目标方法的返回值

既然应用选择使用XML配置方式来配置增强处理,所以切面类里定义切面、切入点和增强处理的注解全都可删除了。

XML配置方式中的切入点指示符

当定义切入点表达式时,XML配置方式和@AspectJ注解方式支持完全相同的切入点指示符,一样可以支持executionwithinargsthistargetbean等切入点指示符。

XML配置方式中的组合运算符

XML配置方式和@AspectJ注解方式一样支持组合切入点表达式,但XML配置方式不再使用简单的&&!作为组合运算符(因为直接在XML文件中需要使用实体引用来表示它们),而是使用如下三个组合运算符:and(相当于&&)、or(相当于)和not(相当于!)。

程序示例

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

下面的程序定义了一个简单的切面类,该切面类只是将前面@Aspect示例中切面类的全部注解删除后的结果。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package org.crazyit.app.aspect;

import org.aspectj.lang.*;
import java.util.Arrays;

public class FourAdviceTest
{
public Object processTx(ProceedingJoinPoint jp) throws java.lang.Throwable
{
System.out.println("Around增强:执行目标方法之前,模拟开始事务...");
// 访问执行目标方法的参数
Object[] args = jp.getArgs();
// 当执行目标方法的参数存在,
// 且第一个参数是字符串参数
if (args != null && args.length > 0
&& args[0].getClass() == String.class)
{
// 修改目标方法调用参数的第一个参数
args[0] = "【增加的前缀】" + args[0];
}
// 执行目标方法,并保存目标方法执行后的返回值
Object rvt = jp.proceed(args);
System.out.println("Around增强:执行目标方法之后,模拟结束事务...");
// 如果rvt的类型是Integer,将rvt改为它的平方
if (rvt != null && rvt instanceof Integer)
rvt = (Integer) rvt * (Integer) rvt;
return rvt;
}
public void authority(JoinPoint jp)
{
System.out.println("②Before增强:模拟执行权限检查");
// 返回被织入增强处理的目标方法
System.out.println(
"②Before增强:被织入增强处理的目标方法为:" + jp.getSignature().getName());
// 访问执行目标方法的参数
System.out
.println("②Before增强:目标方法的参数为:" + Arrays.toString(jp.getArgs()));
// 访问被增强处理的目标对象
System.out.println("②Before增强:被织入增强处理的目标对象为:" + jp.getTarget());
}
public void log(JoinPoint jp, Object rvt)
{
System.out.println("AfterReturning增强:获取目标方法返回值:" + rvt);
System.out.println("AfterReturning增强:模拟记录日志功能...");
// 返回被织入增强处理的目标方法
System.out.println("AfterReturning增强:被织入增强处理的目标方法为:"
+ jp.getSignature().getName());
// 访问执行目标方法的参数
System.out.println(
"AfterReturning增强:目标方法的参数为:" + Arrays.toString(jp.getArgs()));
// 访问被增强处理的目标对象
System.out.println("AfterReturning增强:被织入增强处理的目标对象为:" + jp.getTarget());
}
public void release(JoinPoint jp)
{
System.out.println("After增强:模拟方法结束后的释放资源...");
// 返回被织入增强处理的目标方法
System.out.println(
"After增强:被织入增强处理的目标方法为:" + jp.getSignature().getName());
// 访问执行目标方法的参数
System.out.println("After增强:目标方法的参数为:" + Arrays.toString(jp.getArgs()));
// 访问被增强处理的目标对象
System.out.println("After增强:被织入增强处理的目标对象为:" + jp.getTarget());
}
}

上面的FourAdviceTest.java几乎是一个POJO类,该Java类的4个方法的第一个参数都是JoinPoint类型,之所以将4个方法的第一个参数定义为JoinPoint类型,是为了访问连接点的相关信息,当然Spring AOP只支持使用方法执行作为连接点,所以这里使用JoinPoint只是为了获取目标方法的方法名、参数值等信息
除此之外,本示例程序中还定义了如下一个简单的切面类。

1
2
3
4
5
6
7
8
9
10
11
package org.crazyit.app.aspect;

public class SecondAdviceTest
{
// 定义Before增强处理
public void authority(String aa)
{
System.out.println("①号Before增强:模拟执行权限检查");
System.out.println("目标方法的第一个参数为:" + aa);
}
}

上面切面类的authority方法里多了一个String aa的形参,则应用试图通过aa这个形参来访间目标方法的参数值,这需要在配置该切面Bean时使用args切入点指示符。
本应用中的Spring配置文件如下:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?xml version="1.0" encoding="GBK"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 定义一个普通Bean实例,该Bean实例将被作为Aspect Bean -->
<bean id="fourAdviceBean"
class="org.crazyit.app.aspect.FourAdviceTest" />
<!-- 再定义一个普通Bean实例,该Bean实例将被作为Aspect Bean -->
<bean id="secondAdviceBean"
class="org.crazyit.app.aspect.SecondAdviceTest" />
<bean id="hello" class="org.crazyit.app.service.impl.HelloImpl" />
<bean id="world" class="org.crazyit.app.service.impl.WorldImpl" />
<aop:config>
<!-- 将fourAdviceBean转换成切面Bean 切面Bean的新名称为:fourAdviceAspect 指定该切面的优先级为2 -->
<aop:aspect id="fourAdviceAspect"
ref="fourAdviceBean" order="2">
<!-- 定义一个After增强处理 直接指定切入点表达式 以切面Bean中的release()方法作为增强处理方法 -->
<aop:after
pointcut="execution(* org.crazyit.app.service.impl.*.*(..))"
method="release" />
<!-- 定义一个Before增强处理 直接指定切入点表达式 以切面Bean中的authority()方法作为增强处理方法 -->
<aop:before
pointcut="execution(* org.crazyit.app.service.impl.*.*(..))"
method="authority" />
<!-- 定义一个AfterReturning增强处理 直接指定切入点表达式 以切面Bean中的log()方法作为增强处理方法 -->
<aop:after-returning
pointcut="execution(* org.crazyit.app.service.impl.*.*(..))"
method="log" returning="rvt" />
<!-- 定义一个Around增强处理 直接指定切入点表达式 以切面Bean中的processTx()方法作为增强处理方法 -->
<aop:around
pointcut="execution(* org.crazyit.app.service.impl.*.*(..))"
method="processTx" />
</aop:aspect>
<!-- 将secondAdviceBean转换成切面Bean 切面Bean的新名称为:secondAdviceAspect 指定该切面的优先级为1,该切面里的增强处理将被优先织入 -->
<aop:aspect id="secondAdviceAspect"
ref="secondAdviceBean" order="1">
<!-- 定义一个Before增强处理 直接指定切入点表达式 以切面Bean中的authority()方法作为增强处理方法
且该参数必须为String类型(由authority方法声明中aa参数的类型决定) -->
<aop:before
pointcut="execution(* org.crazyit.app.service.impl.*.*(..)) and args(aa,..)"
method="authority" />
</aop:aspect>
</aop:config>
</beans>

上面的配置文件中依次配置了fourAdviceBeansecondAdviceBeanhelloworld这4个Bean,它们没有丝毫特别之处,完全可以像管理普通Bean一样管理它们。
上面配置文件中的aop:config元素中的代码用于将fourAdviceBean转换成一个切面Bean,并将该Bean里包含的4个方法转换成4个增强处理。

  • 当配置fourAdviceAspect切面时,为其指定了order="2",这将意味着该切面里的增强处理的织入顺序为2;
  • 而配置secondAdviceAspect切面时,为其指定了order="1",
  • 所以Spring AOP将优先织入secondAdviceAspect里的增强处理,再织入fourAdviceAspect里的增强处理。