http://www.developer.com/

Back to article

Aspect-Oriented Programming (AOP) with Spring.Net


January 8, 2009

Introduction

Aspect-Oriented Programming (AOP) allows developers to achieve greater modularity in their applications by allowing the separation of cross-cutting concerns. In other words, with AOP it is easy to separate out the logic for those repetitive and messy tasks such as logging, security, and error handling that span the entire application. In this article, I will discuss basic AOP concepts and their implementation using Spring.Net, a popular open source application framework. I will also walk you through several examples of AOP with Spring.Net.

Aspect-Oriented Programming

Aspect-Oriented Programming (AOP) is defined as a programming paradigm that promotes application modularity through the separation of cross-cutting concerns. To understand what this means, you need to start with understanding the problem it solves.

Object-Oriented Programming (OOP) has been a great innovation in the world of software development. Built on the idea that an application is simply a collection of interacting entities, OOP allows for very modular and easily conceptualized applications. However, OOP is not perfect. Sometimes, there are secondary pieces of logic that do not fit into the actions of any specific entity within an application and instead affect many unrelated parts of the application. These pieces of logic that defy the application model are known as cross-cutting concerns.

Logging is a typical example of a cross-cutting concern. When you need to add or change the basic logging within an application, you generally need to change the logic for multiple entities. This can be messy because it often includes a lot of scattered, repetitive code that requires more effort to develop, test, and maintain. AOP attempts to alleviate this problem by separating out the cross-cutting logic into its own module or modules and applying that logic transparently across the various entities of an application without needing to change the logic for each entity directly.

In AOP terminology, a piece of cross-cutting code you want to apply across the application is known as advice. So, for your logging example, you might have some advice that logs entering and exiting methods. The points in the code where you want the advice to run are known as pointcuts. For your logging example, the pointcuts would be directly before and after the monitored methods are executed. This may be for all methods of an application or only a certain subset that meet some specified criteria. Finally, the combination of advice and pointcut is called an aspect. For your example, youwe would have a method logging aspect. Add a few more aspects to the application and you have aspect-oriented programming.

This is a fairly abbreviated explanation of AOP, but it should be a decent start for the purposes of this article. If you want to learn more and delve into some other AOP concepts, check out http://en.wikipedia.org/wiki/Aspect-oriented_programming and other various resources on the Net.

Aspect-Oriented Programming with Spring.Net

So, where does Spring.Net fit in? Well, Spring.Net is an open source framework that provides a toolset of components that help developers implement industry best practices such as Dependency Injection and Aspect-Oriented Programming.

There are several other worthwhile AOP frameworks out there like PostSharp, AspectDNG, and Aspect.Net. Some even afford greater performance because they implement rather powerful compile-time IL weaving as opposed to runtime weaving like Spring.Net uses. These frameworks, however, do not have the breadth of tools that Spring.Net has.

AOP is not the only purpose for using Spring or even the primary purpose in many cases. Chances are that you have heard of or are already using Spring.Net for your Dependency Injection needs by using Spring.Net's simple yet powerful Inversion of Control (IoC) container. The AOP framework is designed to complement the IoC container and create a very robust overall solution to wiring your application together.

Before you continue, it is important that you understand Dependency Injection with Spring.Net because this article builds on this core concept of Spring.Net. If you need a good introduction or a quick refresher, here is a link to my earlier article on the subject: "Dependency Injection with Spring.Net."

There is also a plethora of documentation and reference links to be found on the Sprint.Net website, www.springframework.net.

Implementing AOP in Spring.Net

Now, you dive into some examples of Spring.Net AOP in action. For this article, you will be working with the following generic ExampleService class and touch on how to create and apply some basic aspects in Spring.Net through the use of advice and pointcuts.

Listing 1: Example Service Class

public interface ExampleService {

   Object getObject(long id);
   void saveObject(Object obj);
   void runOperation(Object obj);
   void runOperationWithError(Object obj);
}

public class ExampleServiceImpl : ExampleService {

