AspectJ claims that it is a 'seamless aspect-oriented extension to the Java programming language that enables clean modularization of these crosscutting concerns'. I.e. it is a framework that allows to define aspects at particular manner and inject corresponding instructions directly to the byte code. Historically there was a dedicated aspect description language and extended java compiler that was able to understand it. AspectJ guys also introduced ability to define aspects via java5 annotations later. Feel free to get more information about AspectJ facilities and syntax at the AspectJ documentation page.
The main difference between Spring AOP and AspectJ AOP is that Spring AOP is proxy-based, i.e. it assumes that the client uses AOP-aware proxies instead of the 'raw' objects. That causes the problem I mentioned before. AspectJ injects its instructions directly to the byte code, hence, it doesn't suffer from that.
Spring users can witch to AspectJ immediately in the case of Spring2 AOP usage - spring uses subset of AspectJ pointcut expression language, and @AspectJ spring aspects are fully eligible for AspectJ weaving.
Lets define a simple test-case that shows AspectJ weaving:
TestTarget.java
package com.aspectj;
public class TestTarget {
public static void main(String[] args) {
System.out.println("----------------------->--------- Start test -----------<---------------------");
new TestTarget().test();
System.out.println("----------------------->--------- End test -----------<---------------------");
}
public void test() {
System.out.println("TestTarget.test()");
}
}
TestAspect.java
package com.aspectj;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.JoinPoint;
@Aspect
public class TestAspect {
@Before("execution (* com.aspectj.TestTarget.test*(..))")
public void advice(JoinPoint joinPoint) {
System.out.printf("TestAspect.advice() called on '%s'%n", joinPoint);
}
}
What do we want is to see that aspect method is called when TestTarget.test() is invoked.
There are three ways to inject instructions implied by AspectJ aspects:
- compile-time weaving - compile either target source or aspect classes via dedicated aspectj compiler;
- post-compile weaving - inject aspect instructions to already compiled classes;
- load-time weaving - inject aspect instructions to the byte code during class loading, i.e. load instrumented class instead of the 'raw' one;
It's possible to use any of the approaches mentioned above via various ways. I'm big fan of the law of leaky abstractions, so, lets perform weaving at the lowest level at first.
Lets define our directories structure for the example:

