Simple AOP: integrating interceptors into Windsor

February 20, 2009 at 3:47 PMAndre Loker

Aspect oriented programming (AOP) allows us to keep implement different concerns in isolation. In this article series I’ll describe ways to make use of AOP without much hassle. In the previous article of this serious we learned how to intercept method invocations using DynamicProxy2 and IInterceptors. However, we had to create the proxy instances manually. If you use Castle Windsor you can let the IoC container create the proxies and inject the interceptors.

I assume that you have used Castle Windsor or a different IoC container before. If not, check why you should use dependency injection.

Luckily Windsor integration of IInterceptors is a snap. In fact, you don’t have to add anything, it’s already been built in, because it has proven to be very useful. Because Windsor controls the instantiation of the registered components it was a logical consequence to add optional proxy creation and interface injection into that process. The Castle people are know what they do!

Basically, Windsor offers three ways to define interceptors that you want it to inject into components, ranging from rather static to fully dynamic.

Static injection with attributes

If all you want is the separation of concerns that injectors offer you and know which classes those aspects need to be applied to you can simply use the InterceptorAttribute.

Let’s start with a simple service interface and its implementation:

   1: public interface IService {
   2:     void DoSomething();
   3: }
   4:  
   5: public class Service : IService {
   6:     public void DoSomething() {
   7:         Console.WriteLine("Doing something");
   8:         throw new InvalidOperationException("Some exception thrown in DoSomething");
   9:     }
  10: }

And here’s the code that sets up Windsor for this service and creates an instance.

   1: var container = new WindsorContainer();
   2: container.Register(
   3:     Component.For<IService>().ImplementedBy<Service>()
   4: );
   5:  
   6: var service = container.Resolve<IService>();
   7: service.DoSomething();

As expected, running this piece of code will print “Doing something” and than fail with an uncaught exception. Now let’s say we want to add an aspect to the code that – in this simple case – prints a message to the console whenever an intercepted method throws an exception. Here’s the simple exception handling aspect from the previous article, which catches exception, prints an information message and optionally re-throws it.

   1: public class ExceptionAspect : IInterceptor {
   2:     public bool EatAll { get; set; }
   3:  
   4:     public void Intercept(IInvocation invocation) {
   5:         try {
   6:             invocation.Proceed();
   7:         } catch (Exception e) {
   8:             Console.WriteLine("{0} caught: {1}", e.GetType(), e.Message);
   9:             if (!EatAll) {
  10:                 throw;
  11:             }
  12:         }
  13:     }
  14: }

 

 

Two automatically inject this aspect into the Service component you only need to add two minor modifications:

1. Register the ExceptionAdvice as a component:

   1: container.Register(
   2:     Component.For<ExceptionAspect>()
   3:     .Parameters(Parameter.ForKey("EatAll").Eq("true"))
   4: );

 

2. Add the Castle.Core.InterceptorAttribute to the Service component:

   1: [Interceptor(typeof(ExceptionAspect))]
   2: public class Service : IService{
   3:     public void DoSomething() {
   4:         Console.WriteLine("Doing something");
   5:         throw new InvalidOperationException("Some exception thrown in DoSomething");
   6:     }
   7: }

This will tell Windsor to automatically create a proxy for the Service component and inject the interceptor of the given type (ExceptionAspect). Running the code now leads to the expected output:

image

Instead of a type you can also provide the key of a component to the InterceptorAttribute.

Simple dynamic injection

 

 

 

If you cannot or don’t want to use attributes – e.g. because you need to disable or enable certain aspects during configuration time – you can of course configure the Windsor container manually.

Let’s add a second aspect, namely a simple logging aspect that prints a message before and after the execution of intercepted methods. This is what our aspect looks like:

   1: public class LoggingAspect : IInterceptor {
   2:     public void Intercept(IInvocation invocation) {
   3:         Console.WriteLine("SomeInterceptor: Before method");
   4:         try {
   5:             invocation.Proceed();
   6:         } finally {
   7:             Console.WriteLine("SomeInterceptor: After method");
   8:         }
   9:     }
  10: }

