分享
 
 
 

跟我学AspectJ(五)

王朝java/jsp·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

第三章 AspectJ实例

使用方面的Tracing程序

写一个具有跟踪能力的类是很简单的事情:一组方法,一个控制其开或关的布尔变量,一种可选的输出流,可能还有一些格式化输出能力。这些都是Trace类需要的东西。当然,如果程序需要的话,Trace类也可以实现的十分的复杂。开发这样的程序只是一方面,更重要的是如何在合适的时候调用它。在大型系统开发过程中,跟踪程序往往影响效率,而且在正式版本中去除这些功能十分麻烦,需要修改任何包含跟踪代码的源码。出于这些原因,开发人员常常使用脚本程序以便向源码中添加或删除跟踪代码。

AspectJ可以更加方便的实现跟踪功能并克服这些缺点。Tracing可以看作是面向整个系统的关注点,因此,Tracing方面可以完全独立在系统之外并且在不影响系统基本功能的情况下嵌入系统。

应用实例

整个例子只有四个类。应用是关于Shape的。TwoShape类是Shape类等级的基类。

public abstract class TwoDShape {

protected double x, y;

protected TwoDShape(double x, double y) {

this.x = x; this.y = y;

}

public double getX() { return x; }

public double getY() { return y; }

public double distance(TwoDShape s) {

double dx = Math.abs(s.getX() - x);

double dy = Math.abs(s.getY() - y);

return Math.sqrt(dx*dx + dy*dy);

}

public abstract double perimeter();

public abstract double area();

public String toString() {

return (" @ (" + String.valueOf(x) + ", " + String.valueOf(y) + ") ");

}

}

TwoShape类有两个子类,Circle和Square

public class Circle extends TwoDShape {

protected double r;

public Circle(double x, double y, double r) {

super(x, y); this.r = r;

}

public Circle(double x, double y) { this( x, y, 1.0); }

public Circle(double r) { this(0.0, 0.0, r); }

public Circle() { this(0.0, 0.0, 1.0); }

public double perimeter() {

return 2 * Math.PI * r;

}

public double area() {

return Math.PI * r*r;

}

public String toString() {

return ("Circle radius = " + String.valueOf(r) + super.toString());

}

}

public class Square extends TwoDShape {

protected double s; // side

public Square(double x, double y, double s) {

super(x, y); this.s = s;

}

public Square(double x, double y) { this( x, y, 1.0); }

public Square(double s) { this(0.0, 0.0, s); }

public Square() { this(0.0, 0.0, 1.0); }

public double perimeter() {

return 4 * s;

}

public double area() {

return s*s;

}

public String toString() {

return ("Square side = " + String.valueOf(s) + super.toString());

}

}

Tracing版本一

首先我们直接实现一个Trace类并不使用方面。公共接口Trace.java

public class Trace {

public static int TRACELEVEL = 0;

public static void initStream(PrintStream s) {...}

public static void traceEntry(String str) {...}

public static void traceExit(String str) {...}

}

如果我们没有AspectJ,我们需要在所有需要跟踪的方法或构造子中直接调用traceEntry和traceExit方法并且初试化TRACELEVEL和输出流。以上面的例子来说,如果我们要跟踪所有的方法调用(包括构造子)则需要40次的方法调用并且还要时刻注意没有漏掉什么方法,但是使用方面我们可以一致而可靠的完成。TraceMyClasses.java

aspect TraceMyClasses {

pointcut myClass(): within(TwoDShape) || within(Circle) || within(Square);

pointcut myConstructor(): myClass() && execution(new(..));

pointcut myMethod(): myClass() && execution(* *(..));

before (): myConstructor() {

Trace.traceEntry("" + thisJoinPointStaticPart.getSignature());

}

after(): myConstructor() {

Trace.traceExit("" + thisJoinPointStaticPart.getSignature());

}

before (): myMethod() {

Trace.traceEntry("" + thisJoinPointStaticPart.getSignature());

}

after(): myMethod() {

Trace.traceExit("" + thisJoinPointStaticPart.getSignature());

}

}

这个方面在合适的时候调用了跟踪方法。根据此方面,跟踪方法在Shape等级中每个方法或构造子的入口和出口处调用,输出的是各个方法的签名。因为方法签名是静态信息,我们可以利用thisJoinPointStaticPart对象获得。运行这个方面的main方法可以获得以下输出:

