![]() |
This is an introductory guide to Aspect Oriented Programming (AOP) with Spring.NET. This guide assumes little to no prior experience of having
used Spring.NET AOP on the part of the reader.
However, it does assume a certain familiarity with
the terminology of AOP in general. It is probably better if you have read
(or at least have skimmed through) the AOP section of the reference
documentation beforehand, so that you are familiar with a) just what AOP
is, b) what problems AOP is addressing, and c) what the AOP concepts of
The examples in this guide are intentionally simplistic. One of the core aims of this guide is to get you up and running with Spring.NET's flavor of AOP in as short a time as possible. Having to comprehend even a simple object model in order to understand the AOP examples would not be conducive to learning Spring.NET AOP. It is left as an exercise for the reader to take the concepts learned from this guide and apply them to his or her own code base. Again, having said all of that, this guide concludes with a number of cookbook-style AOP 'recipes' that illustrate the application of Spring.NET's AOP offering in a real world context; additionally, the Spring.NET reference application contains a number of Spring.NET AOP aspects particular to it's own domain model (see Chapter 41, SpringAir - Reference Application).
This initial section introduces the basics of defining and then applying some simple advice. Lets see (a very basic) example of using Spring.NET AOP. The following example code simply applies advice that writes the details of an advised method call to the system console. Admittedly, this is not a particularly compelling or even useful application of AOP, but having worked through the example, you will then hopefully be able to see how to apply your own custom advice to perform useful work (transaction management, auditing, security enforcement, thread safety, etc). Before looking at the AOP code proper lets quickly look at the domain classes that are the target of the advice (in Spring.NET AOP terminology, an instance of the following class is going to be the advised object. public interface ICommand { object Execute(object context); } public class ServiceCommand : ICommand { public object Execute(object context) { Console.Out.WriteLine("Service implementation : [{0}]", context); return null; } } Find below the advice that is going to be applied to the
public class ConsoleLoggingAroundAdvice : IMethodInterceptor { public object Invoke(IMethodInvocation invocation) { Console.Out.WriteLine("Advice executing; calling the advised method...");
So thus far we have three artifacts: an interface
( ProxyFactory factory = new ProxyFactory(new ServiceCommand()); factory.AddAdvice(new ConsoleLoggingAroundAdvice()); ICommand command = (ICommand) factory.GetProxy(); command.Execute("This is the argument"); The result of executing the above snippet of code will look something like this... Advice executing; calling the advised method... Service implementation : [This is the argument] Advice executed; advised method returned The output shows that the advice (the
So what is happening here? The fact that the preceding code used a
class called The following image shows a graphical view of the flow of execution through a Spring.NET AOP proxy. ![]() One thing to note here is that the AOP proxy that was returned
from the call to the The remainder of this guide is concerned with fleshing out some of the finer details of Spring.NET AOP, but basically speaking, that's about it. As a first example of fleshing out one of those finer details, find below some Spring.NET XML configuration that does exactly the same thing as the previous example; it should also be added that this declarative style approach to Spring.NET AOP is preferred to the programmatic style. <object id="consoleLoggingAroundAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingAroundAdvice"/> <object id="myServiceObject" type="Spring.Aop.Framework.ProxyFactoryObject"> <property name="target"> <object id="myServiceObjectTarget" type="Spring.Examples.AopQuickStart.ServiceCommand"/> </property> <property name="interceptorNames"> <list> <value>consoleLoggingAroundAdvice</value> </list> </property> </object> ICommand command = (ICommand) ctx["myServiceObject"];
command.Execute();
Some comments are warranted concerning the above XML configuration
snippet. Firstly, note that the
Secondly, notice that the object definition corresponding to the
object that is retrieved from the IoC container is a
Thirdly, notice that the target of the
Finally, notice that the advice that is to be applied to the
target object is referred to by its object name in the list of the names
of interceptors for the '... if the The advice that was applied in the previous section was rather
indiscriminate with regard to which methods on the advised object were
to be advised... the This is great for simple examples and suchlike, but not so great
when you only want certain methods of an object to be advised. For
example, you may only want those methods beginning with
The mechanism that Spring.NET AOP uses to discriminate about where
advice is applied (i.e. which method invocations are intercepted) is
encapsulated by the So let's change the configuration of the advice such that it is
only applied to methods that contain the letters
public interface ICommand { void Execute(); void DoExecute(); } public class ServiceCommand : ICommand { public void Execute() { Console.Out.WriteLine("Service implementation : Execute()..."); } public void DoExecute() { Console.Out.WriteLine("Service implementation : DoExecute()..."); } } Please note that the advice itself (encapsulated within the
Programmatic configuration of the advice, taking into account the
fact that we only want methods that contain the letters
ProxyFactory factory = new ProxyFactory(new ServiceCommand()); factory.AddAdvisor(new DefaultPointcutAdvisor( new SdkRegularExpressionMethodPointcut("Do"), new ConsoleLoggingAroundAdvice())); ICommand command = (ICommand) factory.GetProxy(); command.DoExecute(); The result of executing the above snippet of code will look something like this... Intercepted call : about to invoke next item in chain...
Service implementation...
Intercepted call : returned
The output indicates that the advice was applied around the
invocation of the advised method, because the name of the method that
was executed contained the letters ProxyFactory factory = new ProxyFactory(new ServiceCommand()); factory.AddAdvisor( new DefaultPointcutAdvisor( new SdkRegularExpressionMethodPointcut("Do"), new ConsoleLoggingAroundAdvice())); ICommand command = (ICommand) factory.GetProxy(); // note that there is no 'Do' in this method name command.Execute(); Run the code snippet again; you will see that the advice will not
be applied : the pointcut is not matched (the method name does not
contain the letters Service implementation... XML configuration that accomplishes exactly the same thing as the previous programmatic configuration example can be seen below... <object id="consoleLoggingAroundAdvice" type="Spring.Aop.Support.RegularExpressionMethodPointcutAdvisor"> <property name="pattern" value="Do"/> <property name="advice"> <object type="Spring.Examples.AopQuickStart.ConsoleLoggingAroundAdvice"/> </property> </object> <object id="myServiceObject" type="Spring.Aop.Framework.ProxyFactoryObject"> <property name="target"> <object id="myServiceObjectTarget" type="Spring.Examples.AopQuickStart.ServiceCommand"/> </property> <property name="interceptorNames"> <list> <value>consoleLoggingAroundAdvice</value> </list> </property> </object> You'll will perhaps have noticed that this treatment of pointcuts
introduced the concept of an The first section should (hopefully) have demonstrated the basics of firstly defining advice, and secondly, of choosing where to apply that advice using the notion of a pointcut. Of course, there is a great deal more to Spring.NET AOP than the aforementioned single advice type and pointcut. This section continues the exploration of Spring.NET AOP, and describes the various advice and pointcuts that are available for you to use (yes, there is more than one type of advice and pointcut). The advice that was demonstrated and explained in the preceding
section is what is termed 'around advice'. The name
'around advice' is used because the advice is
applied around the target method invocation. In the
specific case of the 'around advice' provides one with the opportunity to do things both before the target gets a chance to do anything, and after the target has returned: one even gets a chance to inspect (and possibly even totally change) the return value. Sometimes you don't need all that power though. If we stick with
the example of the 'before advice' is just that... it is
advice that runs before the target method
invocation is invoked. One does not get access to the target method
invocation itself, and one cannot return a value... this is a good
thing, because it means that you cannot inadvertently forget to call
the 'before advice' in Spring.NET is defined by
the public class ConsoleLoggingBeforeAdvice : IMethodBeforeAdvice { public void Before(MethodInfo method, object[] args, object target) { Console.Out.WriteLine("Intercepted call to this method : " + method.Name); Console.Out.WriteLine(" The target is : " + target); Console.Out.WriteLine(" The arguments are : "); if(args != null) { foreach (object arg in args) { Console.Out.WriteLine("\t: " + arg); } } } } Let's apply a single instance of the
ProxyFactory factory = new ProxyFactory(new ServiceCommand()); factory.AddAdvice(new ConsoleLoggingBeforeAdvice()); ICommand command = (ICommand) factory.GetProxy(); command.Execute(); The result of executing the above snippet of code will look something like this... Intercepted call to this method : Execute The target is : Spring.Examples.AopQuickStart.ServiceCommand The arguments are : The output clearly indicates that the advice was applied
before the invocation of the advised
method. Notice that in contrast to 'around
advice', with 'before advice' there is
no chance of forgetting to call the If you can use 'before advice', then do so. The simpler programming model offered by 'before advice' means that there is less to remember, and thus potentially less things to get wrong. Here is the Spring.NET XML configuration for applying our 'before advice' declaratively... <object id="beforeAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingBeforeAdvice"/> <object id="myServiceObject" type="Spring.Aop.Framework.ProxyFactoryObject"> <property name="target"> <object id="myServiceObjectTarget" type="Spring.Examples.AopQuickStart.ServiceCommand"/> </property> <property name="interceptorNames"> <list> <value>beforeAdvice</value> </list> </property> </object> Just as 'before advice' defines advice that executes before an advised target, 'after advice' is advice that executes after a target has been executed. 'after advice' in Spring.NET is defined by
the public class ConsoleLoggingAfterAdvice : IAfterReturningAdvice { public void AfterReturning( object returnValue, MethodInfo method, object[] args, object target) { Console.Out.WriteLine("This method call returned successfully : " + method.Name); Console.Out.WriteLine(" The target was : " + target); Console.Out.WriteLine(" The arguments were : "); if(args != null) { foreach (object arg in args) { Console.Out.WriteLine("\t: " + arg); } } Console.Out.WriteLine(" The return value is : " + returnValue); } } Let's apply a single instance of the
ProxyFactory factory = new ProxyFactory(new ServiceCommand()); factory.AddAdvice(new ConsoleLoggingAfterAdvice()); ICommand command = (ICommand) factory.GetProxy(); command.Execute(); The result of executing the above snippet of code will look something like this... This method call returned successfully : Execute The target was : Spring.Examples.AopQuickStart.ServiceCommand The arguments were : The return value is : null The output clearly indicates that the advice was applied
after the invocation of the advised
method. Again, it bears repeating that your real world development
will actually have an advice implementation that does something useful
after the invocation of an advised method. Notice that in contrast to
'around advice', with 'after
advice' there is no chance of forgetting to call the
The best-practice rule for 'after advice' is much the same as it is for 'before advice'; namely that if you can use 'after advice', then do so (in preference to using 'around advice'). The simpler programming model offered by 'after advice' means that there is less to remember, and thus less things to get potentially wrong. A possible use case for 'after advice' would include performing access control checks on the return value of an advised method invocation; consider the case of a service that returns a list of document URI's... depending on the identity of the (Windows) user that is running the program that is calling this service, one could strip out those URI's that contain sensitive data for which the user does not have sufficient privileges to access. That is just one (real world) scenario... I'm sure you can think of plenty more that are a whole lot more relevant to your own development needs. Here is the Spring.NET XML configuration for applying the 'after advice' declaratively... <object id="afterAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingAfterAdvice"/> <object id="myServiceObject" type="Spring.Aop.Framework.ProxyFactoryObject"> <property name="target"> <object id="myServiceObjectTarget" type="Spring.Examples.AopQuickStart.ServiceCommand"/> </property> <property name="interceptorNames"> <list> <value>afterAdvice</value> </list> </property> </object> So far we've covered 'around advice', 'before advice', and 'after advice'... these advice types will see you through most if not all of your AOP needs. However, one of the remaining advice types that Spring.NET has in its locker is 'throws advice'. 'throws advice' is advice that executes when an advised method invocation throws an exception.. hence the name. One basically applies the 'throws advice' to a target object in much the same way as any of the previously mentioned advice types. If during the execution of ones application none of any of the advised methods throws an exception, then the 'throws advice' will never execute. However, if during the execution of your application an advised method does throw an exception, then the 'throws advice' will kick in and be executed. You can use 'throws advice' to apply a common exception handling policy across the various objects in your application, or to perform logging of every exception thown by an advised method, or to alert (perhaps via email) the support team in the case of particularly of critical exceptions... the list of possible uses cases is of course endless. The 'throws advice' type in Spring.NET is
defined by the public interface IThrowsAdvice : IAdvice { } Yes, that is really it... it is a marker interface that has no methods on it. You may be wondering how Spring.NET determines which methods to call to effect the running of one's 'throws advice'. An example would perhaps be illustrative at this point, so here is some simple Spring.NET style 'throws advice'... public class ConsoleLoggingThrowsAdvice : IThrowsAdvice { public void AfterThrowing(Exception ex) { Console.Out.WriteLine("Advised method threw this exception : " + ex); } } Lets also change the implementation of the
public class ServiceCommand : ICommand { public void Execute() { throw new UnauthorizedAccessException(); } } Let's programmatically apply the 'throws
advice' (an instance of our
ProxyFactory factory = new ProxyFactory(new ServiceCommand()); factory.AddAdvice(new ConsoleLoggingThrowsAdvice()); ICommand command = (ICommand) factory.GetProxy(); command.Execute(); The result of executing the above snippet of code will look something like this... Advised method threw this exception : System.UnauthorizedAccessException: Attempted to perform an unauthorized operation. As can be seen from the output, the
In Spring.NET, 'throws advice' means that
you have to define a class that implements the
void AfterThrowing(Exception ex)
Basically, your exception handling method has to be named
Your exception handling method must (at the very least) declare
a parameter that is an void AfterThrowing(ArgumentException ex)
Note that your exception handling method can have any return
type, but returning any value from a Spring.NET 'throws
advice' method would be a waste of time... the Spring.NET
AOP infrastructure will simply ignore the return value, so always
define the return type of your exception handling methods to be
Finally, here is the Spring.NET XML configuration for applying the 'throws advice' declaratively... <object id="throwsAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingThrowsAdvice"/> <object id="myServiceObject" type="Spring.Aop.Framework.ProxyFactoryObject"> <property name="target"> <object id="myServiceObjectTarget" type="Spring.Examples.AopQuickStart.ServiceCommand"/> </property> <property name="interceptorNames"> <list> <value>throwsAdvice</value> </list> </property> </object> One thing that cannot be done using 'throws
advice' is exception swallowing. It is not possible to
define an exception handling method in a 'throws
advice' implementation that will swallow any exception and
prevent said exception from bubbling up the call stack. The nearest
thing that one can do is define an exception handling method in a
'throws advice' implementation that will wrap the
handled exception in another exception; one would then throw the
wrapped exception in the body of one's exception handling method. One
can use this to implement some sort of exception translation or
exception scrubbing policy, in which implementation specific
exceptions (such as public class DataAccessExceptionScrubbingThrowsAdvice : IThrowsAdvice { public void AfterThrowing (SqlException ex) { // business objects in higher level service layer need only deal with PersistenceException... throw new PersistenceException ("Cannot access persistent storage.", ex.StackTrace); } } Spring.NET's data access library already has this kind of functionality (and is a whole lot more sophisticated)... the above example is merely being used for illustrative purposes. This treatment of 'throws advice', and of
Spring.NET's implementation of it is rather simplistic.
'throws advice' features that have been omitted
include the fact that one can define exception handling methods that
permit access to the original object, method, and method arguments of
the advised method invocation that threw the original exception. This
is a quickstart guide though, and is not meant to be exhaustive... do
consult the 'throws advice' section of the
reference documentation, which describes how to declare an exception
handling method that gives one access to the above extra objects, and
how to declare multiple exception handling methods on the same
In a nutshell, introductions are all about adding new state and behaviour to arbitrary objects... transparently and at runtime. Introductions (also called mixins) allow one to emulate multiple inheritance, typically with an eye towards applying crosscutting state and operations to a wide swathe of objects in your application that don't share the same inheritance hierarchy. The examples shown so far have all demonstrated the application of a single advice instance to an advised object. Spring.NET's flavor of AOP would be pretty poor if one could only apply a single advice instance per advised object... it is perfectly valid to apply multiple advice to an advised object. For example, one might apply transactional advice to a service object, and also apply a security access checking advice to that same advised service object. In the interests of keeping this section lean and tight, let's simply apply all of the advice types that have been previously described to a single advised object... in this first instance we'll just use the default pointcut which means that every possible joinpoint will be advised, and you'll be able to see that the various advice instances are applied in order. Please do consult the class definitions for the following
previously defined advice types to see exactly what each advice type
implementation does... we're going to be using single instances of the
You can find the following listing and executable application in
the AopQuickStart solution in the project
ProxyFactory factory = new ProxyFactory(new ServiceCommand()); factory.AddAdvice(new ConsoleLoggingBeforeAdvice()); factory.AddAdvice(new ConsoleLoggingAfterAdvice()); factory.AddAdvice(new ConsoleLoggingThrowsAdvice()); factory.AddAdvice(new ConsoleLoggingAroundAdvice()); ICommand command = (ICommand) factory.GetProxy(); command.Execute(); Here is the Spring.NET XML configuration for declaratively applying multiple advice. You can find the following listing and executable application in
the AopQuickStart solution in the project
<object id="throwsAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingThrowsAdvice"/> <object id="afterAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingAfterAdvice"/> <object id="beforeAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingBeforeAdvice"/> <object id="aroundAdvice" type="Spring.Examples.AopQuickStart.ConsoleLoggingAroundAdvice"/> <object id="myServiceObject" type="Spring.Aop.Framework.ProxyFactoryObject"> <property name="target"> <object id="myServiceObjectTarget" type="Spring.Examples.AopQuickStart.ServiceCommand"/> </property> <property name="interceptorNames"> <list> <value>throwsAdvice</value> <value>afterAdvice</value> <value>beforeAdvice</value> <value>aroundAdvice</value> </list> </property> </object> In case it is not immediately apparent, remember that advice is just a plain old CLR object (a POCO); advice can have constructors that can take any number of parameters, and like any other .NET class, advice can have properties. What this means is that one can leverage the power of the Spring.NET IoC container to apply the IoC principle to one's advice, and in so doing reap all the benefits of Dependency Injection. Consider the case of throws advice that needs to report (fatal) exceptions to a first line support centre. The throws advice could declare a dependency on a reporting service via a .NET property, and the Spring.NET container could dependency inject the reporting service dependency into the throws advice when it is being created; the reporting dependency might be a simple Log4NET wrapper, or a Windows EventLog wrapper, or a custom reporting exception reporting service that sends detailed emails concerning the fatal exception. Also bear in mind the fact that Spring.NET's AOP implementation is quite independent of Spring.NET's IoC container. As you have seen, the various examples used in this have illustrated both programmatic and declarative AOP configuration (the latter being illustrated via Spring.NET's IoC XML configuration mechanism). The preceding treatment of Spring.NET AOP has (quite intentionally) been decidedly simple. The overarching aim was to convey the concepts of Spring.NET AOP... this section of the Spring.NET AOP guide contains a number of real world examples of the application of Spring.NET AOP. This example illustrates one of the more common usages of AOP... caching. Lets consider the scenario where we have some static reference
data that needs to be kept around for the duration of an application.
The data will almost never change over the uptime of an application, and
it exists only in the database to satisfy referential integrity amongst
the various relations in the database schema. An example of such static
(and typically immutable) reference data would be a collection of
The Data Access Object (DAO) that will load the collection of
public class AdoCountryDao : ICountryDao { public IList FindAllCountries () { // implementation elided for clarity... return countries; } } Ideally, what we would like to have happen is for the results of
the first call to the
The mechanism that this example is going to use to identify (or
pick out) areas in our application that we would like to apply caching
to is a .NET public class AdoCountryDao : ICountryDao { [Cache] public IList FindAllCountries () { // implementation elided for clarity... return countries; } } The SpringAir reference application that is packaged as part of the Spring.NET distribution comes with a working example of caching applied using Spring.NET AOP (see Chapter 41, SpringAir - Reference Application). This recipe show how easy it is to instrument the classes and objects in an application for performance monitoring. The performance monitoring implementation uses one of the (many) Windows performance counters to display and track the performance data. This final recipe describes a simple (but really quite useful) aspect... retry logic. Using Spring.NET AOP, it is quite easy to surround an operation such as a method that opens a connection to a database with a (configurable) aspect that tries to obtain a database connection any number of times in the event of a failure. Spring.NET AOP is an 80% AOP solution, in that it only tries to solve the 80% of those cases where AOP is a good fit in a typical enterprise application. This final section of the Spring.NET AOP guide describes where Spring.NET AOP is typically useful (the 80%), as well as where Spring.NET AOP is not a good fit (the 20%).
|