8.4.5 基于注解的 零配置 方式 4. 定义AfterThrowing增强处理

8.4.5 基于注解的 零配置 方式 4. 定义AfterThrowing增强处理

使用@AfterThrowing注解可修饰AfterThrowing增强处理, AfterThrowing增强处理主要用于处理程序中未处理的异常
使用@AfterThrowing注解时可指定如下两个常用属性。

属性 描述
pointcut或者value 这两个属性的作用是一样的,它们都用于指定该切入点对应的切入表达式。样既可是一个已有的切入点,也可直接定义切入点表达式。当指定了pointcut属性值后, value属性值将会被覆盖。
throwing 该属性值指定一个形参名,用于表示Advice方法中可定义与此同名的形参,该形参可用于访问目标方法抛出的异常。除此之外,在Advice方法中定义该形参时如果指定了类型,则会限制目标方法必须是抛出该类型的异常的方法

程序示例

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package org.crazyit.app.aspect;

import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;

// 定义一个切面
@Aspect
public class RepairAspect
{
// 匹配org.crazyit.app.service.impl包下所有类的所有方法的执行作为切入点
@AfterThrowing(
throwing = "ex",
pointcut = "execution(* org.crazyit.app.service.impl.*.*(..))"
)
// 声明ex时指定的类型会限制目标方法必须抛出指定类型的异常
// 此处将ex的类型声明为Throwable,意味着对目标方法抛出的异常不加限制
public void doRecoveryActions(Throwable ex)
{
System.out.println("目标方法中抛出的异常:" + ex);
System.out.println("模拟Advice对异常的修复...");
}
}

正如在上面的程序中看到的,程序中使用@AfterThrowing注解时指定了一个throwing属性,该属性值为ex,这允许在增强处理方法doRecoveryActions中定义名为ex的形参,程序可通过该形参访问目标方法所抛出的异常
将前面示例中的HelloImpl.java类做一些修改,用于模拟程序抛出异常,修改后的Hellolmpl.java类的代码如下:

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
package org.crazyit.app.service.impl;

import org.springframework.stereotype.Component;

import org.crazyit.app.service.*;

@Component("hello")
public class HelloImpl implements Hello
{
// 定义一个deleteUser方法,模拟应用中删除用户的方法
public void deleteUser(Integer id)
{
if (id < 0)
{
throw new IllegalArgumentException("被删除用户的id不能小于0:" + id);
}
System.out.println("执行Hello组件的deleteUser删除用户:" + id);
}
// 定义一个addUser()方法,模拟应用中的添加用户的方法
public int addUser(String name , String pass)
{
System.out.println("执行Hello组件的addUser添加用户:" + name);
return 20;
}
}

上面程序中的deleteUser()方法可能抛出异常,当调用deleteUser()方法传入的参数小于0时,deleteUser()方法就会抛出异常,且该异常没有被任何程序所处理,故Spring AOP会对该异常进行处理。
该示例的主程序略作改变,将调用deleteUser()方法的参数改为-2。运行该主程序,将看到如下效果。

1
2
3
4
5
6
7
8
执行Hello组件的addUser添加用户:悟空
目标方法中抛出的异常:java.lang.IllegalArgumentException: 被删除用户的id不能小于0:-2
模拟Advice对异常的修复...
Exception in thread "main" java.lang.IllegalArgumentException: 被删除用户的id不能小于0:-2
at org.crazyit.app.service.impl.HelloImpl.deleteUser(HelloImpl.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
...

throwing属性增强处理方法接收异常的参数

可以看到的,@AfterThrowing注解的throwing属性中指定的参数名必须与增强处理方法内的一个形参对应。当目标方法抛出一个未处理的异常时,该异常将会传给增强处理方法对应的参数。

使用throwing属性限定触发的异常类型

使用throwing属性还有一个额外的作用:它可用于限定切入点只匹配指定类型的异常;假如在上面的doRecoveryActions()方法中定义了ex形参的类型是NullPointerException,则该切入点只匹配抛出NullPointerException异常的方法。上面doRecoveryActions方法的ex形参类型是Throwable,这表明该切入点可匹配拋出任何异常的情况。

AOP的AfterThrowing处理与catch语句的不同

AOPAfterThrowing处理虽然可以对目标方法的异常进行处理,但这种处理与直接使用catch捕捉不同:
catch捕捉意味着完全处理该异常,如果catch块中没有重新抛出新异常,则该方法可以正常结束;
AfterThrowing处理虽然处理了该异常,但它不能完全处理该异常,该异常依然会传播到上一级调用者,在本示例程序中异常直接传播到JVM,故导致程序中止。