--> tracing.TwoDShape(double, double)

<-- tracing.TwoDShape(double, double)

--> tracing.Circle(double, double, double)

<-- tracing.Circle(double, double, double)

--> tracing.TwoDShape(double, double)

<-- tracing.TwoDShape(double, double)

--> tracing.Circle(double, double, double)

<-- tracing.Circle(double, double, double)

--> tracing.Circle(double)

<-- tracing.Circle(double)

--> tracing.TwoDShape(double, double)

<-- tracing.TwoDShape(double, double)

--> tracing.Square(double, double, double)

<-- tracing.Square(double, double, double)

--> tracing.Square(double, double)

<-- tracing.Square(double, double)

--> double tracing.Circle.perimeter()

<-- double tracing.Circle.perimeter()

c1.perimeter() = 12.566370614359172

--> double tracing.Circle.area()

<-- double tracing.Circle.area()

c1.area() = 12.566370614359172

--> double tracing.Square.perimeter()

<-- double tracing.Square.perimeter()

s1.perimeter() = 4.0

--> double tracing.Square.area()

<-- double tracing.Square.area()

s1.area() = 1.0

--> double tracing.TwoDShape.distance(TwoDShape)

--> double tracing.TwoDShape.getX()

<-- double tracing.TwoDShape.getX()

--> double tracing.TwoDShape.getY()

<-- double tracing.TwoDShape.getY()

<-- double tracing.TwoDShape.distance(TwoDShape)

c2.distance(c1) = 4.242640687119285

--> double tracing.TwoDShape.distance(TwoDShape)

--> double tracing.TwoDShape.getX()

<-- double tracing.TwoDShape.getX()

--> double tracing.TwoDShape.getY()

<-- double tracing.TwoDShape.getY()

<-- double tracing.TwoDShape.distance(TwoDShape)

s1.distance(c1) = 2.23606797749979

--> String tracing.Square.toString()

--> String tracing.TwoDShape.toString()

<-- String tracing.TwoDShape.toString()

<-- String tracing.Square.toString()

s1.toString(): Square side = 1.0 @ (1.0, 2.0)

Tracing版本二

版本二实现了可重用的tracing方面,使其不仅仅用于Shape的例子。首先定义如下的抽象方面Trace.java

abstract aspect Trace {

public static int TRACELEVEL = 2;

public static void initStream(PrintStream s) {...}

protected static void traceEntry(String str) {...}

protected static void traceExit(String str) {...}

abstract pointcut myClass();

}

为了使用它,我们需要定义我们自己的子类。

public aspect TraceMyClasses extends Trace {

pointcut myClass(): within(TwoDShape) || within(Circle) || within(Square);

public static void main(String[] args) {

Trace.TRACELEVEL = 2;

Trace.initStream(System.err);

ExampleMain.main(args);

}

}

注意我们仅仅在类中声明了一个切点,它是超类中声明的抽象切点的具体实现。版本二的Trace类的完整实现如下

abstract aspect Trace {

// implementation part

public static int TRACELEVEL = 2;

protected static PrintStream stream = System.err;

protected static int callDepth = 0;

public static void initStream(PrintStream s) {

stream = s;

}

protected static void traceEntry(String str) {

if (TRACELEVEL == 0) return;

if (TRACELEVEL == 2) callDepth++;

printEntering(str);

}

protected static void traceExit(String str) {

if (TRACELEVEL == 0) return;

printExiting(str);

if (TRACELEVEL == 2) callDepth--;

}

private static void printEntering(String str) {

printIndent();

stream.println("--> " + str);

}

private static void printExiting(String str) {

printIndent();

stream.println("<-- " + str);

}

private static void printIndent() {

for (int i = 0; i < callDepth; i++)

stream.print(" ");

}

// protocol part

abstract pointcut myClass();

pointcut myConstructor(): myClass() && execution(new(..));

pointcut myMethod(): myClass() && execution(* *(..));

before(): myConstructor() {

traceEntry("" + thisJoinPointStaticPart.getSignature());

}

after(): myConstructor() {

traceExit("" + thisJoinPointStaticPart.getSignature());

}

before(): myMethod() {

traceEntry("" + thisJoinPointStaticPart.getSignature());

}

after(): myMethod() {

traceExit("" + thisJoinPointStaticPart.getSignature());

}

}

