在Java中抛出异常时,会将其向上传递给调用链,直到它被try/catch
块中的catch
语句处理,或者它到达Java运行库并在控制台上引发一条出错的消息。如果捕获到Java异常,就会把该异常作为一个对象传递给相应的catch
语句,在其中处理该异常。
使用切面可以捕获异常的处理,其应用的场景可以是:除了catch
块的正常异常处理行为之外,使用切面还需要做某些事情;或者干脆使用切面代替catch
块的正常行为。
假设你想捕获程序对特定异常的处理。AspectJ提供了handler(异常类)
切入点来实现这一功能。它的语法是:
pointcut [切入点名字](参数列表): handler(异常类);
需要注意的几点是:
hander(异常类)
捕获的连接点是catch
语句。也就是在程序捕捉异常的地方捕获连接点,而不是在引发异常的地方。Throwable
及其子类。handler(异常类)
只能使用before()
指定前置通知,不支持其他形式的通知。下表表示了异常类使用通配符的示例:
带有通配符的异常类 | 描述 |
---|---|
mypackage..* |
捕获mypackage 包及其子包中的类的连接点 |
MyClass+ |
捕获MyClass 类及其任何子类中的连接点 |
我们在Test5
包下做一个简单的测试。
首先我们自定义异常MyException
,使其继承自Exception
,内容可以为空。
package Test5;
public class MyException extends Exception {
}
接着,我们创建业务类Service
,并在其中定义test
方法,在该方法中,我们抛出自定义的MyException
异常,并且使用try/catch
语句来捕获。
package Test5;
public class Service {
private String name;
public Service(String name) {
this.name = name;
}
public void test(){
try {
throw new MyException();
} catch (MyException e) {
System.out.println("MyException Catch 处理语句...");
}
}
@Override
public String toString() {
return "Service{" +
"name='" + name + '\'' +
'}';
}
}
接着,我们定义主方法来测试,如下Main
类。
package Test5;
public class Main {
public static void main(String[] args) {
Service service = new Service("Gavin");
service.test();
}
}
如果此时不添加切面,其运行效果是这样的:
接着我们添加HandlerAspect
切面。如下:
package Test5;
public aspect HandlerAspect {
pointcut handlerPointcut(): handler(MyException);
before(): handlerPointcut(){
System.out.println("MyException 切面处理...");
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
}
}
可以看到,我们在切面中定义了切入点,名字为handlerPointcut
,使用handler(MyException)
切入点语法,该切入点捕获对MyException
异常的处理。我们对该切入点织入了前置通知,在通知中,我们打印了连接点的Signature
和连接点在源代码中的位置。
此时的运行结果如下:
通过上面的介绍,我们知道handler(异常类)
也可以使用通配符,所以下述写法对于本示例来说也是可以的:
pointcut handlerPointcut(): handler(Exception+);
或者:
pointcut handlerPointcut(): handler(Test5..*);
在捕获方法调用连接点时,我们可以使用args
原生切入点来捕获方法传入的参数,同样的,在捕获异常处理连接点时,我们也可以通过使用args
原生切入点来捕获异常对象。
比如,在上例中,我们可以在切面中获取MyException
异常对象。此时,要对切面做如下的修改:
package Test5;
public aspect HandlerAspect {
pointcut handlerPointcut(MyException e): handler(MyException) && args(e);
before(MyException e): handlerPointcut(e){
System.out.println("MyException 切面处理...");
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
e.printStackTrace();
}
}
如代码所示,我们在切入点添加参数MyException e
,并通过args(e)
获取了该异常对象。在通知中,我们使用e.printStackTrace()
打印出了异常信息。
运行结果如下:
同样的,我们可以结合使用handler(异常类)
与this
原生切入点,来获取处理异常的对象。
我们在上例的基础上添加this
原生切入点,如下:
package Test5;
public aspect HandlerAspect {
pointcut handlerPointcut(MyException e, Service service): handler(MyException) && args(e) && this(service);
before(MyException e, Service service): handlerPointcut(e, service){
System.out.println("MyException 切面处理...");
System.out.println("Signature: " + thisJoinPoint.getSignature());
System.out.println("Source Line: " + thisJoinPoint.getSourceLocation());
System.out.println(service);
e.printStackTrace();
}
}
在该切面中,我们不仅获取了MyException
异常对象,也获取了处理异常的对象。
运行结果如下: