Chapter 13. Aspect Oriented Programming with Spring.NET

13.1. Introduction

Aspect-Oriented Programming (AOP) complements OOP by providing another way of thinking about program structure. Whereas OO decomposes applications into a hierarchy of objects, AOP decomposes programs into aspects or concerns. This enables the modularization of concerns such as transaction management that would otherwise cut across multiple objects (such concerns are often termed crosscutting concerns).

One of the key components of Spring.NET is the AOP framework. While the Spring.NET IoC container does not depend on AOP, meaning you don't need to use AOP if you don't want to, AOP complements Spring.NET IoC to provide a very capable middleware solution.

AOP is used in Spring.NET:

  • To provide declarative enterprise services, especially as a replacement for COM+ declarative services. The most important such service is declarative transaction management, which builds on Spring.NET's transaction abstraction. This functionality is planed for an upcoming release of Spring.NET

  • To allow users to implement custom aspects, complementing their use of OOP with AOP.

Thus you can view Spring.NET AOP as either an enabling technology that allows Spring.NET to provide declarative transaction management without COM+; or use the full power of the Spring.NET AOP framework to implement custom aspects.

For those who would like to hit the ground running and start exploring how to use Spring's AOP functionality, head on over to Chapter 34, AOP Guide.

13.1.1. AOP concepts

Let us begin by defining some central AOP concepts. These terms are not Spring.NET-specific. Unfortunately, AOP terminology is not particularly intuitive. However, it would be even more confusing if Spring.NET used its own terminology.

  • Aspect: A modularization of a concern for which the implementation might otherwise cut across multiple objects. Transaction management is a good example of a crosscutting concern in enterprise applications. Aspects are implemented using Spring.NET as Advisors or interceptors.

  • Joinpoint: Point during the execution of a program, such as a method invocation or a particular exception being thrown.

  • Advice: Action taken by the AOP framework at a particular joinpoint. Different types of advice include "around," "before" and "throws" advice. Advice types are discussed below. Many AOP frameworks, including Spring.NET, model an advice as an interceptor, maintaining a chain of interceptors "around" the joinpoint.

  • Pointcut: A set of joinpoints specifying when an advice should fire. An AOP framework must allow developers to specify pointcuts: for example, using regular expressions.

  • Introduction: Adding methods or fields to an advised class. Spring.NET allows you to introduce new interfaces to any advised object. For example, you could use an introduction to make any object implement an IAuditable interface, to simplify the tracking of changes to an object's state.

  • Target object: Object containing the joinpoint. Also referred to as advised or proxied object.

  • AOP proxy: Object created by the AOP framework, including advice. In Spring.NET, an AOP proxy is a dynamic proxy that uses IL code generated at runtime.

  • Weaving: Assembling aspects to create an advised object. This can be done at compile time (using the Gripper-Loom.NET compiler, for example), or at runtime. Spring.NET performs weaving at runtime.

Different advice types include:

  • Around advice: Advice that surrounds a joinpoint such as a method invocation. This is the most powerful kind of advice. Around advice will perform custom behaviour before and after the method invocation. They are responsible for choosing whether to proceed to the joinpoint or to shortcut executing by returning their own return value or throwing an exception.

  • Before advice: Advice that executes before a joinpoint, but which does not have the ability to prevent execution flow proceeding to the joinpoint (unless it throws an exception).

  • Throws advice: Advice to be executed if a method throws an exception. Spring.NET provides strongly typed throws advice, so you can write code that catches the exception (and subclasses) you're interested in, without needing to cast from Exception.

  • After returning advice: Advice to be executed after a joinpoint completes normally: for example, if a method returns without throwing an exception.

Spring.NET provides a full range of advice types. We recommend that you use the least powerful advice type that can implement the required behaviour. For example, if you need only to update a cache with the return value of a method, you are better off implementing an after returning advice than an around advice, although an around advice can accomplish the same thing. Using the most specific advice type provides a simpler programming model with less potential for errors. For example, you don't need to invoke the proceed() method on the IMethodInvocation used for around advice, and hence can't fail to invoke it.

The pointcut concept is the key to AOP, distinguishing AOP from older technologies offering interception. Pointcuts enable advice to be targeted independently of the OO hierarchy. For example, an around advice providing declarative transaction management can be applied to a set of methods spanning multiple objects. Thus pointcuts provide the structural element of AOP.

13.1.2. Spring.NET AOP capabilities

Spring.NET AOP is implemented in pure C#. There is no need for a special compilation process - all weaving is done at runtime. Spring.NET AOP does not need to control or modify the way in which assemblies are loaded, nor does it rely on unmanaged APIs, and is thus suitable for use in any CLR environment.

Spring.NET currently supports interception of method invocations. Field interception is not implemented, although support for field interception could be added without breaking the core Spring.NET AOP APIs.

Field interception arguably violates OO encapsulation. We don't believe it is wise in application development.

Spring.NET provides classes to represent pointcuts and different advice types. Spring.NET uses the term advisor for an object representing an aspect, including both an advice and a pointcut targeting it to specific joinpoints.

Different advice types are IMethodInterceptor (from the AOP Alliance interception API); and the advice interfaces defined in the Spring.Aop namespace. All advices must implement the AopAlliance.Aop.IAdvice tag interface. Advices supported out the box are IMethodInterceptor ; IThrowsAdvice; IBeforeAdvice; and IAfterReturningAdvice. We'll discuss advice types in detail below.

Spring.NET provides a .NET translation of the Java interfaces defined by the AOP Alliance. Around advice must implement the AOP Alliance AopAlliance.Interceptr.IMethodInterceptor interface. Whilst there is wide support for the AOP Alliance in Java, Spring.NET is currently the only .NET AOP framework that makes use of these interfaces. In the short term, this will provide a consistent programming model for those doing development in both .NET and Java, and in the longer term, we hope to see more .NET projects adopt the AOP Alliance interfaces.

The aim of Spring.NET AOP support is not to provide a comprehensive AOP implementation on par with the functionality available in AspectJ. However, Spring.NET AOP provides an excellent solution to most problems in .NET applications that are amenable to AOP.

Thus, it is common to see Spring.NET's AOP functionality used in conjunction with a Spring.NET IoC container. AOP advice is specified using normal object definition syntax (although this allows powerful "autoproxying" capabilities); advice and pointcuts are themselves managed by Spring.NET IoC.

13.1.3. AOP Proxies in Spring.NET

Spring.NET generates AOP proxies at runtime using classes from the System.Reflection.Emit namespace to create necessary IL code for the proxy class. This results in proxies that are very efficient and do not impose any restrictions on the inheritance hierarchy.

Another common approach to AOP proxy implementation in .NET is to use ContextBoundObject and the .NET remoting infrastructure as an interception mechanism. We are not very fond of ContextBoundObject approach because it requires classes that need to be proxied to inherit from the ContextBoundObject either directly or indirectly. In our opinion this an unnecessary restriction that influences how you should design your object model and also excludes applying AOP to "3rd party" classes that are not under your direct control. Context-bound proxies are also an order of magnitude slower than IL-generated proxies, due to the overhead of the context switching and .NET remoting infrastructure.

Spring.NET AOP proxies are also "smart" - in that because proxy configuration is known during proxy generation, the generated proxy can be optimized to invoke target methods via reflection only when necessary (i.e. when there are advices applied to the target method). In all other cases the target method will be called directly, thus avoiding performance hit caused by the reflective invocation.

Finally, Spring.NET AOP proxies will never return a raw reference to a target object. Whenever a target method returns a raw reference to a target object (i.e. "return this;"), AOP proxy will recognize what happened and will replace the return value with a reference to itself instead.

The current implementation of the AOP proxy generator uses object composition to delegate calls from the proxy to a target object, similar to how you would implement a classic Decorator pattern. This means that classes that need to be proxied have to implement one or more interfaces, which is in our opinion not only a less-intruding requirement than ContextBoundObject inheritance requirements, but also a good practice that should be followed anyway for the service classes that are most common targets for AOP proxies.

In a future release we will implement proxies using inheritance, which will allow you to proxy classes without interfaces as well and will remove some of the remaining raw reference issues that cannot be solved using composition-based proxies.

13.2. Pointcut API in Spring.NET

Let's look at how Spring.NET handles the crucial pointcut concept.

13.2.1. Concepts

Spring.NET's pointcut model enables pointcut reuse independent of advice types. It's possible to target different advice using the same pointcut.

The Spring.Aop.IPointcut interface is the central interface, used to target advices to particular types and methods. The complete interface is shown below:

public interface IPointcut
{
    ITypeFilter TypeFilter { get; }

    IMethodMatcher MethodMatcher { get; }
}

Splitting the IPointcut interface into two parts allows reuse of type and method matching parts, and fine-grained composition operations (such as performing a "union" with another method matcher).

The ITypeFilter interface is used to restrict the pointcut to a given set of target classes. If the Matches() method always returns true, all target types will be matched:

public interface ITypeFilter
{
    bool Matches(Type type);
}

The IMethodMatcher interface is normally more important. The complete interface is shown below:

public interface IMethodMatcher
{
    bool IsRuntime { get; }

    bool Matches(MethodInfo method, Type targetType);

    bool Matches(MethodInfo method, Type targetType, object[] args);
}

The Matches(MethodInfo, Type) method is used to test whether this pointcut will ever match a given method on a target type. This evaluation can be performed when an AOP proxy is created, to avoid the need for a test on every method invocation. If the 2-argument matches method returns true for a given method, and the IsRuntime property for the IMethodMatcher returns true, the 3-argument matches method will be invoked on every method invocation. This enables a pointcut to look at the arguments passed to the method invocation immediately before the target advice is to execute.

Most IMethodMatchers are static, meaning that their IsRuntime property returns false. In this case, the 3-argument Matches method will never be invoked.

Whenever possible, try to make pointcuts static... this allows the AOP framework to cache the results of pointcut evaluation when an AOP proxy is created.

13.2.2. Operations on pointcuts

Spring.NET supports operations on pointcuts: notably, union and intersection.

Union means the methods that either pointcut matches.

Intersection means the methods that both pointcuts match.

Union is usually more useful.

Pointcuts can be composed using the static methods in the Spring.Aop.Support.Pointcuts class, or using the ComposablePointcut class in the same namespace.

13.2.3. Convenience pointcut implementations

Spring.NET provides several convenient pointcut implementations. Some can be used out of the box; others are intended to be subclassed in application-specific pointcuts.

13.2.3.1. Static pointcuts

Static pointcuts are based on method and target class, and cannot take into account the method's arguments. Static pointcuts are sufficient--and best--for most usages. It's possible for Spring.NET to evaluate a static pointcut only once, when a method is first invoked: after that, there is no need to evaluate the pointcut again with each method invocation.

Let's consider some static pointcut implementations included with Spring.NET.

13.2.3.1.1. Regular expression pointcuts

One obvious way to specify static pointcuts is using regular expressions. Several AOP frameworks besides Spring.NET make this possible. The Spring.Aop.Support.SdkRegularExpressionMethodPointcut class is a generic regular expression pointcut, that uses the regular expression classes from the .NET BCL.

Using this class, you can provide a list of pattern Strings. If any of these is a match, the pointcut will evaluate to true (so the result is effectively the union of these pointcuts.). The matching is done against the full class name so you can use this pointcut if you would like to apply advice to all the classes in a particular namespace.

The usage is shown below:

<object id="settersAndAbsquatulatePointcut" 
    type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop">
    <property name="patterns">
        <list>
            <value>.*set.*</value>
            <value>.*absquatulate</value>
        </list>
    </property>
</object>

As a convenience, Spring provides the RegularExpressionMethodPointcutAdvisor class that allows us to reference an IAdvice instance as well as defining the pointcut rules (remember that an IAdvice instance can be an interceptor, before advice, throws advice etc.) This simplifies wiring, as the one object serves as both pointcut and advisor, as shown below:

<object id="settersAndAbsquatulateAdvisor" 
    type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop">
    <property name="advice">
        <ref local="objectNameOfAopAllianceInterceptor"/>
    </property>
    <property name="patterns">
        <list>
            <value>.*set.*</value>
            <value>.*absquatulate</value>
        </list>
    </property>
</object>

The RegularExpressionMethodPointcutAdvisor class can be used with any Advice type.

If you only have one pattern you can use the property name pattern and specify a single value instead of using the property name patterns and specifying a list.

You may also specify a Regex object from the System.Text.RegularExpressions namespace. The built in RegexConverter class will perform the conversion. See Section 6.4, “Built-in TypeConverters” for more information on Spring's build in type converters. The Regex object is created as any other object within the IoC container. Using an inner-object definition for the Regex object is a handy way to keep the definition close to the PointcutAdvisor declaration. Note that the class SdkRegularExpressionMethodPointcut has a DefaultOptions property to set the regular expression options if they are not explicitly specified in the constructor.

13.2.3.1.2. Attribute pointcuts

Pointcuts can be specified by matching an attribute type that is associated with a method. Advice associated with this pointcut can then read the metadata associated with the attribute to configure itself. The class AttributeMatchMethodPointcut provides this functionality. Sample usage that will match all methods that have the attribute Spring.Attributes.CacheAttribute is shown below.

<object id="cachePointcut" type="Spring.Aop.Support.AttributeMatchMethodPointcut, Spring.Aop"> 
    <property name="Attribute" value="Spring.Attributes.CacheAttribute, Spring.Core"/>        
</object>

This can be used with a DefaultPointcutAdvisor as shown below

<object id="cacheAspect" type="Spring.Aop.Support.DefaultPointcutAdvisor, Spring.Aop"> 
  <property name="Pointcut"> 
      <object type="Spring.Aop.Support.AttributeMatchMethodPointcut, Spring.Aop"> 
          <property name="Attribute" value="Spring.Attributes.CacheAttribute, Spring.Core"/>        
      </object> 
  </property> 
  <property name="Advice" ref="aspNetCacheAdvice"/> 
</object> 

where aspNetCacheAdvice is an implementation of an IMethodInterceptor that caches method return values. See the SDK docs for Spring.Aop.Advice.CacheAdvice for more information on this particular advice.

As a convenience the class AttributeMatchMethodPointcutAdvisor is provided to defining an attribute based Advisor as a somewhat shorter alternative to using the generic DefaultPointcutAdvisor. An example is shown below.

<object id="AspNetCacheAdvice" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop"> 
   <property name="advice"> 
      <object type="Aspect.AspNetCacheAdvice, Aspect"/> 
   </property> 
   <property name="attribute" value="Framework.AspNetCacheAttribute, Framework" /> 
</object>

13.2.3.2. Dynamic Pointcuts

Dynamic pointcuts are costlier to evaluate than static pointcuts. They take into account method arguments, as well as static information. This means that they must be evaluated with every method invocation; the result cannot be cached, as arguments will vary.

The main example is the control flow pointcut.

13.2.3.2.1. Control Flow Pointcuts

Spring.NET control flow pointcuts are conceptually similar to AspectJ cflow pointcuts, although less powerful. (There is currently no way to specify that a pointcut executes below another pointcut.). A control flow pointcut is dynamic because it is evaluated against the current call stack for each method invocation. For example, if method ClassA.A() calls ClassB.B() then the execution of ClassB.B() has occurred in ClassA.A()'s control flow. A control flow pointcut allows advice to be applied to the method ClassA.A() but only when called from ClassB.B() and not when ClassA.A() is executed from another call stack. Control flow pointcuts are specified using the Spring.Aop.Support.ControlFlowPointcut class.

[Note]Note

Control flow pointcuts are significantly more expensive to evaluate at runtime than even other dynamic pointcuts.

When using control flow point cuts some attention should be paid to the fact that at runtime the JIT compiler can inline the methods, typically for increased performance, but with the consequence that the method no longer appears in the current call stack. This is because inlining takes the callee's IL code and inserts it into the caller's IL code effectively removing the method call. The information returned from System.Diagnostics.StackTrace, used in the implementation of ControlFlowPointcut is subject to these optimizations and therefore a control flow pointcut will not match if the method has been inlined.

Generally speaking, a method will be a candidate for inlining when its code is 'small', just a few lines of code (less than 32 bytes of IL). For some interesting reading on this process read David Notario's blog entries (JIT Optimizations I and JIT Optimizations II). Additionally, when an assembly is compiled with a Release configuration the assembly metadata instructs the CLR to enable JIT optimizations. When compiled with a Debug configuration the CLR will disable (some?) these optimizations. Empirically, method inlining is turned off in a Debug configuration.

The way to ensure that your control flow pointcut will not be overlooked because of method inlining is to apply the System.Runtime.CompilerServices.MethodImplAttribute attribute with the value MethodImplOptions.NoInlining. In this (somewhat artificial) simple example, if the code is compiled in release mode it will not match a control flow pointcut for the method "GetAge".

public int GetAge(IPerson person)
{
    return person.GetAge();
}

However, applying the attributes as shown below will prevent the method from being inlined even in a release build.

[MethodImpl(MethodImplOptions.NoInlining)] 
public int GetAge(IPerson person)
{
    return person.GetAge();
}

13.2.4. Custom pointcuts

Because pointcuts in Spring.NET are .NET types, rather than language features (as in AspectJ) it is possible to declare custom pointcuts, whether static or dynamic. However, there is no support out of the box for the sophisticated pointcut expressions that can be coded in the AspectJ syntax. However, custom pointcuts in Spring.NET can be as arbitrarily complex as any object model.

Spring.NET provides useful pointcut superclasses to help you to implement your own pointcuts.

Because static pointcuts are the most common and generally useful pointcut type, you'll probably subclass StaticMethodMatcherPointcut, as shown below. This requires you to implement just one abstract method (although it is possible to override other methods to customize behaviour):

public class TestStaticPointcut : StaticMethodMatcherPointcut {

    public override bool Matches(MethodInfo method, Type targetType) {
        // return true if custom criteria match
    }
}

13.3. Advice API in Spring.NET

Let's now look at how Spring.NET AOP handles advice.

13.3.1. Advice Lifecycle

Spring.NET advices can be shared across all advised objects, or unique to each advised object. This corresponds to per-class or per-instance advice.

Per-class advice is used most often. It is appropriate for generic advice such as transaction advisors. These do not depend on the state of the proxied object or add new state; they merely act on the method and arguments.

Per-instance advice is appropriate for introductions, to support mixins. In this case, the advice adds state to the proxied object.

It's possible to use a mix of shared and per-instance advice in the same AOP proxy.

13.3.2. Advice types

Spring.NET provides several advice types out of the box, and is extensible to support arbitrary advice types. Let us look at the basic concepts and standard advice types.

13.3.2.1. Interception Around Advice

The most fundamental advice type in Spring.NET is interception around advice.

Spring.NET is compliant with the AOP Alliance interface for around advice using method interception. Around advice is implemented using the following interface:

public interface IMethodInterceptor : IInterceptor
{
    object Invoke(IMethodInvocation invocation);
}

The IMethodInvocation argument to the Invoke() method exposes the method being invoked; the target joinpoint; the AOP proxy; and the arguments to the method. The Invoke() method should return the invocation's result: the return value of the joinpoint.

A simple IMethodInterceptor implementation looks as follows:

public class DebugInterceptor : IMethodInterceptor {

    public object Invoke(IMethodInvocation invocation) {
        Console.WriteLine("Before: invocation=[{0}]", invocation);
        object rval = invocation.Proceed();
        Console.WriteLine("Invocation returned");
        return rval;
    }
}

Note the call to the IMethodInvocation's Proceed() method. This proceeds down the interceptor chain towards the joinpoint. Most interceptors will invoke this method, and return its return value. However, an IMethodInterceptor, like any around advice, can return a different value or throw an exception rather than invoke the Proceed() method. However, you don't want to do this without good reason!

13.3.2.2. Before advice

A simpler advice type is a before advice. This does not need an IMethodInvocation object, since it will only be called before entering the method.

The main advantage of a before advice is that there is no need to invoke the Proceed()method, and therefore no possibility of inadvertently failing to proceed down the interceptor chain.

The IMethodBeforeAdvice interface is shown below.

public interface IMethodBeforeAdvice : IBeforeAdvice
{
    void Before(MethodInfo method, object[] args, object target);
}

Note the return type is void. Before advice can insert custom behaviour before the joinpoint executes, but cannot change the return value. If a before advice throws an exception, this will abort further execution of the interceptor chain. The exception will propagate back up the interceptor chain. If it is unchecked, or on the signature of the invoked method, it will be passed directly to the client; otherwise it will be wrapped in an unchecked exception by the AOP proxy.

An example of a before advice in Spring.NET, which counts all methods that return normally:

public class CountingBeforeAdvice : IMethodBeforeAdvice {

    private int count;
    
    public void Before(MethodInfo method, object[] args, object target) {
        ++count;
    }

    public int Count  {
        get { return count; }
    }
}

Before advice can be used with any pointcut.

13.3.2.3. Throws advice

Throws advice is invoked after the return of the joinpoint if the joinpoint threw an exception. The Spring.Aop.IThrowsAdvice interface does not contain any methods: it is a tag interface identifying that the implementing advice object implements one or more typed throws advice methods. These throws advice methods must be of the form:

AfterThrowing([MethodInfo method, Object[] args, Object target], Exception subclass)

Throws-advice methods must be named 'AfterThrowing'. The return value will be ignored by the Spring.NET AOP framework, so it is typically void. With regard to the method arguments, only the last argument is required. Thus there are exactly one or four arguments, depending on whether the advice method is interested in the method, method arguments and the target object.

The following method snippets show examples of throws advice.

This advice will be invoked if a RemotingException is thrown (including subclasses):

public class RemoteThrowsAdvice : IThrowsAdvice {

    public void AfterThrowing(RemotingException ex) {
        // Do something with remoting exception
    }
}

The following advice is invoked if a SqlException is thrown. Unlike the above advice, it declares 4 arguments, so that it has access to the invoked method, method arguments and target object:

public class SqlExceptionThrowsAdviceWithArguments : IThrowsAdvice {

    public void AfterThrowing(MethodInfo method, object[] args, object target, SqlException ex) {
        // Do something will all arguments
    }
}

The final example illustrates how these two methods could be used in a single class, which handles both RemotingException and SqlException. Any number of throws advice methods can be combined in a single class, as can be seen in the following example.

public class CombinedThrowsAdvice : IThrowsAdvice {

    public void AfterThrowing(RemotingException ex)  {
        // Do something with remoting exception
    }
 
    public void AfterThrowing(MethodInfo method, object[] args, object target, SqlException ex) {
        // Do something will all arguments
    }
}

Finally, it is worth stating that throws advice is only applied to the actual exception being thrown. What does this mean? Well, it means that if you have defined some throws advice that handles RemotingExceptions, the applicable AfterThrowing method will only be invoked if the type of the thrown exception is RemotingException... if a RemotingException has been thrown and subsequently wrapped inside another exception before the exception bubbles up to the throws advice interceptor, then the throws advice that handles RemotingExceptions will never be called. Consider a business method that is advised by throws advice that handles RemotingExceptions; if during the course of a method invocation said business method throws a RemoteException... and subsequently wraps said RemotingException inside a business-specific BadConnectionException (see the code snippet below) before throwing the exception, then the throws advice will never be able to respond to the RemotingException... because all the throws advice sees is a BadConnectionException. The fact that the RemotingException is wrapped up inside the BadConnectionException is immaterial.

public void BusinessMethod()
    {
        try
        {
            // do some business operation...
        }
        catch (RemotingException ex)
        {
            throw new BadConnectionException("Couldn't connect.", ex);
        }
    }
[Note]Note
Please note that throws advice can be used with any pointcut.

13.3.2.4. After Returning advice

An after returning advice in Spring.NET must implement the Spring.Aop.IAfterReturningAdvice interface, shown below:

public interface IAfterReturningAdvice : IAdvice
{
  void AfterReturning(object returnValue, MethodBase method, object[] args, object target);
}

An after returning advice has access to the return value (which it cannot modify), invoked method, methods arguments and target.

The following after returning advice counts all successful method invocations that have not thrown exceptions:

public class CountingAfterReturningAdvice : IAfterReturningAdvice {
    private int count;

    public void AfterReturning(object returnValue, MethodBase m, object[] args, object target) {
        ++count;
    }

    public int Count  {
        get { return count; }
    }
}

This advice doesn't change the execution path. If it throws an exception, this will be thrown up the interceptor chain instead of the return value.

[Note]Note
Please note that after-returning advice can be used with any pointcut.

13.3.2.5. Advice Ordering

When multiple pieces of advice want to run on the same joinpoint the precedence is determined by having the advice implement the IOrdered interface or by specifying order information on an advisor.

13.3.2.6. Introduction advice

Spring.NET allows you to add new methods and properties to an advised class. This would typically be done when the functionality you wish to add is a crosscutting concern and want to introduce this functionality as a change to the static structure of the class hierarchy. For example, you may want to cast objects to the introduction interface in your code. Introductions are also a means to emulate multiple inheritance.

Introduction advice is defined by using a normal interface declaration that implements the tag interface IAdvice.

[Note]Note
The need for implementing this marker interface will likely be removed in future versions.

As an example, consider the interface IAuditable that describes the last modified time of an object.

public interface IAuditable : IAdvice
{
    DateTime LastModifiedDate
    {
        get;
        set;
    }
}
where
public interface IAdvice
{        
}

Access to the advised object can be obtained by implementing the interface ITargetAware

public interface ITargetAware
{
  IAopProxy TargetProxy
  {
    set;
  }
}

with the IAopProxy reference providing a layer of indirection through which the advised object can be accessed.

public interface IAopProxy
{
  object GetProxy();
}

A simple class that demonstrates this functionality is shown below.

public interface IAuditable : IAdvice, ITargetAware
{
    DateTime LastModifiedDate
    {
        get;
        set;
    }
}

A class that implements this interface is shown below.

public class AuditableMixin : IAuditable
{
    private DateTime date;
    private IAopProxy targetProxy;

    public AuditableMixin()
    {
      date = new DateTime();
    }

    public DateTime LastModifiedDate
    {
       get { return date; }
       set { date = value; }
    }

    public IAopProxy TargetProxy
    {
       set { targetProxy = value; }
    }
}

Introduction advice is not associated with a pointcut, since it applies at the class and not the method level. As such, introductions use their own subclass of the interface IAdvisor, namely IIntroductionAdvisor, to specify the types that the introduction can be applied to.

public interface IIntroductionAdvisor : IAdvisor
{
    ITypeFilter TypeFilter { get; }
    
    Type[] Interfaces { get; }

    void ValidateInterfaces();
}

The TypeFilter property returns the filter that determines which target classes this introduction should apply to.

The Interfaces property returns the interfaces introduced by this advisor.

The ValidateInterfaces() method is used internally to see if the introduced interfaces can be implemented by the introduction advice.

Spring.NET provides a default implementation of this interface (the DefaultIntroductionAdvisor class) that should be sufficient for the majority of situations when you need to use introductions. The most simple implementation of an introduction advisor is a subclass that simply passes a new instance the base constructor. Passing a new instance is important since we want a new instance of the mixin classed used for each advised object.

public class AuditableAdvisor : DefaultIntroductionAdvisor
{
  public AuditableAdvisor() : base(new AuditableMixin())
  {
  }
}

Other constructors let you explicitly specify the interfaces of the class that will be introduced. See the SDK documentation for more details.

We can apply this advisor Programatically, using the IAdvised.AddIntroduction(), method, or (the recommended way) in XML configuration using the IntroductionNames property on ProxyFactoryObject, which will be discussed later.

Unlike the AOP implementation in the Spring Framework for Java, introduction advice in Spring.NET is not implemented as a specialized type of interception advice. The advantage of this approach is that introductions are not kept in the interceptor chain, which allows some significant performance optimizations. When a method is called that has no interceptors, a direct call is used instead of reflection regardless of whether the target method is on the target object itself or one of the introductions. This means that introduced methods perform the same as target object methods, which could be useful for adding introductions to fine grained objects. The disadvantage is that if the mixin functionality would benefit from having access to the calling stack, it is not available. Introductions with this functionality will be addressed in a future version of Spring.NET AOP.

13.4. Advisor API in Spring.NET

In Spring.NET, an advisor is a modularization of an aspect. Advisors typically incorporate both an advice and a pointcut.

Apart from the special case of introductions, any advisor can be used with any advice. The Spring.Aop.Support.DefaultPointcutAdvisor class is the most commonly used advisor implementation. For example, it can be used with a IMethodInterceptor, IBeforeAdvice or IThrowsAdvice and any pointcut definition.

Other convenience implementations provided are: AttributeMatchMethodPointcutAdvisor shown in usage previously in Section 13.2.3.1.2, “Attribute pointcuts” for use with attribute based pointcuts. RegularExpressionMethodPointcutAdvisor that will apply pointcuts based on the matching a regular expression to method names.

It is possible to mix advisor and advice types in Spring.NET in the same AOP proxy. For example, you could use a interception around advice, throws advice and before advice in one proxy configuration: Spring.NET will automatically create the necessary interceptor chain.

13.5. Using the ProxyFactoryObject to create AOP proxies

If you're using the Spring.NET IoC container for your business objects - generally a good idea - you will want to use one of Spring.NET's AOP-specific IFactoryObject implementations (remember that a factory object introduces a layer of indirection, enabling it to create objects of a different type - Section 5.3.8, “Setting a reference using the members of other objects and classes.”).

The basic way to create an AOP proxy in Spring.NET is to use the Spring.Aop.Framework.ProxyFactoryObject class. This gives complete control over ordering and application of the pointcuts and advice that will apply to your business objects. However, there are simpler options that are preferable if you don't need such control.

13.5.1. Basics

The ProxyFactoryObject, like other Spring.NET IFactoryObject implementations, introduces a level of indirection. If you define a ProxyFactoryObject with name foo, what objects referencing foo see is not the ProxyFactoryObject instance itself, but an object created by the ProxyFactoryObject's implementation of the GetObject() method. This method will create an AOP proxy wrapping a target object.

One of the most important benefits of using a ProxyFactoryObject or other IoC-aware classes that create AOP proxies, is that it means that advice and pointcuts can also be managed by IoC. This is a powerful feature, enabling certain approaches that are hard to achieve with other AOP frameworks. For example, an advice may itself reference application objects (besides the target, which should be available in any AOP framework), benefiting from all the pluggability provided by Dependency Injection.

13.5.2. ProxyFactoryObject Properties

Like most IFactoryObject implementations provided with Spring.NET, the ProxyFactoryObject is itself a Spring.NET configurable object. Its properties are used to:

  • Specify the target object that is to be proxied.

  • Specify the advice that is to be applied to the proxy.