它与版本一的不同包括几个部分。首先在版本一中Trace用单独的类来实现而方面是针对特定应用实现的,而版本二则将Trace所需的方法和切点定义融合在一个抽象方面中。这样做的结果是traceEntry和traceExit方法不需要看作是公共方法,它们将由方面内部的通知调用,客户完全不需要知道它们的存在。这个方面的一个关键点是使用了抽象切点,它其实与抽象方法类似,它并不提供具体实现而是由子方面实现它。

Tracing版本三

在前一版本中,我们将traceEntry和traceExit方法隐藏在方面内部,这样做的好处是我们可以方便的更改接口而不影响余下的代码。

重新考虑不使用AspectJ的程序。假设,一段时间以后,tracing的需求变了,我们需要在输出中加入方法所属对象的信息。至少有两种方法实现,一是保持traceEntry和traceExit方法不变,那么调用者有责任处理显示对象的逻辑,代码可能如下

Trace.traceEntry("Square.distance in " + toString());

另一种方法是增强方法的功能,添加一个参数表示对象,例如

public static void traceEntry(String str, Object obj);

public static void traceExit(String str, Object obj);

然而客户仍然有责任传递正确的对象,调用代码如下

Trace.traceEntry("Square.distance", this);

这两种方法都需要动态改变其余代码,每个对traceEntry和traceExit方法的调用都需要改变。

这里体现了方面实现的另一个好处,在版本二的实现中,我们只需要改变Trace方面内部的一小部分代码,下面是版本三的Trace方面实现

abstract aspect Trace {

public static int TRACELEVEL = 0;

protected static PrintStream stream = null;

protected static int callDepth = 0;

public static void initStream(PrintStream s) {

stream = s;

}

protected static void traceEntry(String str, Object o) {

if (TRACELEVEL == 0) return;

if (TRACELEVEL == 2) callDepth++;

printEntering(str + ": " + o.toString());

}

protected static void traceExit(String str, Object o) {

if (TRACELEVEL == 0) return;

printExiting(str + ": " + o.toString());

if (TRACELEVEL == 2) callDepth--;

}

private static void printEntering(String str) {

printIndent();

stream.println("Entering " + str);

}

private static void printExiting(String str) {

printIndent();

stream.println("Exiting " + str);

}

private static void printIndent() {

for (int i = 0; i < callDepth; i++)

stream.print(" ");

}

abstract pointcut myClass(Object obj);

pointcut myConstructor(Object obj): myClass(obj) && execution(new(..));

pointcut myMethod(Object obj): myClass(obj) &&

execution(* *(..)) && !execution(String toString());

before(Object obj): myConstructor(obj) {

traceEntry("" + thisJoinPointStaticPart.getSignature(), obj);

}

after(Object obj): myConstructor(obj) {

traceExit("" + thisJoinPointStaticPart.getSignature(), obj);

}

before(Object obj): myMethod(obj) {

traceEntry("" + thisJoinPointStaticPart.getSignature(), obj);

}

after(Object obj): myMethod(obj) {

traceExit("" + thisJoinPointStaticPart.getSignature(), obj);

}

}

在此我们必须在methods切点排除toString方法的执行。问题是toString方法在通知内部调用,因此如果我们跟踪它,我们将陷入无限循环中。这一点不明显,所以必须在写通知时格外注意。如果通知回调对象,通常都回存在循环的可能性。

事实上,简单的排除连接点的执行并不够,如果在这之中调用了其他跟踪方法,那么就必须提供以下限制

&& !cflow(execution(String toString()))

排除toString方法的执行以及在这之下的所有连接点。

总之,为了实现需求的改变我们必须在Trace方面中做一些改变,包括切点说明。但是实现的改变只局限于Trace方面内部,而如果没有方面,则需要更改每个应用类的实现。

更多信息

1.AspectJ安装和配置指南

2.跟我学AspectJ(一)

参考资料

The AspectJTM Programming Guide http://www.eclipse.org/aspectj/

如果需要转贴请写名作者和出处。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有