Now we need to tell Windsor to inject the LoggingAspect into our Service. As with the ExceptionAspect we need to register the advice as a component:

   1: container.Register( Component.For<LoggingAspect>() );

Now we only need to change the registration of the service to inlcude the given aspect:

   1: container.Register(
   2:     Component
   3:     .For<IService>()
   4:     .ImplementedBy<Service>()
   5:     .Interceptors(InterceptorReference.ForType<LoggingAspect>()).Anywhere
   6: );

 

The only change is shown in line 5. The Interceptors() method expects an array of interceptors to apply to the component, Anywhere tells Windsor that we are not interested in the order in which multiple interceptors are applied (see below).

Running the application now yields:

image

 

If you configure the container in source code as shown above you can decide at configuration time which interceptors to inject. Of course, the interceptors can also be configured using an external XML file for full configurability.

If you configure multiple interceptors sometimes the order in which they are applied does matter. For example, assume you have an interceptor that caches method results and another one that secures the method for authorized access. In this case you will want the security interceptor to be applied before the caching interceptor, otherwise unauthorized user might see cached values that they are not allowed to see.  Windsor allows you to explicitly state the relative or absolute order of the configured interceptors.

  • ...Interceptors(interceptors).First
    The defined interceptors are prepended to the current list of interceptors.
  • ...Interceptors(interceptors).Last
    The defined interceptors are append to the current list of interceptors.
  • ...Interceptors(interceptors).AtIndex(x)
    The defined interceptors are inserted at the given index of the current list of interceptors.
  • ...Interceptors(interceptors).Anywhere 
    You don’t care where the interceptors are added.

Fully dynamic injection

You can even go one step further. Instead of defining the interceptors explicitly for the different components, Windsor allows you to install a hook that allows you to define the interceptors for each component on the fly during creation.

You can install that hook by implementing the interface IModelnterceptorsSelector which exposes two methods:

   1: public interface IModelInterceptorsSelector {
   2:     bool HasInterceptors(ComponentModel model);
   3:     InterceptorReference[] SelectInterceptors(ComponentModel model);
   4: }

Before a component is activated, Windsor calls HasInterceptor with the component model. If it returns true Windsor will invoke SelectInterceptors to request the actual interceptor references.

As an exammple, we’ll use another interceptor – TransactionAspect – that is injected with an IModelInterceptorSelector. First, here’s the interceptor:

   1: public class TransactionAspect : IInterceptor {
   2:     public void Intercept(IInvocation invocation) {
   3:         try {
   4:             Console.WriteLine("Opening transaction");
   5:             invocation.Proceed();
   6:             Console.WriteLine("Commit");
   7:         } catch (Exception e) {
   8:             Console.WriteLine("Rollback");
   9:             throw;
  10:         }
  11:     }
  12: }

And here’s our implementation of the IModelInterceptorsSelector:

   1: public class MyInterceptorSelector : IModelInterceptorsSelector {
   2:     public bool HasInterceptors(ComponentModel model) {
   3:         return typeof(TransactionAspect) != model.Implementation &&
   4:             model.Implementation.Namespace.StartsWith("SimpleAopWindsor");
   5:     }
   6:  
   7:     public InterceptorReference[] SelectInterceptors(ComponentModel model) {
   8:         return new[] { InterceptorReference.ForType<TransactionAspect>() };
   9:     }        
  10: }

As you see there’s nothing fancy going on here. HasInterceptors returns true for types in the SimpleAopWindsor namespace that are not TransactionAspect. The check for TransactionAspect is required in this specific case to prevent infinite recursion – otherwise, the TransactionAspect interceptor would be applied to itself.

SelectInterceptors simply returns an array of InterceptorReferences: in this case it contains only one element, ie. a reference to TransactionAspect.

Of course, we need to register the TransactionAspect as a component in Windsor:

   1: container.Register(
   2:     Component.For<TransactionAspect>()                
   3: );

And finally, we need to tell Windsor to use our interceptors selector.

   1: container.Kernel.ProxyFactory.AddInterceptorSelector(
   2:     new MyInterceptorSelector()
   3: );

