JBoss AOP - User Guide 学习笔记

JBoss AOP - User Guide

The Case For Aspects

1.0


Preface

Aspect-Oriented Programming (AOP) is a new paradigm that allows you to organize and layer your software applications in ways that are impossible with traditional object-oriented approaches. Aspects allow you to transparently glue functionality together so that you can have a more layered design. AOP allows you to intercept any event in a Java program and trigger functionality based on those events. Mixins allow you to introduce multiple inheritance to Java so that you can provide APIs for your aspects. Combined with JDK 5.0 annotations, it allows you to extend the Java language with new syntax.

JBoss AOP is a 100% Pure Java aspected oriented framework usuable in any programming environment or as tightly integrated with our application server.

This document walks you through how AOP can be used to build your applications. A large number of real-world examples are given in each section to illustrate various ideas. The book is broken up in the following manner:

What is an Aspect?

The section gives an overview on exactly what Aspect-Oriented Programming is.

Terms

Defines some basic AOP terms that you will need when reading this document.

Building Aspects

The chapter walks through some examples of building an aspect. One example shown is how to detect JDBC connection leakages in your code. Another example is using the Observer/Observable pattern Another is doing dependency injection.

Aspect-Oriented Annotations

This chapter walks through how you can use JDK 5.0 annotations and AOP to actually extend the Java language with new syntax. Numerous real-world examples are given.

Mixins and Introductions

This chapter walks through how you can use AOP introductions and mixins to have aspects with APIs, or how to extend a POJO's functionality transparently. Specific examples are an Asynchronous framework and making a POJO into a JMX MBean.

Dynamic AOP

This chapter walks through how you can use the dynamic per-instance API that each aspectized class has to define aspects on a per instance basis rather than for every instance of a class.

Integration with Pointcuts

This chapter steps away from writing aspects, but rather talks about how you can publish pointcuts that can be used by users/customers to integrate with your products.

Testing with AOP

This chapter shows how you can use AOP to test your applications in a easier way.

If you have questions, use the user forum linked on the JBoss AOP website. We also provide a tracking links for tracking bug reports and feature requests. If you are interested in the development of JBoss AOP, post a message on the forum. If you are interested in translating this documentation into your language, contact us on the developer mailing list.