Some key properties are inherited from the Spring.Aop.Framework.ProxyConfig class: this class is the superclass for all AOP proxy factories in Spring.NET. Some of the key properties include:

  • ProxyTargetType: a boolean value that should be set to true if the target class is to be proxied directly, as opposed to just proxying the interfaces exposed on the target class.

  • Optimize: whether to apply aggressive optimization to created proxies. Don't use this setting unless you understand how the relevant AOP proxy handles optimization. The exact meaning of this flag will differ between proxy implementations and will generally result in a trade off between proxy creation time and runtime performance. Optimizations may be ignored by certain proxy implementations and may be disabled silently based on the value of other properties such as ExposeProxy.

  • IsFrozen: whether advice changes should be disallowed once the proxy factory has been configured. The default is false.

  • ExposeProxy: whether the current proxy should be exposed via the AopContext so that it can be accessed by the target. (It's available via the IMethodInvocation without the need for the AopContext.) If a target needs to obtain the proxy and ExposeProxy is true, the target can use the AopContext.CurrentProxy property.

  • AopProxyFactory: the implementation of IAopProxyFactory to use when generating a proxy. Offers a way of customizing whether to use remoting proxies, IL generation or any other proxy strategy. The default implementation will use IL generation to create composition-based proxies.

Other properties specific to the ProxyFactoryObject class include:

  • ProxyInterfaces: the array of string interface names we're proxying.

  • InterceptorNames: string array of IAdvisor, interceptor or other advice names to apply. Ordering is significant... first come, first served that is. The first interceptor in the list will be the first to be able to interceptor the invocation (assuming it concerns a regular MethodInterceptor or BeforeAdvice).

    The names are object names in the current container, including objectnames from container hierarchies. You can't mention object references here since doing so would result in the ProxyFactoryObject ignoring the singleton setting of the advise.

  • IntroductionNames: The names of objects in the container that will be used as introductions to the target object. If the object referred to by name does not implement the IIntroductionAdvisor it will be passed to the default constructor of DefaultIntroductionAdvisor and all of the objects interfaces will be added to the target object. Objects that implement the IIntroductionAdvisor interface will be used as is, giving you a finer level of control over what interfaces you may want to expose and the types for which they will be matched against.

  • IsSingleton: whether or not the factory should return a single proxy object, no matter how often the GetObject() method is called. Several IFactoryObject implementations offer such a method. The default value is true. If you would like to be able to apply advice on a per-proxy object basis, use a IsSingleton value of false and a IsFrozen value of false. If you want to use stateful advice--for example, for stateful mixins--use prototype advices along with a IsSingleton value of false.

13.5.3. Proxying Interfaces

Let's look at a simple example of ProxyFactoryObject in action. This example involves:

  • A target object that will be proxied. This is the "personTarget" object definition in the example below.

  • An IAdvisor and an IInterceptor used to provide advice.

  • An AOP proxy object definition specifying the target object (the personTarget object) and the interfaces to proxy, along with the advices to apply.

<object id="personTarget" type="MyCompany.MyApp.Person, MyCompany">
    <property name="name" value="Tony"/>
    <property name="age" value="51"/>
</object>

<object id="myCustomInterceptor" type="MyCompany.MyApp.MyCustomInterceptor, MyCompany">
    <property name="customProperty" value="configuration string"/>
</object>

<object id="debugInterceptor" type="Spring.Aop.Advice.DebugAdvice, Spring.Aop">
</object>

<object id="person" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
    
    <property name="proxyInterfaces" value="MyCompany.MyApp.IPerson"/>

    <property name="target" ref="personTarget"/>

    <property name="interceptorNames">
        <list>
            <value>debugInterceptor</value>
            <value>myCustomInterceptor</value>
        </list>
    </property>

</object>

Note that the InterceptorNames property takes a list of strings: the object names of the interceptor or advisors in the current context. Advisors, interceptors, before, after returning and throws advice objects can be used. The ordering of advisors is significant.

You might be wondering why the list doesn't hold object references. The reason for this is that if the ProxyFactoryObject's singleton property is set to false, it must be able to return independent proxy instances. If any of the advisors is itself a prototype, an independent instance would need to be returned, so it's necessary to be able to obtain an instance of the prototype from the context; holding a reference isn't sufficient.

The "person" object definition above can be used in place of an IPerson implementation, as follows:

IPerson person = (IPerson) factory.GetObject("person");

Other objects in the same IoC context can express a strongly typed dependency on it, as with an ordinary .NET object:

<object id="personUser" type="MyCompany.MyApp.PersonUser, MyCompany">
    <property name="person" ref="person"/>
</object>

The PersonUser class in this example would expose a property of type IPerson. As far as it's concerned, the AOP proxy can be used transparently in place of a "real" person implementation. However, its type would be a proxy type. It would be possible to cast it to the IAdvised interface (discussed below).

It's possible to conceal the distinction between target and proxy using an anonymous inline object, as follows. (for more information on inline objects see Section 5.3.2.3, “Inline objects”.) Only the ProxyFactoryObject definition is different; the advice is included only for completeness:

<object id="myCustomInterceptor" type="MyCompany.MyApp.MyCustomInterceptor, MyCompany">
    <property name="customProperty" value="configuration string"/>
</object>

<object id="debugInterceptor" type="Spring.Aop.Advice.DebugAdvice, Spring.Aop">
</object>

<object id="person" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
    
    <property name="proxyInterfaces" value="MyCompany.MyApp.IPerson"/>

    <property name="target">
      <!-- Instead of using a reference to target, just use an inline object -->
      <object type="MyCompany.MyApp.Person, MyCompany">
        <property name="name" value="Tony"/>
        <property name="age" value="51"/>
      </object>
    </property>

    <property name="interceptorNames">
        <list>
            <value>debugInterceptor</value>
            <value>myCustomInterceptor</value>
        </list>
    </property>

</object>

This has the advantage that there's only one object of type Person: useful if we want to prevent users of the application context obtaining a reference to the un-advised object, or need to avoid any ambiguity with Spring IoC autowiring. There's also arguably an advantage in that the ProxyFactoryObject definition is self-contained. However, there are times when being able to obtain the un-advised target from the factory might actually be an advantage: for example, in certain test scenarios.

13.5.1. Applying advice on a per-proxy basis.

Let's look at an example of configuring the proxy objects retrieved from ProxyFactoryObject.

          <!-- create the object to reference -->
          <object id="RealObjectTarget" type="MyRealObject" singleton="false"/>
          <!-- create the proxied object for everyone to use-->
          <object id="MyObject" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
          <property name="proxyInterfaces" value="MyInterface" />
          <property name="isSingleton" value="false"/>
          <property name="targetName" value="RealObjectTarget" />
          </object>
        

If you are using a prototype as the target you must set the TargetName property with the name/object id of your object and not use the property Target with a reference to that object. This will then allow a new proxy to be created around a new prototype target instance.

Consider the above Spring.Net object configuration. Notice that the IsSingleton property of the ProxyFactoryObject instance is set to false. This means that each proxy object will be unique. Thus, you can configure each proxy object with its' own individual advice(s) using the following syntax

// Will return un-advised instance of proxy object
MyInterface myProxyObject1 = (MyInterface)ctx.GetObject("MyObject");

// myProxyObject1 instance now has an advice attached to it.
IAdvised advised = (IAdvised)myProxyObject1;
advised.AddAdvice( new DebugAdvice() );

// Will return a new, un-advised instance of proxy object
MyInterface myProxyObject2 = (MyInterface)ctx.GetObject("MyObject");

13.5.4. Proxying Classes

What if you need to proxy a class, rather than one or more interfaces?

Imagine that in our example above, there was no IPerson interface, rather we needed to advise a class called Person that didn't implement any business interface. In this case the ProxyFactoryObject will proxy all public virtual methods and properties if no interfaces are explicitly specified or if no interfaces are found to be present on the target object. One can configure Spring.NET to force the use of class proxies, rather than interface proxies, by setting the ProxyTargetType property on the ProxyFactoryObject above to true.

Class proxying works by generating a subclass of the target class at runtime. Spring.NET configures this generated subclass to delegate method calls to the original target: the subclass is used to implement the Decorator pattern, weaving in the advice.

Class proxying should generally be transparent to users. However, there is an important issue to consider: Non-virtual methods can't be advised, as they can't be overridden. This may be a limiting factor when using existing code as it has been common practice not to declare methods as virtual by default.

13.5.5. Concise proxy definitions

Especially when defining transactional proxies, if you do not make use of the transaction namespace, you may end up with many similar proxy definitions. The use of parent and child object definitions, along with inner object definitions, can result in much cleaner and more concise proxy definitions.

First a parent, template, object definition is created for the proxy:

<object id="txProxyTemplate"  abstract="true"
            type="Spring.Transaction.Interceptor.TransactionProxyFactoryObject, Spring.Data">
        
        <property name="PlatformTransactionManager" ref="adoTransactionManager"/>
        <property name="TransactionAttributes">
            <name-values>
                <add key="*" value="PROPAGATION_REQUIRED"/>
            </name-values>
        </property>
    </object>

This will never be instantiated itself, so may actually be incomplete. Then each proxy which needs to be created is just a child object definition, which wraps the target of the proxy as an inner object definition, since the target will never be used on its own anyway.

<object name="testObjectManager" parent="txProxyTemplate">
        <property name="Target">
            <object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests">
                <property name="TestObjectDao" ref="testObjectDao"/>
            </object>
        </property> 
</object>

It is of course possible to override properties from the parent template, such as in this case, the transaction propagation settings:

<object name="testObjectManager" parent="txProxyTemplate">
        <property name="Target">
            <object type="Spring.Data.TestObjectManager, Spring.Data.Integration.Tests">
                <property name="TestObjectDao" ref="testObjectDao"/>
            </object>
        </property> 
        <property name="TransactionAttributes">
            <name-values>
                <add key="Save*" value="PROPAGATION_REQUIRED"/>
                <add key="Delete*" value="PROPAGATION_REQUIRED"/>
                <add key="Find*" value="PROPAGATION_REQUIRED,readonly"/>
            </name-values>
        </property>
</object>

Note that in the example above, we have explicitly marked the parent object definition as abstract by using the abstract attribute, as described previously, so that it may not actually ever be instantiated. Application contexts (but not simple object factories) will by default pre-instantiate all singletons. It is therefore important (at least for singleton object) that if you have a (parent) object definition which you intend to use only as a template, and this definition specifies a class, you must make sure to set the abstract attribute to true, otherwise the application context will actually try to pre-instantiate it.

13.6. Proxying mechanisms

Spring creates AOP proxies built at runtime through the use of the TypeBuilder API.

Two types of proxies can be created, composition based or inheritance based. If the target object implements at least one interface then a composition based proxy will be created, otherwise an inheritance based proxy will be created.

The composition based proxy is implemented by creating a type that implements all the interfaces specified on the target object. The actual class name of this dynamic type is 'GUID' like. A private field holds the target object and the dynamic type implementation will first execute any advice before or after making the target object method call on the target object.

The inheritance based mechanism creates a dynamic type where that inherits from the target type. This lets you downcast to the target type if needed. Please note that in both cases a target method implementation that calls other methods on the target object will not be advised. To force inheritance based proxies you should either set the ProxyTargetType to true property of a ProxyFactory or set the XML namespace element proxy-target-type = true when using an AOP schema based configuration.

[Note]Note

An important alternative approach to inheritance based proxies is disucssed in the next section.

In .NET 2.0 you can define the assembly level attribute, InternalsVisibleTo, to allow access of internal interfaces/classes to specified 'friend' assemblies. If you need to create an AOP proxy on an internal class/interface add the following code, [assembly: InternalsVisibleTo("Spring.Proxy")] and [assembly: InternalsVisibleTo("Spring.DynamicReflection")] to your to AssemblyInfo file.

13.6.1. InheritanceBasedAopConfigurer

There is an important limitation in the inheritance based proxy as described above, all methods that manipulate the state of the object should be declared as virtual. Otherwise some method invocations get directed to the private 'target' field member and others to the base class. Winform object are an example of case where this approach does not apply. To address this limitation, a new post-processing mechanism was introduced in version 1.2 that creates a proxy type without the private 'target' field. Interception advice is added directly in the method body before invoking the base class method.

To use this new inheritance based proxy described in the note above, declare an instance of the InheritanceBasedAopConfigurer, and IObjectFactoryPostProcessor, in yoru configuraiton file. Here is an example.

<object type="Spring.Aop.Framework.AutoProxy.InheritanceBasedAopConfigurer, Spring.Aop">
  <property name="ObjectNames">
      <list>
          <value>Form*</value>
          <value>Control*</value>
      </list>
  </property>
  <property name="InterceptorNames">
      <list>
          <value>debugInterceptor</value>
      </list>
  </property>
</object>

<object id="debugInterceptor" type="AopPlay.DebugInterceptor, AopPlay"/>

This configuraiton style is similar to the autoproxy by name approach described here and is particuarly appropriate when you want to apply advice to WinForm classes.

13.7. Creating AOP Proxies Programatically with the ProxyFactory

It's easy to create AOP proxies Programatically using Spring.NET. This enables you to use Spring.NET AOP without dependency on Spring.NET IoC.

The following listing shows creation of a proxy for a target object, with one interceptor and one advisor. The interfaces implemented by the target object will automatically be proxied:

ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.AddAdvice(myMethodInterceptor);
factory.AddAdvisor(myAdvisor);
IBusinessInterface tb = (IBusinessInterface) factory.GetProxy();

The first step is to construct an object of type Spring.Aop.Framework.ProxyFactory. You can create this with a target object, as in the above example, or specify the interfaces to be proxied in an alternate constructor.

You can add interceptors or advisors, and manipulate them for the life of the ProxyFactory.

There are also convenience methods on ProxyFactory (inherited from AdvisedSupport) allowing you to add other advice types such as before and throws advice. AdvisedSupport is the superclass of both ProxyFactory and ProxyFactoryObject.

[Note]Note
Integrating AOP proxy creation with the IoC framework is best practice in most applications. We recommend that you externalize configuration from .NET code with AOP, as in general.

13.8. Manipulating Advised Objects

However you create AOP proxies, you can manipulate them using the Spring.Aop.Framework.IAdvised interface. Any AOP proxy can be cast to this interface, whatever other interfaces it implements. This interface includes the following methods and properties:

public interface IAdvised
{    
    IAdvisor[] Advisors {	get; }
    
    IIntroductionAdvisor[] Introductions { get; }
	
    void  AddInterceptor(IInterceptor interceptor);
	
    void  AddInterceptor(int pos, IInterceptor interceptor);		

    void  AddAdvisor(IAdvisor advisor);

    void  AddAdvisor(int pos, IAdvisor advisor);
		
    void  AddIntroduction(IIntroductionAdvisor advisor);
		
    void  AddIntroduction(int pos, IIntroductionAdvisor advisor);    

    int IndexOf(IAdvisor advisor);

    int IndexOf(IIntroductionAdvisor advisor);

    bool RemoveAdvisor(IAdvisor advisor);

    void RemoveAdvisor(int index);
	
    bool RemoveInterceptor(IInterceptor interceptor);

    bool RemoveIntroduction(IIntroductionAdvisor advisor);
	
    void RemoveIntroduction(int index);

    void ReplaceIntroduction(int index, IIntroductionAdvisor advisor);

    bool ReplaceAdvisor(IAdvisor a, IAdvisor b);
}

The Advisors property will return an IAdvisor for every advisor, interceptor or other advice type that has been added to the factory. If you added an IAdvisor, the returned advisor at this index will be the object that you added. If you added an interceptor or other advice type, Spring.NET will have wrapped this in an advisor with a IPointcut that always returns true. Thus if you added an IMethodInterceptor, the advisor returned for this index will be a DefaultPointcutAdvisor returning your IMethodInterceptor and an IPointcut that matches all types and methods.

The AddAdvisor() methods can be used to add any IAdvisor. Usually this will be the generic DefaultPointcutAdvisor, which can be used with any advice or pointcut (but not for introduction).

By default, it's possible to add or remove advisors or interceptors even once a proxy has been created. The only restriction is that it's impossible to add or remove an introduction advisor, as existing proxies from the factory will not show the interface change. (You can obtain a new proxy from the factory to avoid this problem.)

It's questionable whether it's advisable (no pun intended) to modify advice on a business object in production, although there are no doubt legitimate usage cases. However, it can be very useful in development: for example, in tests. I have sometimes found it very useful to be able to add test code in the form of an interceptor or other advice, getting inside a method invocation I want to test. (For example, the advice can get inside a transaction created for that method: for example, to run SQL to check that a database was correctly updated, before marking the transaction for roll back.)

Depending on how you created the proxy, you can usually set a Frozen flag, in which case the IAdvised IsFrozen property will return true, and any attempts to modify advice through addition or removal will result in an AopConfigException. The ability to freeze the state of an advised object is useful in some cases: For example, to prevent calling code removing a security interceptor.

13.9. Using the "autoproxy" facility

So far we've considered explicit creation of AOP proxies using a ProxyFactoryObject or similar factory objects. For applications that would like create many AOP proxies, say across all the classes in a service layer, this approach can lead to a lengthy configuration file. To simplify the creation of many AOP proxies Spring provides "autoproxy" capabilities that will automatically proxy object definitions based on higher level criteria that will group together multiple objects as candidates to be proxied.

This functionality is built on Spring "object post-processor" infrastructure, which enables modification of any object definition as the container loads. Refer to Section 5.9.1, “Customizing objects with IObjectPostProcessors” for general information on object post-processors.

In this model, you set up some special object definitions in your XML object definition file configuring the auto proxy infrastructure. This allows you just to declare the targets eligible for autoproxying: you don't need to use ProxyFactoryObject.

  • Using an autoproxy creator that refers to specific objects in the current context.

  • A special case of autoproxy creation that deserves to be considered separately; autoproxy creation driven by source-level attributes.

Autoproxying in general has the advantage of making it impossible for callers or dependencies to obtain an un-advised object. Calling GetObject("MyBusinessObject1") on an ApplicationContext will return an AOP proxy, not the target business object. The "inline object" idiom shown earlier in Section 13.5.3, “Proxying Interfaces” also offers this benefit.)