If we run the application now we’ll see this:

image

What’s important here is the fact that our LoggingAspect and ExceptionAspect seem to be gone. This is important: if IModelInterceptorsSelector.SelectInterceptors returns a non-null array, only those interceptor will be used. Interceptors configured using one of the previously described methods (attributes or registration) will be ignored. If we want those interceptors to be applied as well our IModelnterceptorsSelector needs to return those references explicitly, eg. like this:

   1: public InterceptorReference[] SelectInterceptors(ComponentModel model) {
   2:     var interceptors = new List<InterceptorReference>(model.Interceptors.Count + 1);
   3:     // add all interceptors configured otherwise
   4:     foreach (InterceptorReference inter in model.Interceptors) {
   5:         interceptors.Add(inter);
   6:     }
   7:     // add an additional interceptor
   8:     interceptors.Add(InterceptorReference.ForType<TransactionAspect>());
   9:     
  10:     return interceptors.ToArray();
  11: }

Now our application uses all interceptors:

image

Conclusion

As you’ve seen there are many different ways to inject inspectors into components in a Windsor container, ranging from static definition of inspectors using attributes to fully dynamic injection using IModelInterceptorsSelector.

What’s missing in comparison toclassic” AOP frameworks such as AspectJ? Pointcuts of course! With IModelInterceptorsSelector we are able to choose which types get interceptors applied, but those interceptors intercept every method of the target. We’d like to be able to define something similar to pointcuts to select the methods that get intercepted. And that’s what’s the next article in this series will be about. Stay tuned!

Souce code for this article: SimpleAopWindsor.zip (600.79 kb)

Previous articles:

Posted in: C# | Castle | Patterns

Tags: , ,

Simple AOP: call interception with DynamicProxy

February 14, 2009 at 2:37 AMAndre Loker

Aspect oriented programming (AOP) allows us to keep implement different concerns in isolation. In this article series I’ll describe ways to make use of AOP without much hassle. After the introduction to AOP in the first article I’ll show how we can use Castle DynamicProxy to intercept method calls for the injection of advice code.

Introduction

If you’re reading this, you are probably interested in AOP and want to know how you can practise separation of concerns in you .NET application. There are quite some AOP frameworks available for .NET, so you might just grab the one that best fits your need. Below you’ll find an (incomplete!) overview of existing AOP frameworks for .NET

Examples for frameworks using compile time weaving/IL level manipulation:

Those frameworks weave the aspect code into compiled assemblies by modifying the IL code.

Examples for frameworks using proxy-based runtime weaving

The common technique behind those framework is this: for each class that needs to have advices applied to it the framework creates a subclass at runtime using the runtime code generation facilities of .NET. Each virtual call or interface method implementation is overridden. Those overridden methods can then redirect the call to the advices. As a result those frameworks normally only support a very small join point model, limited to the calls of virtual methods or interface methods. However, in many practical cases, this limitation is not that much of an issue.

Choose your destiny

Here we already have six frameworks to choose from. The question is: which one should I use or should I even use a different approach? I haven’t used all of those frameworks, so I can’t go into a detailed analysis of their commonalities and differences. Nevertheless, here are some general thoughts:

  1. If a specific concern is suitable for static weaving you probably want to go that way. As mentioned in the first article, compile time weaving has several benefits, especially allowing a much richer join point model and slightly less overhead.
  2. If you use Spring.NET or S2Container as your IoC container, using the respective AOP facility is probably the recommended way.
  3. If you need dynamic weaving and use the Castle stack read on, that’s what the rest of this article is about!

Castle facilities

Interestingly enough, Castle once had an AOP facility itself. It has been discontinued a while ago because apparently it wasn’t used much. It’s not that people find AOP as a concept useless, but it seems that a fully blown AOP framework is not required in many cases. Instead, people find a different facility to be sufficient, namely DynamicProxy2.

DynamicProxy2 (DP2) is a library that generates proxy types at runtime. DP2 supports different proxy strategies. In the case of AOP, two strategies are the most important:

  1. Create a class proxy: DP2 derives a proxy class from a given type.

