AspectJ Tutorial

AspectJ Tutorial

Abstract

Aspect Oriented Programming (AOP) is a new programming technique for modularizing behavior that cuts across many classes. This code-level tutorial will focus on AspectJ, a popular AOP implementation for Java.

The tutorial will cover an introduction to Aspects, the AspectJ language, and practical immediate uses of aspects: plugable logging, tracing, debugging, profiling, and testing.

This tutorial based on an AspectJ presentation I have given, and is still under development. If you have any comments, please email me.

Introduction to AOP -- What is AOP?
AOP is a technique for making programs more modular by identifying and separating "cross-cutting concerns". A concern is a feature point, or a requirement. A crosscutting concern is a concern that needs to be applied in many different compilation modules. In Java, this means we are talking about a requirement that touches many different classes. For example:

These sorts of requirements would normally require implementation in many different classes. AOP calls this `code-scattering' or `tangling'. This can lead to code that is difficult to change (e.g. a new requirement says - now when logging this, log the current stacktrace as well.)

Why AOP?
OOP has become accepted as a mainstream paradigm for analysis design and development. AOP is not a replacement for OOP. It is a tool to help deal with some of the problems inherent in OOP. Some problems map well to object hierarchies. Some do not. Aspects are in some ways like Dynamic Proxies, XDoclet, EJB/JSP containers, and CLR (& JSR 201) meta-data. All are trying to resolve some long-standing problems that have become obvious as OOP matures. All are trying to remove redundant code, make it possible to supply additional behavior at runtime to objects that is declaratively proposed and interjected at runtime.

AOP wants to:

  1. Separate concerns
  2. Provide a way via language and runtime to describe crosscuts

How does it work?

When do you weave?
When you weave is implementation dependent. AspectJ 1.0 did it at compile time (meaning that you needed the source for the code you wanted to weave.) AspectJ 1.1 uses a bytecode weaver, so classes without source can be woven. In reality it could be done at compile time, link time (1.1), load time (classloader), or run time (vm).

AspectJ
AspectJ is an AOP implementation by Gregor Kiczales and others. Initially started at Xerox PARC, now an Eclipse.org subproject. I t was a winner in this years JavaWorld Editors' Choice Awards in "Most Innovative Java Product or Technology". It is an extension to java, with a new syntax.

Definitions

Getting/installing and runtime
Download the AspectJ distribution from www.eclipse.org/aspectj. It is a standalone distribution and the JAR file you download is an executable JAR install file. It also includes a structure browser, and a debugger.

You can use the compiler on sources, or it can weave with jars, output to a jar, create aspect libraries, compile without weaving, compile incrementally, etc.

Writing the aspect

The pointcut (which selects the joinpoint, and the advice) are packaged into the compilation unit, the aspect. Aspects can be named with aj extensions, or java.

Example
The following example shows a simple Java class, BankAccount, and two simple aspects. The first will fire when the balance is changed, the second, the balance or the owner.

	import java.math.BigDecimal;

	public class BankAccount {
		private String owner;
		private BigDecimal balance;

		public String getOwner() {
			return owner;
		}

		public void setOwner(String owner) {
			this.owner = owner;
		}

		public BigDecimal getBalance() {
			return balance;
		}

		public void setBalance(BigDecimal balance) {
			System.out.println("in BankAccount.setBalance:"+balance);
			this.balance = balance;
		}

		public static void main(String[] args) {
			BankAccount b = new BankAccount();
			b.increment();
			b.setOwner("Owner");
			b.setBalance(new BigDecimal ("300.00"));
			b.setBalance(b.getBalance().subtract(new BigDecimal("100.00")));
		}
	}

	public aspect BankAspect {

		//The pointcut designates where we want to cross cut the application
		pointcut changeBalance() :
		call (public void BankAccount.setBalance(java.math.BigDecimal));

		//the advice is what we want to do when we crosscut
		before() : changeBalance() {
			System.out.println("Before setBalance advice:"+thisJoinPoint.getSignature());
		}
	}


	public aspect BankAspect2 {

		pointcut changeBalance() : call (public void setBalance(java.math.BigDecimal));
		pointcut changeOwner()   : call (public void setOwner(String));

		before() : changeBalance() {
			System.out.println("setBalance:"+thisJoinPoint.getSignature());
		}

		before() : changeOwner() {
			System.out.println("setOwner:"+thisJoinPoint.getSignature());
		}
	}