   public Object getObject(long id) {
      // Run some logic to retrive an object
      return "testObject";
   }

   public void saveObject(Object obj) {
      // Run some logic to save an object
   }

   public void runOperation(Object obj) {
      // Run some logic
   }

   public void runOperationWithError(Object obj) {
      // Run some logic and throw an error...
      throw new ApplicationException("This method threw
                                      an error!!!");
   }
}

Note that there are many other AOP concepts to explore in Spring.Net and this sample is far from a comprehensive showcase of everything Spring.Net can do, but it is a good introduction.

To download the example code in this article, click here: Download Code Example.

Advice

Remember from before that advice is the cross-cutting code that we want to run transparently at several scattered points in the code. In Spring.Net, advice is applied at the method level and there are a few flavors of advice you can choose from.

Before Advice

Before advice is the most basic type of advice. It is a piece of cross-cutting code that runs before a method executes. Look at the following example of some before advice that logs the entrance to a method.

Listing 2: Before Advice Code Example: Method Logging

public class MethodLoggingBeforeAdvice : IMethodBeforeAdvice {

   public void Before(MethodInfo method, Object[] args,
      Object target) {

      // Log method start
      Console.Out.WriteLine(
         "MethodLoggingBeforeAdvice: Entering method '"
         + method.Name + "'");

      // Log method arguments
      for (int i = 0; i < args.Length; i++) {
         Console.Out.WriteLine(
            "MethodLoggingBeforeAdvice: Argument " + (i + 1)
            + " - " + args[0].ToString());
      }
   }
)

This advice will simply output the name of the executed method and the arguments passed in. This is a common example of some information that could be useful when debugging an application. (Note that, for the purposes of simplicity, you are just outputting the messages to the console. In a real application, you would want to use a more robust logging solution, such as log4net.)

A key point of this example is the implementation of the interface Spring.Aop.IMethodBeforeAdvice. This allows Spring.Net to recognize this code as before advice and run it where specified. To specify where this advice is applied, you turn to the core of any Spring.Net application, the XML configuration as defined in the App.config (or Web.config for web apps).

Listing 3: Before Advice Configuration Example: Method Logging

<!-- Before Advice Example: Method Logging -->
<object id="methodLoggingBeforeAdvice"
   type="SpringAOPExample.Aspects.MethodLoggingBeforeAdvice" />

<!-- Example Service Proxy w/ Advice Applied -->
<object id="exampleService"
   type="Spring.Aop.Framework.ProxyFactoryObject">
   <property name="target" ref="exampleServiceTarget" />
   <property name="interceptorNames">
      <list>
         <value>methodLoggingBeforeAdvice</value>
      </list>
   </property>
</object>

<object id="exampleServiceTarget"
   type="SpringAOPExample.Service.Impl.ExampleServiceImpl">
   <!-- Note: This is just a standard service object. Inject
        dependencies as needed (ie. DAOs, Other Services). -->
</object>

What we have done here is set up an AOP proxy for the example service and specified the before advice to intercept any call to a method within the example service. So, whenever one of the methods inside the example service is called, the before advice will run first and write the method logging information.

A key concept here is that you can apply this advice to multiple services if you want. In this example, you only apply the method logging advice to a single service. However, you could just as easily apply the same advice to other services at the same time, even if the services were completely unrelated. And, the great thing is that this is all achieved with configuration and not directly within the code. This reduces coupling because the code itself doesn't need to know who exactly it will be interacting with and can avoid unnecessary dependencies.

After Advice

After advice is fairly self-explanatory after looking at before advice. Instead of running the cross-cutting code before a method executes, after advice runs after the method executes.

Listing 4: After Advice Code Example: Method Logging

public class MethodLoggingAfterAdvice : IAfterReturningAdvice {