class proxy

  1. Create an interface proxy with a target: instead of deriving from the target type, this strategy creates an implementation of one (or multiple) of the interfaces that the target type implements. This strategy resembles the classic Proxy pattern, because the instance created by DP2 replaces the target object.

interface proxy

Interceptors

Just creating new types at runtime is rather useless if that’s all. Of course, there is more. When you use DP2 to create the proxy instances you can provide one or more interceptors. An interceptor is an object implementing IInterceptor, which looks like this:

   1: public interface IInterceptor
   2: {
   3:     void Intercept(IInvocation invocation);
   4: }

Each virtual or interface method that the DP2 proxy generator overrides or implements respectively will call the Intercept() method of the interceptors you pass to the generator method. The IInvocation instance passed to the interceptor contains information about the method being intercepted:

   1: public interface IInvocation
   2: {
   3:     // Methods
   4:     object GetArgumentValue(int index);
   5:     MethodInfo GetConcreteMethod();
   6:     MethodInfo GetConcreteMethodInvocationTarget();
   7:     void Proceed();
   8:     void SetArgumentValue(int index, object value);
   9:  
  10:     // Properties
  11:     object[] Arguments { get; }
  12:     Type[] GenericArguments { get; }
  13:     object InvocationTarget { get; }
  14:     MethodInfo Method { get; }
  15:     MethodInfo MethodInvocationTarget { get; }
  16:     object Proxy { get; }
  17:     object ReturnValue { get; set; }
  18:     Type TargetType { get; }
  19: }

One of the most important members is Proceed() which will call the next IInterceptor for the current proxy or – if the current interceptor is the last one – it will invoke the “real” method.

DP2 in practice

Let’s put all this theoretic mumbo-jumbo into practice, shall we?

First, here’s our ultra complex domain model:

   1: // A service interface...
   2: public interface IService {
   3:   void DoSomething();
   4: }
   5:  
   6: // ... and its implementation
   7: public class Service : IService {
   8:   public void DoSomething() {
   9:     Console.Out.WriteLine("Service.DoSomething()");
  10:   }
  11: }

Nothing fancy here. Here’s our application code:

   1: public class Program {
   2:   public static void Main() {
   3:     IService service = CreateService();
   4:     service.DoSomething();
   5:   }
   6:  
   7:   private static IService CreateService() {
   8:     return new Service();
   9:   }
  10: }

If we run this – surprise – this is the result (the last line is “Press any key” in German)

image

Now we introduce DynamicProxy2. First off, add a reference to Castle.Core.dll and Castle.DynamicProxy2.dll.  Now we can write our first interceptor:

   1: public class LogInterceptor : IInterceptor {
   2:   public void Intercept(IInvocation invocation) {
   3:     Console.WriteLine("Intercepting {0}", invocation.Method.Name);
   4:   }
   5: }

Not bad so far. Finally, we need to inject the interceptor into the target. To do this, modify CreateService like this:

   1: private static IService CreateService() {
   2:   var dp = new ProxyGenerator();
   3:   var target = new Service();
   4:   var interceptor = new LogInterceptor();
   5:   return dp.CreateInterfaceProxyWithTarget<IService>(target, interceptor);
   6: } 

Now run the code again:

image

Wait, where’s “Service.DoSomething()”? We forgot one thing: if we don’t call Proceed() on the IInvocation object in Intercept(), the original method (or the next interceptor) won’t be called, so adjust the LogInterceptor like this:

   1: public void Intercept(IInvocation invocation) {
   2:   Console.WriteLine("Intercepting {0}", invocation.Method.Name);
   3:   // invoke next interceptor/intercepted method
   4:   invocation.Proceed();
   5: }

And voilá:

image

You see, calling Proceed() is necessary to have the intercepted method executed. This has several positive side effects:

  1. We can intentionally decide to block the call to the intercepted methods, for example for security reasons
  2. In the interceptor we can provide that has to be called before and after the execution of the intercepted method (“around advice” anyone?)
  3. We can “fork”, i.e. we can invoke the intercepted method multiple times