Commercial development support, production support and training for JBoss AOP is available through JBoss Inc. (see http://www.jboss.org/). JBoss AOP is a project of the JBoss Professional Open Source product suite.

In some of the example listings, what is meant to be displayed on one line does not fit inside the available page width. These lines have been broken up. A '\' at the end of a line means that a break has been introduced to fit in the page, with the following lines indented. So:

   Let's pretend to have an extremely \
      long line that \
      does not fit
   This one is short
         

Is really:

   Let's pretend to have an extremely long line that does not fit
   This one is short
         

Chapter1.What Is Aspect-Oriented Programming?

1.1.What is it?

An aspect is a common feature that's typically scattered across methods, classes, object hierarchies, or even entire object models. It is behavior that looks and smells like it should have structure, but you can't find a way to express this structure in code with traditional object-oriented techniques.

For example, metrics is one common aspect. To generate useful logs from your application, you have to (often liberally) sprinkle informative messages throughout your code. However, metrics is something that your class or object model really shouldn't be concerned about. After all, metrics is irrelevant to your actual application: it doesn't represent a customer or an account, and it doesn't realize a business rule. It's simply orthogonal.

In AOP, a feature like metrics is called a crosscutting concern, as it's a behavior that "cuts" across multiple points in your object models, yet is distinctly different. As a development methodology, AOP recommends that you abstract and encapsulate crosscutting concerns.

For example, let's say you wanted to add code to an application to measure the amount of time it would take to invoke a particular method. In plain Java, the code would look something like the following.

public class BankAccountDAO
{
  public void withdraw(double amount)
  {
    long startTime = System.currentTimeMillis();
    try
    {
      // Actual method body...
    }
    finally
    {
      long endTime = System.currentTimeMillis() - startTime;
      System.out.println("withdraw took: " + endTime);
    }
  }
}

While this code works, there are a few problems with this approach:

  1. It's extremely difficult to turn metrics on and off, as you have to manually add the code in the try>/finally block to each and every method or constructor you want to benchmark.
  2. The profiling code really doesn't belong sprinkled throughout your application code. It makes your code bloated and harder to read, as you have to enclose the timings within a try/finally block.
  3. If you wanted to expand this functionality to include a method or failure count, or even to register these statistics to a more sophisticated reporting mechanism, you'd have to modify a lot of different files (again).

This approach to metrics is very difficult to maintain, expand, and extend, because it's dispersed throughout your entire code base. And this is just a tiny example! In many cases, OOP may not always be the best way to add metrics to a class.

Aspect-oriented programming gives you a way to encapsulate this type of behavior functionality. It allows you to add behavior such as metrics "around" your code. For example, AOP provides you with programmatic control to specify that you want calls to BankAccountDAO to go through a metrics aspect before executing the actual body of that code.

1.2.Creating Aspects in JBoss AOP

In short, all AOP frameworks define two things: a way to implement crosscutting concerns, and a programmatic construct -- a programming language or a set of tags -- to specify how you want to apply those snippets of code.

Let's take a look at how JBoss AOP, its cross-cutting concerns, and how you can implement a metrics aspect in JBoss.

The first step in creating a metrics aspect in JBoss AOP is to encapsulate the metrics feature in its own Java class. Listing Two extracts the try/finally block in Listing One's BankAccountDAO.withdraw() method into Metrics, an implementation of a JBoss AOP Interceptor class.

Listing Two: Implementing metrics in a JBoss AOP Interceptor

01. public class Metrics implements org.jboss.aop.Interceptor
02. {
03.   public Object invoke(Invocation invocation) throws Throwable
04.   {
05.     long startTime = System.currentTimeMillis();
06.     try
07.     {
08.       return invocation.invokeNext();
09.     }
10.     finally
11.     {
12.       long endTime = System.currentTimeMillis() - startTime;
13.       java.lang.reflect.Method m = ((MethodInvocation)invocation).method;
14.       System.out.println("method " + m.toString() + " time: " + endTime + "ms");
15.     }
16.   }
17. }

Under JBoss AOP, the Metrics class wraps withdraw(): when calling code invokes withdraw(), the AOP framework breaks the method call into its parts and encapsulates those parts into an Invocation object. The framework then calls any aspects that sit between the calling code and the actual method body.

When the AOP framework is done dissecting the method call, it calls Metric's invoke method at line 3. Line 8 wraps and delegates to the actual method and uses an enclosing try/finally block to perform the timings. Line 13 obtains contextual information about the method call from the Invocation object, while line 14 displays the method name and the calculated metrics.

Having the metrics code within its own object allows us to easily expand and capture additional measurements later on. Now that metrics are encapsulated into an aspect, let's see how to apply it.

1.3.Applying Aspects in JBoss AOP

To apply an aspect, you define when to execute the aspect code. Those points in execution are called pointcuts. An analogy to a pointcut is a regular expression. Where a regular expression matches strings, a pointcut expression matches events/points within your application. For example, a valid pointcut definition would be "for all calls to the JDBC method executeQuery(), call the aspect that verifies SQL syntax."

An entry point could be a field access, or a method or constructor call. An event could be an exception being thrown. Some AOP implementations use languages akin to queries to specify pointcuts. Others use tags. JBoss AOP uses both. Listing Three shows how to define a pointcut for the metrics example.

Listing Three: Defining a pointcut in JBoss AOP

1. <bind pointcut="public void com.mc.BankAccountDAO->withdraw(double amount)">
2.       <interceptor class="com.mc.Metrics"/>
3. </bind >

4. <bind pointcut="* com.mc.billing.*->*(..)">
5.       <interceptor class="com.mc.Metrics"/>
6. </bind >

Lines 1-3 define a pointcut that applies the metrics aspect to the specific method BankAccountDAO.withdraw(). Lines 4-6 define a general pointcut that applies the metrics aspect to all methods in all classes in the com.mc.billing package.

There is also an optional annotation mapping if you do not like XML. See our Reference Guide for more information.

JBoss AOP has a rich set of pointcut expressions that you can use to define various points/events in your Java application so that you can apply your aspects. You can attach your aspects to a specific Java class in your application or you can use more complex compositional pointcuts to specify a wide range of classes within one expression.

With AOP, as this example shows, you're able to pull together crosscutting behavior into one object and apply it easily and simply, without polluting and bloating your code with features that ultimately don't belong mingled with business logic. Instead, common crosscutting concerns can be maintained and extended in one place.

Notice too that the code within the BankAccountDAO class has no idea that it's being profiled. This is what aspect-oriented programmers deem orthogonal concerns. Profiling is a orthogonal concern. In the OOP code snippet in Listing One, profiling was part of the application code. With AOP, you can remove that code. A modern promise of middleware is transparency, and AOP (pardon the pun) clearly delivers.

Just as important, orthogonal behavior could be bolted on after development. In Listing One, monitoring and profiling must be added at development time. With AOP, a developer or an administrator can (easily) add monitoring and metrics as needed without touching the code. This is a very subtle but significant part of AOP, as this separation (obliviousness, some may say) allows aspects to be layered on top of or below the code that they cut across. A layered design allows features to be added or removed at will. For instance, perhaps you snap on metrics only when you're doing some benchmarks, but remove it for production. With AOP, this can be done without editing, recompiling, or repackaging the code.

Chapter2.Terms

2.1.Overview

The section defines some basic terms that will be used throughout this guide.

Joinpoint

A joinpoint is any point in your java program. The call of a method. The execution of a constructor the access of a field. All these are joinpoints. You could also think of a joinpoint as a particular Java event. Where an event is a method call, constructor call, field access etc...

Invocation

An Invocation is a JBoss AOP class that encapsulates what a joinpiont is at runtime. It could contain information like which method is being called, the arguments of the method, etc...

Advice

An advice is a method that is called when a particular joinpoint is executed, i.e., the behavior that is triggered when a method is called. It could also be thought of as the code that does the interception. Another analogy is that an advice is an "event handler".

Pointcut

Pointcuts are AOP's expression language. Just as a regular expression matches strings, a pointcut expression matches a particular joinpoint.

Introductions

An introduction modifies the type and structure of a Java class. It can be used to force an existing class to implement an interface or to add an annotation to anything.

Aspect

An Aspect is a plain Java class that encapsulates any number of advices, pointcut definitions, mixins, or any other JBoss AOP construct.

Interceptor

An interceptor is an Aspect with only one advice named "invoke". It is a specific interface that you can implement if you want your code to be checked by forcing your class to implement an interface. It also will be portable and can be reused in other JBoss environments like EJBs and JMX MBeans.

Chapter3.Building Aspects

The last chapter had a basic boring introduction to aspects with the lame, commonly used example of metrics. AOP can be applied in a much broader sense than the overused examples of tracing and security and this chapter looks into other more compelling examples of using basic AOP.

3.1.Identifying Aspects

Aspect-Oriented programming is not a replacement for object-oriented programming, but rather a compliment to it. AOPers generally say that OOP solves 90% of problems and AOP solves the 10% of problems that OOP isn't good at. This section of the docbook will expand over time, but let's discuss some ways in which you can identify whether or not AOP is a good solution for a particular problem.

Cross-cutting Concerns

The metrics example in the previous chapter is an example of a cross-cutting concern in its purest form. Sometimes you see structure in your code that can't be expressed as an object because it completly wraps around the method you are invoking. If the behavior in question is something that you want to be able to extend and maintain within its own structure then it may be a candidate for aspectizing.

Layering Based on Deployment

Another place where AOP may be very useful is to layer your applications. Sometimes you want to model a particular service or object that has many configuration options yet you don't want to bloat your service with unmaintainable code. AOP provides a nice way to layer such complex services. JBoss AOP provides a XML configurable mechanism to configure such aspects per deployment. A good example of this is a caching service that might have different locking policies. It is easier to encapsulate such locking policies as aspects so that the base caching code doesn't get polluted with locking concerns. This makes the code easier to maintain.

Transparency

Many times you want to write plain Java code that focuses solely on business or application logic and do not want to introduce any concerns like middleware. AOP allows you to apply things like middleware transparently to your code without polluting your code. Some examples include the transaction demarcation and role-based security features in the JBoss AOP Aspect Library.

3.2.Exception Handling

Metrics and tracing are simple examples of building aspects. Another great simple example is to use AOP for exception handling. For example, SQLException is an exception that contains error information like invalid sql statement or deadlock that is similar per database vendor, but is expressed as different error codes and string messages. You can use AOP to intercept statement execution, catch SQLException errors, and turn them into typed exceptions that application code can handle independent of database vendor. So let's code an example of this.

public class InvalidSQlException extends SQLException
{
   InvalidSQLException(SQLException ex)
   {
       super(ex.getMessage(), ex.getSQLState(), ex.getErrorCode());
   }
}

What we'll do is write an aspect that wraps calls to all java.sql.Statement execute methods and turn them into typed exceptions like the example above. We'll leave some code up to your imagination since such an aspect would be quite long to deal with every error code of every database vendor, but hopefully you can get the idea here.

public class SQLExceptionAspect
{
   public Object handleSqlException(Invocation invocation) throws Throwable
   {
      try
      {
         return invocation.invokeNext();
      }
      catch (SQLException ex)
      {
         if (isVendorInvalidSqlErrorCode(ex.getErrorCode())) throw new InvalidSQLException(ex);
         if (isVendorDeadlockErrorCode(ex.getErrorCode()) throw new SQLDeadlockException(ex);
         ... and so on ...
      }
   }
   ... impl of isVendor methods ...
}

Now that the aspect is defined we use a call pointcut expression to intercept all the execute methods of java.sql.Statement.

<aspect class="SQLExceptionAspect" scope="PER_VM"/>
<bind pointcut="call(* $instanceof{java.sql.Statement}->execute*(..))">
   <advice name="handleSqlException" aspect="SQLExceptionAspect"/>
</bind>

Chapter4.Aspect-Oriented Annotations

Annotations are a new feature of JDK 5.0 that allow you to attach metadata to any Java construct. They allow you to define metadata in a typesafe way and apply it to a class, method, constructor, field, or parameter. For those of you familiar with XDoclet, annotations will be very intuitive to you in that you are used to declaring tags to generate code. The main difference between the two is that annotations are a typed part of the Java language, while XDoclet tags can be mistyped and are harder to create. In a nutshell, JDK 5.0 annotations allow you to define new Java syntax.

AOP provides a unique way of encapsulating behavior and applying it transparently to your application code. If you combine it with annotations, you basically have a very structured, simple way of extending the Java language. The annotation is the syntax, and the aspect provides the functionality for that aspect. This chapter walks through detailed examples on how you can use AOP and annotations to turn your frameworks into Java language features.

4.1.Methods and Annotations

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.

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 ther 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.

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.

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.

<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!

4.2.Fields and Annotations

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.

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.

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.

<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.

4.3.Dependency Injection

Another interesting place where field annotations and AOP can be used is with dependency injection. Dependency injection is about objects declaring what information, configuration, or service references they need, and having the runtime automagically inject those dependencies rather than having your code do explicit lookups on a registry service. In J2EE-land, getting access to a javax.transaction.TransactionManager service is not standardized and is actually different per vendor implementation. Many framework developers need to use the TransactionManager to implement custom transactional services. The use of AOP with field annotations is a great way to provide this dependency injection and to abstract away the details of how a TransactionManager is referenced by components that need it. Let's define an aspect that will inject a reference to a TransactionManager into the value of a field.

First, we must again define our annotation.

package org.jboss.aspects;

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

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

Next we will define the aspect class that will encapsulate the resolving of the TransactionManager. This aspect will be specific to the JBoss application server, but you could define different implementations per vendor.

package org.jboss.aspects;

import org.jboss.aop.joinpoint.*;
import java.lang.reflect.Field;
import javax.transaction.TransactionManager;
import org.jboss.tm.TxManager;

public InjectTMAspect
{
   private TransactionManager tm = TxManager.getInstance();

   public Object access(FieldReadInvocation invocation)
       throws Throwable
   {
      return tm;
   }

   public Object access(FieldWriteInvocation invocation)
       throws Throwable
   {
      throw new RuntimeException(
          "Setting an @Injected variable is illegal");
   }
}

Finally, we have to define the XML binding that will trigger the application of the InjectTMAspect when the @Inject tag is applied to a field. The pointcut expression basically states that for any field of type TransactionManager and tagged as @Inject, apply the InjectTMAspect.

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

  <bind pointcut="field(javax.transaction.TransactionManager *->@org.jboss.aspects.Inject)">
     <advice name="access"
             aspect="org.jboss.aspects.InjectTMAspect"/>
  </bind>
</aop>

Now that the annotation, aspect class, and XML binding have been defined, we can use it within our code.

import javax.transaction.TransactionManager;
import org.jboss.aspects.Inject;

public class MyTransactionalCache
{
   @Inject private TransactionManager tm;
...
}

Chapter5.Mixins and Introductions

When people thing of AOP, they usually think of interception, pointcut expressions, aspects, and advices. AOP isn't just about those things. Another important feature in JBoss AOP is the ability to introduce an interface to an existing Java class in a transparent way. You can force a class to implement an interface and even specify an additional class called a mixin that implements that interface. Very similar to C++'s multiple inheritance. Now, why would you want to use introductions/mixins? That's what this chapter is all about.

5.1.Introducing Introductions

The first thing we'll show here is how to force an existing Java class to implement any interface you want. The JBoss AOP tutorial is a good place to start for an example, so let's grab the code from the introductions tutorial.

The first example we'll show is how to take an existing non-serializable class and make it serializable. This use case may be usable if there's a thirdparty library you don't have the source for, or you want to control whether your class is serializable or not based on how you deploy your application.

public class POJO
{
  private String field;
}

To take this class and make it serializable is very simple. Just the following XML is required:

<introduction class="POJO">
   <interfaces>java.io.Serializable</interfaces>
</introduction>
 

The above XML just states that the AOP framework is to apply the java.io.Serializable interface to the POJO class. You can have one or more interfaces specified with the interfaces element. These interfaces are comma delimited.

If the introduced interfaces have methods not implemented by the class, then the AOP framework with add an implementation of these methods to the class. The methods will delegate to the AOP framework and must be handled/serviced by an interceptor or advice otherwise a NullPointerException will result.

5.2.Mixin It Up

Introducing interfaces only is quite limited when the introduced interfaces have methods that the class doesn't implement as you have to write a lot of generically inclined code that handle these types of method calls within an advice or interceptor. This is where mixins come in. The AOP framework allows you to define a mixin class that implements the introduced interface(s). An instance of this mixin class will be allocated the first time you invoke a method of the introduced interface.

Again, let's steal from the introductions tutorial. We'll take an existing class, force it to implement the java.io.Externalizable interface and provide a class that implements Externalizable

public class POJO
{
  private String field;
}

To take this class and make it externalizable is very simple. Just the following XML is required:

<introduction class="POJO">
   <mixin>
      <interfaces>
         java.io.Externalizable
      </interfaces>
      <class>ExternalizableMixin</class>
      <construction>new ExternalizableMixin(this)</construction>
   </mixin>
</introduction>

The above XML just states that the AOP framework is to apply the java.io.Externalizable interface to the POJO class. You can have one or more interfaces specified with the interfaces element. These interfaces are comma delimited.

The class element defines the mixin class that will implement the externalizable interface and handle serialization of the POJO class.

The construction element allows you to specify Java code that will be used to initialize the mixin class when it is created. JBoss AOP will create a field within the POJO class that will hold the instance of the mixin. This field will be initialized with the Java code you provide in the construction element. The this pointer in the construction above pertains to the POJO class instance.

Finally, you need to implement the mixin class that will handle externalization.

public class ExternalizableMixin implements java.io.Externalizable
{
   POJO pojo;

   public ExternalizableMixin(POJO pojo)
   {
      this.pojo = pojo;
   }

   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
   {
      pojo.stuff2 = in.readUTF();
   }
   public void writeExternal(ObjectOutput out) throws IOException
   {
      out.writeUTF(pojo.stuff2);
   }
}

5.2.1.Multiple Inheritance

One thing that should be noted about mixins is that they allow you to do true multiple inheritance within Java. Yes, it is not explicit multiple inheritance as you must define the inheritance mappings in XML or via an annotation binding, but it does allow you to take advantage of something C++ has had for years. Many thought leaders argue against the use of multiple inheritance, but when used correctly and purely as an abstract mixin, it can be a very useful tool in application design.

5.3.Aspects with APIs

The serialization and externalization examples show previously in this chapter are kinda lame. Sure, you can use introductions/mixins for multiple inheritance, or to do nice tricks like forcing an existing class to be serializable. A more compelling use case is needed to justify this particular feature. This is what this section is about.

The best use cases for introductions/mixins comes when you have an aspect that requires an API for the user to interact with. Many aspects have runtime APIs so that the application developer can interact with a particular aspect to set configuration or to gain added behavior. A good example of an aspect with an API is if we expand on the @Oneway example in the "Aspect-Oriented Annotations" section of this user guide. @Oneway allows you to tag a method as oneway and when you invoke that method it runs in the background. The problem with this example is that you can only run void methods in the background and cannot interact asynchronously with methods that return a value. You have no way of obtaining the return value of an asynchronous call. Let's walk through an example of taking the oneway aspect and addding a runtime API for that aspect so that application developers can obtain method return values asynchronously.

The end goal of this example is to allow an application developer to tag a method as @Asynchronous have the method run in the background, but to provide an API so that the developer can obtain the value of a method return asynchronously. What we'll use here is an introduction and mixin to provide an API to obtain a java.util.concurrent.Future instance (from JDK 5.0 concurrent package) that will allow us to get access to the asynchronous method's return value.

Using the @Asynchronous annotation

public class POJO
{
   @Asynchronous int someMethod() { ... }
}

This is the interface we want to introduce to any class that has a method tagged as @Asynchronous

public interface AsynchronousFacade
{
   java.util.concurrent.Future getLastFuture();
}

So, the user would interact with this asynchronous aspect in the following way.

{
   POJO pojo = new POJO();
   AsynchronousFacade facade = (AsynchronousFacade)pojo;
...
   pojo.someMethod(); // invokes in background
   Future future = facade.getLastFuture();
... do other work...
   // go back and get result. block until it returns.
   int result = (Integer)future.get();
}

The first thing we need to do is define the mixin that will provide Futures. This mixin should also have a private interface so that the asynchronous aspect has a way to set the current invocation's future after it spawns the method invocation to the background. The mixin will be very very simple. It will basically expose a java.lang.ThreadLocal so that the Future can be set and acquired.

public class AsynchMixin implements AsynchronousFacade, FutureProvider
{
    private ThreadLocal currentFuture = new ThreadLocal();

    public Future getLastFuture()
    {
       return (Future)currentFuture.get();
    }

    public void setFuture(Future future)
    {
       currentFuture.set(future);return (Future)currentFuture.get();
    }
}

The FutureProvider is an additional interface introduction that the aspect will use to set the future when after it spawns the task in the background.

public interface FutureProvider
{
   public void setFuture(Future future);
}

Next, let's look at the aspect that will implement the asynchronous behavior. The aspect is made up of an advice that will create a java.util.concurrent.Callable instance so that the current method invocation will run in the background.

public class AsynchAspect
{
   ExecutorService executor = Executors.newCachedThreadPool();

   public Object invokeAsynch(MethodInvocation invocation) throws Throwable
   {
      final Invocation copy = invocation.copy();
      Future future = executor.submit( new Callable()
      {
         public Object call()
         {
            try
            {
               return copy.invokeNext();
            }
            catch (Throwable throwable)
            {
               return throwable;
            }
         }
      });
      FutureProvider provider = (FutureProvider)invocation.getTargetObject();
      provider.setFuture(future);

      return nullOrZero(invocation.getMethod().getReturnType());
   }

   private Object nullOrZero(Class type)
   {
      if (type.equals(long.class)) return 0;
      //... other types ...
      return null;
   }
}

The invokeAsynch advice first copies the invocation. A copy copies the entire state of the invocation objec and remembers exactly in the interceptor/advice chain to continue on when the method is spawned off into a separate thread. The copy allows the current Java call stack to return while allowing the copy to live in a separate thread and continue down the interceptor stack towards the actual method call.

After creating a callable and running the method in a separate thread, the advice gets the target object from the invocation, and typecasts it to FutureProvider so that it can make the future available to the app developer.

So the mixin and aspect are written. The next thing to do is to define an advice binding so that when a method is tagged as asynchronous, the asynch advice will be triggered, and the method will run in the background.

         <aspect class="AsynchAspect" scope="PER_VM"/>
<bind pointcut="execution(!static * *->@Asynchronous(..))">
   <advice name="invokeAsynch" aspect="AsynchAspect"/>
</bind>

After defining the aspect binding, we then come to the introduction definition itself. We want the introduction to be added to any class that has any method tagged as @Asynchronous. The JBoss AOP pointcut expression language has a keyword has to allow for this type of matching. Let's look at the introduction binding.

<introduction expr="has(!static * *->@Asynchronous(..))">
   <mixin>
      <interfaces>AsynchronousFacade, FutureProvider</interfaces>
      <class>AsynchMixin</class>
      <construction>new AsynchMixin()</construction>
   </mixin>
</introduction>

The example is now complete. Introductions/mixins aren't solely limited to pseudo-multiple inheritance and the asynch aspect is a great example of an aspect with a runtime API.

Chapter6.Dynamic AOP

6.1.Hot Deployment

Any joinpoint that has been aspectized by the aopc compiler or by a load time transformation is set up to be able to have advices/interceptors added or removed from it at runtime. This is JBoss AOP's first definition of Dynamic AOP. Using the prepare action allows you to aspectize any joinpoint in your application so that advices/interceptors can be applied later at runtime. The over head of such a massaging of the bytecode is very minimal as it is just an extra boolean expression. The benefits for search an architecture allow you to do things like deploy and undeploy metrics or statistic gathering on a needed basis. If you are using AOP for testing (See "Testing with AOP"), it allows you to deploy/undeploy testing aspects as you run your automated tests on your live system.

6.2.Per Instance AOP

JBoss AOP has the ability to apply interceptors on a per instance basis rather than having interceptors be applied entirely to the class. This is very useful when you have instances of an object that need to behave differently in different circumstances.

A perfect example of this is JBoss Cache AOP (TreeCacheAOP). It uses AOP to prepare classes so that field access may be intercepted. When an object is inserted into the cache, TreeCacheAOP adds field interceptors for that particular instance so that it can do automatic replication across a cluster or to automatically provide transaction properties to the object's state. When the object is removed from cache, the field interceptors are removed from that particular instance.

Chapter7.Integration With Pointcuts

This docbook has talked a lot about building aspects either with regualr aspects, annotations, and introductions. This chapter takes a step back and doesn't talk about building aspects, but rather how you can use plain old pointcuts in your application to provide logical integration points.

7.1.Integration

What you've seen by reading this docbook and the "Reference Manual" on JBoss AOP is that AOP provides a rich pointcut expression language that allows you to intercept various points in the Java language. If you think about it, the pointcut language allows you to turn any point in your Java language into an event. An event that can be caught and handled by any piece of code.

After productizing and shipping an application, sometimes users want to integrate with such "events". They want to be able to hook into different places of your application so that they can trigger things specific to their particular deployment of your product. Using object-oriented techniques to provide these hooks to users would require special gluecode every time a user request like this was made. Also, as more and more of these hooks are exposed through object-orientation, it becomes harder and harder to redesign, refactor, or change APIs as the user base is tightly coupled to existing code.

This is were AOP and pointcuts come in. Instead of writing sometimes complicated gluecode, or refactoring the application to provide such user-request integration points, the application can provide pointcut expressions the user base can use to integrate their specific integration use cases. The application provides logical names to code points as pointcut expressions. The pointcut expression can change over time as the application code is redesigned and/or refactored, but the logical name of the join point/event/integration point stays the same and user hooks don't have to change either. Let's look at an example:

public class BankAccount
{
   public void withdraw(double ammount) {...}
}

Let's say the user of this bank account ERP system wanted to have an email sent to the account holder whenever a successful withdraw was made. The ERP system could provide the hook as a pointcut and then the user can write an email aspect that binds with this pointcut.

<pointcut name="WITHDRAW" expr="execution(public void BankAccount->withdraw(double))"/>

The BankAccount class would be instrumented with AOP hooks. The overhead is quite tiny as only an additional boolean expression is needed to instrument this kind of hook. If the class or method name changes, the user integration code is unaffected as they bind their email hook to the logical pointcut name.

JBoss currently provides integration points in its EJB and MBean layers in such the same way. Recently, BEA Weblogic published AspectJ style pointcuts into the Weblogic runtime so that users could integrate using AspectJ. As AOP becomes more popular you'll see more and more software products offering integration points through pointcut expressions.

Chapter8.Testing with AOP

In the previous sections we talked more about using AOP to build and design applications and services. This chapter focuses on how you can use AOP to test your applications.

8.1.Testing Exception Handling

The sign of a well design application is how gracefully it can handle errors. To be able to handle errors gracefully in all circumstances though requires lots and lots of testing. You have to test that your application is catching and handling exceptions carefully. Sometimes its hard to produce error conditions because your code is interacting with a third party library, or third party service like a database or something. You can write complex mock objects in these scenarios, but let's see how you can create these error conditions in an easier and more flexible way with JBoss AOP.

The example scenario we'll give is an application that needs to be tested on whether or not it handles an Oracle database deadlock exception gracefully. What we'll do is write an advice that intercepts calls to java.sql.Statement execute methods and always throw a SQLException with the appropriate deadlock error code.

public class SQLDeadlockExceptionInjector
{
   public Object throwDeadlock(Invocation invocation) throws Throwable
   {
      throw new SQLException("Oracle Deadlock", "RETRY", ORACLE_DEADLOCK_CODE);
   }
}

What's great about the JBoss AOP approach to testing exception handling is that you can use it on a live system and change how your tests run by deploying and/or undeploying certain aspects at runtime during your automatic testing phase. Let's apply this aspect.

<aspect class="SQLDeadlockExceptionInjector/>
<bind pointcut="call(* $instanceof{java.sql.Statement}->execute*(..))">
   <advice name="throwDeadlock" aspect="SQLDeadlockExceptionInjector"/>
</bind>

So, the above binding well throw a deadlock exception every time an execute method of a Statement is invoked. This example is a bit limited though. Maybe not all code paths can handle deadlock exceptions, or they should not handle deadlock exceptions and just rollback and such. The pointcut expression language allows you to do more fine grain application of this particular exception aspect. Let's say that only our BankAccount class is designed to successfully recover from a Oracle deadlock exception. We can change the pointcut expression to be as follows:

<bind pointcut="call(* $instanceof{java.sql.Statement}->execute*(..)) AND /
                                                           within(BankAccount)">
   <advice name="throwDeadlock" aspect="SQLDeadlockExceptionInjector"/>
</bind>

The difference in this expression is the within. It is saying that any call to execute methods that are within the BankAccount class. We can even get more fine grained than that. We can even specify which methods within BankAccount the exception aspect should be applied to.

<bind pointcut="call(* $instanceof{java.sql.Statement}->execute*(..)) AND /
                                                  withincode(void BankAccount->withdraw(double))">
   <advice name="throwDeadlock" aspect="SQLDeadlockExceptionInjector"/>
</bind>

In the above listing the withincode keyword specificies to match the calling of any execute method that is invoked within the BankAccount.withdraw() method.

AOP gives you a lot of flexibilty in testing error conditions, JBoss AOP in particular. Because JBoss AOP allows you to hotdeploy

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值