//show output

Identify the joinpoint
AspectJ defines a number of join points:

Patterns

Examples of Naming Patterns, Tracing

	public aspect BankAspectWild {
		pointcut changeBalance() : call (public * set*(..));
		before() : changeBalance() {
			System.out.println("ADVICE:"+thisJoinPoint.getSignature());
		}
	}

	public aspect TraceAspect {
		pointcut trace () : call (* * (..)) && ! within(TraceAspect);
		before() : trace() {
			System.out.println("ADV:"+thisJoinPoint.getSignature());
		}
	}

	public aspect TraceAspect2 {
		pointcut interestingJoinPoints() : within(BankAccount);
		Object around() : interestingJoinPoints() {
			System.out.println("About to do " + thisJoinPoint);
			Object o = proceed();
			System.out.println("Finished " + thisJoinPoint);
			return o;
		}
	}


Pointcuts
A point cust is designated as follows: pointcut name([parameters]): designator(ajoinpoint);

	public aspect BankAspectOr {

		pointcut change() :
			call (public void setBalance(java.math.BigDecimal))
			|| call (public void setOwner(String));

		before() : change() {
			System.out.println("ADVICE:"+thisJoinPoint.getSignature());
		}
	}

Designators
Pointcut designators match join points, and exposes additional functionality

We will focus on just a few: call, exception
Call
call - use this when you are interested in the calling of a method
format: call (join point signature)
e.g. call (public void BankAccount.setOwner() );

handler
Captures the execution of an exception handler anywhere in the primary application.

format: handler(type)
e.g. handler(ClassCastException)

(Remember, patterns also apply here, * and +)

	import java.util.Date;
		public class ExceptionHandlerExample {
			public static void main(String[] args) throws SampleException {
			Object b = new BankAccount();
			try {
				throw new SampleException();
			} catch (SampleException e) {
				e.printStackTrace();
			}
		}
	}

	public aspect ExceptionHandlerAspect {
		pointcut handle() : handler(SampleException);
		before() : handle() {
			System.out.println( "Exception occurred: " + thisJoinPoint.toString() );
		}
	}
State based designators
	pointcut setBalance(BankAccount b) : call(public void setBalance(*)) && target (b);
		before (BankAccount b) : setBalance(b) {
			//b is accessible here
		}
Control Flow Designators
So far join points have matched throughout an application. Control flow designators allow us to match within certain flows. cflow and cflowbelow

Class Initialization
Program Text
Dynamic Property Based
if()
adviceexectuion()
Interfaces
Pointcuts are valid on interfaces.
Advice
Advice is the second half of AOP, the code that gets executed when a join point is reached. Advice is always relative to a joinpoint.
	public aspect BankAspectSysout {
		pointcut sysout() :
			call (public void print*(*))
			&& target(java.io.PrintStream)
			&& !within(BankAspectSysout) ;

		before() : sysout() {
			System.out.println("Wrote to sysout at:"+thisJoinPoint.getSignature());
			System.out.print(thisJoinPointStaticPart.getSourceLocation() + ":");
		}
	}
Give the advice before: excellent for preconditions argument checking, setup code, lazy init before() : pointcutName() { System.out.println("ADVICE:"+thisJoinPoint.getSignature()); } after After can be qualified with: after returning, or after throwing Cleanup of resources checking/manipulating the return value around the most powerful advice can replace invocation, or just surround use proceed() to call method
	public class NullReturnClass {

		public String lookup(String s){
			return null;
		}
		public static void main(String[] args) {
			new NullReturnClass().lookup("someval");
		}
	}

	public aspect NoNullReturnAspect {

	//The first pointcut matches all calls,
	//The second avoids those that have a void return type.
		pointcut methodsThatReturnObjects():
		call(* *.*(..))
		&& !call(void *.*(..));

	Object around(): methodsThatReturnObjects() {
		Object lRetVal = proceed();
		if (lRetVal == null) {
			System.err.println(
			"Detected null return value after calling " +
			thisJoinPoint.getSignature().toShortString() +
			" in file " +
			thisJoinPoint.getSourceLocation().getFileName() +
			" at line " +
			thisJoinPoint.getSourceLocation().getLine());
		}
		return lRetVal;
	}
	}

