TOC \h \z \t ".Head 1,1,.Head 2,2,.Head 3,3" HYPERLINK \l "_Toc202549007" 2.1 Writing the First AspectJ Program PAGEREF _Toc202549007 \h 2
HYPERLINK \l "_Toc202549008" 2.1.1 Setting up the example PAGEREF _Toc202549008 \h 2
HYPERLINK \l "_Toc202549009" 2.1.2 Introducing security PAGEREF _Toc202549009 \h 3
HYPERLINK \l "_Toc202549010" 2.2 AspectJ from 20,000 feet PAGEREF _Toc202549010 \h 5
HYPERLINK \l "_Toc202549011" 2.2.1 AspectJ syntax choices PAGEREF _Toc202549011 \h 5
HYPERLINK \l "_Toc202549012" 2.2.2 Understanding Crosscutting Elements PAGEREF _Toc202549012 \h 5
HYPERLINK \l "_Toc202549013" Aspect PAGEREF _Toc202549013 \h 5
HYPERLINK \l "_Toc202549014" Join point PAGEREF _Toc202549014 \h 6
HYPERLINK \l "_Toc202549015" Pointcut PAGEREF _Toc202549015 \h 6
HYPERLINK \l "_Toc202549016" Advice PAGEREF _Toc202549016 \h 6
HYPERLINK \l "_Toc202549017" Inter-type declaration PAGEREF _Toc202549017 \h 8
HYPERLINK \l "_Toc202549018" Compile-time declaration PAGEREF _Toc202549018 \h 10
HYPERLINK \l "_Toc202549019" 2.3 AspectJ: under the hood PAGEREF _Toc202549019 \h 10
HYPERLINK \l "_Toc202549020" 2.3.1 The aspect PAGEREF _Toc202549020 \h 11
HYPERLINK \l "_Toc202549021" 2.3.2 The woven class PAGEREF _Toc202549021 \h 12
HYPERLINK \l "_Toc202549022" 2.4 AspectJ alternative syntax PAGEREF _Toc202549022 \h 13
HYPERLINK \l "_Toc202549023" 2.5 Weaving mechanisms PAGEREF _Toc202549023 \h 15
HYPERLINK \l "_Toc202549024" 2.5.1 Source weaving PAGEREF _Toc202549024 \h 15
HYPERLINK \l "_Toc202549025" 2.5.2 Binary weaving PAGEREF _Toc202549025 \h 16
HYPERLINK \l "_Toc202549026" Step 1: Compile the Java sources PAGEREF _Toc202549026 \h 16
HYPERLINK \l "_Toc202549027" Step 2: Compile the aspect PAGEREF _Toc202549027 \h 16
HYPERLINK \l "_Toc202549028" Step 3: Weave the aspects PAGEREF _Toc202549028 \h 16
HYPERLINK \l "_Toc202549029" 2.5.3 Load-time weaving PAGEREF _Toc202549029 \h 17
HYPERLINK \l "_Toc202549030" 2.6 AspectJ-Spring integration PAGEREF _Toc202549030 \h 18
HYPERLINK \l "_Toc202549031" 2.7 AspectJ logistics overview PAGEREF _Toc202549031 \h 20
HYPERLINK \l "_Toc202549032" 2.7.1 IDE integration PAGEREF _Toc202549032 \h 20
HYPERLINK \l "_Toc202549033" 2.7.2 AspectJ documentation tool PAGEREF _Toc202549033 \h 22
HYPERLINK \l "_Toc202549034" 2.8 Summary PAGEREF _Toc202549034 \h 23
2
Introducing AspectJ
This chapter covers
AspectJ ÒHello, world!Ó
AspectJ language overview
Weaving mechanisms
Spring integration
xe "AspectJ\: relation to Java"AspectJ is an aspect-oriented extension to the Java programming language. xe "AspectJ\: overview"AspectJ consists of two parts: language specification and the language implementation. The language specification part defines the grammar and semantic of the language in which you write the code. The use of Java as the base language enables a Java programmer to understand the AspectJ language, and allows leveraging the vast repertoire of libraries and frameworks. The language implementation part includes weavers that take various forms such as a compiler and linker. A weaver produces byte code that conforms to the Java byte-code specification, allowing any compliant Java virtual machine (VM) to execute those class files. Further, the language implementation part offers support for integrated development environments (IDEs) to simplify building and debugging applications.
AspectJ started and initially grew as a special language along with a special compiler. Recently, however, a lot has changed in its form as a language as well as in the weaver. First, it offers an alternative syntax based on Java 5 annotation facility to express crosscutting constructs. This enables using a plain-Java compiler instead of the special compiler. Second, it offers new options for weaving classes with aspects. Last, it has gained a strong foothold in the Spring framework with several integration options. All these changes have made adoption of AspectJ easier than ever before. In this chapter, we will examine important facets of AspectJÑstarting with language constructs, passing through syntax and weaving choices, peeking into the Spring integration, and ending with the tools supportÑall from a high level perspective. So letÕs start.
2.1 xe "AspectJ\: simple program"Writing the First AspectJ Program
We begin our journey by writing a simple application. This code introduces a few AspectJ concepts and gives you a feel for the language.
2.1.1 Setting up the example
LetÕs create a regular Java class, as shown in listing 2.1, which contains two methods that will print messages. Later in this section, we will create a few aspects to introduce additional behavior without modifying the class.
Listing 2.1 Class encapsulating the message delivering functionality
package ajia;
public class MessageCommunicator {
public void deliver(String message) {
System.out.println(message);
}
public void deliver(String person, String message) {
System.out.println(person + ", " + message);
}
}
The MessageCommunicator class has two methods: one to deliver a general message and the other to deliver a message to a specific person. Next letÕs write a simple class to exercise the functionality of the MessageCommunicator class, as shown in listing 2.2.
Listing 2.2 Class to exercise message delivering functionality
package ajia;
public class Main {
public static void main(String[] args) {
MessageCommunicator messageCommunicator = new MessageCommunicator();
messageCommunicator.deliver("Wanna learn AspectJ?");
messageCommunicator.deliver("Harry", "having fun?");
}
}
xe "AspectJ\: compiling sources"When we compile the MessageCommunicator and the Main class together and run the Main program, we see the following output:
xe "AspectJ\: compiling sources"> ajc ajia\MessageCommunicator.java ajia\Main.java
xe "AspectJ\: running programs"> java ajia.Main
Wanna learn AspectJ?
Harry, having fun?
Setting up the environment
To simplify command-line operations, I assume that you have set the CLASSPATH environment variable to include aspectjrt.jar from the AspectJ distribution. I also assume that you have path to java, javac, and ajc included in the PATH variable. Please see the downloadable source code for more information.
Every valid Java program is a valid AspectJ program. Therefore, we could use the AspectJ compiler (ajc) to compile the classes much as with a Java compiler such as ÔjavacÕ. Now that we have basic setup ready, letÕs add a few aspects into the system to improve the message delivering functionality.
2.1.2 Introducing security
Without changing even a single line of code in the MessageCommunicator class, we can enhance its functionality by adding an aspect to the system. LetÕs add an implementation for the crosscutting concern of authentication, where before delivering a message, we would like to check if the user has been authenticated. Here we use the traditional syntax in listing 2.3. Later, we will see alternative syntax to implement the same functionality.
Listing 2.3 Aspect to secure access
package ajia;
import ajia.auth.Authenticator;
public aspect SecurityAspect { #1
private Authenticator authenticator = new Authenticator();
pointcut secureAccess() #2
: execution(* MessageCommunicator.deliver(..)); #2
before() : secureAccess() { #3
System.out.println("Checking and authenticating user"); #3
authenticator.authenticate(); #3
} #3
}
#1 Aspect declaration
#2 Pointcut declaration
#3 Advice
I donÕt show the Authenticator for brevity's sake (however you can download it from the bookÕs web site). Also, ideally dependencies such as authenticator should be injected using a framework such as Spring. However, I take a simpler approach to allow focusing on AOP.
LetÕs compile our classes along with the aspect. Now when we run the program, we see the following output:
> ajc ajia\MessageCommunicator.java ajia\Main.java ajia\SecurityAspect.aj
[CA] ajia\auth\*.java
> java ajia.Main
Checking and authenticating user
Username: ajia
Password: ajia
Wanna learn AspectJ?
Checking and authenticating user
Harry, having fun?
LetÕs understand the magic this new aspect and ajc perform. The SecurityAspect.aj file declares the SecurityAspect aspect. Note that we could have declared the aspect in SecurityAspect.java file, since AspectJ accepts both .aj and .java extension for input source files. While the file extension doesnÕt matter to the compiler, it is typical for aspects to use the .aj extension and Java code to use the .java extension:
#1 An aspect is a unit of modularization in AOP, much like a class is a unit of modularization in OOP. The declaration of an aspect is similar to a class declaration.
xe "pointcut\: example"#2 A pointcut selects interesting points of execution in a system, called join points. The aspect defines a pointcut secureAccess() that selects execution of all the methods named deliver() in the MessageCommunicator class. The * indicates that the selection criterion matches any return type, and the .. inside parentheses after deliver specifies that the selection criterion matches regardless of the number of arguments or their types. In our example, the pointcut would select execution of both of the overloaded versions of deliver() in the MessageCommunicator class. We will learn about join points and pointcuts in detail in the next chapter.
xe "before advice\: example"#3 An advice defines the code to execute upon reaching join points selected by the associated pointcut. Here, we define a piece of advice to execute before reaching the join points selected by the secureAccess() pointcut. The before() part indicates that the advice should run prior to the execution of the advised join pointÑin our case, prior to executing any MessageCommunicator.deliver() method. In the advice, we authenticate the current user.
With the aspect now present in the system, each time that MessageCommunicator.deliver() is executed, the advice code perform the authentication logic before the method.
Now that we have gotten the flavor of the AspectJ language, it is time to take an overview of the language and its core building blocks.
2.2 AspectJ from 20,000 feet
In chapter 1, we introduced the AOP concept of weaving the crosscutting concerns into the core logic using weaving rules. xe "weaving rules\: role in crosscutting"Weaving rules specify ÒwhatÓ action to perform ÒwhenÓ encountering certain points in the execution of the program. LetÕs examine the two kinds of syntax and crosscutting constructs offered by AspectJ.
2.2.1 AspectJ syntax choices
AspectJ uses extensions to the Java programming language to specify the weaving rules for the dynamic and static crosscutting. These extensions come in two flavors:
Traditional syntax: This original AspectJ syntax extends that Java language specification using new keywords.
@AspectJ syntax: This relatively new syntax uses Java annotations to express AOP constructs with otherwise plain Java.
With either style, a Java programmer should feel at home while using the extensions (we will compare these syntax styles in chapter 7). The AspectJ offers several constructs to specify the weaving rules. We introduce them in this section and discuss them in depth in the next four chapters. Note that code examples in this section use the traditional AspectJ syntax. Later in this chapter, we will use the @AspectJ syntax.
2.2.2 Understanding Crosscutting Elements
Recall the generic AOP model discussed in chapter 1. AspectJ is the most complete implementation of that model supporting all its elements. In this section, we examine how AspectJ maps each model element into program constructs. The most important and central concept in AOP and AspectJ is join point, so it is a good place to get started.
Aspect
xe "aspect\: definiton of"The aspect is the central unit of AspectJ, in the same way that a class is the central unit in Java. It contains the code that expresses the weaving rules for both dynamic and static crosscutting by including pointcuts, advice, ITDs, and compile-time declarations. Additionally, aspects can contain data, methods, and nested class members, just like a normal Java class. LetÕs define an aspect that performs profiling that we will update as we learn about more elements.
package ajia.profile;
public aspect ProfilingAspect {
}
In the next step, we identify points of interest of a crosscutting functionality.
Join point
xe "join point\: definition of"In AOP, and therefore in AspectJ, join points are the places where the crosscutting actions take place. In listing 2.1, we have join points corresponding to the execution of the deliver() methods as well as call to the println() method on System.out object. In listing 2.2, for example, we have join points corresponding to the creation of the MessageCommunicator and calls to the deliver() methods.
Once you identify join points useful for a crosscutting functionality, you need to a way to select them using the construct of pointcut.
Pointcut
xe "pointcut\: definition of"A pointcut is a program construct that selects join points and collects context at those points. For example, a pointcut can select a join point that is an execution of a method, and it could also collect the join point context, such as the ÔthisÕ object and the arguments to the method.
LetÕs write a pointcut that selects the execution of any public method in the system:
execution(public * *.*(..))
The wildcards * and .. indicate that the pointcut selects regardless of the return type, declaring type, method name, or method parameters. Here the only condition we specify is that access specification for the method must be public.
It is a good idea to name a pointcut so that it other programming elements may use it. For example, we can name the earlier pointcut as publicOperation as follows:
pointcut publicOperation() : execution(public * *.*(..));
LetÕs put this pointcut in ProfilingAspect:
package ajia.profile;
public aspect ProfilingAspect {
pointcut publicOperation() : execution(public * *.*(..));
}
All this hard work of learning about join points and pointcuts will pay off when we start using them to perform crosscutting actions using the construct of advice.
Advice
xe "advice\: definition of"Advice is the code to be executed at a join point that has been selected by a pointcut. Advice can execute before, after, or around the join point. Around advice can modify the execution of the code that is at the join point, it can replace it, or it can even bypass it. The body of advice is much like a method bodyÑit encapsulates the logic to be executed upon reaching a join point.
Using the earlier pointcut in the previous section, we can advise all public methods of MessageCommunicator to profile it. LetÕs update ProfilingAspect (shown in listing 2.4) with profiling advice:
Listing 2.4: Profiling all public methods
package ajia.profile;
public aspect ProfilingAspect {
pointcut publicOperation() : execution(public * *.*(..));
Object around() : publicOperation() {
long start = System.nanoTime();
Object ret = proceed();
long end = System.nanoTime();
System.out.println(thisJoinPointStaticPart.getSignature()
+ " took " + (end-start) + " nanoseconds");
return ret;
}
}
The advice records the start time, calls proceed() to continue executing the advised method, records the end time, and prints the time taken by the method execution. The thisJoinPointStaticPart variable is one of the three variables available in each advice that carry information about the currently advised join point such as the method name, the this object, and method arguments.
When we compile this aspect along other code and execute it, we get the following output:
> ajc -source 5 ajia\MessageCommunicator.java ajia\Main.java
[CA] ajia\SecurityAspect.aj ajia\profile\ProfilingAspect.aj ajia\auth\*.java
> java ajia.Main
Checking and authenticating user
boolean ajia.auth.Authenticator.isAuthenticated() took 840051 nanoseconds
Username: ajia
Password: ajia
String[] ajia.auth.Authenticator.getUserNamePassword() took 5248473759 nanoseconds
void ajia.auth.Authenticator.authenticate() took 5250886077 nanoseconds
Wanna learn AspectJ?
void ajia.MessageCommunicator.deliver(String) took 5252761734 nanoseconds
Checking and authenticating user
boolean ajia.auth.Authenticator.isAuthenticated() took 5028 nanoseconds
void ajia.auth.Authenticator.authenticate() took 61740 nanoseconds
Harry, having fun?
void ajia.MessageCommunicator.deliver(String, String) took 307581 nanoseconds
void ajia.Main.main(String[]) took 5253861315 nanoseconds
Pseudo keywords in AspectJ
The proceed and other keywords such as aspect, pointcut, and before are really pseudo keywords that gain special meaning only in the right context. For example, it is perfectly legitimate to use a method named ÔproceedÕ in Java classes. However, when proceed is used in an around advice, it acquires a special meaning. This use of pseudo keywords enables AspectJ to work with any valid Java program that may already include AspectJ keywords.
xe "dynamic crosscuting\: use of pointcut"Pointcuts xe "pointcut\: use with advice"and xe "advice\: use with pointcut"advice together form the dynamic crosscutting rules. While the pointcuts identify the required join points, the advice completes the picture by providing the actions that will occur at the join points.
Static crosscutting that comes in the form of inter-type and compile-time declarations complement dynamic crosscutting. LetÕs see how.
Inter-type declaration
xe "introduction\: defintion of"The inter-type declaration (ITD) (also referred to as ÒintroductionÓ) is a static crosscutting construct that alters the static structure of the classes, interfaces, and aspects of the system. For example, you can add a method or field to a class or declare a type to implement an interface. In an ITD, one type (an aspect) declares structure for the other types (classes, interfaces, and even aspects), hence the name.
The following introduction declares the MessageCommunicator class to implement the AccessTracked interface:
declare parents: MessageCommunicator implements AccessTracked;
Once this declaration is woven in, you may use an MessageCommunicator instance where an instance of AccessTracked is expected.
Inter-type declarations can be used for member introductionÑa way to add new fields and methods to other types. The following declaration adds the lastAccessedTime field and the updateLastAccessedTime() and getLastAccessedTime() method to the AccessTracked type:
private long AccessTracked.lastAccessedTime;
public void AccessTracked.updateLastAccessedTime() {
lastAccessedTime = System.nanoTime();
}
public long AccessTracked.getLastAccessedTime() {
return lastAccessedTime;
}
You may then advise methods in a type that implements the AccessTracked (directly or through a declare parents statement) to update the last accessed time as shown in the following snippet:
before(AccessTracked accessTracked)
: execution(* AccessTracked+.*(..))
&& !execution(* AccessTracked.*(..))
&& this(accessTracked){
accessTracked.updateLastAccessedTime();
}
Here we advise all methods of types that implement the AccessTracked interface (notice, the Ô+Õ wildcard that denotes subtypes), but not the method in the AccessTracked itself (such as the introduced updateLastAccessedTime() method). The this() pointcut collects the tracked object so that we can call updateLastAccessedTime() method on it.
LetÕs put all these snippets in an aspect in listing 2.5 to see their effect.
Listing 2.5 Tracking last updated time using an aspect
package ajia.track;
import ajia.MessageCommunicator;
public aspect TrackingAspect {
declare parents: MessageCommunicator implements AccessTracked;
private long AccessTracked.lastAccessedTime;
public void AccessTracked.updateLastAccessedTime() {
lastAccessedTime = System.nanoTime();
}
public long AccessTracked.getLastAccessedTime() {
return lastAccessedTime;
}
before(AccessTracked accessTracked)
: execution(* AccessTracked+.*(..))
&& !execution(* AccessTracked.*(..))
&& this(accessTracked) {
accessTracked.updateLastAccessedTime();
}
private static interface AccessTracked {
}
}
To see the effect, we modify the Main class to print the last accessed time for the messageCommunicator object as follows:
Listing 2.6 Modified Main class to print the last accessed time
package ajia;
public class Main {
public static void main(String[] args) {
...
System.out.println("Last accessed time for messageCommunicator "
+ messageCommunicator.getLastAccessedTime());
}
}
When we compile and execute this aspect, we get the following output:
> ajc -source 5 ajia\MessageCommunicator.java ajia\Main.java
[CA] ajia\SecurityAspect.aj ajia\profile\ProfilingAspect.aj
[CA] ajia\track\TrackingAspect.aj ajia\auth\*.java
...
Last accessed time for messageCommunicator 443484544331192
...
ITDs also offer a way to annotates program elements and deal with checked exceptions in a systematic manner. However, we will defer its discussion until chapter 5 (Static Crosscutting). An important form of static crosscutting allows detecting and flagging the presence of join points matching a pointcut during compilation. LetÕs see how.
Compile-time declaration
xe "compile-time declaration\: definition of"The compile-time declaration is another static crosscutting construct that allows you to add compile-time warnings and errors upon detecting certain usage patterns. Consider SecurityAspect from listing 2.3. With this aspect in place, you might want to warn direct calls to the Authenticator.authenticate() method. The following declaration will cause the compiler to issue a warning if any part of the system calls the prohibited method except, of course, the SecurityAspect itself.
declare warning
: call(void Authenticator.authenticate()) && !within(SecurityAspect)
: "Authentication should be performed only by SecurityAspect";
Note the use of the call() pointcut to select a method call (as opposed to the method execution, which always is in the Authenticator class) and !within() to restrict to only those occurring outside SecurityAspect. The weaver will then report warnings on detecting the specified condition in the same way as other compile-time warnings such as use of a deprecated class. LetÕs see this in action by modifying SecurityAspect to add this declaration.
package ajia;
import ajia.auth.Authenticator;
public aspect SecurityAspect {
...
declare warning
: call(void Authenticator.authenticate()) && !within(SecurityAspect)
: "Authentication should be performed only by SecurityAspect";
}
We also modify the Main class to add new Authenticator().authenticate() to test a violation. When we compile the code, we get the following output:
> ajc -source 5 ajia\MessageCommunicator.java ajia\Main.java
[CA] ajia\SecurityAspect.aj ajia\auth\*.java
...\ajia\Main.java:13 [warning]
[CA] Authentication should be performed only by SecurityAspect
new Authenticator().authenticate();
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
method-call(void ajia.auth.Authenticator.authenticate())
see also: ...\ajia\SecurityAspect.aj:15::0
As you can see, compiler detected and flagged the violation.
By now, you must be wondering how AspectJ performs its magic. In the next section, weÕll take a quick look at how the source files are compiled into the byte code.
2.3 xe "AspectJ\: internals"AspectJ: under the hood
Since the byte code produced by the AspectJ weaver must run on any compliant Java VM, it must adhere to the Java byte-code specification. This means the weaver must map crosscutting elements to Java constructs. In this section, we outline how the different elements in an AspectJ program map to pure Java byte code. Note that the discussion that follows presents a simplified view of AspectJ code transformation into pure Java byte code.
Here are the typical ways that the AspectJ weaver maps various crosscutting elements to pure Java:
xe "AspectJ\: mapping of elements"Aspects map to classes, with each data member and method becoming the members of the class representing the aspect.
Pointcuts are intermediate elements that map to methods. Furthermore, they may have associated auxiliary methods to help perform matching at runtime.
Advice usually maps to one or more methods. The weaver inserts calls to these methods at potential locations matching the associated pointcut.
Inter-type declarations of fields and methods are added directly to the target classes.
Compile-time warnings and errors have no effect on byte code. They simply cause the compiler to print warnings or abort the compilation when producing an errorxe "AspectJ\: mapping of elements".
Furthermore, each mapped element carries annotations that help the weaver use the crosscutting information even for aspects presented to it in byte code form only. The annotations also help bring in symmetry with the @AspectJ syntax that we will see in section 2.4.
Caution
xe "AspectJ\: internal details verses language semantics"Thinking about the language semantics in terms of the transformed code helps in taking the mystery out of AspectJ. It also makes you appreciate the hard work that the AspectJ compiler is performingÑand the hard work that you no longer need to perform! However, such thinking has inherent within it the danger of bogging down too much in the details of the transformed code. A better approach is to start thinking in terms of language semantics instead of implementation.
In the light of this information, letÕs see how aspects and classes would look after passing through ÔajcÕ. Note that the AspectJ compiler produces byte code and not the Java code as shown here. WeÕre showing you this code only to give you an idea of the source code that would be roughly equivalent to the byte code produced. Also, some of the details (especially related to advanced features such as aspect association) that are beyond the scope of this discussion have been omitted.
2.3.1 The aspect
First, letÕs examine the code in a class that would be equivalent to SecurityAspect from listing 2.3:
package ajia;
import ajia.auth.Authenticator;
@Aspect
xe "aspect\: mapping to Java class"public class SecurityAspect {
private Authenticator authenticator = new Authenticator();
public static final SecurityAspect ajc$perSingletonInstance;
@Pointcut("execution(* MessageCommunicator.deliver(..))")
void ajc$pointcut$$secureAccess$76() {}
@Before("secureAccess()")
public final void ajc$before$ajia_SecurityAspect$1$e248afa3() {
System.out.println("Checking and authenticating user");
authenticator.authenticate();
}
static {
SecurityAspect.ajc$perSingletonInstance = new SecurityAspect();
}
... method aspectOf() and hasAspect() ...
... aspect initialization code ...
}
///Amin: Should this be using Annotations or should it be the traditional approachxe "aspect\: mapping to Java class"
SecurityAspect is mapped to a class of the same name. By default, aspects are singleton and users donÕt instantiate them explicitly. The static block of the aspect ensures that the singleton aspect instance is created as soon as the SecurityAspect class is loaded into the systemÑtypically during the execution of some code that refers to the aspect. The pointcut is mapped to the ajc$pointcut$$secureAccess$76() element. The before advice is mapped to the ajc$before$ajia_SecurityAspect$1$e248afa3() method whose body is identical to the advice body. AspectJ weaver weaves in these methods into the advised code as we see next.
2.3.2 The woven class
Now letÕs see the equivalent code for the MessageCommunicator class (from listing 2.1), after it has been woven with SecurityAspect:
package ajia;
xe "weaving\: example output"public class MessageCommunicator {
public void deliver(String message) {
SecurityAspect.aspectInstance.
ajc$before$ajia_SecurityAspect$1$e248afa3();
System.out.println(message);
}
public void deliver(String person, String message) {
SecurityAspect.aspectInstance.
ajc$before$ajia_SecurityAspect$1$e248afa3();
System.out.print(person + ", " + message);
}
}xe "weaving\: example output"
Recall that the deliverMessage() pointcut in SecurityAspect is defined to select both of the overloaded deliver() methods in MessageCommunicator. Accordingly, we see that the ajc$before$ajia_SecurityAspect$1$e248afa3() call on the aspect instance SecurityAspect.aspectInstance is made from both methods.
Performance implication of AspectJ weaving
ÒHow does it affect performance of woven codeÓ is perhaps the most commonly asked questions about AspectJ. The inquiring mind wants to know, ÒHow does a hand-woven implementation of crosscutting functionality compare with that implemented with AspectJÓ. The code produced by the weaver answers this question quite well. Since the weaver simply encapsulates the advice in a method and calls it from appropriate places, there is no virtually no overhead from the AspectJ weaver. Furthermore, since advice is well isolated in one place, one can easily add optimizations to that code, something that one would cringe at implementing in hundreds of places. If you are looking for more details on weaving and its performance implications, please read ÒAdvice Weaving in AspectJÓ by Erik Hilsdale and Jim Hugunin (http://hugunin.net/papers/aosd-2004-cameraReady.pdf).
Until now, we restricted ourselves only to the traditional AspectJ syntax (which has been available in every version of AspectJ) and compiler as the weaver. This syntax requires the use of the special ÔajcÕ compiler early in the development process to compile aspects and therefore poses a potential barrier in adopting AspectJ. To simplify the adoption, starting with AspectJ 5, there are alternative syntax and weaving models available, due to the merger of AspectJ with AspectWerkz, another implementation of AOP for Java. LetÕs look at a brief overview of these new features; later in chapter 7 (The @AspectJ syntax), we will study these features in detail.
2.4 AspectJ alternative syntax
AspectJ offers an alternative syntaxÑthe @AspectJ syntaxÑthat extends the language using the new annotation facility in Java 5. The main advantage of this syntax style is that the code can be compiled using a plain Java compiler (for example, ÔjavacÕ) and therefore the code works better with conventional Java IDEs and tools that donÕt understand the AspectJ syntax. Furthermore, the proxy-based AOP framework in Spring uses this syntax, further simplifying adoption of AspectJ if the project is already using Spring. The disadvantage of @AspectJ syntax is it verbosity in expressing the same constructs and limitations in expressing certain constructs, especially in the static crosscutting category.
LetÕs create an @AspectJ version of the aspect in listing 2.3 as shown in listing 2.7. The listing should remind you of the annotations in the woven code from the previous section.
Listing 2.7 Introducing security using the @AspectJ syntax
package ajia;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import ajia.auth.Authenticator;
@Aspect #1
public class SecurityAspect { #1
private Authenticator authenticator = new Authenticator();
@Pointcut("execution(* MessageCommunicator.deliver(..))") #2
public void secureAccess() {} #2
@Before("secureAccess()") #3
public void secure() { #3
System.out.println("Checking and authenticating user"); #3
authenticator.authenticate(); #3
} #3
}
#1 Aspect declaration
#2 Pointcut declaration
#3 Advice
LetÕs understand the difference between listing 2.3 and 2.7:
#1 Instead of using the aspect keyword, we use just a class annotated with an @Aspect annotation. The ÔajcÕ compiler, which understands the semantics associated with the @Aspect annotation, will use this information to treat the class as if it was an aspect.
#2 Similarly, the @Pointcut annotation marks an empty method as a pointcut. The @Pointcut annotation requires specifying the pointcut expression. We use exactly the same expression as in the version using the traditional syntax. The name of the method serves as the pointcut name.
#3 The @Before annotation marks a regular method as a before advice. The body of the method consists of the advice logicÑit is the code that will get executed when a matching join point is executed.
First, letÕs compile the same code using ÔjavacÕ and execute the resulting code:
> javac ajia\MessageCommunicator.java ajia\SecurityAspect.java ajia\Main.java
[CA] ajia\auth\*.java
> java ajia.Main
Wanna learn AspectJ?
Harry, having fun?
While code compiled just fine, aspect had no effect on the output. This may not exactly surprise you; after all ÔjavacÕ has no idea of the meaning of annotations such as @Aspect and @Pointcut. In general, ÔjavacÕ has no idea of all but a few standard annotations (such as @Override and @SuppressWarnings). You need an aspect weaver somewhere between compiling source code and executing byte code in the VM. The simplest way to use the @AspectJ syntax is to use the ÔajcÕ compiler instead of ÔjavacÕ. Other alternatives include binary weaving, where code compiled using ÔjavacÕ is then woven using ÔajcÕ, and load-time weaving (LTW), where classes are woven as they are being loaded into the VM.
Now letÕs compile this source file along with the MessageCommunicator and Main classes (in listing 2.1 and 2.2):
> ajc -source 5 ajia\MessageCommunicator.java ajia\SecurityAspect.java ajia\Main.java ajia\auth\*.java
> java ajia.Main
Checking and authenticating user
Username: ajia
Password: ajia
Wanna learn AspectJ?
Checking and authenticating user
Harry, having fun?
It is exactly same output as produced by the aspect in listing 2.3. (Note the Ò-source 5Ó option to allow using Java 5 constructs such as annotations.)
The value proposition of @AspectJ syntax is a simplified adoption curve. First, the aspectÕs syntax is expressed using Java constructs, which reduces the mental block commonly associated with using yet-another-language. You are still using just plain Java! Second, tools such as compilers, IDEs, and code coverage tools work more easily with @AspectJ syntax, since they are working with plain Java code. For example, you can develop using an AspectJ-unaware IDE.
While @AspectJ allows the possibility of using ÔjavacÕ for compiling aspects, you will need to introduce a weaver somewhere between your code and the VM. LetÕs see what choices AspectJ offers for weaving.
2.5 Weaving mechanisms
A weaver needs to weave together classes and aspects so that the crosscutting elements have the desired effect: advice gets executed, inter-type declarations affect the static structure and weave-time declarations produce warnings and errors. AspectJ offers three weaving models to accommodate requirements of various build and deployment scenarios:
Source weaving
Binary weaving
Load-time weaving
Regardless of the weaving model used, the resulting execution of the system is identical. The weaving mechanism is also orthogonal to the AspectJ syntax used; any combination of weaving mechanism and AspectJ syntax will produce identical results. In this section, we will take an overview of the weaving models offered by AspectJ. We will revisit this topic again in chapter 8 (AspectJ Weaving Models), when we will discuss it in details.
2.5.1 Source weaving
In source weaving, the weaver is part of the compiler (all the examples in this chapter so far used source code weaving). The input to the weaver is classes and aspects in source code form. The aspects could be written in either the traditional syntax or the @AspectJ syntax. The weaver, which works in a manner similar to a compiler, processes the source and produces woven byte code. The byte code produced by the compiler is compliant with the Java byte code specification, which any standard compliant VM can execute. Essentially, when used in this manner ajc replaces ÔjavacÕ. However, note that unlike ÔjavacÕ, ÔajcÕ requires that all sources be presented together if the woven byte code is desired. If sources are presented separately, the resulting byte code can be used as input for binary weaving or load-time weaving discussed next.
2.5.2 Binary weaving
In binary weaving too, the weaver is part of the compiler. The input to the weaver, classes and aspects, are in byte code form. The input byte code is compiled separately using the Java compiler or the AspectJ compiler. For example, you can use jar files or class files produced using the Java compiler.
Binary weaver and linker
If you are familiar with languages such as ÔCÕ and ÔC++Õ, think of the weaver as a linker, which is a more accurate comparison. Much the same way a linker takes libraries compiled using a compiler as input to produce an executable or another library, the weaver takes files containing byte-code as input produces woven byte-code.
LetÕs see binary weaving in action in a step-by-step manner. Our goal is to first compile classes and aspect without weaving and then weave the resulting binary (.class) files.
Step 1: Compile the Java sources
Here we compile code in listing 2.1 and 2.2 using ÔjavacÕ. While we could have used ÔajcÕ to have the same effect, to illustrate the effect of binary weaving clearly, we will stay away from ÔajcÕ. We will use the Ðd option to specify the destination directory for the classes to help understand the effect better:
> javac Ðd classes ajia\MessageCommunicator.java ajia\Main.java
[CA] ajia\auth\*.java
Unsurprisingly, executing the Main class shows that there is no effects of the aspect.
xe "AspectJ\: running programs"> java Ðclasspath classes ajia.Main
Wanna learn AspectJ?
Harry, having fun?
Step 2: Compile the aspect
Next, we compile the @AspectJ styled aspect from listing 2.7 directing its output to a different directory.
> javac Ðd aspects ajia\SecurityAspect.java
If we wanted to use the traditional style aspect in listing 2.3, we would have to compile it using ÔajcÕ.
> ajc Ðd aspects ajia\SecurityAspect.aj
Regardless, we will have one class file per source file. Executing the Main class shows that the output still does not have any effects of the aspect:
xe "AspectJ\: running programs"> java Ðclasspath classes;aspects ajia.Main
Wanna learn AspectJ?
Harry, having fun?
Step 3: Weave the aspects
To weave aspect into classes in binary form, we use binary weaving:
> ajc Ðinpath classes;aspects Ðaspectpath aspects Ðd woven
The Ðinpath option specifies the path to the classes that are weaving targets. Since we used javac to compile aspects, we need to pass those as well to Ðinpath so that ajc can add needed support methods. The -aspectpath option specifies the path to the aspects to be woven in.
Executing the Main class shows that we have restored the security into the system. Since we are passing an explicit Ðclasspath option, we need to add the CLASSPATH we have set to make AspectJ runtime available to the VM.
> java Ðclasspath woven;%CLASSPATH% ajia.Main
Checking and authenticating user
Username: ajia
Password: ajia
Wanna learn AspectJ?
Checking and authenticating user
Harry, having fun?
Binary weaving also can take input as a combination of source and byte code form. For example, you may have classes compiled into a jar file and aspect available as source. This would allow you to weave into third-party jars without requiring access to its source files. Similarly, you can also weave aspects in jar or class file into classes and interface available in source code form. This would allow you to weave an aspect library in binary format with your sources. Chapter 8 (AspectJ Weaving Models) discusses all these possibilities. An extension of binary weaver is load-time weaving, which we examine next.
2.5.3 Load-time weaving
Load-time weaver takes input in the form of binary classes and aspects, as well as aspects and configuration defined in XML format. A load-time agent (which can take many forms: a JVMTI agent, a classloader, a VM- and application server-specific class preprocessor) weaves as the classes are loaded into the VM.
LetÕs use LTW to weave in the SecurityAspect. We start with the output of the first two steps in the previous section. Load-time weaving needs an XML file that specifies the weaving configuration. AspectJ supports a few locations for such a file. We will use one of those by naming it aop.xml and placing it in directory named META-INF in a classpath component. Listing 2.8 shows the minimal XML file that serves our purpose:
Listing 2.8 aop.xml specifying the configuration to load-time weaver
This configuration tells the weaver is that the SecurityAspect need to be woven in. As we will see later in chapter 8, aop.xml can include a lot more configuration information, including pointcuts definitions.
We enable load-time weaving by including the Ðjavaagent option when starting the VM:
> java Ðclasspath classes;aspects;. [CA] -javaagent:%ASPECTJ_HOME%\lib\aspectjweaver.jar ajia.Main
Checking and authenticating user
Username: ajia
Password: ajia
Wanna learn AspectJ?
Checking and authenticating user
Harry, having fun?
The Ðjavaagent option specifies the Java VM Tools Interface (JVMTI) agent to be used is aspectjweaver.jar that comes as a part of AspectJ distribution. The weaver uses the information in aop.xml file in listing 2.8 and uses the aspects specified in the section to weave into classes as they are being loaded into the VM.
Spring-native LTW
Spring 2.5 introduces ÒSpring-nativeÓ LTW, an alternative way to configure AspectJ LTW for Spring applications. With it, for certain application- and web-servers you can avoid modifying the launch script (required to specify the Ðjavaagent option); instead you modify the application context to express the desire to use the LTW and Spring handles the rest. We will examine this weaving option in chapter 9.
So far, we studied weaver options offered by AspectJ itself. The Spring Framework, starting with 2.0 version, offers yet another possibility for weaving AspectJ aspects. In the next section, we take a quick look at this possibility.
2.6 AspectJ-Spring integration
Spring, the most widely used lightweight framework for developing enterprise applications, has been offering its own form of AOP to modularize crosscutting concerns typically seen in enterprise applications. Spring AOP uses the proxy design pattern to intercept execution of methods on the target object. Due to the use of proxies, it exposes method execution join points only for objects created by the Spring container (commonly known as Spring beans). However, it is a pragmatic solution in the context of lightweight enterprise application development.
Starting with Spring 2.0, it offers several options to leverage AspectJÕs power in an incremental manner. For example, it allows using AspectJ pointcut expressions in addition to its own pointcut expressions. In this section, we preview the Spring-AspectJ integration for aspects written in @AspectJ syntax. We will examine all integration possibilities in details in chapter 6 when we study alternative syntax and weaving models.
Updating the environment variable
For this section, you need to add jars from Spring distribution to your CLASSPATH. Please see downloaded sources for more details.
LetÕs take a look at a simple example. We would like to introduce simple security into the MessageCommunicator object. We will continue to use the MessageCommunicator class from listing 2.1 and the annotation-style aspect from listing 2.7. First we will write a minimum application context file to define the needed configuration.
Listing 2.9 Defining the application context (applicationContext.xml)
#1
#2
#3
#1 Declaring automatic proxy creation
#2 Aspect bean
#3 Regular bean
#1 The element tells Spring to automatically create proxies for the beans that can be advised by aspects.
#2 The element simply creates a bean for the MessageCommunicator class.
#3 The element creates a bean corresponding to the aspect declared using the @AspectJ syntax.
Next, we modify the Main class from listing 2.2 to create the application context and obtain the messageCommunicator bean from it. Then we use the bean to deliver a few messages.
Listing 2.10 Using Spring container application context
package ajia;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context
= new ClassPathXmlApplicationContext("applicationContext.xml");
MessageCommunicator messageCommunicator
= (MessageCommunicator)context.getBean("messageCommunicator");
messageCommunicator.deliver("Wanna learn AspectJ?");
messageCommunicator.deliver("Harry", "having fun?");
}
}
Now, letÕs compile all these classes and execute the Main class.
> javac ajia\*.java ajia\auth\*.java
> java ajia.Main
Checking and authenticating user
Username: ajia
Password: ajia
Wanna learn AspectJ?
Checking and authenticating user
Harry, having fun?
We see the same output as using an AspectJ weaver. Behind the scenes, the Spring container creates a proxy around the messageCommunicator bean and intercepts methods called on it according to the SecurityAspect defined using the @AspectJ syntax, producing the desired effect. As a result, we can leverage the aspects written using @AspectJ in a Spring application without need for the AspectJ weaver. We will examine details in chapter 9 (Spring AspectJ Integration).
So far in this chapter, we used only command line tools to work with aspects. However, in real-life, virtually no one works using command-line tools anymore. For a programmer to be productive, you need a good IDE. Further, we need support for documentation so that the crosscutting information is available even outside the IDE. In the next section, we will look at the logistical support provided by AspectJ.
2.7 AspectJ logistics overview
AspectJ offers a complete set of tools ranging from a compiler to integrated development environment (IDE) support. We have already examined the compiler and load-time weaver. LetÕs look at a few important tools in more detail.
2.7.1 xe "IDE integration"IDE integration
IDE support offers an integrated approach to editing, compiling, executing, and debugging tasks. AspectJ eases the development process by providing integration with xe "Eclipse\: AspectJ integration"Eclipse. The integration with the IDE is achieved through AspectJ Development Tools (AJDT) plug-in. xe "IDE integration\: functionality"Using this integration, you can edit, compile, and debug your project in the same way you would work with a project written only in Java. Figure 2.1 shows how the example in this chapter looks in the Eclipse IDE. Note that Spring IDE, an Eclipse plugin for developing Spring-based applications, also offers similar functionality for Spring-AspectJ integration discussed in section 2.6.
Eating its own dog food
AJDT uses AspectJ to weave into Java Development Tools (JDT) to offer AspectJ integration.
xe "IDE integration\: crosscutting view"In figure 2.1, xe "crosscutting\: viewing in an IDE"we see that Eclipse shows the standard views, along with the Cross References view that shows how an advice applies to different parts of the code. This is a useful debugging tool when you have applied advice to a method but you do not see any effect.
Figure 2.1 Developing applications using Eclipse-AspectJ integration. The overall feel for editing, building, and debugging is like a plain Java project. The IDE also shows how crosscutting elements affect the various parts of the system.
The AJDT plugin also offers a way to monitor changes to crosscutting structure when the system evolves using Crosscutting Comparison View as well as visualizing the big picture effects of crosscutting concerns using Visualiser. We will study the IDE support for AspectJ in more detail in Appendix C.
What about other IDEs?
Earlier versions of AspectJ supported other IDEs besides Eclipse: NetBeans, JBuilder, Emacs JDEE by offering open source plugins for each of them. However, those plugins havenÕt been kept up-to-date (There is current effort to revive the NetBeans plugin. See HYPERLINK "http://www.jroller.com/ramlog/entry/using_the_aspectj_plug_in1" http://www.jroller.com/ramlog/entry/using_the_aspectj_plug_in1 for more details). This is perhaps a reflection of the market realityÑno other IDE is as popular as Eclipse.
One IDE that has a good market and mind share is IntelliJ IDEA. For it and any IDE without direct support for AspectJ, the possibility of using the @AspectJ syntax makes the lack of direct AspectJ support a less pronounced issue. Since the code is still plain Java, as far as the IDE is concerned, you can edit code and leverage code completion etc. If the IDE allows you to replace the default compiler, you can replace it with ÔajcÕ. If not, you can introduce a post compilation step to run ÔajcÕ to perform binary weaving. Debugging works fine too, since the method representing advice is still executed as if there was a real call.
When using @AspectJ syntax in IDE that doesnÕt support AspectJ directly, you lose source code marker indicating advice applicability, the crosscutting reference view, and crosscutting comparison view. This problem may be alleviated to an extent by use of ÔajbrowserÕ-- a standalone tool that shows how weaving rules affect different parts of program.
While Eclipse IDE integration shows crosscutting information in a crosscutting reference view, you often need the same information in a static document.
2.7.2 AspectJ documentation tool
The AspectJ documentation toolÑÔajdocÕÑextends ÔjavadocÕ to provide crosscutting information in static form. You invoke ÔajdocÕ in a similar way as ÔjavadocÕ:
> ajdoc ajia\*.java ajia\*.aj auth\*.java profile\*.aj track\*.aj
It produces html files similar to the ones produced by ÔjavadocÕ, except the elements carry additional information showing how aspects and classes interact as shown in figure 2.2.
Figure 2.2 Output produced by ajdoc, which works in a similar manner as Javadoc, except in addition to regular documentation, it add markers to show crosscutting information.
The output produced by ÔajdocÕ offers a simple way to examine crosscutting structure without needing an IDE. Since ÔajdocÕ isnÕt tied to a specific IDE, you may use it alongside of the IDE of your choice even if it doesnÕt support AspectJ directly.
2.8 Summary
AspectJ adds AOP constructsÑpointcut, advice, aspect, introductionsÑto Java, creating a powerful language that facilitates modularize crosscutting concerns, while retaining the benefits of Java, such as platform independency. Simply by learning the concepts, a Java programmer can benefit from the AOP paradigm right away. Aspects allow the separation of crosscutting actions from the core modules. You can then add new functionality without changing any code in the core modules, and without them even being aware of it.
Aspect-oriented programming in AspectJ is simple: choose where you want to crosscut, choose the kind of action you need to perform the task, and programmatically specify both of them. The AspectJ language exposes the necessary join points in a Java program. Pointcuts let you choose the join points you want to affect, and advice allows you to specify the action at those join points. The static crosscutting mechanism enables you to modify the static structure of the system in a way that affects multiple modules. AspectJ complementsÑand doesnÕt compete withÑJava. By utilizing its power to modularize the crosscutting concerns, Java programmers no longer need to recode multiple modules when implementing or changing a crosscutting concern.
The new additions to AspectJÑthe various syntax options, weaving models, and integration with Spring make adopting AspectJ much easier. It is typical for Spring developers to start with the Spring-AspectJ integration, using proxy-based weaving, and to learn the power of AOP and AspectJ through experience. Then, they often move to advanced AOP techniques using AspectJ weaving, often along with the @AspectJ syntax. At this point, load-time weaving is often a common choice due to its simplicity in getting started. Later, when they are looking for even more advanced usages, they may go for the traditional syntax along with a combination of source, binary, and load-time weaving.
In this chapter, we studied the core AspectJ concepts from 20,000 feet. It is now time to get a closer view. The next chapter introduces the join point model that is at the heart of AOP. The three chapters that follow will show dynamic crosscutting, static crosscutting, and aspects. We then proceed to understand the @AspectJ syntax, weaving mechanisms, and the Spring integration. All this information will enable you to write aspects useful in complex Java applications, something that we explore in part 2 of the book.
PAGE 22 Chapter 2 Introducing AspectJ
PAGE 21
Thanks for participating in AspectJ in Action 2nd edition Manning Early Access Program.
Please send your feedback to HYPERLINK "mailto:ajia@ramnivas.com" ajia@ramnivas.com or at HYPERLINK "http://www.manning-sandbox.com/forum.jspa?forumID=413" http://www.manning-sandbox.com/forum.jspa?forumID=413. ©Manning Publications Co.
Thanks for participating in AspectJ in Action 2nd edition Manning Early Access Program.
Please send your feedback to HYPERLINK "mailto:ajia@ramnivas.com" ajia@ramnivas.com or at HYPERLINK "http://www.manning-sandbox.com/forum.jspa?forumID=413" http://www.manning-sandbox.com/forum.jspa?forumID=413. ©Manning Publications Co.
Thanks for participating in AspectJ in Action 2nd edition Manning Early Access Program.
Please send your feedback to HYPERLINK "mailto:ajia@ramnivas.com" ajia@ramnivas.com or at HYPERLINK "http://www.manning-sandbox.com/forum.jspa?forumID=413" http://www.manning-sandbox.com/forum.jspa?forumID=413. ©Manning Publications Co.
TOC \h \z \t ".Head 1,1,.Head 2,2,.Head 3,3" HYPERLINK \l "_Toc202549007" 2.1 Writing the First AspectJ Program PAGEREF _Toc202549007 \h 2
HYPERLINK \l "_Toc202549008" 2.1.1 Setting up the example PAGEREF _Toc202549008 \h 2
HYPERLINK \l "_Toc202549009" 2.1.2 Introducing security PAGEREF _Toc202549009 \h 3
HYPERLINK \l "_Toc202549010" 2.2 AspectJ from 20,000 feet PAGEREF _Toc202549010 \h 5
HYPERLINK \l "_Toc202549011" 2.2.1 AspectJ syntax choices PAGEREF _Toc202549011 \h 5
HYPERLINK \l "_Toc202549012" 2.2.2 Understanding Crosscutting Elements PAGEREF _Toc202549012 \h 5
HYPERLINK \l "_Toc202549013" Aspect PAGEREF _Toc202549013 \h 5
HYPERLINK \l "_Toc202549014" Join point PAGEREF _Toc202549014 \h 6
HYPERLINK \l "_Toc202549015" Pointcut PAGEREF _Toc202549015 \h 6
HYPERLINK \l "_Toc202549016" Advice PAGEREF _Toc202549016 \h 6
HYPERLINK \l "_Toc202549017" Inter-type declaration PAGEREF _Toc202549017 \h 8
HYPERLINK \l "_Toc202549018" Compile-time declaration PAGEREF _Toc202549018 \h 10
HYPERLINK \l "_Toc202549019" 2.3 AspectJ: under the hood PAGEREF _Toc202549019 \h 10
HYPERLINK \l "_Toc202549020" 2.3.1 The aspect PAGEREF _Toc202549020 \h 11
HYPERLINK \l "_Toc202549021" 2.3.2 The woven class PAGEREF _Toc202549021 \h 12
HYPERLINK \l "_Toc202549022" 2.4 AspectJ alternative syntax PAGEREF _Toc202549022 \h 13
HYPERLINK \l "_Toc202549023" 2.5 Weaving mechanisms PAGEREF _Toc202549023 \h 15
HYPERLINK \l "_Toc202549024" 2.5.1 Source weaving PAGEREF _Toc202549024 \h 15
HYPERLINK \l "_Toc202549025" 2.5.2 Binary weaving PAGEREF _Toc202549025 \h 16
HYPERLINK \l "_Toc202549026" Step 1: Compile the Java sources PAGEREF _Toc202549026 \h 16
HYPERLINK \l "_Toc202549027" Step 2: Compile the aspect PAGEREF _Toc202549027 \h 16
HYPERLINK \l "_Toc202549028" Step 3: Weave the aspects PAGEREF _Toc202549028 \h 16
HYPERLINK \l "_Toc202549029" 2.5.3 Load-time weaving PAGEREF _Toc202549029 \h 17
HYPERLINK \l "_Toc202549030" 2.6 AspectJ-Spring integration PAGEREF _Toc202549030 \h 18
HYPERLINK \l "_Toc202549031" 2.7 AspectJ logistics overview PAGEREF _Toc202549031 \h 20
HYPERLINK \l "_Toc202549032" 2.7.1 IDE integration PAGEREF _Toc202549032 \h 20
HYPERLINK \l "_Toc202549033" 2.7.2 AspectJ documentation tool PAGEREF _Toc202549033 \h 22
HYPERLINK \l "_Toc202549034" 2.8 Summary PAGEREF _Toc202549034 \h 23
2
Introducing AspectJ
This chapter covers
AspectJ ÒHello, world!Ó
AspectJ language overview
Weaving mechanisms
Spring integration
xe "AspectJ\: relation to Java"AspectJ is an aspect-oriented extension to the Java programming language. xe "AspectJ\: overview"AspectJ consists of two parts: language specification and the language implementation. The language specification part defines the grammar and semantic of the language in which you write the code. The use of Java as the base language enables a Java programmer to understand the AspectJ language, and allows leveraging the vast repertoire of libraries and frameworks. The language implementation part includes weavers that take various forms such as a compiler and linker. A weaver produces byte code that conforms to the Java byte-code specification, allowing any compliant Java virtual machine (VM) to execute those class files. Further, the language implementation part offers support for integrated development environments (IDEs) to simplify building and debugging applications.
AspectJ started and initially grew as a special language along with a special compiler. Recently, however, a lot has changed in its form as a language as well as in the weaver. First, it offers an alternative syntax based on Java 5 annotation facility to express crosscutting constructs. This enables using a plain-Java compiler instead of the special compiler. Second, it offers new options for weaving classes with aspects. Last, it has gained a strong foothold in the Spring framework with several integration options. All these changes have made adoption of AspectJ easier than ever before. In this chapter, we will examine important facets of AspectJÑstarting with language constructs, passing through syntax and weaving choices, peeking into the Spring integration, and ending with the tools supportÑall from a high level perspective. So letÕs start.
2.1 xe "AspectJ\: simple program"Writing the First AspectJ Program
We begin our journey by writing a simple application. This code introduces a few AspectJ concepts and gives you a feel for the language.
2.1.1 Setting up the example
LetÕs create a regular Java class, as shown in listing 2.1, which contains two methods that will print messages. Later in this section, we will create a few aspects to introduce additional behavior without modifying the class.
Listing 2.1 Class encapsulating the message delivering functionality
package ajia;
public class MessageCommunicator {
public void deliver(String message) {
System.out.println(message);
}
public void deliver(String person, String message) {
System.out.println(person + ", " + message);
}
}
The MessageCommunicator class has two methods: one to deliver a general message and the other to deliver a message to a specific person. Next letÕs write a simple class to exercise the functionality of the MessageCommunicator class, as shown in listing 2.2.
Listing 2.2 Class to exercise message delivering functionality
package ajia;
public class Main {
public static void main(String[] args) {
MessageCommunicator messageCommunicator = new MessageCommunicator();
messageCommunicator.deliver("Wanna learn AspectJ?");
messageCommunicator.deliver("Harry", "having fun?");
}
}
xe "AspectJ\: compiling sources"When we compile the MessageCommunicator and the Main class together and run the Main program, we see the following output:
xe "AspectJ\: compiling sources"> ajc ajia\MessageCommunicator.java ajia\Main.java
xe "AspectJ\: running programs"> java ajia.Main
Wanna learn AspectJ?
Harry, having fun?
Setting up the environment
To simplify command-line operations, I assume that you have set the CLASSPATH environment variable to include aspectjrt.jar from the AspectJ distribution. I also assume that you have path to java, javac, and ajc included in the PATH variable. Please see the downloadable source code for more information.
Every valid Java program is a valid AspectJ program. Therefore, we could use the AspectJ compiler (ajc) to compile the classes much as with a Java compiler such as ÔjavacÕ. Now that we have basic setup ready, letÕs add a few aspects into the system to improve the message delivering functionality.
2.1.2 Introducing security
Without changing even a single line of code in the MessageCommunicator class, we can enhance its functionality by adding an aspect to the system. LetÕs add an implementation for the crosscutting concern of authentication, where before delivering a message, we would like to check if the user has been authenticated. Here we use the traditional syntax in listing 2.3. Later, we will see alternative syntax to implement the same functionality.
Listing 2.3 Aspect to secure access
package ajia;
import ajia.auth.Authenticator;
public aspect SecurityAspect { #1
private Authenticator authenticator = new Authenticator();
pointcut secureAccess() #2
: execution(* MessageCommunicator.deliver(..)); #2
before() : secureAccess() { #3
System.out.println("Checking and authenticating user"); #3
authenticator.authenticate(); #3
} #3
}
#1 Aspect declaration
#2 Pointcut declaration
#3 Advice
I donÕt show the Authenticator for brevity's sake (however you can download it from the bookÕs web site). Also, ideally dependencies such as authenticator should be injected using a framework such as Spring. However, I take a simpler approach to allow focusing on AOP.
LetÕs compile our classes along with the aspect. Now when we run the program, we see the following output:
> ajc ajia\MessageCommunicator.java ajia\Main.java ajia\SecurityAspect.aj
[CA] ajia\auth\*.java
> java ajia.Main
Checking and authenticating user
Username: ajia
Password: ajia
Wanna learn AspectJ?
Checking and authenticating user
Harry, having fun?
LetÕs understand the magic this new aspect and ajc perform. The SecurityAspect.aj file declares the SecurityAspect aspect. Note that we could have declared the aspect in SecurityAspect.java file, since AspectJ accepts both .aj and .java extension for input source files. While the file extension doesnÕt matter to the compiler, it is typical for aspects to use the .aj extension and Java code to use the .java extension:
#1 An aspect is a unit of modularization in AOP, much like a class is a unit of modularization in OOP. The declaration of an aspect is similar to a class declaration.
xe "pointcut\: example"#2 A pointcut selects interesting points of execution in a system, called join points. The aspect defines a pointcut secureAccess() that selects execution of all the methods named deliver() in the MessageCommunicator class. The * indicates that the selection criterion matches any return type, and the .. inside parentheses after deliver specifies that the selection criterion matches regardless of the number of arguments or their types. In our example, the pointcut would select execution of both of the overloaded versions of deliver() in the MessageCommunicator class. We will learn about join points and pointcuts in detail in the next chapter.
xe "before advice\: example"#3 An advice defines the code to execute upon reaching join points selected by the associated pointcut. Here, we define a piece of advice to execute before reaching the join points selected by the secureAccess() pointcut. The before() part indicates that the advice should run prior to the execution of the advised join pointÑin our case, prior to executing any MessageCommunicator.deliver() method. In the advice, we authenticate the current user.
With the aspect now present in the system, each time that MessageCommunicator.deliver() is executed, the advice code perform the authentication logic before the method.
Now that we have gotten the flavor of the AspectJ language, it is time to take an overview of the language and its core building blocks.
2.2 AspectJ from 20,000 feet
In chapter 1, we introduced the AOP concept of weaving the crosscutting concerns into the core logic using weaving rules. xe "weaving rules\: role in crosscutting"Weaving rules specify ÒwhatÓ action to perform ÒwhenÓ encountering certain points in the execution of the program. LetÕs examine the two kinds of syntax and crosscutting constructs offered by AspectJ.
2.2.1 AspectJ syntax choices
AspectJ uses extensions to the Java programming language to specify the weaving rules for the dynamic and static crosscutting. These extensions come in two flavors:
Traditional syntax: This original AspectJ syntax extends that Java language specification using new keywords.
@AspectJ syntax: This relatively new syntax uses Java annotations to express AOP constructs with otherwise plain Java.
With either style, a Java programmer should feel at home while using the extensions (we will compare these syntax styles in chapter 7). The AspectJ offers several constructs to specify the weaving rules. We introduce them in this section and discuss them in depth in the next four chapters. Note that code examples in this section use the traditional AspectJ syntax. Later in this chapter, we will use the @AspectJ syntax.
2.2.2 Understanding Crosscutting Elements
Recall the generic AOP model discussed in chapter 1. AspectJ is the most complete implementation of that model supporting all its elements. In this section, we examine how AspectJ maps each model element into program constructs. The most important and central concept in AOP and AspectJ is join point, so it is a good place to get started.
Aspect
xe "aspect\: definiton of"The aspect is the central unit of AspectJ, in the same way that a class is the central unit in Java. It contains the code that expresses the weaving rules for both dynamic and static crosscutting by including pointcuts, advice, ITDs, and compile-time declarations. Additionally, aspects can contain data, methods, and nested class members, just like a normal Java class. LetÕs define an aspect that performs profiling that we will update as we learn about more elements.
package ajia.profile;
public aspect ProfilingAspect {
}
In the next step, we identify points of interest of a crosscutting functionality.
Join point
xe "join point\: definition of"In AOP, and therefore in AspectJ, join points are the places where the crosscutting actions take place. In listing 2.1, we have join points corresponding to the execution of the deliver() methods as well as call to the println() method on System.out object. In listing 2.2, for example, we have join points corresponding to the creation of the MessageCommunicator and calls to the deliver() methods.
Once you identify join points useful for a crosscutting functionality, you need to a way to select them using the construct of pointcut.
Pointcut
xe "pointcut\: definition of"A pointcut is a program construct that selects join points and collects context at those points. For example, a pointcut can select a join point that is an execution of a method, and it could also collect the join point context, such as the ÔthisÕ object and the arguments to the method.
LetÕs write a pointcut that selects the execution of any public method in the system:
execution(public * *.*(..))
The wildcards * and .. indicate that the pointcut selects regardless of the return type, declaring type, method name, or method parameters. Here the only condition we specify is that access specification for the method must be public.
It is a good idea to name a pointcut so that it other programming elements may use it. For example, we can name the earlier pointcut as publicOperation as follows:
pointcut publicOperation() : execution(public * *.*(..));
LetÕs put this pointcut in ProfilingAspect:
package ajia.profile;
public aspect ProfilingAspect {
pointcut publicOperation() : execution(public * *.*(..));
}
All this hard work of learning about join points and pointcuts will pay off when we start using them to perform crosscutting actions using the construct of advice.
Advice
xe "advice\: definition of"Advice is the code to be executed at a join point that has been selected by a pointcut. Advice can execute before, after, or around the join point. Around advice can modify the execution of the code that is at the join point, it can replace it, or it can even bypass it. The body of advice is much like a method bodyÑit encapsulates the logic to be executed upon reaching a join point.
Using the earlier pointcut in the previous section, we can advise all public methods of MessageCommunicator to profile it. LetÕs update ProfilingAspect (shown in listing 2.4) with profiling advice:
Listing 2.4: Profiling all public methods
package ajia.profile;
public aspect ProfilingAspect {
pointcut publicOperation() : execution(public * *.*(..));
Object around() : publicOperation() {
long start = System.nanoTime();
Object ret = proceed();
long end = System.nanoTime();
System.out.println(thisJoinPointStaticPart.getSignature()
+ " took " + (end-start) + " nanoseconds");
return ret;
}
}
The advice records the start time, calls proceed() to continue executing the advised method, records the end time, and prints the time taken by the method execution. The thisJoinPointStaticPart variable is one of the three variables available in each advice that carry information about the currently advised join point such as the method name, the this object, and method arguments.
When we compile this aspect along other code and execute it, we get the following output:
> ajc -source 5 ajia\MessageCommunicator.java ajia\Main.java
[CA] ajia\SecurityAspect.aj ajia\profile\ProfilingAspect.aj ajia\auth\*.java
> java ajia.Main
Checking and authenticating user
boolean ajia.auth.Authenticator.isAuthenticated() took 840051 nanoseconds
Username: ajia
Password: ajia
String[] ajia.auth.Authenticator.getUserNamePassword() took 5248473759 nanoseconds
void ajia.auth.Authenticator.authenticate() took 5250886077 nanoseconds
Wanna learn AspectJ?
void ajia.MessageCommunicator.deliver(String) took 5252761734 nanoseconds
Checking and authenticating user
boolean ajia.auth.Authenticator.isAuthenticated() took 5028 nanoseconds
void ajia.auth.Authenticator.authenticate() took 61740 nanoseconds
Harry, having fun?
void ajia.MessageCommunicator.deliver(String, String) took 307581 nanoseconds
void ajia.Main.main(String[]) took 5253861315 nanoseconds
Pseudo keywords in AspectJ
The proceed and other keywords such as aspect, pointcut, and before are really pseudo keywords that gain special meaning only in the right context. For example, it is perfectly legitimate to use a method named ÔproceedÕ in Java classes. However, when proceed is used in an around advice, it acquires a special meaning. This use of pseudo keywords enables AspectJ to work with any valid Java program that may already include AspectJ keywords.
xe "dynamic crosscuting\: use of pointcut"Pointcuts xe "pointcut\: use with advice"and xe "advice\: use with pointcut"advice together form the dynamic crosscutting rules. While the pointcuts identify the required join points, the advice completes the picture by providing the actions that will occur at the join points.
Static crosscutting that comes in the form of inter-type and compile-time declarations complement dynamic crosscutting. LetÕs see how.
Inter-type declaration
xe "introduction\: defintion of"The inter-type declaration (ITD) (also referred to as ÒintroductionÓ) is a static crosscutting construct that alters the static structure of the classes, interfaces, and aspects of the system. For example, you can add a method or field to a class or declare a type to implement an interface. In an ITD, one type (an aspect) declares structure for the other types (classes, interfaces, and even aspects), hence the name.
The following introduction declares the MessageCommunicator class to implement the AccessTracked interface:
declare parents: MessageCommunicator implements AccessTracked;
Once this declaration is woven in, you may use an MessageCommunicator instance where an instance of AccessTracked is expected.
Inter-type declarations can be used for member introductionÑa way to add new fields and methods to other types. The following declaration adds the lastAccessedTime field and the updateLastAccessedTime() and getLastAccessedTime() method to the AccessTracked type:
private long AccessTracked.lastAccessedTime;
public void AccessTracked.updateLastAccessedTime() {
lastAccessedTime = System.nanoTime();
}
public long AccessTracked.getLastAccessedTime() {
return lastAccessedTime;
}
You may then advise methods in a type that implements the AccessTracked (directly or through a declare parents statement) to update the last accessed time as shown in the following snippet:
before(AccessTracked accessTracked)
: execution(* AccessTracked+.*(..))
&& !execution(* AccessTracked.*(..))
&& this(accessTracked){
accessTracked.updateLastAccessedTime();
}
Here we advise all methods of types that implement the AccessTracked interface (notice, the Ô+Õ wildcard that denotes subtypes), but not the method in the AccessTracked itself (such as the introduced updateLastAccessedTime() method). The this() pointcut collects the tracked object so that we can call updateLastAccessedTime() method on it.
LetÕs put all these snippets in an aspect in listing 2.5 to see their effect.
Listing 2.5 Tracking last updated time using an aspect
package ajia.track;
import ajia.MessageCommunicator;
public aspect TrackingAspect {
declare parents: MessageCommunicator implements AccessTracked;
private long AccessTracked.lastAccessedTime;
public void AccessTracked.updateLastAccessedTime() {
lastAccessedTime = System.nanoTime();
}
public long AccessTracked.getLastAccessedTime() {
return lastAccessedTime;
}
before(AccessTracked accessTracked)
: execution(* AccessTracked+.*(..))
&& !execution(* AccessTracked.*(..))
&& this(accessTracked) {
accessTracked.updateLastAccessedTime();
}
private static interface AccessTracked {
}
}
To see the effect, we modify the Main class to print the last accessed time for the messageCommunicator object as follows:
Listing 2.6 Modified Main class to print the last accessed time
package ajia;
public class Main {
public static void main(String[] args) {
...
System.out.println("Last accessed time for messageCommunicator "
+ messageCommunicator.getLastAccessedTime());
}
}
When we compile and execute this aspect, we get the following output:
> ajc -source 5 ajia\MessageCommunicator.java ajia\Main.java
[CA] ajia\SecurityAspect.aj ajia\profile\ProfilingAspect.aj
[CA] ajia\track\TrackingAspect.aj ajia\auth\*.java
...
Last accessed time for messageCommunicator 443484544331192
...
ITDs also offer a way to annotates program elements and deal with checked exceptions in a systematic manner. However, we will defer its discussion until chapter 5 (Static Crosscutting). An important form of static crosscutting allows detecting and flagging the presence of join points matching a pointcut during compilation. LetÕs see how.
Compile-time declaration
xe "compile-time declaration\: definition of"The compile-time declaration is another static crosscutting construct that allows you to add compile-time warnings and errors upon detecting certain usage patterns. Consider SecurityAspect from listing 2.3. With this aspect in place, you might want to warn direct calls to the Authenticator.authenticate() method. The following declaration will cause the compiler to issue a warning if any part of the system calls the prohibited method except, of course, the SecurityAspect itself.
declare warning
: call(void Authenticator.authenticate()) && !within(SecurityAspect)
: "Authentication should be performed only by SecurityAspect";
Note the use of the call() pointcut to select a method call (as opposed to the method execution, which always is in the Authenticator class) and !within() to restrict to only those occurring outside SecurityAspect. The weaver will then report warnings on detecting the specified condition in the same way as other compile-time warnings such as use of a deprecated class. LetÕs see this in action by modifying SecurityAspect to add this declaration.
package ajia;
import ajia.auth.Authenticator;
public aspect SecurityAspect {
...
declare warning
: call(void Authenticator.authenticate()) && !within(SecurityAspect)
: "Authentication should be performed only by SecurityAspect";
}
We also modify the Main class to add new Authenticator().authenticate() to test a violation. When we compile the code, we get the following output:
> ajc -source 5 ajia\MessageCommunicator.java ajia\Main.java
[CA] ajia\SecurityAspect.aj ajia\auth\*.java
...\ajia\Main.java:13 [warning]
[CA] Authentication should be performed only by SecurityAspect
new Authenticator().authenticate();
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
method-call(void ajia.auth.Authenticator.authenticate())
see also: ...\ajia\SecurityAspect.aj:15::0
As you can see, compiler detected and flagged the violation.
By now, you must be wondering how AspectJ performs its magic. In the next section, weÕll take a quick look at how the source files are compiled into the byte code.
2.3 xe "AspectJ\: internals"AspectJ: under the hood
Since the byte code produced by the AspectJ weaver must run on any compliant Java VM, it must adhere to the Java byte-code specification. This means the weaver must map crosscutting elements to Java constructs. In this section, we outline how the different elements in an AspectJ program map to pure Java byte code. Note that the discussion that follows presents a simplified view of AspectJ code transformation into pure Java byte code.
Here are the typical ways that the AspectJ weaver maps various crosscutting elements to pure Java:
xe "AspectJ\: mapping of elements"Aspects map to classes, with each data member and method becoming the members of the class representing the aspect.
Pointcuts are intermediate elements that map to methods. Furthermore, they may have associated auxiliary methods to help perform matching at runtime.
Advice usually maps to one or more methods. The weaver inserts calls to these methods at potential locations matching the associated pointcut.
Inter-type declarations of fields and methods are added directly to the target classes.
Compile-time warnings and errors have no effect on byte code. They simply cause the compiler to print warnings or abort the compilation when producing an errorxe "AspectJ\: mapping of elements".
Furthermore, each mapped element carries annotations that help the weaver use the crosscutting information even for aspects presented to it in byte code form only. The annotations also help bring in symmetry with the @AspectJ syntax that we will see in section 2.4.
Caution
xe "AspectJ\: internal details verses language semantics"Thinking about the language semantics in terms of the transformed code helps in taking the mystery out of AspectJ. It also makes you appreciate the hard work that the AspectJ compiler is performingÑand the hard work that you no longer need to perform! However, such thinking has inherent within it the danger of bogging down too much in the details of the transformed code. A better approach is to start thinking in terms of language semantics instead of implementation.
In the light of this information, letÕs see how aspects and classes would look after passing through ÔajcÕ. Note that the AspectJ compiler produces byte code and not the Java code as shown here. WeÕre showing you this code only to give you an idea of the source code that would be roughly equivalent to the byte code produced. Also, some of the details (especially related to advanced features such as aspect association) that are beyond the scope of this discussion have been omitted.
2.3.1 The aspect
First, letÕs examine the code in a class that would be equivalent to SecurityAspect from listing 2.3:
package ajia;
import ajia.auth.Authenticator;
@Aspect
xe "aspect\: mapping to Java class"public class SecurityAspect {
private Authenticator authenticator = new Authenticator();
public static final SecurityAspect ajc$perSingletonInstance;
@Pointcut("execution(* MessageCommunicator.deliver(..))")
void ajc$pointcut$$secureAccess$76() {}
@Before("secureAccess()")
public final void ajc$before$ajia_SecurityAspect$1$e248afa3() {
System.out.println("Checking and authenticating user");
authenticator.authenticate();
}
static {
SecurityAspect.ajc$perSingletonInstance = new SecurityAspect();
}
... method aspectOf() and hasAspect() ...
... aspect initialization code ...
}
///Amin: Should this be using Annotations or should it be the traditional approachxe "aspect\: mapping to Java class"
SecurityAspect is mapped to a class of the same name. By default, aspects are singleton and users donÕt instantiate them explicitly. The static block of the aspect ensures that the singleton aspect instance is created as soon as the SecurityAspect class is loaded into the systemÑtypically during the execution of some code that refers to the aspect. The pointcut is mapped to the ajc$pointcut$$secureAccess$76() element. The before advice is mapped to the ajc$before$ajia_SecurityAspect$1$e248afa3() method whose body is identical to the advice body. AspectJ weaver weaves in these methods into the advised code as we see next.
2.3.2 The woven class
Now letÕs see the equivalent code for the MessageCommunicator class (from listing 2.1), after it has been woven with SecurityAspect:
package ajia;
xe "weaving\: example output"public class MessageCommunicator {
public void deliver(String message) {
SecurityAspect.aspectInstance.
ajc$before$ajia_SecurityAspect$1$e248afa3();
System.out.println(message);
}
public void deliver(String person, String message) {
SecurityAspect.aspectInstance.
ajc$before$ajia_SecurityAspect$1$e248afa3();
System.out.print(person + ", " + message);
}
}xe "weaving\: example output"
Recall that the deliverMessage() pointcut in SecurityAspect is defined to select both of the overloaded deliver() methods in MessageCommunicator. Accordingly, we see that the ajc$before$ajia_SecurityAspect$1$e248afa3() call on the aspect instance SecurityAspect.aspectInstance is made from both methods.
Performance implication of AspectJ weaving
ÒHow does it affect performance of woven codeÓ is perhaps the most commonly asked questions about AspectJ. The inquiring mind wants to know, ÒHow does a hand-woven implementation of crosscutting functionality compare with that implemented with AspectJÓ. The code produced by the weaver answers this question quite well. Since the weaver simply encapsulates the advice in a method and calls it from appropriate places, there is no virtually no overhead from the AspectJ weaver. Furthermore, since advice is well isolated in one place, one can easily add optimizations to that code, something that one would cringe at implementing in hundreds of places. If you are looking for more details on weaving and its performance implications, please read ÒAdvice Weaving in AspectJÓ by Erik Hilsdale and Jim Hugunin (http://hugunin.net/papers/aosd-2004-cameraReady.pdf).
Until now, we restricted ourselves only to the traditional AspectJ syntax (which has been available in every version of AspectJ) and compiler as the weaver. This syntax requires the use of the special ÔajcÕ compiler early in the development process to compile aspects and therefore poses a potential barrier in adopting AspectJ. To simplify the adoption, starting with AspectJ 5, there are alternative syntax and weaving models available, due to the merger of AspectJ with AspectWerkz, another implementation of AOP for Java. LetÕs look at a brief overview of these new features; later in chapter 7 (The @AspectJ syntax), we will study these features in detail.
2.4 AspectJ alternative syntax
AspectJ offers an alternative syntaxÑthe @AspectJ syntaxÑthat extends the language using the new annotation facility in Java 5. The main advantage of this syntax style is that the code can be compiled using a plain Java compiler (for example, ÔjavacÕ) and therefore the code works better with conventional Java IDEs and tools that donÕt understand the AspectJ syntax. Furthermore, the proxy-based AOP framework in Spring uses this syntax, further simplifying adoption of AspectJ if the project is already using Spring. The disadvantage of @AspectJ syntax is it verbosity in expressing the same constructs and limitations in expressing certain constructs, especially in the static crosscutting category.
LetÕs create an @AspectJ version of the aspect in listing 2.3 as shown in listing 2.7. The listing should remind you of the annotations in the woven code from the previous section.
Listing 2.7 Introducing security using the @AspectJ syntax
package ajia;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import ajia.auth.Authenticator;
@Aspect #1
public class SecurityAspect { #1
private Authenticator authenticator = new Authenticator();
@Pointcut("execution(* MessageCommunicator.deliver(..))") #2
public void secureAccess() {} #2
@Before("secureAccess()") #3
public void secure() { #3
System.out.println("Checking and authenticating user"); #3
authenticator.authenticate(); #3
} #3
}
#1 Aspect declaration
#2 Pointcut declaration
#3 Advice
LetÕs understand the difference between listing 2.3 and 2.7:
#1 Instead of using the aspect keyword, we use just a class annotated with an @Aspect annotation. The ÔajcÕ compiler, which understands the semantics associated with the @Aspect annotation, will use this information to treat the class as if it was an aspect.
#2 Similarly, the @Pointcut annotation marks an empty method as a pointcut. The @Pointcut annotation requires specifying the pointcut expression. We use exactly the same expression as in the version using the traditional syntax. The name of the method serves as the pointcut name.
#3 The @Before annotation marks a regular method as a before advice. The body of the method consists of the advice logicÑit is the code that will get executed when a matching join point is executed.
First, letÕs compile the same code using ÔjavacÕ and execute the resulting code:
> javac ajia\MessageCommunicator.java ajia\SecurityAspect.java ajia\Main.java
[CA] ajia\auth\*.java
> java ajia.Main
Wanna learn AspectJ?
Harry, having fun?
While code compiled just fine, aspect had no effect on the output. This may not exactly surprise you; after all ÔjavacÕ has no idea of the meaning of annotations such as @Aspect and @Pointcut. In general, ÔjavacÕ has no idea of all but a few standard annotations (such as @Override and @SuppressWarnings). You need an aspect weaver somewhere between compiling source code and executing byte code in the VM. The simplest way to use the @AspectJ syntax is to use the ÔajcÕ compiler instead of ÔjavacÕ. Other alternatives include binary weaving, where code compiled using ÔjavacÕ is then woven using ÔajcÕ, and load-time weaving (LTW), where classes are woven as they are being loaded into the VM.
Now letÕs compile this source file along with the MessageCommunicator and Main classes (in listing 2.1 and 2.2):
> ajc -source 5 ajia\MessageCommunicator.java ajia\SecurityAspect.java ajia\Main.java ajia\auth\*.java
> java ajia.Main
Checking and authenticating user
Username: ajia
Password: ajia
Wanna learn AspectJ?
Checking and authenticating user
Harry, having fun?
It is exactly same output as produced by the aspect in listing 2.3. (Note the Ò-source 5Ó option to allow using Java 5 constructs such as annotations.)
The value proposition of @AspectJ syntax is a simplified adoption curve. First, the aspectÕs syntax is expressed using Java constructs, which reduces the mental block commonly associated with using yet-another-language. You are still using just plain Java! Second, tools such as compilers, IDEs, and code coverage tools work more easily with @AspectJ syntax, since they are working with plain Java code. For example, you can develop using an AspectJ-unaware IDE.
While @AspectJ allows the possibility of using ÔjavacÕ for compiling aspects, you will need to introduce a weaver somewhere between your code and the VM. LetÕs see what choices AspectJ offers for weaving.
2.5 Weaving mechanisms
A weaver needs to weave together classes and aspects so that the crosscutting elements have the desired effect: advice gets executed, inter-type declarations affect the static structure and weave-time declarations produce warnings and errors. AspectJ offers three weaving models to accommodate requirements of various build and deployment scenarios:
Source weaving
Binary weaving
Load-time weaving
Regardless of the weaving model used, the resulting execution of the system is identical. The weaving mechanism is also orthogonal to the AspectJ syntax used; any combination of weaving mechanism and AspectJ syntax will produce identical results. In this section, we will take an overview of the weaving models offered by AspectJ. We will revisit this topic again in chapter 8 (AspectJ Weaving Models), when we will discuss it in details.
2.5.1 Source weaving
In source weaving, the weaver is part of the compiler (all the examples in this chapter so far used source code weaving). The input to the weaver is classes and aspects in source code form. The aspects could be written in either the traditional syntax or the @AspectJ syntax. The weaver, which works in a manner similar to a compiler, processes the source and produces woven byte code. The byte code produced by the compiler is compliant with the Java byte code specification, which any standard compliant VM can execute. Essentially, when used in this manner ajc replaces ÔjavacÕ. However, note that unlike ÔjavacÕ, ÔajcÕ requires that all sources be presented together if the woven byte code is desired. If sources are presented separately, the resulting byte code can be used as input for binary weaving or load-time weaving discussed next.
2.5.2 Binary weaving
In binary weaving too, the weaver is part of the compiler. The input to the weaver, classes and aspects, are in byte code form. The input byte code is compiled separately using the Java compiler or the AspectJ compiler. For example, you can use jar files or class files produced using the Java compiler.
Binary weaver and linker
If you are familiar with languages such as ÔCÕ and ÔC++Õ, think of the weaver as a linker, which is a more accurate comparison. Much the same way a linker takes libraries compiled using a compiler as input to produce an executable or another library, the weaver takes files containing byte-code as input produces woven byte-code.
LetÕs see binary weaving in action in a step-by-step manner. Our goal is to first compile classes and aspect without weaving and then weave the resulting binary (.class) files.
Step 1: Compile the Java sources
Here we compile code in listing 2.1 and 2.2 using ÔjavacÕ. While we could have used ÔajcÕ to have the same effect, to illustrate the effect of binary weaving clearly, we will stay away from ÔajcÕ. We will use the Ðd option to specify the destination directory for the classes to help understand the effect better:
> javac Ðd classes ajia\MessageCommunicator.java ajia\Main.java
[CA] ajia\auth\*.java
Unsurprisingly, executing the Main class shows that there is no effects of the aspect.
xe "AspectJ\: running programs"> java Ðclasspath classes ajia.Main
Wanna learn AspectJ?
Harry, having fun?
Step 2: Compile the aspect
Next, we compile the @AspectJ styled aspect from listing 2.7 directing its output to a different directory.
> javac Ðd aspects ajia\SecurityAspect.java
If we wanted to use the traditional style aspect in listing 2.3, we would have to compile it using ÔajcÕ.
> ajc Ðd aspects ajia\SecurityAspect.aj
Regardless, we will have one class file per source file. Executing the Main class shows that the output still does not have any effects of the aspect:
xe "AspectJ\: running programs"> java Ðclasspath classes;aspects ajia.Main
Wanna learn AspectJ?
Harry, having fun?
Step 3: Weave the aspects
To weave aspect into classes in binary form, we use binary weaving:
> ajc Ðinpath classes;aspects Ðaspectpath aspects Ðd woven
The Ðinpath option specifies the path to the classes that are weaving targets. Since we used javac to compile aspects, we need to pass those as well to Ðinpath so that ajc can add needed support methods. The -aspectpath option specifies the path to the aspects to be woven in.
Executing the Main class shows that we have restored the security into the system. Since we are passing an explicit Ðclasspath option, we need to add the CLASSPATH we have set to make AspectJ runtime available to the VM.
> java Ðclasspath woven;%CLASSPATH% ajia.Main
Checking and authenticating user
Username: ajia
Password: ajia
Wanna learn AspectJ?
Checking and authenticating user
Harry, having fun?
Binary weaving also can take input as a combination of source and byte code form. For example, you may have classes compiled into a jar file and aspect available as source. This would allow you to weave into third-party jars without requiring access to its source files. Similarly, you can also weave aspects in jar or class file into classes and interface available in source code form. This would allow you to weave an aspect library in binary format with your sources. Chapter 8 (AspectJ Weaving Models) discusses all these possibilities. An extension of binary weaver is load-time weaving, which we examine next.
2.5.3 Load-time weaving
Load-time weaver takes input in the form of binary classes and aspects, as well as aspects and configuration defined in XML format. A load-time agent (which can take many forms: a JVMTI agent, a classloader, a VM- and application server-specific class preprocessor) weaves as the classes are loaded into the VM.
LetÕs use LTW to weave in the SecurityAspect. We start with the output of the first two steps in the previous section. Load-time weaving needs an XML file that specifies the weaving configuration. AspectJ supports a few locations for such a file. We will use one of those by naming it aop.xml and placing it in directory named META-INF in a classpath component. Listing 2.8 shows the minimal XML file that serves our purpose:
Listing 2.8 aop.xml specifying the configuration to load-time weaver
This configuration tells the weaver is that the SecurityAspect need to be woven in. As we will see later in chapter 8, aop.xml can include a lot more configuration information, including pointcuts definitions.
We enable load-time weaving by including the Ðjavaagent option when starting the VM:
> java Ðclasspath classes;aspects;. [CA] -javaagent:%ASPECTJ_HOME%\lib\aspectjweaver.jar ajia.Main
Checking and authenticating user
Username: ajia
Password: ajia
Wanna learn AspectJ?
Checking and authenticating user
Harry, having fun?
The Ðjavaagent option specifies the Java VM Tools Interface (JVMTI) agent to be used is aspectjweaver.jar that comes as a part of AspectJ distribution. The weaver uses the information in aop.xml file in listing 2.8 and uses the aspects specified in the section to weave into classes as they are being loaded into the VM.
Spring-native LTW
Spring 2.5 introduces ÒSpring-nativeÓ LTW, an alternative way to configure AspectJ LTW for Spring applications. With it, for certain application- and web-servers you can avoid modifying the launch script (required to specify the Ðjavaagent option); instead you modify the application context to express the desire to use the LTW and Spring handles the rest. We will examine this weaving option in chapter 9.
So far, we studied weaver options offered by AspectJ itself. The Spring Framework, starting with 2.0 version, offers yet another possibility for weaving AspectJ aspects. In the next section, we take a quick look at this possibility.
2.6 AspectJ-Spring integration
Spring, the most widely used lightweight framework for developing enterprise applications, has been offering its own form of AOP to modularize crosscutting concerns typically seen in enterprise applications. Spring AOP uses the proxy design pattern to intercept execution of methods on the target object. Due to the use of proxies, it exposes method execution join points only for objects created by the Spring container (commonly known as Spring beans). However, it is a pragmatic solution in the context of lightweight enterprise application development.
Starting with Spring 2.0, it offers several options to leverage AspectJÕs power in an incremental manner. For example, it allows using AspectJ pointcut expressions in addition to its own pointcut expressions. In this section, we preview the Spring-AspectJ integration for aspects written in @AspectJ syntax. We will examine all integration possibilities in details in chapter 6 when we study alternative syntax and weaving models.
Updating the environment variable
For this section, you need to add jars from Spring distribution to your CLASSPATH. Please see downloaded sources for more details.
LetÕs take a look at a simple example. We would like to introduce simple security into the MessageCommunicator object. We will continue to use the MessageCommunicator class from listing 2.1 and the annotation-style aspect from listing 2.7. First we will write a minimum application context file to define the needed configuration.
Listing 2.9 Defining the application context (applicationContext.xml)
#1
#2
#3
#1 Declaring automatic proxy creation
#2 Aspect bean
#3 Regular bean
#1 The element tells Spring to automatically create proxies for the beans that can be advised by aspects.
#2 The element simply creates a bean for the MessageCommunicator class.
#3 The element creates a bean corresponding to the aspect declared using the @AspectJ syntax.
Next, we modify the Main class from listing 2.2 to create the application context and obtain the messageCommunicator bean from it. Then we use the bean to deliver a few messages.
Listing 2.10 Using Spring container application context
package ajia;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context
= new ClassPathXmlApplicationContext("applicationContext.xml");
MessageCommunicator messageCommunicator
= (MessageCommunicator)context.getBean("messageCommunicator");
messageCommunicator.deliver("Wanna learn AspectJ?");
messageCommunicator.deliver("Harry", "having fun?");
}
}
Now, letÕs compile all these classes and execute the Main class.
> javac ajia\*.java ajia\auth\*.java
> java ajia.Main
Checking and authenticating user
Username: ajia
Password: ajia
Wanna learn AspectJ?
Checking and authenticating user
Harry, having fun?
We see the same output as using an AspectJ weaver. Behind the scenes, the Spring container creates a proxy around the messageCommunicator bean and intercepts methods called on it according to the SecurityAspect defined using the @AspectJ syntax, producing the desired effect. As a result, we can leverage the aspects written using @AspectJ in a Spring application without need for the AspectJ weaver. We will examine details in chapter 9 (Spring AspectJ Integration).
So far in this chapter, we used only command line tools to work with aspects. However, in real-life, virtually no one works using command-line tools anymore. For a programmer to be productive, you need a good IDE. Further, we need support for documentation so that the crosscutting information is available even outside the IDE. In the next section, we will look at the logistical support provided by AspectJ.
2.7 AspectJ logistics overview
AspectJ offers a complete set of tools ranging from a compiler to integrated development environment (IDE) support. We have already examined the compiler and load-time weaver. LetÕs look at a few important tools in more detail.
2.7.1 xe "IDE integration"IDE integration
IDE support offers an integrated approach to editing, compiling, executing, and debugging tasks. AspectJ eases the development process by providing integration with xe "Eclipse\: AspectJ integration"Eclipse. The integration with the IDE is achieved through AspectJ Development Tools (AJDT) plug-in. xe "IDE integration\: functionality"Using this integration, you can edit, compile, and debug your project in the same way you would work with a project written only in Java. Figure 2.1 shows how the example in this chapter looks in the Eclipse IDE. Note that Spring IDE, an Eclipse plugin for developing Spring-based applications, also offers similar functionality for Spring-AspectJ integration discussed in section 2.6.
Eating its own dog food
AJDT uses AspectJ to weave into Java Development Tools (JDT) to offer AspectJ integration.
xe "IDE integration\: crosscutting view"In figure 2.1, xe "crosscutting\: viewing in an IDE"we see that Eclipse shows the standard views, along with the Cross References view that shows how an advice applies to different parts of the code. This is a useful debugging tool when you have applied advice to a method but you do not see any effect.
Figure 2.1 Developing applications using Eclipse-AspectJ integration. The overall feel for editing, building, and debugging is like a plain Java project. The IDE also shows how crosscutting elements affect the various parts of the system.
The AJDT plugin also offers a way to monitor changes to crosscutting structure when the system evolves using Crosscutting Comparison View as well as visualizing the big picture effects of crosscutting concerns using Visualiser. We will study the IDE support for AspectJ in more detail in Appendix C.
What about other IDEs?
Earlier versions of AspectJ supported other IDEs besides Eclipse: NetBeans, JBuilder, Emacs JDEE by offering open source plugins for each of them. However, those plugins havenÕt been kept up-to-date (There is current effort to revive the NetBeans plugin. See HYPERLINK "http://www.jroller.com/ramlog/entry/using_the_aspectj_plug_in1" http://www.jroller.com/ramlog/entry/using_the_aspectj_plug_in1 for more details). This is perhaps a reflection of the market realityÑno other IDE is as popular as Eclipse.
One IDE that has a good market and mind share is IntelliJ IDEA. For it and any IDE without direct support for AspectJ, the possibility of using the @AspectJ syntax makes the lack of direct AspectJ support a less pronounced issue. Since the code is still plain Java, as far as the IDE is concerned, you can edit code and leverage code completion etc. If the IDE allows you to replace the default compiler, you can replace it with ÔajcÕ. If not, you can introduce a post compilation step to run ÔajcÕ to perform binary weaving. Debugging works fine too, since the method representing advice is still executed as if there was a real call.
When using @AspectJ syntax in IDE that doesnÕt support AspectJ directly, you lose source code marker indicating advice applicability, the crosscutting reference view, and crosscutting comparison view. This problem may be alleviated to an extent by use of ÔajbrowserÕ-- a standalone tool that shows how weaving rules affect different parts of program.
While Eclipse IDE integration shows crosscutting information in a crosscutting reference view, you often need the same information in a static document what a load of bollocks!.
2.7.2 AspectJ documentation tool
The AspectJ documentation toolÑÔajdocÕÑextends ÔjavadocÕ to provide crosscutting information in static form. You invoke ÔajdocÕ in a similar way as ÔjavadocÕ:
> ajdoc ajia\*.java ajia\*.aj auth\*.java profile\*.aj track\*.aj
It produces html files similar to the ones produced by ÔjavadocÕ, except the elements carry additional information showing how aspects and classes interact as shown in figure 2.2.
Figure 2.2 Output produced by ajdoc, which works in a similar manner as Javadoc, except in addition to regular documentation, it add markers to show crosscutting information.
The output produced by ÔajdocÕ offers a simple way to examine crosscutting structure without needing an IDE. Since ÔajdocÕ isnÕt tied to a specific IDE, you may use it alongside of the IDE of your choice even if it doesnÕt support AspectJ directly.
2.8 Summary
AspectJ adds AOP constructsÑpointcut, advice, aspect, introductionsÑto Java, creating a powerful language that facilitates modularize crosscutting concerns, while retaining the benefits of Java, such as platform independency. Simply by learning the concepts, a Java programmer can benefit from the AOP paradigm right away. Aspects allow the separation of crosscutting actions from the core modules. You can then add new functionality without changing any code in the core modules, and without them even being aware of it.
Aspect-oriented programming in AspectJ is simple: choose where you want to crosscut, choose the kind of action you need to perform the task, and programmatically specify both of them. The AspectJ language exposes the necessary join points in a Java program. Pointcuts let you choose the join points you want to affect, and advice allows you to specify the action at those join points. The static crosscutting mechanism enables you to modify the static structure of the system in a way that affects multiple modules. AspectJ complementsÑand doesnÕt compete withÑJava. By utilizing its power to modularize the crosscutting concerns, Java programmers no longer need to recode multiple modules when implementing or changing a crosscutting concern.
The new additions to AspectJÑthe various syntax options, weaving models, and integration with Spring make adopting AspectJ much easier. It is typical for Spring developers to start with the Spring-AspectJ integration, using proxy-based weaving, and to learn the power of AOP and AspectJ through experience. Then, they often move to advanced AOP techniques using AspectJ weaving, often along with the @AspectJ syntax. At this point, load-time weaving is often a common choice due to its simplicity in getting started. Later, when they are looking for even more advanced usages, they may go for the traditional syntax along with a combination of source, binary, and load-time weaving.
In this chapter, we studied the core AspectJ concepts from 20,000 feet. It is now time to get a closer view. The next chapter introduces the join point model that is at the heart of AOP. The three chapters that follow will show dynamic crosscutting, static crosscutting, and aspects. We then proceed to understand the @AspectJ syntax, weaving mechanisms, and the Spring integration. All this information will enable you to write aspects useful in complex Java applications, something that we explore in part 2 of the book.
PAGE 22 Chapter 2 Introducing AspectJ
PAGE 21
Thanks for participating in AspectJ in Action 2nd edition Manning Early Access Program.
Please send your feedback to HYPERLINK "mailto:ajia@ramnivas.com" ajia@ramnivas.com or at HYPERLINK "http://www.manning-sandbox.com/forum.jspa?forumID=413" http://www.manning-sandbox.com/forum.jspa?forumID=413. ©Manning Publications Co.
Thanks for participating in AspectJ in Action 2nd edition Manning Early Access Program.
Please send your feedback to HYPERLINK "mailto:ajia@ramnivas.com" ajia@ramnivas.com or at HYPERLINK "http://www.manning-sandbox.com/forum.jspa?forumID=413" http://www.manning-sandbox.com/forum.jspa?forumID=413. ©Manning Publications Co.
Thanks for participating in AspectJ in Action 2nd edition Manning Early Access Program.
Please send your feedback to HYPERLINK "mailto:ajia@ramnivas.com" ajia@ramnivas.com or at HYPERLINK "http://www.manning-sandbox.com/forum.jspa?forumID=413" http://www.manning-sandbox.com/forum.jspa?forumID=413. ©Manning Publications Co.