Wednesday, June 29, 2011

Writing method interceptors using Spring AOP

Spring is a great Java technology that has become a very popular application framework during the past few years. My intention is not to go through the whole concepts and architectural details of the framework, because that kind of information can be easily looked up starting at http://www.springframework.org. As the article title indicates, I intend to provide hands-on examples showing the minimal requirements to bundle certain Spring functionalities in your Java applications. So, because I will not go into the “what’s under the hood” approach unless absolutely necessary, most of the examples might require the knowledge of basic Spring concepts. Anyway, the basic idea is that you must RTFM before deciding if Spring is right for your application.
The first example is a short look at a simple method intercepting strategy. You can read all about this and the whole Spring AOP API here.The source code for this example can be found here. In the project directory run ant compile run to launch the application.
For the beginning let’s consider that we have the service MyService that that has a method doSomething() performing an operation which takes a long time to execute. Below you can see the (pretty dumb) code of this method.
public class MyService {
  public void doSomething() {
    for (int i = 1; i < 10000; i++) {
      System.out.println("i=" + i);
    }
  }
}

In order to print out the performance statistics on the method call, we must first implement the interceptor that actually calculates the execution time for this method. To do this we need to implement the org.aopalliance.intercept.MethodInterceptor interface shipped with Spring. This is actually a callback providing access to the actual call of the methods of our service. The JavaDoc for this interface is here.

public class ServiceMethodInterceptor implements MethodInterceptor {
  public Object invoke(MethodInvocation methodInvocation) throws Throwable {
    long startTime = System.currentTimeMillis();
    Object result = methodInvocation.proceed();
    long duration = System.currentTimeMillis() - startTime;
    Method method = methodInvocation.getMethod();
    String methodName = method.getDeclaringClass().getName()  
                                   + "." + method.getName();
    System.out.println("Method '" + methodName  
                         + "' took " + duration + " milliseconds to run");
    return null;
  }
}

Next we need to proxy our service in order to obtain an instance whose methods are being intercepted by our ServiceMethodInterceptor. To achieve this, all it takes is a little magic in Spring’s bean configuration file, as you can see below.

<beans>
  <bean id="myService" class="com.test.MyService">
  </bean>

  <bean id="interceptor" class="com.test.ServiceMethodInterceptor">
  </bean>

  <bean id="interceptedService" class="org.springframework
                      .aop.framework.ProxyFactoryBean">
    <property name="target">
      <ref bean="myService"/>
    </property>
    <property name="interceptorNames">
      <list>
        <value>interceptor</value>
      </list>
    </property>
  </bean>
</beans>

The key in this XML snippet is Spring’s built-in class org.springframework.aop.framework.ProxyFactoryBean which provides the actual proxying of our service. In order to obtain the desired effect we must set the target and interceptorNames properties for this bean. The target property represents the name of the bean that we want to proxy, which in our case is the myService bean. The interceptorNames property holds a list of bean names that will be used as interceptors for the proxied bean. So, yes, you can define more than one interceptor for your bean.
As everything seems to be packed pretty nice, all we need to do now is to have our service instantiated using Spring and call it√Ęs doSomething method.

public class Test {
  public static void main(String[] args) {
    ApplicationContext ctx =  
           new ClassPathXmlApplicationContext("com/test/applicationContext.xml");
    MyService myService = (MyService)ctx.getBean("interceptedService");
    myService.doSomething();
  }
}

So we need to look up the interceptedService bean in order to get the proxied service, but if we choose to remove the performance monitor we can simply lookup the initial myService bean.
Normally, after the method doSomething has run, you should see, as the last output line, something like this:
Method 'com.test.MyService.doSomething' took 281 milliseconds to run

Except from the MethodInterceptor Spring also offers other method interception strategies. For example you can choose to handle a method execution right before or immediately after the actual call, or when an exception is thrown during the execution of your method. The reference documentation about these types of interceptors that Spring offers is available here.

Please note that basic performance monitoring can also be achieved by using Spring’s built-in PerformanceMonitorInterceptor. We used this logic just as a sample for method intercepting, but as your intuition might tell you, this is just one of the many things you can do with this feature of Spring. For example, if you need to implement a fine-grained security module, you might choose not to allow the method call to execute if the user does not have rights on the business method. So, basically, you will have to see for yourself how you can use this functionality in your application.
I hope you find this article useful.

No comments:

Post a Comment

Chitika