AOP介绍 二

joseph3001 2003-06-02 07:45:39
2. 将crosscutting功能模块化
为了去除程序中零乱且纠缠的代码,需要使crosscutting功能与系统其他功能分离,即separate of concern。对每一个分离出来的crosscutting功能,称之为一个aspect。AOP的核心就是提供定义aspect的能力,因此才叫做面向aspect的程序设计。AOP还有许多不同的叫法,也有许多与AOP类似的方法,比如Subject-Oriented Programming(现在更名为MDSOC: Multi-Dimensional Separation of Concerns),Adaptive Programming(Adaptive Programming的使用非常广泛,这里特指美国东北大学的方法),Composition Patterns,等等。
AOP到目前为止已有了许多实现,较著名的有Xerox PARC的AspectJ,IBM Watson Research Center的Hyper/J以及美国东北大学的DemeterJ和DJ(DJ是一个简化的纯 Java的DemeterJ)。这三个实现都是为了使crosscutting功能模块化,都是基于Java的,目前都是免费的。其中,我们最感兴趣的是AspectJ,并将以它来描述本文以后AOP的例子。选择AspectJ的理由如下:
l PARC在语言设计上的声誉。第一个真正的OO产品Smalltalk就出自PARC。
l 找到的关于AspectJ的资料远多于Hyper/J和DemeterJ或DJ
l 从资料和软件本身来看,Hyper/J和DemeterJ或DJ这一年多来没什么进展,而AspectJ则在不断推出新版本,于11月30日刚推出release1.0版(甚至在它还处于测试阶段时,就有一家叫做Checkfree 的公司使用它开发自己的商业应用了)。而在最新的各种关于AOP的国际会议上,AspectJ也是明显的热点。

在后文,我们将以AspectJ1.0 release版为例来详细介绍AOP的特点和作用。www.aspectj.org上有着AspectJ的全部资料,包括源代码,大家可以直接上去下载需要的东西。


§2 AspectJ初步——在开发阶段使用AspectJ
AspectJ——以及AOP——是一种比较新的技术。为了尽量降低使用风险,AspectJ小组建议一个组织按下列过程逐步采纳AspectJ:
1) 现系统的辅助功能。包括编码规则检查,追踪,日志,调试,测试等。这些功能的特征是在系统正式发布时可以从程序中去掉。因此,用AspectJ来实现它们不会冒最终系统不稳定的危险。
2) 现系统的核心功能。这些功能将存在于最后发布的系统中。

1. 用AspectJ来实现追踪功能
针对第一节的例子,如果使用AspectJ的话,具体实现如图3所示。
可以看出,除了有一些新的关键字,AspectJ与Java还是很象的。因为它本来就是普通Java的一个扩展,Java的所有语法在AspectJ中都是支持的。但与图2相比,图3中基于AspectJ的实现还是有很大不同:
1) 引入了一个新的模块结构aspect,所有关于此追踪功能的代码都在该模块中
2) class A和class B不需要改变
3) 不必具体指明需追踪的每一个函数

这三点不同代表了AspectJ——和其他AOP方法——的三大优点:
1) 使crosscutting功能模块化。
2) 事后适应(Late adaptations)。
3) 强大的描述能力。

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.CodeSignature;

class Trace_C
{
static public void Enter(String strMethod)
{
System.out.println("Entering method "+
strMethod);
}
static public void Exit(String strMethod)
{
System.out.println("Exiting method "+
strMethod);
}
}
aspect Trace
{
pointcut ToBeTraced() :
cflow(execution(void A.foo())) &&
execution(* *(..)) && !cflow(within(Trace));

before() : ToBeTraced()
{
Trace_C .Enter(thisJoinPoint.getSignature().
toString());
}
after() : ToBeTraced()
{
Trace_C .Exit(thisJoinPoint.getSignature().
toString ());
}
}
图3 用AspectJ来实现追踪功能(黑体为AspectJ中的关键字)

