前面的例子显示,BusinessLogic类中的foo()方法可以通过AroundAdvice类中的invoke(..)方法彻底重写。原来的 foo()方法完全不能被invoke(..)方法调用。如果希望从around通知内调用foo()方法,可以使用proceed()方法,可从 invoke(..)方法的MethodInvocation参数中得到它。
public class AroundAdvice
implements MethodInterceptor
{
public Object invoke(
MethodInvocation invocation)
throws Throwable
{
System.out.println(
"Hello world! (by " +
this.getClass().getName() +
")");
invocation.proceed();
System.out.println("Goodbye! (by " +
this.getClass().getName() +
")");
return null;
}
}
图4显示了对proceed()的调用如何影响操作的顺序(与图3所示的初始around通知执行相比较)。

图4. 从around通知内使用proceed()调用原来的方法
当调用proceed()时,实际是在指示被截获的方法(在本例中是foo()方法)利用包含在MethodInvocation对象中的信息运行。您可以通过调用MethodInvocation类中的其他方法来改变该信息。
您可能希望更改包含在MethodInvocation类中的信息,以便在使用proceed()调用被截获的方法之前对被截获方法的参数设置新值。
通过对MethodInvocation对象调用getArguments()方法,然后在返回的数组中设置其中的一个参数对象,最初传递给被截获的方法的参数可以被更改。
如果IbusinessClass和BusinessLogic类的foo()方法被更改为使用整型参数,那么就可以将传递给被截获的调用的值由在AroundAdvice的notify(..)方法中传递改为在foo(int)中传递。
public class AroundAdvice
implements MethodInterceptor
{
public Object invoke(
MethodInvocation invocation)
throws Throwable
{
System.out.println(
"Hello world! (by " +
this.getClass().getName() +
")");
invocation.getArguments()[0] = new Integer(20);
invocation.proceed();
System.out.println(
"Goodbye! (by " +
this.getClass().getName() +
")");
return null;
}
}
在本例中,被截获的方法的第一个形参被假设为int。实参本身是作为对象传递的,所以通过将其包装在Integer类实例中的方法,基本的 int类型的形参被改为对应数组中的新值。如果您将该参数设置为一个非Integer对象的值,那么在运行时就会抛出 IllegalArgumentException异常。
您还将注意到,invoke(..)方法必须包含一个return语句,因为该方法需要返回值。但是,被重写的foo()方法并不返回对象,所以invoke(..)方法可以以返回null结束。如果在foo()方法不需要的情况下,您仍然返回了一个对象,那么该对象将被忽略。
如果foo()方法确实需要返回值,那么需要返回一个与foo()方法的初始返回类型在同一个类或其子类中的对象。如果foo()方法返回一个简单类型,例如,一个integer,那么您需要返回一个Integer类的对象,当方法被重写时,该对象会自动由AOP代理拆箱,如图5所示:

图5. around通知的装箱和自动拆箱
图字:
Object invoke:对象调用
The integer return value is boxed in a Integer object in the AroundAdvice and then unboxed by the AOP Proxy:整型返回值被装箱在AroundAdvic通知的一个Integer对象中,然后由AOP代理拆箱。
面向方面编程还是一个比较新的领域,尤其是与衍生出它的面向对象编程相比。设计模式通常被认为是常见问题的通用解决方案,因为面向方面发展的时间还不长,所以已发现的面向方面设计模式比较少。
此处要介绍的是一种正在浮现的模式,即Cuckoo's Egg设计模式。该模式还有其他的叫法,它在面向对象领域的对等体包括模仿对象(Mock Object)和模仿测试(Mock Testing),甚至代理模式也与它有一些类似之处。
Cuckoo's Egg面向方面设计模式可以被定义为应用程序上下文中功能部件的透明和模块化的置换。就像杜鹃偷偷地把自己的蛋放在另一种鸟的巢中一样,Cuckoo's Egg设计模式用一个替代功能部件实现置换现有的功能部件,而使造成的干扰尽可能少。
这种置换的实现方式可以是静态的、动态的、部分的、完全的,针对一个对象的多个部分,或针对多个组件。使用面向方面的方法可以透明地实现功能部件的置换,而无需对应用程序的其余部分进行更改。要置换应用程序中现有功能部件的替代功能部件就是“杜鹃的蛋”。图6显示了Cuckoo's Egg设计模式中的主要组成元素。

图6. Cuckoo's Egg设计模式中的主要组成元素
图字:
Application:应用程序
Component:组件
Replacement Feature:替代功能部件
Component 1 and 2 together encompass a distinct feature of the software:组件1和2共同包含了软件的一个独立的功能部件
The Cuckoo's Egg pattern transparently replaces an existing feature of the software:Cuckoo's Egg模式透明地置换了软件现有的功能部件
Before the pattern is applied:应用该模式前
After the pattern is applied:应用该模式后
Cuckoo's Egg设计模式依赖于around通知的概念。您需要借助于积极的和侵入性的around通知来截获并有效置换应用程序中现有的功能部件。
有关Cuckoo's Egg设计模式的更多信息,以及AspectJ中的一个可选实现,请参见《AspectJ Cookbook》(O'Reilly,2004年12月出版)。