If we have more than one interceptor, calling Proceed() will only invoke the intercepted method if the current interceptor is the last one. Calling Proceed() in interceptors that are earlier in the chain will instead invoke the next interceptor. Here’s a collaboration diagram of a proxy with two interceptors:

image

Personally I think that having Proceed() do the right thing transparently is a wise choice.

The link to AOP

You’ve probably already noticed where this is going. We can use DP2 and the interceptors to implement our advices. Out advices are in fact implementations of IInterceptor and the ProxyGenerator acts as the weaver. Here’s another (not really useful) advice using IInterceptor:

   1: public class ExceptionAdvice : IInterceptor {
   2:  
   3:   public bool EatAll { get; set; }
   4:  
   5:   public void Intercept(IInvocation invocation) {
   6:     try {
   7:       invocation.Proceed();
   8:     } catch (Exception e) {
   9:       Console.WriteLine("{0} caught: {1}", e.GetType().Name, e.Message);
  10:       if (!EatAll) {
  11:         throw;
  12:       }
  13:     }
  14:   }
  15: }

Let’s add it into our case:

   1: private static IService CreateService() {
   2:   var dp = new ProxyGenerator();
   3:   var target = new Service();
   4:   var interceptor = new LogInterceptor();
   5:   // swallow all exceptions
   6:   var exceptionAdvice = new ExceptionAdvice {EatAll = true};
   7:   return dp.CreateInterfaceProxyWithTarget<IService>(target, interceptor, exceptionAdvice);
   8: }

If Service.SomeMethod now throws an exception we see this:

image

I’m sure you can come up with a bunch of more realistic concerns that could be implemented using IInterceptors.

Loose ends

Intercepting join points is probably one of the technically most difficult parts of an AOP framework. Luckily Castle DynamicProxy is a feasible tool for this. Let’s see how this intermediate solution compares to a “real” AOP framework:

  • All methods of the target are intercepted. Sometimes you want only a subset of the methods based e.g. on an attribute or the name of the method. Therefore the Intercept method needs to further filter invocations. It would be cool if we could somehow more declaratively define our pointcuts.
  • We have to explicitly generate the proxies with the interceptors. We’d like to have this done automatically. Luckily Castle Windsor, the IoC container of the Castle stack, has this feature built in, so we can make the interceptors part of the component model.
  • We can only intercept virtual methods or interface methods. Well, there’s not much we can do about it, this is an inherent problem of the proxy based weaving approach. If you need a richer join point model, have a look at frameworks supporting compile time weaving.
  • We currently only support around advices. That’s not too bad, given that it is the “mother” of all advices we can “derive” all kind of advice types. For example, you can provide a set of abstract advice classes from which the concrete advices are derived. Just derive from the correct advice type and implement the abstract method (decide for yourself whether this approach is useful for you):
   1: public abstract class AroundAdvice : IInterceptor {
   2:   void IInterceptor.Intercept(IInvocation invocation) {
   3:     Around(invocation);
   4:   }
   5:  
   6:   protected abstract void Around(IInvocation invocation);
   7: }
   8:  
   9: public abstract class BeforeAdvice : IInterceptor {
  10:   void IInterceptor.Intercept(IInvocation invocation) {
  11:     Before(invocation);
  12:     invocation.Proceed();
  13:   }
  14:  
  15:   protected abstract void Before(IInvocation invocation);
  16: }
  17:  
  18: public abstract class AfterAdvice : IInterceptor {
  19:   void IInterceptor.Intercept(IInvocation invocation) {
  20:     try {
  21:       invocation.Proceed();
  22:     } finally {
  23:       After(invocation);
  24:     }
  25:   }
  26:  
  27:   protected abstract void After(IInvocation invocation);
  28: }
  29:  
  30: public abstract class AfterReturningAdvice : IInterceptor {
  31:   void IInterceptor.Intercept(IInvocation invocation) {
  32:     invocation.Proceed();
  33:     AfterReturning(invocation);
  34:   }
  35:  
  36:   protected abstract void AfterReturning(IInvocation invocation);
  37: }
  38:  
  39: public abstract class AfterThrowingAdvice : IInterceptor {
  40:   void IInterceptor.Intercept(IInvocation invocation) {
  41:     try {
  42:       invocation.Proceed();
  43:     } catch (Exception e) {
  44:       AfterThrowing(invocation, e);
  45:     }
  46:   }
  47:  
  48:   protected abstract void AfterThrowing(IInvocation invocation, Exception e);
  49: }