13.9.1. Autoproxy object definitions

The namespace Spring.Aop.Framework.AutoProxy provides generic autoproxy infrastructure, should you choose to write your own autoproxy implementations, as well as several out-of-the-box implementations. Two implementations are provided, ObjectNameAutoProxyCreator and DefaultAdvisorAutoProxyCreator. These are discussed in the following sections.

13.9.1.1. ObjectNameAutoProxyCreator

The ObjectNameAutoProxyCreator automatically creates AOP proxies for object with names matching literal values or wildcards. The pattern matching expressions supported are of the form "*name", "name*", and "*name*" and exact name matching, i.e. "name". The following simple classes are used to demonstrate this autoproxy functionality.

public enum Language
{
    English = 1,
    Portuguese = 2,
    Italian = 3
}


public interface IHelloWorldSpeaker
{
   void SayHello();
}


public class HelloWorldSpeaker : IHelloWorldSpeaker
{
    private Language language;

    public Language Language
    {
        set { language = value; }
        get { return language; }
    }

    public void SayHello()
    {
        switch (language)
        {
            case Language.English:
                Console.WriteLine("Hello World!");
                break;
            case Language.Portuguese:
                Console.WriteLine("Oi Mundo!");
                break;
            case Language.Italian:
                Console.WriteLine("Ciao Mondo!");
                break;
        }
    }
}


public class DebugInterceptor : IMethodInterceptor
{
    public object Invoke(IMethodInvocation invocation)
    {
        Console.WriteLine("Before: " + invocation.Method.ToString());
        object rval = invocation.Proceed();
        Console.WriteLine("After:  " + invocation.Method.ToString());
        return rval;
    }

}

The following XML is used to automatically create an AOP proxy and apply a Debug interceptor to object definitions whose names match "English*" and "PortugueseSpeaker".

<object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
  <property name="ObjectNames">
      <list>
          <value>English*</value>
          <value>PortugeseSpeaker</value>
      </list>
  </property>
  <property name="InterceptorNames">
      <list>
          <value>debugInterceptor</value>
      </list>
  </property>
</object>

<object id="debugInterceptor" type="AopPlay.DebugInterceptor, AopPlay"/>

