Aspect-Oriented Programming and JBoss

Overview

Aspect-oriented programming (AOP) is an exciting new paradigm that should
have the same effect on software development that object-oriented programming
(OOP) had 15-20 years ago. AOP and OOP are not competing technologies, but
actually complement each other quite nicely. OOP is great for modeling common
behavior on a hierarchy of objects. Its weakness is in applying common
behavior that spans multiple non-related object models; this is where AOP
comes in. AOP allows you to define cross-cutting concerns that can be applied
across separate, and very different, object models. It allows you to layer
— rather than embed — functionality so that code is more readable
and easier to maintain. We like to think of OOP as top-down software
development, while AOP is left-right; they're completely orthogonal technologies
that complement each other quite nicely.

Where the tools of OOP are inheritance, encapsulation, and polymorphism, the
components of AOP are advices/interceptors, introductions, metadata, and
pointcuts. Let's take a look at these definitions.

Advices/Interceptors

An advice is logic that is triggered by a certain event. It is
behavior that can be inserted between a caller and a callee, a method invoker
and the actual method. Advices are really the key to AOP. These constructs
allow you to define cross-cutting behavior. Advices allow you to transparently apply things
like logging and metrics to an existing object model.

In JBoss AOP, we implement advices using interceptors. You can
define interceptors that intercept method invocations, constructor invocations,
and field access. Later, we'll examine how to apply these interceptions to an
existing object model.

Introductions

Introductions are a way to add methods or fields to an existing
class. They allow you to even change the interfaces an existing class
currently implements and to introduce a mix-in class that implements these new
interfaces. Introductions allow you to bring multiple inheritance to plain Java
classes. A great use case for an introduction is when you have an aspect for which
you want to have a runtime interface. You want to apply your aspect across
different object hierarchies, but you still want application developers to be
able to invoke aspect-specific APIs.

Introductions can be a means to attach a new API to an existing object
model.

Metadata

Metadata is additional information that can be attached to a class,
either statically or at runtime. It's even more powerful when you can dynamically attach
metadata to a given instance of an object. Metadata is great when
you are writing truly generic aspects that can be applied to any object, but
the logic needs to know class-specific information. A good analogy of metadata
in use is the EJB specification. In EJB XML deployment descriptors, you define
transaction attributes on a per-method basis. The application server knows
when and where to begin, suspend, or commit a transaction because you have
defined methods as Required, RequiresNew, Supports, etc., within metadata
bindings between your EJB class and the transaction manager, in the bean's
XML configuration files.

C# has metadata built right into the language. XDoclet is another fine
example of metadata in action. If you have ever used XDoclet to generate EJB
files and deployment descriptors, you know the power of metadata. The Java
Community Process (JCP) agrees, as metadata is being added to the Java
language in JDK 1.5 (see JSR175). Until JSR 175 becomes a reality,
though, a good AOP framework should provide a mechanism to declare class-level
metadata that is available at runtime.

Pointcuts

If interceptors, introductions, and metadata are the features of AOP,
pointcuts are the glue. Pointcuts tell the AOP framework which
interceptors to bind to which classes, what metadata to apply to which classes,
or what classes to which an introduction will be introduced. Pointcuts define
how various AOP features are applied to the classes in your application.

AOP in Action

Example 1. Using Interceptors

JBoss 4.0 comes with an AOP framework. This framework is tightly integrated
with the JBoss application server, but you can also run it standalone on your
own applications as well. You can never truly understand a concept until you
have seen it in action, so let's use examples from JBoss AOP to illustrate how
all of this stuff works together. In the rest of this article, we will build a
simple tracing framework using AOP.

Defining an Interceptor

The first thing that must be done to implement our little tracing framework
is to define the interceptor that will do the actual work. All interceptors in
JBoss AOP must implement the org.jboss.aop.Interceptor
interface.

All fields, constructors, and methods that are intercepted in JBoss AOP get
turned into a generic invoke call. Method parameters are stuffed
into an Invocation object, and the return value of a method, field
access, or constructor is stuffed into an InvocationResponse
object. The Invocation object also drives the interceptor chain.
In the interest of clarification, let's see how all of these objects fit together
in an example.

The above interceptor will intercept all calls to a field, constructor, or
method. If the invocation type is a method or constructor, a trace message
with the signature of the method or constructor will be output to the
console.

Attaching an Interceptor

Okay, so we've defined the interceptor. But how do we attach the
interceptor to an actual class? To do this, we need to define a pointcut. For
JBoss AOP, pointcuts are defined within an XML file. Let's see what this looks
like.

The pointcut above attaches the TracingInterceptor to a class
named POJO. This seems a bit cumbersome; do we have to
create a pointcut for each class we want to trace? Luckily, the class
attribute of interceptor-pointcut can take any regular expression.
So if you wanted to trace every class loaded by the JVM, the class expression
would change to .*. If you only wanted to trace a particular
package, then the expression would be com.acme.mypackge.*.

When running JBoss AOP standalone, any XML file that fits the
META-INF/jboss-aop.xml pattern will be loaded by the JBoss AOP
runtime. If that relative path is contained in any JAR or directory in
your CLASSPATH, that particular XML file will be loaded by the
JBoss AOP runtime at startup.

Running the Example

We'll use the pointcut defined above to run the example. The
POJO class looks like this:

JBoss AOP does bytecode manipulation to attach interceptors. Because there
is no compilation step, the AOP runtime must have total control of the
ClassLoader. So you must override the system classloader with a
JBoss-specific one if you are running outside of the JBoss application
server.