Você está na página 1de 12

ONJava.

com: Aspect-Oriented Programming and JBoss

Published on ONJava.com (http://www.onjava.com/)


http://www.onjava.com/pub/a/onjava/2003/05/28/aop_jboss.html
See this if you're having trouble printing code examples

Aspect-Oriented Programming and JBoss

by Bill Burke and Adrian Brock


05/28/2003

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

http://www.onjava.com/lpt/a/3878 (1 of 12)02/07/2004 15:17:20


ONJava.com: Aspect-Oriented Programming and JBoss

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.

Apple apple = new Apple();


LoggingAPI logging = (LoggingAPI)apple;
Apple.setLoggingLevel(VERBOSE);

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

http://www.onjava.com/lpt/a/3878 (2 of 12)02/07/2004 15:17:20


ONJava.com: Aspect-Oriented Programming and JBoss

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.

public interface Interceptor


{
public String getName();
public InvocationResponse invoke(Invocation invocation) throws
Throwable;
}

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.

import org.jboss.aop.*;
import java.lang.reflect.*;

public class TracingInterceptor implements Interceptor


{
public String getName() { return TracingInterceptor; }
public InvocationResponse invoke(Invocation invocation)
throws Throwable
{
String message = null;

if (invocation.getType() == InvocationType.METHOD)
{
Method method = MethodInvocation.getMethod(invocation);
message = method: + method.getName();
}
else if (invocation.getType() == InvocationType.CONSTRUCTOR)
{
Constructor c = ConstructorInvocation.getConstructor
(invocation);
message = constructor: + c.toString();
}

http://www.onjava.com/lpt/a/3878 (3 of 12)02/07/2004 15:17:20


ONJava.com: Aspect-Oriented Programming and JBoss

else
{
// Do nothing for fields. Just too verbose.
return invocation.invokeNext();
}

System.out.println(Entering + message);

// Continue on. Invoke the real method or constructor.


InvocationResponse rsp = invocation.invokeNext();
System.out.println(Leaving + message);
return rsp;
}
}

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.

<?xml version="1.0" encoding="UTF-8">


<aop>
<interceptor-pointcut class="POJO">
<interceptors>
<interceptor class="TracingInterceptor" />
</interceptors>
</interceptor-pointcut>
</aop>

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.

http://www.onjava.com/lpt/a/3878 (4 of 12)02/07/2004 15:17:20


ONJava.com: Aspect-Oriented Programming and JBoss

Running the Example

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

public class POJO


{
public POJO() {}
public void helloWorld() { System.out.println(Hello World!); }
public static void main(String[] args)
{
POJO pojo = new POJO();
pojo.helloWorld();
}
}

The TracingInterceptor will intercept calls to main(), POJO(), and helloWorld(). The
output should look like this:

Entering method: main


Entering constructor: public POJO()
Leaving constructor: public POJO()
Entering method: helloWorld
Hello World!
Leaving method: helloWorld
Leaving method: main

You can download JBoss AOP and sample code here. To compile and execute:

$ cd oreilly-aop/example1
$ export CLASSPATH=.;jboss-common.jar;jboss-aop.jar;javassist.jar
$ javac *.java
$ java -Djava.system.class.loader=org.jboss.aop.standalone.
SystemClassLoader POJO

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.

Example 2. Using Metadata

http://www.onjava.com/lpt/a/3878 (5 of 12)02/07/2004 15:17:20


ONJava.com: Aspect-Oriented Programming and JBoss

The TracingInterceptor does not trace field access because it is a bit too verbose. It is a
common practice for developers to implement get() and set() methods to encapsulate field
access. It would be nice if the TracingInterceptor could filter out and not trace these methods.
This example shows you how to use JBoss AOP metadata to implement this filtering on a per-method
basis. Usually, metadata is used for more complex things like defining transaction attributes, per-
method security roles, or persistence mappings, but this example should be good enough to illustrate
how metadata could be used in an AOP-enabled application.

Defining Class Metadata

To add this filtering functionality, we will provide a flag that you can use to turn tracing off. We will
go back to our AOP XML file to define the tags that will remove tracing for get() and set()
methods. Actually, it's kind of pointless to be tracing the main() function, so let's filter that out, too.

<?xml version="1.0" encoding="UTF-8">


<aop>
<class-metadata group="tracing" class="POJO">
<method name="(get.*)|(set.*)">
<filter>true</filter>
</method>
<method name="main">
<filter>true</filter>
</method>
</class-metadata>
</aop>

The above XML defines a grouping of attributes called tracing. The filter attribute will be
attached to every method starting with get or set. The regular expression format uses JDK-1.4-
defined expressions. This metadata can be accessed within the TracingInterceptor through the
Invocation object.

Accessing Metadata

For this metadata to be useful, it must be accessible at runtime. Class metadata is accessible through
the Invocation object. To make use of it in our example, the TracingInterceptor must be
modified a tiny bit.

public class TracingInterceptor implements Interceptor


{
public String getName() { return TracingInterceptor; }
public InvocationResponse invoke(Invocation invocation)

http://www.onjava.com/lpt/a/3878 (6 of 12)02/07/2004 15:17:20


ONJava.com: Aspect-Oriented Programming and JBoss

throws Throwable
{
String filter = (String)invocation.getMetaData(tracing,
filter);
if (filter != null && filter.equals(true))
return invocation.invokeNext();

String message = null;

if (invocation.getType() == InvocationType.METHOD)
{
Method method = MethodInvocation.getMethod(invocation);
message = method: + method.getName();
}
else if (invocation.getType() == InvocationType.CONSTRUCTOR)
{
Constructor c = ConstructorInvocation.getConstructor
(invocation);
message = constructor: + c.toString();
}
else
{
// Do nothing for fields. Just too verbose.
return invocation.invokeNext();
}

System.out.println(Entering + message);

// Continue on. Invoke the real method or constructor.


InvocationResponse rsp = invocation.invokeNext();
System.out.println(Leaving + message);
return rsp;
}
}

Running Example 2

The POJO class has been expanded a bit to add get() and set() methods.

public class POJO


{
public POJO() {}
public void helloWorld() { System.out.println(Hello World!); }

private int counter = 0;

http://www.onjava.com/lpt/a/3878 (7 of 12)02/07/2004 15:17:20


ONJava.com: Aspect-Oriented Programming and JBoss

public int getCounter() { return counter; }


public void setCounter(int val) { counter = val; }
public static void main(String[] args)
{
POJO pojo = new POJO();
pojo.helloWorld();
pojo.setCounter(32);
System.out.println(counter is: + pojo.getCounter());
}
}

The TracingInterceptor will intercept calls to main(), POJO(), and helloWorld(). The
output should look like this:

Entering constructor: public POJO()


Leaving constructor: public POJO()
Entering method: helloWorld
Hello World!
Leaving method: helloWorld

You can download JBoss AOP and sample code here. To compile and execute:

$ cd oreilly-aop/example2
$ export CLASSPATH=.;jboss-common.jar;jboss-aop.jar;javassist.jar
$ javac *.java
$ java -Djava.system.class.loader=org.jboss.aop.standalone.
SystemClassLoader POJO

Example 3. Using Introductions

It would be cool if we could turn tracing off and on for specific instances. JBoss AOP has an API to
attach metadata to an object instance, but let's pretend an actual tracing API is a better solution. In
this example, we'll change the definition of the POJO class itself through the use of an introduction.
We will force the POJO class to implement a tracing interface and provide a mix-in class that handles
the new tracing API. This will be the tracing interface:

public interface Tracing


{
public void enableTracing();
public void disableTracing();

http://www.onjava.com/lpt/a/3878 (8 of 12)02/07/2004 15:17:20


ONJava.com: Aspect-Oriented Programming and JBoss

Defining a Mix-in Class

The Tracing interface will be implemented in a mix-in class. An instance of this mix-in class will
be attached to the POJO class when a POJO is instantiated. Here's the implementation:

import org.jboss.aop.Advised;

public class TracingMixin implements Tracing


{
Advised advised;

Public TracingMixin(Object obj)


{
this.advised = (Advised)obj;
}

public void enableTracing()


{
advised._getInstanceAdvisor().getMetaData().addMetaData(
"tracing", "filter", true);
}

public void disableTracing()


{
advised._getInstanceAdvisor().getMetaData().addMetaData(
"tracing", "filter", false);
}
}

The enableTracing() method attaches the filter attribute to the object instance. The
disableTracing() method does the same, but turns the filter attribute to false. These two
methods are examples of how metadata can be applied to more than the class level. That metadata
can applied at the instance level, as well.

Attaching an Introduction

Okay, so we've defined the tracing interface and implemented the mix-in class. The next step is to
apply the introduction to the POJO class. As for interceptors, we must define another pointcut in
XML. Let's see what this looks like.

<?xml version="1.0" encoding="UTF-8">

http://www.onjava.com/lpt/a/3878 (9 of 12)02/07/2004 15:17:20


ONJava.com: Aspect-Oriented Programming and JBoss

<aop>
<introduction-pointcut class="POJO">
<mixin>
<interfaces>Tracing</interfaces>
<class>TracingMixin</class>
<construction>new TracingMixin(this)</construction>
</mixin>
</introduction-pointcut>
</aop>

The above pointcut will force the POJO class to implement the Tracing interface. Now, when an
instance of POJO is instantiated, an instance of TracingMixin will also be instantiated. The way
TracingMixin is instantiated is defined in the <construction> tag. You can put any one-line
Java code you want in the <construction> tag.

Running Example 3

The POJO class has been expanded a bit to show how the Tracing API can now be accessed. The
TracingInterceptor hasn't changed from Example 2.

public class POJO


{
public POJO() {}
public void helloWorld() { System.out.println(Hello World!); }

public static void main(String[] args)


{
POJO pojo = new POJO();
Tracing trace = (Tracing)this;
pojo.helloWorld();

System.out.println("Turn off tracing.");

trace.disableTracing();
pojo.helloWorld();

System.out.println("Turn on tracing.");

trace.enableTracing();
pojo.helloWorld();
}
}

http://www.onjava.com/lpt/a/3878 (10 of 12)02/07/2004 15:17:20


ONJava.com: Aspect-Oriented Programming and JBoss

Notice that we can typecast POJO to the Tracing interface. The output should look like this:

Entering constructor: POJO()


Leaving constructor: POJO()
Entering method: helloWorld
Hello World!
Leaving method: helloWorld
Turn off tracing.
Entering method: disableTracing
Leaving method: disableTracing
Hello World!
Turn on tracing.
Entering method: helloWorld
Hello World!
Leaving method: helloWorld

Notice that the interceptor-pointcut that added the TracingInterceptor also applies to the
methods introduced by the Tracing introduction.

To compile and run this example:

$ cd oreilly-aop/example3
$ export CLASSPATH=.;jboss-common.jar;jboss-aop.jar;javassist.jar
$ javac *.java
$ java -Djava.system.class.loader=org.jboss.aop.standalone.
SystemClassLoader POJO

Conclusions

Aspect-oriented programming is a powerful new tool for software development. With JBoss 4.0, you
can implement your own interceptors, metadata, and introductions, to make your software
development process more dynamic and fluid. Come visit us at www.jboss.org for more detailed
documentation. There are a few surprises waiting for you, as we've implemented a suite of services
on top of our new framework. Have a look and happy coding.

Bill Burke Chief Architect of JBossGroup, LLC and lead for JBoss 4.0.

Adrian Brock is Director of Support for JBossGroup, LLC.

Return to ONJava.com.

http://www.onjava.com/lpt/a/3878 (11 of 12)02/07/2004 15:17:20


ONJava.com: Aspect-Oriented Programming and JBoss

Copyright 2004 O'Reilly Media, Inc.

http://www.onjava.com/lpt/a/3878 (12 of 12)02/07/2004 15:17:20

Você também pode gostar