<object id="EnglishSpeakerOne" type="AopPlay.HelloWorldSpeaker, AopPlay">
  <property name="Language" value="English"/>
</object>

<object id="EnglishSpeakerTwo" type="AopPlay.HelloWorldSpeaker, AopPlay">
  <property name="Language" value="English"/>
</object>

<object id="PortugeseSpeaker" type="AopPlay.HelloWorldSpeaker, AopPlay">
  <property name="Language" value="Portuguese"/>
</object>
            
<object id="ItalianSpeakerOne" type="AopPlay.HelloWorldSpeaker, AopPlay">
  <property name="Language" value="Italian"/>
</object>

As with ProxyFactoryObject, there is an InterceptorNames property rather than a list of interceptors, to allow correct behavior for prototype advisors. Named "interceptors" can be advisors or any advice type.

The same advice will be applied to all matching objects. Note that if advisors are used (rather than the interceptor in the above example), the pointcuts may apply differently to different objects.

Running the following simple program demonstrates the application of the AOP interceptor.

IApplicationContext ctx = ContextRegistry.GetContext();
IDictionary speakerDictionary = ctx.GetObjectsOfType(typeof(IHelloWorldSpeaker));
foreach (DictionaryEntry entry in speakerDictionary)
{
    string name = (string)entry.Key;
    IHelloWorldSpeaker worldSpeaker = (IHelloWorldSpeaker)entry.Value;
    Console.Write(name + " says; ");
    worldSpeaker.SayHello(); 
}

The output is shown below

ItalianSpeakerOne says; Ciao Mondo!
EnglishSpeakerTwo says; Before: Void SayHello()
Hello World!
After:  Void SayHello()
PortugeseSpeaker says; Before: Void SayHello()
Oi Mundo!
After:  Void SayHello()
EnglishSpeakerOne says; Before: Void SayHello()
Hello World!
After:  Void SayHello()

13.9.1.2. DefaultAdvisorAutoProxyCreator

A more general and extremely powerful auto proxy creator is DefaultAdvisorAutoProxyCreator. This will automatically apply eligible advisors in the current application context, without the need to include specific object names in the autoproxy advisor's object definition. It offers the same merit of consistent configuration and avoidance of duplication as ObjectNameAutoProxyCreator.

Using this mechanism involves:

  • Specifying a DefaultAdvisorAutoProxyCreator object definition

  • Specifying any number of Advisors in the same or related contexts. Note that these must be Advisors, not just interceptors or other advices. This is necessary because there must be a pointcut to evaluate, to check the eligibility of each advice to candidate object definitions.

The DefaultAdvisorAutoProxyCreator will automatically evaluate the pointcut contained in each advisor, to see what (if any) advice it should apply to each object defined in the application context.

This means that any number of advisors can be applied automatically to each business object. If no pointcut in any of the advisors matches any method in a business object, the object will not be proxied.

The DefaultAdvisorAutoProxyCreator is very useful if you want to apply the same advice consistently to many business objects. Once the infrastructure definitions are in place, you can simply add new business objects without including specific proxy configuration. You can also drop in additional aspects very easily--for example, tracing or performance monitoring aspects--with minimal change to configuration.

The following example demonstrates the use of DefaultAdvisorAutoProxyCreator. Expanding on the previous example code used to demonstrate ObjectNameAutoProxyCreator we will add a new class, SpeakerDao, that acts as a Data Access Object to find and store IHelloWorldSpeaker objects.

public interface ISpeakerDao
{
    IList FindAll();

    IHelloWorldSpeaker Save(IHelloWorldSpeaker speaker);
}

public class SpeakerDao : ISpeakerDao
{
    public System.Collections.IList FindAll()
    {
        Console.WriteLine("Finding speakers...");
	// just a demo...fake the retrieval.
        Thread.Sleep(10000);
        HelloWorldSpeaker speaker = new HelloWorldSpeaker();
        speaker.Language = Language.Portuguese;

        IList list = new ArrayList();
        list.Add(speaker);
        return list;
    }

    public IHelloWorldSpeaker Save(IHelloWorldSpeaker speaker)
    {
        Console.WriteLine("Saving speaker...");
        // just a demo...not really saving...
        return speaker;
    }

}
The XML configuration specifies two Advisors, that is, the combination of advice (the behavior to add) and a pointcut (where the behavior should be applied). A RegularExpressionMethodPointcutAdvisor is used as a convenience to specify the pointcut as a regular expression that matches methods names. Other pointcuts of your own creation could be used, in which case a DefaultPointcutAdvisor would be used to define the Advisor. The object definitions for these advisors, advice, and SpeakerDao object are shown below
<object id="SpeachAdvisor" type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop">
    
    <property name="advice" ref="debugInterceptor"/>            
    <property name="patterns">
        <list>
            <value>.*Say.*</value>
        </list>
    </property>
    
</object>

<object id="AdoAdvisor" type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor, Spring.Aop">
    
    <property name="advice" ref="timingInterceptor"/>            
    <property name="patterns">
        <list>
            <value>.*Find.*</value>
        </list>
    </property>
    
</object>

// Advice
<object id="debugInterceptor" type="AopPlay.DebugInterceptor, AopPlay"/>

<object id="timingInterceptor" type="AopPlay.TimingInterceptor, AopPlay"/>

// Speaker DAO Object - has 'FindAll' Method.
<object id="speakerDao" type="AopPlay.SpeakerDao, AopPlay"/>

// HelloWorldSpeaker objects as previously listed.

Adding an instance of DefaultAdvisorAutoProxyCreator to the configuration file

<object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoProxyCreator, Spring.Aop"/>

will apply the debug interceptor on all objects in the context that have a method that contains the text "Say" and apply the timing interceptor on objects in the context that have a method that contains the text "Find". Running the following code demonstrates this behavior. Note that the "Save" method of SpeakerDao does not have any advice applied to it.

IApplicationContext ctx = ContextRegistry.GetContext();
IDictionary speakerDictionary = ctx.GetObjectsOfType(typeof(IHelloWorldSpeaker));
foreach (DictionaryEntry entry in speakerDictionary)
{
    string name = (string)entry.Key;
    IHelloWorldSpeaker worldSpeaker = (IHelloWorldSpeaker)entry.Value;
    Console.Write(name + " says; ");
    worldSpeaker.SayHello(); 
}
ISpeakerDao dao = (ISpeakerDao)ctx.GetObject("speakerDao");
IList speakerList = dao.FindAll();
IHelloWorldSpeaker speaker = dao.Save(new HelloWorldSpeaker());

This produces the following output

ItalianSpeakerOne says; Before: Void SayHello()
Ciao Mondo!
After:  Void SayHello()
EnglishSpeakerTwo says; Before: Void SayHello()
Hello World!
After:  Void SayHello()
PortugeseSpeaker says; Before: Void SayHello()
Oi Mundo!
After:  Void SayHello()
EnglishSpeakerOne says; Before: Void SayHello()
Hello World!
After:  Void SayHello()
Finding speakers...
Elapsed time = 00:00:10.0154745
Saving speaker...

The DefaultAdvisorAutoProxyCreator offers support for filtering (using a naming convention so that only certain advisors are evaluated, allowing use of multiple, differently configured, AdvisorAutoProxyCreators in the same factory) and ordering. Advisors can implement the Spring.Core.IOrdered interface to ensure correct ordering if this is an issue. The default is unordered.

13.9.1.3. AbstractAutoProxyCreator

This is the superclass of DefaultAdvisorAutoProxyCreator. You can create your own autoproxy creators by subclassing this class, in the unlikely event that advisor definitions offer insufficient customization to the behavior of the framework DefaultAdvisorAutoProxyCreator.

13.9.2. Using attribute-driven auto-proxying

A particularly important type of autoproxying is driven by attributes. The programming model is similar to using Enterprise Services with ServicedComponents.

In this case, you use the DefaultAdvisorAutoProxyCreator, in combination with Advisors that understand attributes. The Advisor pointcut is identified by the presence of .NET attribute in the source code and it is configured via the data and/or methods of the attribute. This is a powerful alternative to identifying the advisor pointcut and advice configuration through traditional property configuration, either programmatic or through XML based configuration.

Several of the aspect provided with Spring use attribute driven autoproxying. The most prominent example is Transaction support.

13.10. Using AOP Namespace

The AOP namespace allows you to define an advisor, i.e pointcut + 1 piece of advice, in a more declarative manner. Under the covers the DefaultAdvisorAutoProxyCreator is being used. Here is an example,