下面来看看AspectJ是如何做到这几点的。首先,AspectJ引入了join point的概念。一个join point是程序动态执行流程中有着明确定义的执行点。它可以是一个函数的执行,也可是一个函数的调用,也可是一个异常的产生,还可是一个属性的读取或修改,等等。图3的例子只使用了函数执行这一种join point。由join point组成的集合被定义为pointcut。AspectJ定义了一些基本的(primitive)pointcut定义符,在图3用到的有三个,它们是:
l execution: execution(void A.foo())这个pointcut中只包括void A.foo()执行这一个join point。
l within:within(Trace)表示aspect Trace中的所有join point。within的参数也可是类名。比如,within(A)表示A这个类中的所有join point。
l cflow: cflow是AspectJ中功能最强大也是最难掌握的一个基本pointcut。它的参数必须是一个pointcut。cflow(PC)代表执行PC这个pointcut所包含的所有join point时遇到的所有join point,包括PC定义的join point。比如,cflow(execution(void A.foo())代表执行A.foo()时所遇到的所有join point,包括A.foo()的执行join point本身;cflow(within(Trace))则包括执行Trace中的所有join point时遇到的所有join point,也包括Trace中的所有join point。
定义pointcut时可以使用通配符:*和..。其中“*”可以匹配任意不含“.”的字符串,“..”则可以匹配任意的参数或以“.”开始和结尾的任意字符串。比如,
l execution(* (..))这个pointcut包括所有方法的执行;
l execution(String X.Get*(..))则包括类X中返回类型为String且名字以Get开头的所有方法的执行;
l execution(A.new(..))则包括所有类A的构造函数的执行;
l execution(public * com.neusoft.unieap.*)则包括com.neusoft.unieap这个包中所有public方法的执行;
l execution(public * com.neusoft..*)则包括名字以com.neusoft.开头的所有包中的所有public方法的执行;
l within(X*)表示类名以X开头的所有类中的join point。

AspectJ允许将pointcut用逻辑运算符组合起来表示新的pointcut,现在支持的运算符有“与”(“&&“),“或”(“||”),“非”(“!”)。比如,图3中
cflow(execution(void A.foo())) && execution(* *(..)) && !cflow(within(Trace));
表示的是执行A.foo时遇到的所有函数执行,但不包括执行Trace中的join point时遇到的。AspectJ允许使用关键字pointcut为自定义的pointcut取名字,在图3中就为上面的pointcut起名为ToBeTraced()。注意,后面这一对括号是必须的。
有了join point和pointcut,AspectJ还引入了一个新的概念叫做advice。一个advice定义到达属于某pointcut中的join point时要执行的代码。AspectJ1.0支持三种advice,图3中使用了两种:
l before:在执行相应join point的代码之前执行。在图3中,使用了一个before advice来表示在进入每个需要跟踪的函数之前先执行Trace_C.Enter (thisJoinPoint.getSignature().toString()),以输出“Entering method XXX”的跟踪信息。其中,thisJoinPoint是AspectJ提供的一个系统变量,它是一个表示当前join point的对象,存储了当前join point的信息。
l after:在执行相应join point的代码之后执行。在图3中,使用了一个after advice来表示在执行每个需要跟踪的函数之后要执行Trace_C.Exit(thisJoinPoint. getSignature().toString()),以输出“Exiting method XXX”的跟踪信息。

最后,AspectJ使用一个叫做aspect的结构将上述pointcut和advice封装起来。
假设图1的代码在文件core.java中,图3中的代码在aspect.java中,则定义一个文件files.lst,其内容为:
core.java
aspect.java
运行ajc @files.lst将它们编译成class文件。AspectJ的ajc实际分成两个阶段:第一阶段叫做weave,它根据aspect生成新的纯Java类和改变原有的Java代码,;第二阶段是调用普通的javac来做编译。如果运行ajc时带上参数-preprocess,则它只做weave。AspectJ1.0的一个局限是它只能修改存在源代码的类。对上面的例子来说,如果class A和class B没有源代码,则上述aspect中的advice将没有作用。
图3和图1中代码的运行结果为:
Entering method void A.foo()
Entering method void A.boo()
Entering method void B.doX()
Exiting method void B.doX()
Exiting method void A.boo()
Entering method void A.coo()
Entering method void B.doY()
Exiting method void B.doY()
Exiting method void A.coo()
Exiting method void A.foo()

如果在files.lst中去掉aspect.java,则ajc @files.lst就等价于javac core.java。可见,通过aspect将功能加到已有Java程序或从中去掉是非常灵活的,简直可以说是轻盈。
...全文
30 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
klbt 2003-06-15
  • 打赏
  • 举报
回复
解渴!
qiuafa 2003-06-03
  • 打赏
  • 举报
回复
接分,上。

3,423

社区成员

发帖
与我相关
我的任务
社区描述
其他开发语言 其他开发语言
社区管理员
  • 其他开发语言社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