Original URL: https://www.theregister.com/2006/10/26/aspects_java_aop/

Aspect oriented programming with Java

A practical introduction

By John Hunt

Posted in Software, 26th October 2006 13:56 GMT

Writing software is a complex business - not only do you have to get the enterprise logic of the application correct, typically you also have to deal with multiple other concerns at the same time, such as "what should happen if something goes wrong", "how should I make sure we know what is happening during execution", "how to enforce security throughout my application" and in some languages "how do I handle memory" or "when should I free up memory", etc.

In Java, of course, we have already been able to move much of the responsibility, and thus the implementation of, memory manage and garbage collection into the Virtual machine. However, Java software still often needs to handle multiple concerns within the same body of code. A classic example is the use of user validation code and logging code within a Java class that performs some form of system behaviour (such as requesting a book from a library etc).

Such cross cutting concerns are one of the underlying motivators behind what is know as Aspect Oriented Programming (or AOP). The basic idea behind AOP is that each concern within an application should be implemented within its own independent module (or Aspect). Thus, a logging module only deals with logging and isn't tangled up with other business logic.

The Basic Concepts of AOP

Software development has progressed a long way in the last 20 or 30 years. Twenty years ago it was not uncommon to find assembler and high-level code mixed together in the same (pages long) source code listing. However, we have improved awareness of the benefits of highly cohesive, but loosely coupled code, we have accepted the need for modularity in our systems, and we have adopted (within Java at least) Object Orientation as a basic software engineering technique.

However, I can still find myself looking at a source code listing and having to try to pull out the basic business logic, from all the "stuff" that sits around it.

All this "stuff", such as calls to logging components, authentication checks, exception handling, transaction management, etc, is all very important within any large software system. However, it adds to the fog that can surround a potentially simple piece of business logic.

Interestingly, one of the compelling arguments for making techniques such as Automatic Garbage Collection and Memory Management mainstream was that it separated out the implementation detail of managing memory from the business logic required by an application. Mixing the two made software more difficult to read, understand, and maintain; by placing the memory management in the background, the core business logic was that much simpler. This is essentially the premise behind AOP.

Let's take logging as an example. It's quite possible to design and implement a logging subsystem (indeed many have done this for Java including the Apache Log4J Logging framework).

However, the problem is that such a subsystem just provides the ability to log; it says nothing about where and when you should log system behaviour. To do this you most go through your software system and add such logging statements to appropriate points within methods. The end result is that the user of the logger needs to know that it is available, what its API is and, of course, must decide where to place the logging statements.

In turn the pure business logic methods are now augmented by additional (non-business related) method calls. As this process may be repeated for numerous such subsystems, the initially clean code may now be far more complex. This, in extreme cases, may make it more difficult to understand the underlying, relatively simple, business logic.

Logging here is an example of a cross cutting concern. That is, it is an aspect of the systems behaviour that cuts across many other modules. AOP allows Logging to be implemented as a completely self-contained module that is dynamically linked with the initial business logic in such a way that the original source code is unchanged. The self-contained, or stand-alone modules, are referred to as Aspects within AOP.

AOP programs can achieve the linking of the Aspect with the pure business logic code in a number of ways. For example, a pre-processor could be used to produce a combined program that is then compiled to produce the runtime executable (this was the approach originally used by AspectJ back in 2001). Another approach is to provide some form of post compilation modification.

In Java';s case, this means modification of the byte codes generated for classes such that links to an appropriate Aspect are incorporated into the byte codes. Such byte code weavers can be deployed during the build process or, if the weave model is per-class, during class loading. AspectJ has evolved from providing per-class bytecode weaving in 2002 to advanced load-time integration form 2005. The final approach is to augment the underlying execution environment (in Java's case the JVM) so that it understands AOP language elements directly.

AspectJ for Java

AspectJ is currently the most complete implementation of an AOP language for Java. In practical terms, it is an extension to Java that treats AOP concepts as first-class elements of the language. It allows aspects to be implemented as part of a Java based application using familiar Eclipse based tools.

As AspectJ is an extension to Java, Java is the implementation language for the constructs that comprise an Aspect. That is, you use Java to implement whatever behaviour the Aspect should provide. A set of rules is then used to determine how to weave the aspect into the main body of your Java application. These rules are implemented by pointcuts, join points, and advice. A pointcut specifies what join points there are. In turn, a join point defines where in a Java programs' execution the aspect should be applied and advice is the implementation of what to do at that point.

The actual syntax of the AspectJ language is Java-like, details of which can be obtained from the AspectJ home page.

A simple example

As a simple example of an implementation of an Aspect let us implement the world's simplest logging Aspect. To do this we will log what happens during the execution of the classic Hello World Java program. For the purposes of this example, the Hello World program I shall use is presented below:

package com.reg.dev.aspects;

public class HelloWorld {

        public void print() {
                System.out.println("Hello World");
        }
        
        public static void main(String [] args) {
                HelloWorld hw = new HelloWorld();
                hw.print();
        }
}

We can now implement an Aspect using AspectJ to provide for system logging. To do this I downloaded the AJDT: AspectJ Development Tools Eclipse Based plug-in (make sure you get the correct version for your Eclipse) from here. This provided me with not only the AspectJ runtime, but also various Wizards and browsers to support AOP development.

I then implemented my logging aspect within Eclipse. A screen dump of this is presented in Figure 1. Note that the file generated has a *.aj extension and that the implementation of the before and after advice is a piece of standard Java code. Before exploring what the implementation does, let us describe in plain English the purpose of this Aspect. Essentially this aspect specifies that before executing the HellWorld.print method, we will printout "Entering print" and after returning normally from the method we will printout "Existing print".

Screenshot showing an AspectJ Logger Aspect

In the example in Figure 1, the whole aspect is called Logger, log is the pointcut that implements the join point for this aspect and there are two advice points. Note that AspectJ makes the following pointcuts available:

In the example above, I have defined a single execution pointcut. This specifies that on the execution of the HelloWorld.print method two advices should be applied. One before the method executes and one after the method has returned (other options include running after an exception has been thrown). The advice implements the behaviour of the aspect.

To run the combined application (i.e. the HelloWorld program and the Aspect) within Eclipse, I merely run the HelloWorld application as normal. However, the byte codes of the class and the aspect point cuts are dynamically weaved to create the following output:

Entering print
Hello World
Exiting print

Thus the core behaviour of the HelloWorld program and the behaviour of the logging Aspect have successfully been interwoven in the running system without the need to clutter up the original clean source code with calls to the logging subsystem.

Development methodology

A quick note on AOP development. It is clear to me that to identify the concerns which should be implemented as an Aspect is not necessarily straightforward nor is it directly covered by traditional software engineering methodologies. However, a fairly intuitive approach is to separate the AOP development process into some basic steps. These are essentially:

See the References, below for more information on Aspect Oriented Software Development.

Summary

AOP is certainly an important element in the Software Engineers toolbox. It is not, I believe, a replacement for Object Orientation, rather it is an augmentation. Using Java to implement the core business logic of an application is still an excellent approach. Indeed, using Java to implement the core behaviour of an aspect also works very well. What AOP gives you is a way of separating and managing the different aspects of modern, complex, software systems.

References