用AspectJ构造方面库

1.方面库

大家都知道,各种AOP工具的核心就是切入点(pointcut)和通知(advice) 的声明。切入点描述了主程序执行与方面执行相遇的地方,也就是被横切的位置 ;通知则描述了在程序执行过程中遇到匹配的切入点时应当采取什么行动。假设 已经开发了一个方面,并且感觉它适用于其他项目,那么可以泛化这个方面,并 把它隔离到自己独立的项目中,形成一个库,即方面库(Aspect Library)。方 面库提供了某个功能的内部执行逻辑和基础设施,通过切入点的实例化将方面库 与某个特定项目连接起来。例如提供应用程序性能监视的方面库,实现了所有性 能监视相关的方法和通知,某应用程序使用该库的时候,只需要把库的切入点定 义为应用特定的连接点即可,而无需关心性能监视功能的具体实现。这就是方面 库的基本概念。

方面库是AOP工具具有扩展性的体现,目前常用的AOP工具,如JBoss AOP、 Spring AOP、AspectJ等,都有对方面库的支持,但由于它们实现AOP的方法不同 ,方面库的定义和使用方法也不相同。在JBoss AOP和Spring AOP中,通知的实现 都是通过普通Java语法定义,切入点到通知的绑定是通过显式的XML文档或者注释 实现的。所以将方面库应用于应用程序就可以很容易地通过在 XML 中或通过注释 定义新的通知绑定而实现。并且JBoss和Spring系统本身已经提供了很好的方面库 供用户使用。

AspectJ是Java 语言语法和语义的扩展,它提供了自己的一套处理方面的关键 字,这些都决定了用AspectJ构造方面库的方法具有一定的特殊性。所以AspectJ 构造的方面库一直不象JBoss AOP和Spring AOP那样普遍,AspectJ也并没有提供 任何方面库。令人欣慰的是, AspectJ从AspectJ5版本开始提供对注释的支持, 它的编辑工具AJDT中逐渐加入了对方面库的支持功能,从而使得使用AspectJ构造 方面库变得越来越容易。本文就来介绍如何用AspectJ构造方面库。

2.AspectJ对方面库的技术支持

方面库的实现在于切入点的实例化方式。AspectJ对切入点定义方法的支持导 致了两种完全不同的方面库实现方法–使用抽象方面(abstract aspect)的方法 和使用注释的方法。本章将详细介绍这两种方式。

AspectJ是Java语言的扩展。方面在AspectJ语言中用aspect关键字标示,它类 似于Java类,可以定义成抽象的,也存在继承关系。切入点在AspectJ语言中用关 键字pointcut标示,它有一套完整的语法来描述切入点,也可以定义成抽象的, 即没有实际定义的切入点。抽象 pointcut只能定义在抽象方面中。如清单 1所示 ,抽象方面A里面定义了一个抽象pointcut名叫publicCall。抽象方面如同抽象类 一样,可以被继承,继承抽象方面的方面必须重载抽象 pointcut,即赋予抽象 pointcut实际的定义。如清单 2所示,方面B继承了抽象方面A,重载了名叫 publicCall的pointcut,给它一个具体的定义。

清单 1 抽象方面

public abstract aspect A {    abstract pointcut publicCall(int i);  }

清单 2 继承抽象方面的子方面

public aspect B extends A {    pointcut publicCall(int i): call(public Foo.m(int))  && args(i);}

AspectJ对继承和抽象的支持正是我们构造方面库的基础。抽象方面包含抽象 的切入点和具体的通知,正符合方面库的特征,可以使用抽象方面来构造方面库 文件。继承抽象方面的子方面必须具体化切入点,可以把它当作方面库在具体应 用程序中的实施。为了加深理解,我们将在接下来的章节中使用抽象方面技术制 作一个简单的记录踪迹的方面库,并扩展它以应用到具体项目中。

