Aspect oriented programming with Java
A practical introduction
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.
Next page: A simple example