Preview

The next part of the series will cover integration of interceptors into Castle Windsor. Have fun!

Attached you'll find the source code for this article.

DynamicProxyEx1.zip (243.14 kb)

Other articles in this series:

Posted in: Castle | Design | Snippets

Tags: , , , , , ,

Simple AOP: introduction to AOP

February 13, 2009 at 12:46 PMAndre Loker

Aspect oriented programming (AOP) allows us to keep implement different concerns in isolation. In this article series I’ll describe ways to make use of AOP without much hassle. This first article serves as an introduction to AOP to those that are not familiar with aspect orientation.

Why AOP

If we analyse the source code software system we will soon realize that the different parts of the code can be grouped according to their responsibilities. For example some parts of the code are responsible for implementing business logic, other parts manage transactions, yet other parts are used to handle exceptions etc. In AOP terms those responsibilities are called concerns. The business logic is considered the core concern of the application, whereas the latter examples represent aspects that somehow interact with the core concerns. Because they interact with the core concerns, aspects are often called cross-cutting concerns. When we look closer at those interactions we can identify two problems:

1. Scattering

Scattering occurs if the same aspect has impact at many different places in the code. For example, logging or transaction management are often used at many different places in the code, but each time almost exactly the same way. This is problematic because it is harder to maintain scattered functionality.

2. Tangle

The second problem that often occurs is that of tangling concerns. At a given place in code – let’s say a single method – different concerns are present and interleaved. To give you an example, here’s a method that has the core responsibility to change the email address of a user identified by some  ID. Three concerns can be identified: the core concern (changing the email address) and two additional aspects (exception handling and transaction management). I highlighted the different concerns using different colours.

tangle

Tangling concerns are a problem because they bloat the code and distract from the core concerns. Here’s the same core concern without any extra aspects:

   1: public Result ChangeEmailAddressBusinessOnly(int userID, string newEmailAddress) {
   2:   var user = Users.GetUserByID(userID);
   3:   if (user != null) {
   4:     user.ChangeEmailAddress(newEmailAddress);
   5:     return Result.Success;
   6:   }
   7:   return Result.Error("User not found");
   8: }

Tangling also makes unit testing more difficult, because all the extra non-core concerns have to be considered during the tests as well, at least they have to be stubbed or mocked.

Using AOP to solve scattering and tangle

AOP tries to solve the two problems using the separation of concerns paradigm. With AOP the different aspects are described and implemented in isolation. Because the behaviour is defined once only and than applied automatically at all places of interest in the code, scattering won’t appear. And secondly, because aspects are defined in isolation the core concerns can be implemented without any intermingling aspects which solves the problem of code tangle.

Advices

The implementation of the behaviour is called the advice of the concern. Here’s an example of the advice belonging to the exception handling concern in pseudocode:

   1: someAdvice(context) {
   2:   try {
   3:     context.Proceed();
   4:   } catch(Exception e) {
   5:     if(ExceptionHandler.ShouldRethrow(e)) {
   6:       throw;
   7:     }
   8:   }
   9: }

In this example context.Proceed() would execute whatever has to be executed at the point where this concern is present. If that code throws an exception we can handle it.

Pointcuts

We must of course somehow define where the concern is present. This is defined by the so-called pointcut. A pointcut is a boolean expression answering the question “should this concern be applied here?” for every possible “here”. Now, what’s a “here”? A “here" in this context is every possible point in the code where a concern can possibly be applied. Those points are called join points. Examples are: “A call to a method named X” or “The assignment of a value to a variable”. What kinds of join points are supported depends on the AOP implementation and is defined in that implementation’s join point model. Adding pointcuts to our pseudocode example could look like this:

   1: pointcut ServiceMethods : call to any method of class Service
   2:  
   3: someAdvice(context) at ServiceMethods {
   4:   try {
   5:     context.Proceed();
   6:   } catch(Exception e) {
   7:     if(ExceptionHandler.ShouldRethrow(e)) {
   8:       throw;
   9:     }
  10:   }
  11: }

