ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Aspect-Oriented Annotations
Pages: 1, 2, 3

Annotations and AOP

Phew! Now that we've been through the overviews, let's start diving into the meat of the article. I'm going to throw a lot of examples at you again, as it's the best way I know of to teach a new concept.

As I said before, annotations plus AOP almost give you the ability to extend the Java language. Annotations provide the mechanism for declaring new, compilable, typesafe syntax. AOP provides the mechanism to encapsulate and apply new behavior to a syntactical expression.

Method Annotations and AOP

Let's take a look at how you can use method annotations with AOP. Using annotations and AOP together and applying this to a method is very analogous to using Java's synchronized keyword with a method. When you tag a method as synchronized, you are telling the JVM that you want that method to behave in a special way when it is invoked. Annotations allow you to define new keywords that you want to have trigger your own special custom behavior. AOP gives you the ability to encapsulate this behavior and weave it into the execution of the method. Again, this concept is best described in an example.

Let's say we want to add a new syntax that will allow us to fire void methods in the background, in another thread, if they are tagged as @Oneway. Using this new syntax would look like this:

import org.jboss.aspects.Oneway;

public class Foo
{
   @Oneway public static void someMethod() {...}

   public static void main(String[] args) 
   {  
      someMethod(); // executes in background
   }
}

When someMethod() is invoked within main, it will run asynchronously so that the code in main is free to do other tasks in parallel.

To implement this functionality, the first thing that must be done is to define the new Java syntax for our @Oneway tag within an annotation.

Oneway.java

package org.jboss.aspects;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
public @interface Oneway {}

Simple enough. The @Target tag allows you to narrow down where the annotation is allowed to be applied. In this case, our @Oneway annotation can only be applied to a method. Remember, this is all pure 100 percent Java that is available in J2SE 5.0.

The next thing we have to do is to define an aspect class that will encapsulate our @Oneway behavior.

OnewayAspect.java

package org.jboss.aspects;

public OnewayAspect
{
   private static class Task implements Runnable
   {
      private MethodInvocation invocation;

      public Task(MethodInvocation invocation)
      {
        this.invocation = invocation;
      }
      public void run()
      {
        try { invocation.invokeNext(); }
        catch (Throwable ignore) { }
      }
   }
   

   public Object oneway(MethodInvocation invocation) throws Throwable
   {
      MethodInvocation copy = invocation.copy();
      Thread t = new Thread(new Task(copy));
      t.setDaemon(false);
      t.start();
      return null;
   }
}

The aspect is simple enough. The oneway() method copies the invocation, creates a thread, fires off the complete invocation in the background, and returns. We could imagine a more sophisticated example using some of the new Executors within the J2SE 5.0 java.util.concurrent package, but hopefully this code illustrates how you could build on this example to implement more complete implementations.

The last thing that must be done is to specify the pointcut expression that will trigger the application of the OnewayAspect when the @Oneway annotation is declared on a method.

jboss-aop.xml

<aop>
   <aspect class="org.jboss.aspects.OnewayAspect"/>

   <bind pointcut="execution(void *->@org.jboss.Oneway(..))">
      <advice name="oneway"
              aspect="org.jboss.aspects.OnewayAspect"/>
   </bind>
</aop>

The pointcut expression states that any void method that is tagged as @Oneway should have the OnewayAspect.oneway() method executed before it itself executes. With the annotation, aspect, and pointcut expression now defined, the @Oneway syntax is now usable in your application. A simple, clean, easy way of extending the Java language!

Field Annotations and AOP

Let's look at how you could use field annotations and AOP. Using annotations and AOP, you can can actually change how a field is stored by an object or as a static member of a class. What we want to accomplish in this example is that when you tag a field (static or member) as @ThreadBased, its value will behave as though it were stored in a java.lang.ThreadLocal. Sure, you could use a ThreadLocal variable directly, but the problem with ThreadLocal is that it is untyped and you have to use "verbose" (okay, they're not that verbose) get() and set() methods. So what we'll do here is create a typed ThreadLocal field. Basically, we'll create a new Java field type called the @Threadbased variable.

Using this new type would look like this:

import org.jboss.aspects.Threadbased;

public class Foo
{
   @Threadbased private int counter;
}

To implement this functionality, we must first define the annotation.

Threadbased.java

package org.jboss.aspects;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

@Target({ElementType.FIELD})
public @interface Threadbased {}

Simple enough. The @Target tag allows you to narrow down where the annotation is allowed to be applied. In this case, our @Threadbased annotation can only be applied to fields.

The next thing to do is to define the aspect that will encapsulate our ThreadLocal behavior.

ThreadbasedAspect.java

package org.jboss.aspects;

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

public class ThreadbasedAspect
{
   private ThreadLocal threadbased = new ThreadLocal();

   public Object access(FieldReadInvocation invocation)
       throws Throwable
   {
      // just in case we have a primitive,
      // we can't return null
      if (threadbased.get() == null)
          return invocation.invokeNext();
      return threadbased.get();
   }

   public Object access(FieldWriteInvocation invocation)
       throws Throwable
   {
      threadbased.set(invocation.getValue());
      return null;
   }
}

ThreadbasedAspect encapsulates the access to a Java field. It has a dedicated ThreadLocal variable within it to track threadlocal changes to a particular field. It also has separate access() methods that are invoked depending upon whether a get or set of the field is called. These methods delegate to the ThreadLocal to obtain the current value of the field.

Finally, we must define a pointcut expression that will trigger the application of the ThreadbasedAspect when the @Threadbased annotation is specified on a particular field.

jboss-aop.xml

<aop>
   <aspect class="org.jboss.aspects.ThreadbasedAspect" 
           scope="PER_JOINPOINT"/>
   <bind pointcut="field(* *->@org.jboss.aspects.Threadbased)">
      <advice name="access"
              aspect="org.jboss.aspects.ThreadbasedAspect"/>
   </bind>
</aop>

Just in case we have multiple @Threadbased variables defined in one class, we want an instance of ThreadbasedAspect to be allocated per field for static fields. For member fields, we want an instance of ThreadbasedAspect to be allocated per field, per object instance. To facilitate this behavior, the aspect definition scopes the instance of when and where the aspect class will be allocated by setting it to PER_JOINPOINT. If we didn't do this scoping, JBoss AOP would only allocate one instance of ThreadbasedAspect and different fields would be sharing the same instance of the ThreadLocal -- something that we don't want.

Well that's it. A clean, easy way of extending Java to specify a new special type. Note: This particular aspect comes bundled with JBoss AOP.

Pages: 1, 2, 3

Next Pagearrow