thisJoinPoint

Formal Access to Parameters To access parameters statically:

use target(), args() and this()

Reflective Access to Join Point The joinpoint can be reflectively accessed via objects:

You can get access to the almost all of the same info reflectively, but it is slower.

Advice and Exceptions
Advice is expected not to throw and exceptions not thrown in the code being called. This means things would normally get wrapped in a Runtime.

Precedence
use the precedence keyword in an aspect: declare precedence : A , B; Sub aspects execute before parents. Otherwise undefined. Multiple advice in an aspect: natural order order of declaration

Inter-type Declarations (Introductions)
Add new functionality to an application, new attributes, new methods, and change inheritance. To do this, you will write normal variable and methods in your aspect, but prefix them with your "classname.". You can then access those fields and methods in a) normal java code, or b) in your aspects. To get access to the object being references, : Add a parameter to the pointcut name Add && target(t) to the designator Add parameter to advice designator Add variable name to advice body Very powerful, a little too complicated to get into. You can: add members (id fields, dirty-ness) add methods (toXML, storeToJDBC) add types that extend existing types or implement interfaces declare custom compilation errors or warnings convert checked exceptions to unchecked Can use from aspects & regular code. Can do wacky things: Add concrete fields & methods to interfaces (no constructors) Modify aspects Make an existing class dynamically implement an interface Make an existing class extend another Like EJB?

	public aspect BankAspectIntro {
		private int BankAccount.count = 0;

		public void BankAccount.increment() {
			count++;
		}

		public int BankAccount.getCount() {
			return count;
		}

		pointcut changeBalance(BankAccount b, java.math.BigDecimal d) :
			call (public void setBalance(java.math.BigDecimal))
			&& args(d)
			&& target(b);

		before(BankAccount b, java.math.BigDecimal d) : changeBalance(b,d) {
			b.increment();
			System.out.println("Change amt: "+d.toString()+" balance now "+ b.getBalance()
			+" has been changed:"+b.getCount()+" times");
		}
	}
Declare custom compilation warn/error declare [error|warning] : statically determinable pointcut
	import java.sql.*;
	public class SQLAccess {
		public static void main (String[] args) throws java.sql.SQLException {
			Connection con = java.sql.DriverManager.getConnection("connect");
			Date d = new Date(System.currentTimeMillis());
			//do something with the connection
		}
	}

	public aspect SQLAccessAspect {
		/* Any call to methods or constructors in java.sql */
		pointcut restrictedCall():
		call(* java.sql.*.*(..)) || call(java.sql.*.new(..));

		/* Any code in my system not in the sqlAccess package */
		pointcut illegalSource():
		!within(com.foo.sqlAccess.*);

		declare error: restrictedCall() && illegalSource():
		"java.sql package can only be accessed from com.foo.sqlAccess";
	}

Softening Exceptions Suppress checked as unchecked declare soft : java.io.IOException: execution (* * (..)) && within (SomeClass); Then does not need to be handled in the code. [Example: 11]Soften exception
  import java.io.FileInputStream;
  import java.io.ObjectInputStream;

  public class CacheClass {

    public Object getFromCache(Object o) {
        FileInputStream f = new FileInputStream("o.obj");
        ObjectInputStream ois = new ObjectInputStream(f);
        Object o2 = ois.readObject();
        ois.close();
        return o2;
    }

    public static void main(String[] args) {
        CacheClass c = new CacheClass();
        c.getFromCache(new Object());
    }
  }

  public aspect CacheClassAspect {

    pointcut cache() : within(CacheClass);

    declare soft : Exception: cache();

  }
Aspect Structure Can be written standalone, same file, or embedded within a class. Aspects can extend, be abstract, inherit from classes, and implement interfaces. Instantiation All of the aspects seen so far have been singletons. This is the default, but can be specified explicitly: aspect Foo issingleton { } Per-Object aspect Foo perthis(pointcut) {} aspect Foo pertarget(pointcut) {} create a new one whenever the designated pointcut is matched Per control flow aspect Foo percflow(pointcut) {} aspect Foo percflowbelow(pointcut) {} create a new one whenever the designated flow of execution is entered Development Examples Advise methods in Java API Development Toolbox Production examples Conclusions
So, is it a good idea?

straight to the top