<objects xmlns="http://www.springframework.net"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xmlns:aop="http://www.springframework.net/aop">

	<aop:config>

		<aop:advisor id="getDescriptionAdvisor" pointcut-ref="getDescriptionCalls" advice-ref="getDescriptionCounter"/>

	</aop:config>

	<object id="getDescriptionCalls" 
		type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop">
		<property name="patterns">
			<list>
				<value>.*GetDescription.*</value>
			</list>
		</property>
	</object>
	
	
	<object id="getDescriptionCounter" type="Spring.Aop.Framework.CountingBeforeAdvice, Spring.Aop.Tests"/>

	<object name="testObject" type="Spring.Objects.TestObject, Spring.Core.Tests"/>


</objects>

In this example, the TestObject, which implements the interface ITestObject, is having AOP advice applied to it. The method GetDescription() is specified as a regular expression pointcut. The aop:config tag and subsequent child tag, aop:advisor, brings together the pointcut with the advice.

In order to have Spring.NET recognise the aop namespace, you need to declare the namespace parser in the main Spring.NET configuration section. For convenience this is shown below. Please refer to the section titled context configuration for more extensive information..

<configuration>

  <configSections>
    <sectionGroup name="spring">

      <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
      <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
        
      <section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core"/> 
        
     </sectionGroup>
  </configSections>

  <spring>

    <parsers> 
      <parser type="Spring.Aop.Config.AopNamespaceParser, Spring.Aop" /> 
    </parsers>
                                            
        
    <context>
      <resource uri="config://spring/objects"/>       
    </context>     

    <objects xmlns="http://www.springframework.net">
        ...
    </objects>

  </spring>

</configuration>
        

13.11. Using TargetSources

Spring.NET offers the concept of a TargetSource, expressed in the Spring.Aop.ITargetSource interface. This interface is responsible for returning the "target object" implementing the joinpoint. The TargetSource implementation is asked for a target instance each time the AOP proxy handles a method invocation.

Developers using Spring.NET AOP don't normally need to work directly with TargetSources, but this provides a powerful means of supporting pooling, hot swappable and other sophisticated targets. For example, a pooling TargetSource can return a different target instance for each invocation, using a pool to manage instances.

If you do not specify a TargetSource, a default implementation is used that wraps a local object. The same target is returned for each invocation (as you would expect).

Let's look at the standard target sources provided with Spring.NET, and how you can use them.

When using a custom target source, your target will usually need to be a prototype rather than a singleton object definition. This allows Spring.NET to create a new target instance when required.

13.11.1. Hot swappable target sources

The org.Spring.NETframework.aop.target.HotSwappableTargetSource exists to allow the target of an AOP proxy to be switched while allowing callers to keep their references to it.

Changing the target source's target takes effect immediately. The HotSwappableTargetSource is thread safe.

You can change the target via the swap() method on HotSwappableTargetSource as follows:

HotSwappableTargetSource swapper = 
    (HotSwappableTargetSource) objectFactory.GetObject("swapper");
object oldTarget = swapper.swap(newTarget);

The XML definitions required look as follows:

<object id="initialTarget" type="MyCompany.OldTarget, MyCompany">
</object>

<object id="swapper" 
    type="Spring.Aop.Target.HotSwappableTargetSource, Spring.Aop">
    <constructor-arg><ref local="initialTarget"/></constructor-arg>
</object>

<object id="swappable" 
    type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop"
>
    <property name="targetSource">
        <ref local="swapper"/>
    </property>
</object>

The above swap() call changes the target of the swappable object. Clients who hold a reference to that object will be unaware of the change, but will immediately start hitting the new target.

Although this example doesn't add any advice--and it's not necessary to add advice to use a TargetSource--of course any TargetSource can be used in conjunction with arbitrary advice.

13.11.2. Pooling target sources

Using a pooling target source provides a programming model in which a pool of identical instances is maintained, with method invocations going to free objects in the pool.

A crucial difference between Spring.NET pooling and pooling in .NET Enterprise Services pooling is that Spring.NET pooling can be applied to any PONO. (Plain old .NET object). As with Spring.NET in general, this service can be applied in a non-invasive way.

Spring.NET provides out-of-the-box support using a pooling implementation based on Jakarta Commons Pool 1.1, which provides a fairly efficient pooling implementation. It's also possible to subclass Spring.Aop.Target.AbstractPoolingTargetSource to support any other pooling API.

Sample configuration is shown below:

<object id="businessObjectTarget" type="MyCompany.MyBusinessObject, MyCompany" singleton="false">
    ... properties omitted
</object>

<object id="poolTargetSource" type="Spring.Aop.Target.SimplePoolTargetSource, Spring.Aop">
    <property name="targetObjectName" value="businessObjectTarget"/>
    <property name="maxSize" value="25"/>
</object>

<object id="businessObject" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
    <property name="targetSource" ref="poolTargetSource"/>
    <property name="interceptorNames" value="myInterceptor"/>
</object>

Note that the target object--"businessObjectTarget" in the example--must be a prototype. This allows the PoolingTargetSource implementation to create new instances of the target to grow the pool as necessary. See the SDK documentation for AbstractPoolingTargetSource and the concrete subclass you wish to use for information about it's properties: maxSize is the most basic, and always guaranteed to be present.

In this case, "myInterceptor" is the name of an interceptor that would need to be defined in the same IoC context. However, it isn't necessary to specify interceptors to use pooling. If you want only pooling, and no other advice, don't set the interceptorNames property at all.

It's possible to configure Spring.NET so as to be able to cast any pooled object to the Spring.Aop.Target.PoolingConfig interface, which exposes information about the configuration and current size of the pool through an introduction. You'll need to define an advisor like this:

<object id="poolConfigAdvisor" 
    type="Spring.Object.Factory.Config.MethodInvokingFactoryObject, Spring.Aop">
    <property name="target" ref="poolTargetSource" />
    <property name="targetMethod" value="getPoolingConfigMixin" />
</object>

This advisor is obtained by calling a convenience method on the AbstractPoolingTargetSource class, hence the use of MethodInvokingFactoryObject. This advisor's name ('poolConfigAdvisor' here) must be in the list of interceptor names in the ProxyFactoryObject exposing the pooled object.

The cast will look as follows:

PoolingConfig conf = (PoolingConfig) objectFactory.GetObject("businessObject");
Console.WriteLine("Max pool size is " + conf.getMaxSize());

Pooling stateless service objects is not usually necessary. We don't believe it should be the default choice, as most stateless objects are naturally threadsafe, and instance pooling is problematic if resources are cached.

Simpler pooling is available using autoproxying. It's possible to set the TargetSources used by any autoproxy creator.

13.11.3. Prototype target sources

Setting up a "prototype" target source is similar to a pooling TargetSource. In this case, a new instance of the target will be created on every method invocation. Although the cost of creating a new object may not be high, the cost of wiring up the new object (satisfying its IoC dependencies) may be more expensive. Thus you shouldn't use this approach without very good reason.

To do this, you could modify the poolTargetSource definition shown above as follows. (the name of the definition has also been changed, for clarity.)

<object id="prototypeTargetSource" 
        type="Spring.Aop.Target.PrototypeTargetSource, Spring.Aop">
    <property name="targetObjectName" value="businessObject" />
</object>

There is only one property: the name of the target object. Inheritance is used in the TargetSource implementations to ensure consistent naming. As with the pooling target source, the target object must be a prototype object definition, the singleton property of the target should be set to false.

13.11.4. ThreadLocal target sources

ThreadLocal target sources are useful if you need an object to be created for each incoming request (per thread that is). The concept of a ThreadLocal provides a facility to transparently store resource alongside a thread. Setting up a ThreadLocalTargetSource is pretty much the same as was explained for the other types of target source:

<object id="threadlocalTargetSource" 
        type="Spring.Aop.Target.ThreadLocalTargetSource, Spring.Aop">
    <property name="targetObjectName" value="businessObject" />
</object>

13.12. Defining new Advice types

Spring.NET AOP is designed to be extensible. While the interception implementation strategy is presently used internally, it is possible to support arbitrary advice types in addition to interception around, before, throws, and after returning advice, which are supported out of the box.

The Spring.Aop.Framework.Adapter package is an SPI (Service Provider Interface) package allowing support for new custom advice types to be added without changing the core framework. The only constraint on a custom Advice type is that it must implement the AopAlliance.Aop.IAdvice tag interface.

Please refer to the Spring.Aop.Framework.Adapter namespace documentation for further information.

13.13. Further reading and resources

The Spring.NET team recommends the excellent AspectJ in Action by Ramnivas Laddad (Manning, 2003) for an introduction to AOP.

If you are interested in more advanced capabilities of Spring.NET AOP, take a look at the test suite as it illustrates advanced features not discussed in this document.