从AspectJ 5开始支持的注释是另外一种构造方面库的技术基础。Java5引入了 注释这种类型,它以注释的形式来表达程序中各成员的元数据信息,采用符号@标 示。 Java5中可以被注释修饰的Java程序成员有很多,AspectJ 5能支持的注释包 括修饰方面、方法、属性、构造函数和通知,修饰方法和通知的参数的注释也能 支持,但是不支持pointcut和declare语句上的注释。为了支持注释类型, AspectJ 5扩展了pointcut语法,可以匹配存在或者不存在的注释类型。例如清单 3中的名叫onewayMethod的pointcut可以匹配所有被注释@Oneway修饰的方法调用 。

清单 3 含有注释的pointcut

public aspect C {pointcut onewayMethod: call(@Oneway * *(..));}

AspectJ 5对注释的支持简化了实施库的方法,我们可以很容易地想到,在把 方面库实施到应用程序时,可以利用注释标明具体的切入点的位置。在构造方面 库文件时,只需要定义好与注释相关的切入点,并规定该切入点上的具体的通知 内容就可以了。我们同样会在下面的章节中介绍使用注释制作简单的方面库的过 程。

3.使用抽象方面构造方面库

曾经有一段时间,我们只有在获得了所有源代码的情况才才能顺利的编译用到 了AspectJ的软件项目。现在情况当然已经和以往大大不同,由于 AspectJ今年来 的迅速发展,在项目中使用AspectJ的要求大大放松了。我们可以在没有源代码的 情况下进行AOP开发。无论是程序主逻辑部分还是 Aspect部分,都可以用jar文件 的形式提供,并且能够顺利的通过AspectJ的编译。

下面我们将通过一个例子演示如何在AJDT中构造Aspect库。在构建这个例子的 过程中,我们用到了Eclipse及其上的AJDT插件。这两个工具可以分别从 http://www.eclipse.org 和http://www.eclipse.org/ajdt 上下载。AJDT从多个 方面扩展了Eclipse的Java开发环境使其支持面向方面的编程。当在Eclipse平台 上安装了AJDT插件之后,我们现在能新建AspectJ项目和创建新的Aspect了(图 1 )。

图 1 AJDT提供的Wizard

利用图 1中所示的向导,我们建立一个AspectJ Project来存放我们的方面库 。我们将这个工程命名为sample.aspects.library。随后,我们可以利用AJDT提 供的工具按钮建立一个新的Aspect, 用到的工具栏按钮如图 2所示。

图 2 使用工具栏按钮建立一个新的Aspect

通过AJDT提供的向导,我们建立了如下的Aspect:

清单 4 抽象方面AbstractTrace

package sample.aspects.library;public abstract aspect AbstractTrace {  public abstract pointcut TraceScope();  Object around():TraceScope(){  Object sig = thisJoinPoint.getSignature();  System.out.println("Enter "+sig);  Object res = proceed();  System.out.println("Exit "+sig);  return res;  }}

清单 4中建立了一个名为AbstractTrace的抽象方面,它包含了一个pointcut 和一个advice。我们看到,名为TraceScope的 pointcut也被标明是抽象的。根据 AspectJ的规范,一个抽象的方面是不会起作用的。抽象方面存在的唯一目的是为 了被用户继承。接下来,我们将整个工程导出成为一个.jar文件。在AspectJ中, 我们可以将这种形式的Jar交给用户,它们随后就可以利用继承来使用方面库提供 的功能了。

如果我们希望在一个新的工程中使用方面库,我们需要在工程中指定用到的方 面库,并继承方面库中提供的抽象方面。在Eclipse中,我们首先将这个 jar文件 加入到工程的”Java Build Path”中,就如同使用一个普通的Java类库一样。随后 ,我们就可以利用AJDT提供的向导来继承抽象方面了,如图 3所示。

图 3 继承抽象Aspect

在图 3中,我们可以指定在新的Aspect中实现所有的抽象pointcut,这样能让 AJDT帮助我们多做一些工作。最后生成并修改后的代码如清单 5所示,它具体化 了抽象pointcut,指明这个方面库应用的范围,即切入点是任何函数执行的时刻 。

清单 5 继承抽象Aspect