Here *.jar files are AspectJ binaries:
- aspectjrt.jar - necessary in runtime for correct aspects processing;
- aspectjtools.jar - contains implementation of aspectj compiler;
- aspectjweaver.jar - bridge between aspectj logic and java instrumentation;
*.xml files are:
- aop.xml - aspectj loadtime descriptor;
- build.xml - ant script;
- pom.xml - maven descriptor;
Command-line weaving
Compile-time and post-compile-time weaving is performed via ajc tool that stands for aspectj compiler. It allows to weave aspects at compile-time. Feel free to read more about it at its documentation.
Compile-time weaving
compile-time-weaving.sh
#!/bin/bash
# Prepare
echo "Preparing the environment..."
rm -rf ./target 2>/dev/null
CLASSES_DIR=./target/classes/compile-time
COUNTER=1
CURRENT_DIR=
while :
do
DIR=`echo "$CLASSES_DIR" | cut -d'/' -f $COUNTER`
test "$DIR" = "" && break
CURRENT_DIR=${CURRENT_DIR}${DIR}/
mkdir $CURRENT_DIR 2>/dev/null
COUNTER=`expr $COUNTER + 1`
done
CLASSPATH=./src/main/java
for i in 'aspectjtools.jar' 'aspectjrt.jar'
do
CLASSPATH=$CLASSPATH:./src/main/resources/$i
done
# Compile the sources
echo "Compiling..."
java -cp $CLASSPATH org.aspectj.tools.ajc.Main -source 1.5 -d $CLASSES_DIR src/main/java/com/aspectj/TestTarget.java src/main/java/com/aspectj/TestAspect.java
# Run the example and check that aspect logic is applied
echo "Running the sample..."
java -cp $CLASSPATH:$CLASSES_DIR com.aspectj.TestTarget
It compiles target class and aspect class and runs target class. Following output is produced:
denis@harmony:/storage/projects/java/test$ ./compile-time-weaving.sh
Preparing the environment...
Compiling...
Running the sample...
----------------------->--------- Start test -----------<---------------------
TestAspect.advice() called on 'execution(void com.aspectj.TestTarget.test())'
TestTarget.test()
----------------------->--------- End test -----------<---------------------
We can see that aspect logic is introduced to the target class.
Note: it is assumed that java remains at the path.
Post-compile weaving
The general idea here is to inject aspect logic to the existing binaries. It's very useful when you work with third-party libraries. AspectJ keeps original byte code untouched and produces the new one with aspect logic inside it.
post-compile-weaving.sh script shows that approach:
#!/bin/bash
function ensure-dir-exists {
COUNTER=1
CURRENT_DIR=
while :
do
DIR=`echo "$1" | cut -d'/' -f $COUNTER`
test "$DIR" = "" && break
CURRENT_DIR=${CURRENT_DIR}${DIR}/
mkdir $CURRENT_DIR 2>/dev/null
COUNTER=`expr $COUNTER + 1`
done
}
# Prepare
echo "Preparing the environment..."
rm -rf ./target 2>/dev/null
JAR_DIR=./target/classes/post-compile-time
ensure-dir-exists $JAR_DIR
CLASSES_DIR=./target/classes/pure
ensure-dir-exists $CLASSES_DIR
CLASSPATH=./src/main/java
for i in 'aspectjtools.jar' 'aspectjrt.jar'
do
CLASSPATH=$CLASSPATH:./src/main/resources/$i
done
# Compile the sources
echo "Compiling..."
javac -classpath $CLASSPATH -g -d $CLASSES_DIR src/main/java/com/aspectj/TestTarget.java src/main/java/com/aspectj/TestAspect.java
echo "Weaving aspect..."
java -cp $CLASSPATH org.aspectj.tools.ajc.Main -source 1.5 -inpath $CLASSES_DIR -aspectpath ./src/main/java -outjar $JAR_DIR/test.jar
# Run the example and check that aspect logic is applied
echo "Running the sample..."
java -cp $CLASSPATH:$JAR_DIR/test.jar com.aspectj.TestTarget
This script compiles sources using standard javac compiler and weaves the aspects to the binary code. The output shows that aspect is correctly woven.
Load-time weaving
Aspects logic is injected to the class byte code during loading classes to the JVM. Standard java instrumentation facilities are used for that. More information about load-time weaving may be found here.
load-time-weaving.sh contains the following instructions:
#!/bin/bash
function ensure-dir-exists {
COUNTER=1
CURRENT_DIR=
while :
do
DIR=`echo "$1" | cut -d'/' -f $COUNTER`
test "$DIR" = "" && break
CURRENT_DIR=${CURRENT_DIR}${DIR}/
mkdir $CURRENT_DIR 2>/dev/null
COUNTER=`expr $COUNTER + 1`
done
}
# Prepare
echo "Preparing the environment..."
rm -rf ./target 2>/dev/null
CLASSES_DIR=./target/classes/pure
ensure-dir-exists $CLASSES_DIR
CLASSPATH=./src/main/java
for i in 'aspectjweaver.jar' 'aspectjrt.jar'
do
CLASSPATH=$CLASSPATH:./src/main/resources/$i
done
# Compile the sources
echo "Compiling..."
javac -classpath $CLASSPATH -g -d $CLASSES_DIR src/main/java/com/aspectj/TestTarget.java src/main/java/com/aspectj/TestAspect.java
# Run the example and check that aspect logic is applied
echo "Running the sample..."
java -javaagent:./src/main/resources/aspectjweaver.jar -cp $CLASSPATH:$CLASSES_DIR:./src/main/resources com.aspectj.TestTarget
If we run the example we get the following:
denis@harmony:/storage/projects/java/test$ java -version
java version "1.6.0_15"
Java(TM) SE Runtime Environment (build 1.6.0_15-b03)
Java HotSpot(TM) Client VM (build 14.1-b02, mixed mode, sharing)
denis@harmony:/storage/projects/java/test$ ./load-time-weaving.sh
Preparing the environment...
Compiling...
Running the sample...
[AppClassLoader@17590db] info AspectJ Weaver Version 1.6.5 built on Thursday Jun 18, 2009 at 03:42:32 GMT
[AppClassLoader@17590db] info register classloader sun.misc.Launcher$AppClassLoader@17590db
[AppClassLoader@17590db] info using configuration /storage/projects/java/test/src/main/resources/META-INF/aop.xml
[AppClassLoader@17590db] info register aspect com.aspectj.TestAspect
----------------------->--------- Start test -----------<---------------------
TestAspect.advice() called on 'execution(void com.aspectj.TestTarget.test())'
TestTarget.test()
----------------------->--------- End test -----------<---------------------
Ant weaving
We know now how to weave by hand, lets consider using more convenient ways. The first one is a honorable ant:
build.xml
<?xml version="1.0"?>
<project name="aspectj-example" xmlns:aspectj="antlib:org.aspectj">
<property name="src.dir" value="src/main/java"/>
<property name="resource.dir" value="src/main/resources"/>
<property name="target.dir" value="target"/>
<property name="classes.dir" value="${target.dir}/classes"/>
<taskdef uri="antlib:org.aspectj"
resource="org/aspectj/antlib.xml"
classpath="${resource.dir}/aspectjtools.jar"/>
<path id="aspectj.libs">
<fileset dir="${resource.dir}"/>
</path>
<target name="clean">
<delete dir="${target.dir}"/>
<mkdir dir="${target.dir}"/>
<mkdir dir="${classes.dir}"/>
</target>
<target name="compiletime" depends="clean">
<aspectj:iajc source="1.5" srcdir="${src.dir}" classpathref="aspectj.libs" destDir="${classes.dir}"/>
<java classname="com.aspectj.TestTarget" fork="true">
<classpath>
<path refid="aspectj.libs"/>
<pathelement path="${classes.dir}"/>
</classpath>
</java>
</target>
<target name="postcompile" depends="clean">
<echo message="Compiling..."/>
<javac debug="true" srcdir="${src.dir}" classpathref="aspectj.libs" destdir="${classes.dir}"/>
<echo message="Weaving..."/>
<aspectj:iajc classpathref="aspectj.libs" inpath="${classes.dir}" aspectpath="${src.dir}" outJar="${classes.dir}/test.jar"/>
<echo message="Running..."/>
<java classname="com.aspectj.TestTarget" fork="true">
<classpath>
<path refid="aspectj.libs"/>
<pathelement path="${classes.dir}/test.jar"/>
</classpath>
</java>
</target>
<target name="loadtime" depends="clean">
<echo message="Compiling..."/>
<javac debug="true" srcdir="${src.dir}" classpathref="aspectj.libs" destdir="${classes.dir}"/>
<echo message="Running..."/>
<java classname="com.aspectj.TestTarget" fork="true">
<jvmarg value="-javaagent:${resource.dir}/aspectjweaver.jar"/>
<classpath>
<path refid="aspectj.libs"/>
<pathelement path="${classes.dir}"/>
<pathelement path="${resource.dir}"/>
</classpath>
</java>
</target>
</project>
We can run 'ant compiletime', 'ant postcompile' and 'ant loadtime' and check that output is pretty much the same as the one from command-line scenarios.
Maven weaving
Finally let's consider the most convenient build tool - maven. There is a dedicated aspectj plugin that relieves the job.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.test.aspectj</groupId>
<artifactId>test</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>test</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.2</version>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.aspectj.TestTarget</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
If we run 'mvn install' we get the following output:
denis@harmony:/storage/projects/java/test$ mvn install
[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building test
[INFO] task-segment: [install]
[INFO] ------------------------------------------------------------------------
[INFO] [aspectj:compile {execution: default}]
[INFO] [resources:resources {execution: default-resources}]
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 4 resources
[INFO] [compiler:compile {execution: default-compile}]
[INFO] Nothing to compile - all classes are up to date
[INFO] [resources:testResources {execution: default-testResources}]
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /storage/projects/java/test/src/test/resources
[INFO] [compiler:testCompile {execution: default-testCompile}]
[INFO] No sources to compile
[INFO] [surefire:test {execution: default-test}]
[INFO] No tests to run.
[INFO] [jar:jar {execution: default-jar}]
[INFO] Building jar: /storage/projects/java/test/target/test-1.0-SNAPSHOT.jar
[INFO] Preparing exec:java
[WARNING] Removing: java from forked lifecycle, to prevent recursive invocation.
[INFO] No goals needed for project - skipping
[INFO] [exec:java {execution: default}]
----------------------->--------- Start test -----------<---------------------
TestAspect.advice() called on 'execution(void com.aspectj.TestTarget.test())'
TestTarget.test()
----------------------->--------- End test -----------<---------------------
[INFO] [install:install {execution: default-install}]
[INFO] Installing /storage/projects/java/test/target/test-1.0-SNAPSHOT.jar to /storage/maven-repository/com/test/aspectj/test/1.0-SNAPSHOT/test-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11 seconds
[INFO] Finished at: Sat Aug 15 09:58:30 MSD 2009
[INFO] Final Memory: 12M/21M
[INFO] ------------------------------------------------------------------------
It's also easy to weave dependencies to the existing jars that are used as a project dependencies - Weaving already compiled jar artifacts
Example
I created an archive that contains the same example that I described before, so, you can simply download it and test - link. Note that the size is ~9.5 MB (because big aspectj binaries are included).

When you said weaving with AspectJ, do you mean weaving with AspectJ via the AspectJ framework or via the Spring framework?
ReplyDeleteSpring supports AspectJ-style annotations.
Regarding this article, I'm getting the impression that I need to have the AspectJ Framework besides the Spring Framework on my projects.
Or can I perform AspectJ just with the Spring 2 AOP?
I'm afraid there is a little mix of concerns here. Let me describe the situation as I see it:
ReplyDeleteAspectJ allows to define particular logic to be injected directly to the classes's byte code. That injection can occur at compile- or class byte-code load-time.
Spring consists of many modules and we can build different relations between every module and AspectJ:
* IoC: it's possible to use spring-managed beans as AspectJ aspects dependencies (see http://denis-zhdanov.blogspot.com/2009/08/injecting-dependencies-to-aspectj.html). It's also possible to get aspect logic injected by AspectJ to the IoC container-managed beans;
* AOP - spring offers its own proxy-based AOP. However, there are particular limitations of that approach that may make it unacceptable for you (for example these one: http://denis-zhdanov.blogspot.com/2009/05/spring-aop-top-problem-2.html, http://denis-zhdanov.blogspot.com/2009/07/spring-aop-top-problem-1-aspects-are.html). At the same time spring offers @AspectJ aspects configuration approach that allows to switch to/from using AspectJ weaving. That's the reason why @AspectJ approach is more preferable than 'aop' schema-based for me. Also spring offers convenient AspcectJ load-time weaving. I haven't written about that yet but you can check spring reference chapter 6 for more details;
* Transactions - if you use @Transactional-based configuration you can use convenient AspectJ tx stuff weaving as well via 'mode="aspect"' attribute of '<tx:annotation-driven>' element;
hey thnaks for this content and it is very helpful for a newbee on ASpectJ
ReplyDeletei copied this content in my blog and i given a link this post
http://stuffjava.blogspot.in/2012/05/weaving-with-aspectj.html
thanks
Anil.E
You are welcome
ReplyDeleteHI denis Im new to AspectJ but I worked on spring AOP but this aspect j is bit difficult.
ReplyDeleteWhen I try to run your build.xml I got below problem
[taskdef] Could not load definitions from resource
org/aspectj/antlib.xml
How to make it recover. please help me.
You can just download the complete samples set from the link mentioned at the post's end (http://dl.getdropbox.com/u/1648086/example/aspectj/weaving-example/aspectj-example.tar.gz). Regarding the problem - you use wrong aspectjtools.jar or aspectj guys changed antlib setup.
DeleteHi denis, Could you please explain whats is the difference between by write the aspectj program as aspect.aj and write normal java class then anonate as @Annotation.
ReplyDeleteWhich one is good.
Hi,
ReplyDeleteI believe google has more expertise for that. E.g. http://stackoverflow.com/questions/6102746/aspectj-why-use-annotations-instead-of-ajc-compiler
Thank you very much. This article was very helpful!. I-m a new on this and From Chile, my best for you!. My english sucks!, i know!.
ReplyDeleteYou are welcome
ReplyDelete