   public void AfterReturning(
      Object returnValue, MethodInfo method,
      Object[] args, Object target) {

      // Log method exit
      Console.Out.WriteLine("MethodLoggingAfterAdvice:
         Exiting method '" + method.Name + "'");

      // Log method results
      if (returnValue != null) {
         Console.Out.WriteLine("MethodLoggingAfterAdvice:
            Returned " + returnValue.ToString());
      }
   }
}

This example code logs the end of the method and the return value (if one is returned). Also, in the same way that before advice implements a special interface, this code implements the Spring.Aop.IMethodAfterAdvice interface indicating that it is after advice.

For the configuration, after advice is fairly similar to before advice. Just insert the after advice as the interceptor instead of the before advice.

Listing 5: After Advice Configuration Example: Method Logging

<!-- After Advice Example: Method Logging -->
<object id="methodLoggingAfterAdvice"
   type="SpringAOPExample.Aspects.MethodLoggingAfterAdvice" />

<!-- Example Service Proxy w/ Advice Applied -->
<object id="exampleService"
   type="Spring.Aop.Framework.ProxyFactoryObject">
   <property name="target" ref="exampleServiceTarget" />
   <property name="interceptorNames">
      <list>
         <value>methodLoggingAfterAdvice</value>
      </list>
   </property>
</object>

<object id="exampleServiceTarget"
   type="SpringAOPExample.Service.Impl.ExampleServiceImpl">
   <!-- Note: This is just a standard service object. Inject
        dependencies as needed (ie. DAOs, Other Services). -->
</object>
Throws Advice

The next type of advice you will look at is throws advice. Unlike the other types of advice, throws advice is not run every time a method is called. Instead, it runs only when an exception is thrown in the method. Throws advice is useful whenever you want to set up some special error handling or error logging and apply it across your application.

Listing 6: Throws Advice Code Example: Error Handling

public class ErrorHandlingThrowsAdvice : IThrowsAdvice {

   public void AfterThrowing(ApplicationException ex) {

      // Do some special error handling for an application
      // exception...

      // Log the error
      Console.Error.WriteLine("Method threw an application
                               exception: "
         + ex.Message);
   }

   public void AfterThrowing(Exception ex) {

      // Do some general error handling...

      // Log the error
      Console.Error.WriteLine("Method threw a general
                               exception: "
         + ex.Message);
   }
}

Notice that in this example there are two implementations of the AfterThrowing() method with different exception types. With throws advice, you can specify which type or types of exceptions to handle. In this example, if an ApplicationException is thrown, the first method will run; if some other exception is thrown, the second method will run. Also, if you want, you can remove the second generic Exception method and then this advice will only run if an ApplicationException is thrown and be ignored if any other type of exception is thrown. This is useful if you only want to run special error handling for a certain exception type.

Also, as with other advice, throws advice implements the Spring.Aop.IThrowsAdvice interface in the same manner that the other types of advice implement their own interfaces.

As far as the configuration of throws advice goes, it is pretty much the same deal as before and after advice.

Listing 7: Throws Advice Configuration Example: Error Handling

<!-- Throws Advice Example: Error Handling -->
<object id="errorHandlingThrowsAdvice"
   type="SpringAOPExample.Aspects.ErrorHandlingThrowsAdvice" />

<!-- Example Service Proxy w/ Advice Applied -->
<object id="exampleService"
   type="Spring.Aop.Framework.ProxyFactoryObject">
   <property name="target" ref="exampleServiceTarget" />
   <property name="interceptorNames">
      <list>
         <value>errorHandlingThrowsAdvice</value>
      </list>
   </property>
</object>

<object id="exampleServiceTarget"
   type="SpringAOPExample.Service.Impl.ExampleServiceImpl">
   <!-- Note: This is just a standard service object. Inject
        dependencies as needed (ie. DAOs, Other Services). -->
</object>
Around Advice

The last and most powerful type of advice you will look at is also the root type of advice on which the other types of advice are built. Around advice does not specify where your advice will be run in relation to a method; instead, it wraps the method execution and allows you to control where around the method execution you want to run your advice code.

Listing 8: Around Advice Code Example: Performance Logging

public class PerformanceLoggingAroundAdvice :
   IMethodInterceptor {

   public object Invoke(IMethodInvocation invocation) {

      Stopwatch stopwatch = new System.Diagnostics.Stopwatch();

      // Start timing the method
      stopwatch.Start();

      // Execute the method
      Object returnValue = invocation.Proceed();

      // Stop timing the method
      stopwatch.Stop();

      // Log method runtime
      Console.Out.WriteLine("ExampleAroundAdvice: '"
         + invocation.Method.Name + "' took "
         + stopwatch.ElapsedMilliseconds
         + " milliseconds to run.");

      return returnValue;
   }
}

In this example, you use the system stopwatch to time how long it takes to run the method; this gives you a simple performance monitor at the method level.

Note the use of the invocation.Proceed() method to execute the advised method. Around advice gives you control of when and even if the advised method is called. It also gives you control of what to do with the result of the method execution, allowing you to modify or filter the result if needed. This combination affords you great flexibility in setting up your advice.

In fact, with the power of around advice, you could just ignore the other advice types and use around advice to implement before, after, and throws advice. This is not really recommended, though, because the extra power of around advice comes at the cost of requiring you to make sure you call invocation.Proceed() and return the results properly for every piece of advice. You can avoid the extra coding and testing for potential errors by using one of the more specific implementations.

Also, note that instead of implementing a specific Spring.Net interface, around advice implements the AopAlliance.Intercept.IMethodInterceptor interface, which is a standard AOP interface meant to standardize the various AOP implementations. The other types of advice (before, after, and throws) are really just more specific implementations of this general IMethodInterceptor interface.

And again, as far as the configuration of around advice goes, it is pretty much the same deal as the other types of advice.

Listing 9: After Advice Configuration Example: Method Logging

<!-- Around Advice Example: Performance Logging -->
<object id="performanceLoggingAroundAdvice"
   type="SpringAOPExample.Aspects.PerformanceLoggingAroundAdvice" />

<!-- Example Service Proxy w/ Advice Applied -->
<object id="exampleService"
   type="Spring.Aop.Framework.ProxyFactoryObject">
   <property name="target" ref="exampleServiceTarget" />
   <property name="interceptorNames">
      <list>
         <value>performanceLoggingAroundAdvice</value>
      </list>
   </property>
</object>

<object id="exampleServiceTarget"
   type="SpringAOPExample.Service.Impl.ExampleServiceImpl">
   <!-- Note: This is just a standard service object. Inject
        dependencies as needed (ie. DAOs, Other Services). -->
</object>
Pointcuts

So, now you know how to create some advice and apply it to the methods of one or more services. But, what if you don't want to apply the advice to all the service methods? For example, what if your advice is a security check algorithm that only needs to run for protected methods, such as saving a new or updated object?

This is where pointcuts come into the picture. A pointcut allows you to specify criteria for when to execute advice so you can direct advice to only execute for certain methods.

Start by creating some generic security advice to run before secured methods.

Listing 10: Pointcut Code Example: Security Check Before Advice

public class SecurityCheckBeforeAdvice : IMethodBeforeAdvice {

   public void Before(MethodInfo method, Object[] args,
      Object target) {

      // Put your security logic here
      // Throw security exception if there is an error...

      // Log method start
      Console.Out.WriteLine(
         "SecurityCheckBeforeAdvice: Access granted for '"
         + method.Name + "' method");
   }
}

As you can see, this is just a standard piece of before advice. There is nothing special in the advice code to specify any pointcuts. Instead, pointcuts are set up in the configuration.

Listing 11: Pointcut Configuration Example: Security Check Before Advice

<!-- Pointcut Example: Security Check w/ Before Advice -->
<object id="securityCheckBeforeAdvice"
   type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor">
   <!-- Only run for methods starting with "save" -->
   <property name=" value="save" />
   <property name="advice" ref="securityCheckBeforeAdviceTarget" />
</object>

<object id="securityCheckBeforeAdviceTarget"
   type="SpringAOPExample.Aspects.SecurityCheckBeforeAdvice" />

<!-- Example Service Proxy w/ Advice Applied -->
<object id="exampleService"
   type="Spring.Aop.Framework.ProxyFactoryObject">
   <property name="target" ref="exampleServiceTarget" />
   <property name="interceptorNames">
      <list>
         <value>performanceLoggingAroundAdvice</value>
      </list>
   </property>
</object>

<object id="exampleServiceTarget"
   type="SpringAOPExample.Service.Impl.ExampleServiceImpl">
   <!-- Note: This is just a standard service object. Inject
        dependencies as needed (ie. DAOs, Other Services). -->
</object>

Here, you use the Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor class to specify some criteria for running the advice. You define a method pattern to match via the "pattern" property. For this example, the advice will be applied only when the method name begins with "save". So in effect, the security check advice will execute only when the user runs a method like saveObject() where you want security applied and not for other methods like getObject() where everyone has access and you don't need security.

Stacking Advice

For the last topic, let me talk about stacking advice. So far, you have only been applying one piece of advice at a time, but there are many cases where you will want to run several pieces of advice on the same methods. Well, in Spring.Net this is not a problem. Just add all the pieces of advice you want to run to the "interceptorNames" list in the configuration.

Listing 12: Advice Stacking Example

<!-- Example Service Proxy w/ Advice Applied -->
<object id="exampleService"
   type="Spring.Aop.Framework.ProxyFactoryObject">
   <property name="target" ref="exampleServiceTarget" />
   <property name="interceptorNames">
      <list>
         <value>methodLoggingBeforeAdvice</value>
         <value>methodLoggingAfterAdvice</value>
         <value>performanceLoggingAroundAdvice</value>
         <value>errorHandlingThrowsAdvice</value>
         <value>securityCheckBeforeAdvice</value>
      </list>
   </property>
</object>

<object id="exampleServiceTarget"
   type="SpringAOPExample.Service.Impl.ExampleServiceImpl">
   <!-- Note: This is just a standard service object. Inject
        dependencies as needed (ie. DAOs, Other Services). -->
</object>

Now, all of the advice will run when you call the various example service functions. Here is a quick test run of everything you have covered in this article; the resulting output is shown in Figure 1.

Listing 13: Example Test Code

// Get Spring.Net context
using (IApplicationContext ctx = ContextRegistry.GetContext()) {

   // Get the example service
   ExampleService exampleService =
      (ExampleService)ctx.GetObject("exampleService");

   // Run some operations to see the aspects in action

   Console.WriteLine("Example: Get Object");
   exampleService.getObject(25);

   Console.WriteLine("\nExample: Save Object");
   exampleService.saveObject("testSaveObject");

   Console.WriteLine("\nExample: Run Operation");
   exampleService.runOperation("testRunObject");

   Console.WriteLine("\nExample: Run Operation With Error");
   exampleService.runOperationWithError("testRunErrorObject");
}



Click here for a larger image.

Figure 1: Example Test Output

Conclusion

I hope this article helped introduce you to Aspect-Oriented Programming with Spring.Net. AOP is a wonderful tool that really enhances object-oriented concepts by allowing greater application modularity through the separation of cross-cutting concerns. In this article, I covered some of the basic concepts of AOP and Spring.Net AOP including advice, pointcuts, and aspects and how to use them for some common scenarios such as logging, error handling, performance monitoring, and security. There is much more out there, though. Be sure to check out more advanced AOP concepts like introductions and also keep in mind the many other uses for AOP like caching and retry rules. Next time you are developing or maintaining an application keep AOP in mind. AOP is a powerful tool to have in any developer's coding arsenal.

Code Examples

To download the example code in this article, click here: Download Code Example.

About the Author:

David Consdorf resides in Chicago, Illinois. He graduated with a Bachelor's Degree in Computer Science from the University of Illinois Urbana-Champaign. For the last four years, he has been developing Java and ASP.Net web applications as a software consultant working in the financial, manufacturing, and government sectors in the Greater Chicagoland area.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date