Now everytime we call a method of class  Service the advice will be executed. The call to Proceed() will in this case execute the method that is being called.

Advice types

Now that we have defined what is advised and where we can add the minor detail of when it is applied, because we have different kinds of advices. The example above uses a so called around advice. That is, the advice is wrapped around the joinpoint. It controls the moment when the joinpoint is executed explicitly by calling Proceed(). Other examples of advice types are:

  • before and after advices, which are executed before or after the execution of the joinpoint respectively. Those advices can’t explicitly define when the joinpoint is executed.
  • throwing advices, which are only executed if the execution of the joinpoint raised an exception
  • returning advices, which are only executed if the execution of the joinpoint did not raise an exception

Finally, here’s a fine-tuned version of our pseudo code example:

   1: pointcut ServiceMethods : call( Service.* )
   2:  
   3: around(context) : ServiceMethods {
   4:   try {
   5:     context.Proceed();
   6:   } catch(Exception e) {
   7:     if(ExceptionHandler.ShouldRethrow(e)) {
   8:       throw;
   9:     }
  10:   }
  11: }

By the way, I didn’t come up with this notation all by myself. It resembles the syntax used with AspectJ, which is more or less the most prominent (Java) AOP framework available. It has grown quite mature and if you’re interested in AOP I highly recommend to have a look at it. Yes - even it uses Java :-) 

Weaving

So, we have defined a bunch of aspects with their pointcuts and advices. The missing link is obvious: how do those advices get applied to the base code (the code containing core concerns)?  For this process – called weaving - several approaches exist, all with different pros and cons.

Compile time weaving

This approach is taken by AspectJ for example. An additional step is taken during compilation that inserts the advices into the source code or an existing binary assembly. This results in binaries that don’t look any different from conventional binaries.

Pros

  • the join point model can be very detailed (e.g. the assignment of a value to a variable)
  • performance wise there’s little overhead

Cons

  • weaving is static only, we can’t change aspects at runtime

Runtime weaving

With runtime weaving the aspects are woven into the base code – well – at runtime. There are different approaches to achieve this. Some implementations for example modify the runtime environment to support injection of code. A simpler approach is the creation of runtime proxies for advised objects.

Pros

  • no need for an extra compiler
  • aspects can be added or removed dynamically

Cons

  • depending on the implementation, the join point model can be rather limited

For this article series we’ll use proxy based runtime weaving: it is simple and in many cases sufficient.

Critical view

In principle AOP sounds promising: separation of concerns helps to concentrate on core concerns and keeps our code free of tangle and scattering. Where there’s light, there’s also shadow they say and this counts for AOP. If you look at the base code you can’t see at a glimpse which aspects are applied (except if you have tool support). Because the aspects are injected “invisibly” you might encounter unexpected runtime behaviour, especially if the aspects affect each other. Likewise, you have to be careful where the different aspects are advised to avoid some unintended application of concerns. Keep an eye on your pointcuts!

Also, AOP is generally not a first class citizen but an “artificial” extension to existing software environments the woven code comes with an overhead, especially with runtime weavers. Most business applications are I/O bounded, so whether a method call takes 1 microsecond or 1 millisecond does not matter much if the method itself takes 50 milliseconds to run. Just be aware that advising a join point in a performance critical loop might be something to reconsider.

Therefore – as with any tool – use AOP wisely. Use it when it makes sense, just don’t go crazy with your new toy.

Wrapping it up

This concludes my introduction to AOP. I haven’t covered all details of the topic (like inter type declarations or advice precedence). Still I’ve hopefully shown the need for and benefits of AOP well enough to have awaken your interest in the next parts of this series. Feel free to leave a comment for questions and suggestions.

In the next part of the series I’ll how we can use Castle DynamicProxy as a starting point for a AOP-esque separatio of concerns.

Other articles in this series:

Posted in: Tools | Other

Tags: ,