package aspects;import sample.aspects.library.AbstractTrace;public aspect Trace extends AbstractTrace {  public pointcut TraceScope():execution(* *(..));}

从清单 5中我们可以看出,通过使用已有的方面库,用户需要编写的代码变得 非常简洁。商务逻辑的开发人员可以不去了解AspectJ的一些高级用法,他们只需 要指定哪些时候需要用到方面中提供的功能就可以了。

4.使用注释构造方面库

前面介绍了使用抽象方面构造一个记录踪迹的方面库并实施到具体应用中,这 章我们采用注释技术构造一个方面库,以完成同样的功能。

与前面一章相同,首先创建一个AspectJ的工程,工程名为 sample.annotation.library。需要注意的是,因为要在工程中使用注释类型,所 以JDK依从的版本必须为5.0。如果你目前的缺省设置不是5.0,请如图所示在JDK Compliance中选择”为工程指定一个”,设置为5.0。或者在工程的properties对话 框中选择Java Compiler页面修改。

图 4 修改JDK Compilance

接下来要做的就是新建一个标准Java注释类型,在Eclipse中点击File- >New->Other弹出New对话框,选择 Java下面的Annotation,设定它的 Package为sample.annotation.library,名称叫做trace,其他接受缺省值,点击 完成。这样,一个名叫trace的注释类型就被创建出来了,我们将基于它创建一个 记录踪迹的方面库。

接下来创建方面库中的Aspect,即一个aj文件,文件如清单 6所示。

清单 6 捕捉注释的Aspect

package sample.annotation.library;public aspect AnnotationTrace {  pointcut traceScope():execution(@trace * *(..));  Object around():traceScope(){  Object sig = thisJoinPoint.getSignature();  System.out.println("Enter "+sig);  Object res = proceed();  System.out.println("Exit "+sig);  return res;  }}

这样,方面库的编码工作就完成了,与上一个例子类似,我们把它打包成 tracelib.jar文件备用。

现在我们来介绍如何应用方面库tracelib.jar。创建一个新的AspectJ项目 sample.annotation.application,注意,因为要使用注释类型,修改JDK Compliance为5.0。新建一个标准Java类business.logic.MainLogic,添加简单函 数testTrace(),用这个 Java类来模拟应用程序。

为了使用方面库,首先必须将方面库的jar文件添加到AspectJ Aspect Path中 ,在工程的properties对话框中选择AspectJ Aspect Path页面添加方面库的jar 文件,如图 5所示。在应用程序MainLogic类中使用记录踪迹的功能时,只需要简 单的将@trace添加到想要使用这个功能的函数前面即可。MainLogic 类的源码如 清单 7所示。将MainLogic类作为AspectJ/Java Application来运行,可以看到输 出结果,在testTrace()调用之前和之后,加入了记录踪迹的语句,如清单 8所示 。这样就达到了我们使用方面库tracelib.jar的目的。相比较而言,通过注释技 术实现的方面库使用起来是更为便捷。

图 5 添加AspectJ Aspect Path

清单 7 应用程序MainLogic源码

package business.logic;import sample.annotation.library.trace;public class MainLogic {  @trace  public static void testTrace(){  System.out.println("call testTrace function.");  }  public static void main(String[] args) {  MainLogic.testTrace();  }}

清单 8 MainLogic输出结果

Enter void business.logic.MainLogic.testTrace()call testTrace function.Exit void business.logic.MainLogic.testTrace()

5.总结

与OOP中的可重用库类似,AOP方面库可以在整个工程或者多个工程中重复使用 ,从而提高程序编写的准确性和方便性。使用方面库的编程人员只需要了解简单 的AOP知识就能享受到AOP带来的好处,把方面库实施到具体的应用中。本文在介 绍方面库基本概念的基础上,介绍了利用AspectJ构造方面库的两种技术,希望能 够对AOP在应用项目中广泛应用有所帮助。

长江后浪催前浪,一辈新人换旧人。

用AspectJ构造方面库

相关文章:

你感兴趣的文章:

标签云: