Chapter 3 AspectJ instance
Tracing program in use
Writing a class with tracking capabilities is very simple: a group of methods, a Boolean variable that controls its open or off, an optional output stream, there may be some formatting output capabilities. These are all those needed for Trace. Of course, if the program needs, the Trace class can also be implemented very complicated. Developing such a procedure is only one aspect, and more important is how to call it when appropriate. During large system development, tracking procedures often affect efficiency, and remove these features in the official version, you need to modify any source code that contains tracking code. For these reasons, developers often use scripts to add or delete tracking code to the source code.
AspectJ can more conveniently implement tracking features and overcome these shortcomings. Tracing can be seen as an interest in the entire system, so traction can be completely independent of the system and embedding the system without affecting the basic function of the system.
Applications
The entire example has only four classes. The application is about Shape. The Twoshape class is the base class of the Shape class level.
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 class has two subclasses, Circle and 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; // Sidepublic 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 version
First we directly implement a Trace class and not use. Public interface trace.java
Public class trace {
Public static int trackevel = 0;
Public static void initstream (printstream s) {...}
Public static void traceentry (string str) {...}
Public static void traceexit (string str) {...}
}
If we don't have AspectJ, we need to directly call TraceEntry and TraceExit methods in all methods or constructors that need to track, and initiate TraceLevel and output streams. In the above example, if we want to track all methods calls (including constructors), 40 methods are required to call and also pay attention to no way, but we can consistent and reliable completion in use. 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 ());
}
}
This aspect calls the tracking method when appropriate. According to this aspect, the tracking method is called at the port and the exit of the SHAPE level or the construct, and the output is the signature of each method. Because the method signature is static information, we can use thisjoinPointStaticPart object. The main method running this aspect can be obtained: -> 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 traacing.square.area ()
s1.area () = 1.0
-> Double Tracing.twodshape.Distance (Twodshape)
-> Double Tracing.twodshape.getx ()
<- double traacing.twodshape.getx ()
-> Double Tracing.twodshape.gety ()
<- double tracing.twodshape.gety ()
<- double traacing.twodshape.distance (twodshape)
C2.Distance (C1) = 4.242640687119285
-> Double Tracing.twodshape.Distance (Twodshape)
-> Double Tracing.twodshape.getx ()
<- double traacing.twodshape.getx ()
-> Double Tracing.twodshape.gety () <- DOUBLE TRAING.TWODSHAPE.GETY ()
<- double traacing.twodshape.distance (twodshape)
S1.Distance (C1) = 2.23606797749979
-> String Tracing.square.tostring ()
-> String Tracing.twodshape.toString ()
<- string traacing.twodshape.toString ()
<- string traacing.square.tostring ()
S1.toString (): Square Side = 1.0 @ (1.0, 2.0)
TRACING version 2
Version II enables reusable TRACING to make it not only for Shape examples more than SHAPE. First define the following abstraction TRACE.JAVA
Abstract aspect trace {
Public static int trackevel = 2;
Public static void initstream (printstream s) {...}
protected static void traceentry (string str) {...}
Protected static void traceexit (string str) {...}
Abstract Pointcut myclass ();
}
In order to use it, we need to define our own subclasses.
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);
}
}
Note We only declare a cut point in the class, which is the specific implementation of abstract cleavage points declared in superclars. The complete implementation of the version II Trace class is as follows
Abstract aspect trace {
// Implementation Part
Public static int trackevel = 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 ();
Street.println ("->" str);
}
Private static void printexiting (string str) {printundent ();
Stream.Println ("<-" STR);
}
Private static void printundent () {
For (int i = 0; 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 ()); } } It consists of several parts with the version one. First, TRACE is implemented in a separate class in terms of the separate class, and the version II fused to define the methods and cleavage points required by Trace in an abstract aspect. The result of doing this is that the Traceentry and TraceExit methods do not need to be considered as a public approach, which will be called by the internal notice, and the customer does not need to know their existence. One key point in this area is to use abstract cleavage points, which is actually similar to the abstract method, which does not provide specific implementation but is implemented by sub-aspects. TRACING version three In the previous version, we hide the Traceentry and TraceExit methods in terms of aspects, so the advantage is that we can easily change the interface without affecting the rest of the code. Recise the procedure that does not use aspectj. Assuming, after a while, Tracing's demand has changed, and we need to add information to the object to which the method belongs to the output. There are at least two ways to implement, one is to keep the Traceentry and TraceExit methods unchanged, then the caller is responsible for processing the logic of the display object, the code may be as follows Trace.Tracentry ("Square.Distance in" TString ()); Another method is to enhance the function of the method, add a parameter representation object, for example Public Static Void TRACEENTRY (String Str, Object Obj); Public Static Void TraceExit (String Str, Object Obj); However, customers still have a responsibility to deliver the correct object, and the calling code is as follows. Trace.Tracentry ("Square.Distance", this); Both methods need to dynamically change the rest of the code, each of the calls to the Traceentry and TraceExit methods need to be changed. Here, there is another benefit of implementation. In the implementation of version 2, we only need to change a small part of the code inside the trace, which is a version three TRACE to implement Abstract aspect trace { Public static int trackevel = 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.toTString ()); IF (tracelevel == 2) CallDepth--; } Private static void printentering (string str) { PRINTINDENT (); Stream.println ("Entering" STR); } Private static void printexiting (string str) { PRINTINDENT (); Street.println ("exiting" str); } Private static void printundent () { For (int i = 0; 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); } Here we must exclude the execution of the toString method in the Methods cleaning point. The problem is that the toString method is invoked within the notification, so we will fall into an infinite loop if we track it. This is not obvious, so you must pay more attention when writing notices. If the callback object is notified, there is usually the possibility of a loop. In fact, the implementation of a simple exclusion connection point is not enough. If other tracking methods are called, then the following restrictions must be provided. &&! cflow (execution ())) Exclude the execution of the Tostring method and all connection points under this. In summary, in order to achieve a change in demand, we must do some changes in TRACE, including explanation. However, the change in implementation is limited to TRACE, and if there is no aspect, it is necessary to change the implementation of each application class. More information 1. Aspectj Installation and Configuration Guide 2. Learn Aspectj with me (1) Reference The aspectjtm programming guide http://www.eclipse.org/aspectj/ If you need to pay, please write a name author and source.