Introduction to Spring.NET Factories and AOP
Posted on 7/17/2007
Aspect-Oriented Programming (AOP) is cooler than the iPhone. I know it's hard to believe but it's just the truth. For the uninitiated, AOP is probably best described as software modularization taken to a whole new level. The Spring.NET framework has some fantastic AOP functionality built right in to facilitate Inversion of Control (IoC) through several very powerful object factory patterns. In this first of several articles exploring Spring.NET, let's examine how object factories are used to make Spring.NET's AOP and IoC containers really simple and powerful for solving everyday programming problems.
Download the ObjectFactoryBenchmarks sample code related to this article (258KB).
Backgrounder
Let's start with a little backgrounder on AOP and Dependency Injection (DI). In the 1980s and 1990s, the big challege was in getting software developers to start using encapsulation as a way to separate what AOP calls concerns. A concern is a feature or set of them that represent something important to a particular program's implementation. One key idea behind encapsulation is the sheer protection that grouping data and code into relatively opaque objects affords. Hiding implementation and relying on well-known interfaces for communication has also become a hallmark of Service-Oriented Architecture (SOA) in recent years.
While encapsulation helps tremendously in getting programmers to reduce the intimacy between modules or components, it doesn't go far enough in the minds of OOD purists. The concerns behind the façade of an object often cut across that object boundaries. For example, one object might serve as a composition or aggregation for many other objects. Unfortunately, in this scenario, which is quite common, the likelihood that the composing object needs to know a lot about how the composed objects become instantiated is often very high. AOP describes this type of knowledge as a set of cross-cutting concerns. One of the goals of AOP is to further separate the dependencies that objects have with one another. It does that by addressing how these cross-cutting concerns are managed.
The concept of Inversion of Control (IoC) was born in part to deal with the problem. The key idea of IoC is that things like composition or aggregation objects should not be the controllers in most software systems. Using an external controller, the composition object can be instructed how and what to compose by injecting dependent objects into the mix through some other mechanism. This is why you'll often hear the terms IoC and Dependency Injection (DI) interchanged freely. DI is what most people mean when they talk about IoC anyway. Again, the goal of all of these ideas is to reduce the intimacy between classes to enhance the overall agility of the system, because when it comes right down to it, agility is measured as much by one's ability to throw away what doesn't work as it is by the ability to adopt what might work in its place. As Samuel Clemens might say, "Them's the bare bodkins."
Singing the Praises of Spring.NET
So, for this article, let's focus on how Spring.NET's object factories and AOP capabilities work together to create a framework were IoC can really work cleanly. If you are a serious .NET developer and you don't know about Spring.NET, run, don't walk, to the Spring.NET website and start taking a serious look at this enterprise framework. Having poured through the source code for Spring.NET, Microsoft's Enterprise Library 3.0 and several other frameworks, I can say unequivocally that Spring.NET is the lightest, most capable and most agile one out there. I just love it. It's not perfect but it's clean, easy to understand and very stable. Having been ported from the Java world after years of development there, the science and the patterns in Spring.NET are solid. Furthermore, when Spring was ported from Java to .NET, it was done correctly by preserving the very best practices but implementing them in very .NET-centric ways. I'll come off the soap box now.
The Spring.NET Context
One of the magic tricks that Spring.NET performs is that so much of your programming can be done through configuration files. The idea is simple. If the language used to describe the objects that make up your program is rich enough, lot's of IoC can be handled right there. Rather than creating so many objects in C# or Java code, Spring.NET and Spring encourage the use of configuration files to incubate objects that the program will use. This is done through what's called the Spring.NET context. For an ASP.NET web application, the Spring.NET context may be described in one or more web.config files, for example. In a console or Windows Forms-based application, an app.config file could be used. To get started, the Spring.NET configuration sections must be registered in your configuration file. Once you've added a reference to the Spring.Core assembly, you can register the configuration sections with something like this:
<?xml version="1.0" encoding="utf-8" ?>
<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" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects"/>
</context>
<objects xmlns="http://www.springframework.net">
</objects>
</spring>
</configuration>
Notice that both the spring/context type is registered and a sibling named spring/objects. The former one references the latter one with a <resource> directive. This separation and linking is important in web applications where objects may be defined across multiple web.config files. When your Spring.NET enabled application starts up, a configuration handler will parse the spring/context and subsequently the spring/objects sections to do whatever you tell it to. So, let's assume we have a class in our application called Worker defined in the ObjectFactoryBenchmarks namespace in an assembly called Utils. The class is so simple, I won't even define it here. It's just a little class with a method in it called DoSomething(). We could tell Spring.NET to create a Worker at startup with a simple configuration like this:
<?xml version="1.0" encoding="utf-8" ?>
<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" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects"/>
</context>
<objects xmlns="http://www.springframework.net">
<object id="OneWorker" type="ObjectFactoryBenchmarks.Worker, Utils"/>
</objects>
</spring>
</configuration>
Notice that the only difference from the first configuration shown is the introduction of an <object> element with an ID of OneWorker. Notice in that element that the fully qualified type name and assembly name are referenced. At startup, Spring.NET will load the assembly named Utils.dll, find the type called ObjectFactoryBenchmarks.Worker and create one of them stored by the name OneWorker within the Spring.NET context. To get a reference to the Worker object at runtime, you could write code like this:
Spring.Context.IApplicationContext ctx =
Spring.Context.Support.ContextRegistry.GetContext();
Worker w = ctx["OneWorker"] as Worker;
w.DoSomething();
Singletons and Prototypes
Of course, this could be made easier to read by importing the Spring.Context and Spring.Context.Support namespaces. The idea is very simple: Spring.NET creates the Worker on startup and your code gets that object from the context by the name OneWorker. It's important to understand that by default, objects created in the Spring.NET context are singletons. So if you created another Worker reference in the code above from the context object called OneWorker, the first one and the second one would actually be the same exact object. Singletons are great for thread-safe helper classes that operate in a completely stateless way. For the previous example, this is why I called the object created through the Spring.NET context OneWorker. There's just one of them, no matter how many times you ask for the object by that name. For our Worker class, perhaps we don't want that sort of behavior at all. When we ask for a Worker from the Spring.NET context, we want a new one every time. This is easy to do. Let's change the configuration a bit to look like this:
<?xml version="1.0" encoding="utf-8" ?>
<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" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects"/>
</context>
<objects xmlns="http://www.springframework.net">
<object id="Worker" type="ObjectFactoryBenchmarks.Worker, Utils"
singleton="false"/>
</objects>
</spring>
</configuration>
Notice that the only substantive change was the addition of the singleton attribute to the object element that defines the element with the new ID of Worker. I didn't call it OneWorker because now each time I ask for a Worker from the Spring.NET context, I'll get a new one. Assuming I have the right using directives in place, look at this example code to understand what I mean:
IApplicationContext ctx = ContextRegistry.GetContext();
Worker w1 = ctx["Worker"] as Worker;
w1.DoSomething();
Worker w2 = ctx["Worker"] as Worker;
w2.DoSomething();
In this case, Worker w1 and Worker w2 are two separate and distinct instances of the Worker class. The other important nuance is that by marking the Worker type as a non-singleton, Spring.NET does not create an instance of the type at startup. Spring.NET calls these non-singletons prototypes because each one must be created individually and each one is prototypical of the defining class. You should recognize that this is a factory pattern. In fact, the IApplicationContext interface we're using here is defined as an IObjectFactory interface as well. The indexers that we used above could easily have been written this way instead:
IApplicationContext ctx = ContextRegistry.GetContext();
Worker w1 = ctx.GetObject( "Worker" ) as Worker;
w1.DoSomething();
Worker w2 = ctx.GetObject( "Worker" ) as Worker;
w2.DoSomething();
Spring.NET Context IS-A Object Factory
So the IApplicationContext in Spring.NET is an object factory capable of producing singletons and prototypes. Interesting… And powerfully simple, too. Now that we know that the context is actually an object factory, I wonder what else can we do with it? Exploring through Spring.NET, you'll find all sorts of interesting object factory classes. One of these, called the ProxyFactoryClass sounds interesting. What can that do for us? In the following configuration, I have left out the upper part of the configuration file for brevity, showing just the <objects> section:
<objects xmlns="http://www.springframework.net">
<object id="WorkerPrototype"
type="ObjectFactoryBenchmarks.Worker, Utils"
singleton="false" />
<object id="Worker"
type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
<property name="ProxyTargetType">
<value>true</value>
</property>
<property name="TargetSource">
<object type="Spring.Aop.Target.PrototypeTargetSource, Spring.Aop">
<property name="TargetObjectName" value="WorkerPrototype" />
</object>
</property>
</object>
</objects>
Creating Object Proxies
In this configuration, I have renamed what we called the Worker type before to the name WorkerPrototype to make it clear what it really is. And I've added a new singleton object called Worker which is of type ProxyFactoryObject from the Spring.Aop.Framework namespace. Why I chose to call the ProxyFactoryObject a Worker will become clear shortly. The code I showed above to create Worker w1 and Worker w2 will work exactly as it did before. By asking for a "Worker", I'll get the real Worker type. How is that working? A "Worker" as defined in this last configuration is not a Worker type at all. It's a ProxyFactoryObject. The key to understanding what's going on is to look at the rest of the ProxyFactoryObject setup. Let's look at the second part first:
<property name="TargetSource">
<object type="Spring.Aop.Target.PrototypeTargetSource, Spring.Aop">
<property name="TargetObjectName" value="WorkerPrototype" />
</object>
</property>
The <property> element in this context language that Spring.NET uses is part of the IoC mechanism. Through configuration, what we are attempting to do above is to set the TargetSource property on the ProxyFactoryObject to a certain value. That value is an object of type PrototypeTargetSource which in turn has one of its properties called TargetObjectName set to the value WorkerPrototype. Head spinning yet? It's really pretty simple once you get a feel for the context language that Spring.NET uses. To recap this in English, we're saying through this configuration that we want to create a factory that proxies other objects of type WorkerPrototype. The first bit of extra information that we're injecting into the ProxyFactoryObject is this:
<property name="ProxyTargetType">
<value>true</value>
</property>
Introducing Advisors and Interceptors
This simply says to set a Boolean property called ProxyTargetType on the new factory to true. This will be important in a moment. So you may be asking, "What's the big deal? You wrapped a prototype for the Worker class with another factory?" It doesn't make any sense, right? Yes and no. Now that we have the right kind of configuration in place, let me introduce you to the concept of advice in AOP. Advice deals with those cross-cutting concerns I spoke about earlier. Spring.NET lets you create advisors which can be attached through configuration to objects created through the various objet factory classes that Spring.NET supports. There are different types of advice such as:
- Before - an advisor that gets called before something happens
- After - an advisor that gets called after something happens
- Surrounds - an advisor that gets called before and after something happens
- Interception - an advisor that gets called instead of something else
There are other types of advice but these are the ones you are likely to use first. Let's imagine some before advice that we would like to inject into the proxy factory we created above. For simplicity, the before advice, in this case, will just write out a message to the Debug console. Here's the code:
using System;
using System.Diagnostics;
using System.Reflection;
using Spring.Aop;
namespace ObjectFactoryBenchmark.Advice
{
public class DebugBeforeAdvice : IMethodBeforeAdvice
{
public void Before( MethodInfo method, object[] args, object target )
{
Debug.WriteLine( String.Format( "Before {0}", method.Name ) );
}
}
}
To make this advice execute before every call to the DoSomething method on our Worker class, we need to do two things: create an advisor of the DebugBeforeAdvice type defined above and make the Worker objects that get created use it whenever the DoSomething method gets called. Well, since we know that the Spring.NET context is an object factory, creating a DebugBeforeAdvice instance as startup should be easy. Let's take a look at the configuration that makes this work:
<objects xmlns="http://www.springframework.net">
<object id="DebugBeforeAdvisor"
type="Spring.Aop.Support.NameMatchMethodPointcutAdvisor, Spring.Aop">
<property name="Advice">
<object id="beforeDebugAdvice"
type="ObjectFactoryBenchmark.Advice.DebugBeforeAdvice, Utils"/>
</property>
<property name="MappedNames">
<list>
<value>DoSomething</value>
</list>
</property>
</object>
<object id="WorkerPrototype"
type="ObjectFactoryBenchmarks.Worker, Utils" singleton="false" />
<object id="Worker"
type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
<property name="ProxyTargetType">
<value>true</value>
</property>
<property name="TargetSource">
<object type="Spring.Aop.Target.PrototypeTargetSource, Spring.Aop">
<property name="TargetObjectName" value="WorkerPrototype" />
</object>
</property>
<property name="InterceptorNames">
<list>
<value>DebugBeforeAdvisor</value>
</list>
</property>
</object>
</objects>
Notice that the bottom section of the <objects> element looks mostly the same. At the top, an object called DebugBeforeAdvisor is created. And because it's not marked as a prototype, we know that it will be implemented as a singleton, i.e. exactly one of them will automatically be created at startup. The type of the advisor is interesting. It's called a NameMatchMethodPointcutAdvisor which is one type of those cross-cutting concerns I mentioned before. I won't get into the nuances of what a point-cut is here. Perhaps I'll do that in a future article. What's important for now is the first part of the type name: "NAME MATCH METHOD". That means to me that this advisor has the ability to match against the names of methods that get called on the object for which it provides advice. Indeed, the two properties injected inside the advisor are telling. The first one called Advice names the type of the before advice class shown above. This is the one that will simply write a message to the Debug console. The second property called MappedNames lists the name of the DoSomething method that we want to catch.
In the ProxyFactoryObject called Worker at the bottom of the configuration, I've inserted another property called InterceptorNames which lists that we would like to use that method name matching DebugBeforeAdvisor created at the top of the configuration as an interceptor. Now when you run the code that creates Worker w1 and Worker w2 above, each time the DoSomething method is called, you should see a message in the Debug console window (if you're running the code in a debugger).
The Epiphany
So, sit back and absorb what's just happened. I've been able to take a working bit of code and inject other code to intercept (advise) completely through the configuration file. This is the heart of Aspect-Oriented Programming (AOP). By using features of the Spring.NET's object factories, dependency injection and AOP proxies, we're now able to inject behaviors (if you will) into the method execution pipeline. Think of the possibilities for a moment. If I can inject code to write a message to the Debug console, why couldn't I write to a log file instead? Notice the parameters to the Before method defined in the DebugBeforeAdvice class above? They are:
- MethodInfo method - if you're familiar with reflection in .NET, you know that you can use this class to find out all sorts of interesting things about the method that was being called at the moment of advice
- object[] args - these are the arguments being passed to the method that's about to be invoked
- object target - this is a reference to the object that contains the method that's about to be invoked
Wow! Not only could you log during the before advisor, there's lots of juicy contextual information available through these parameters. In my example, I simply got the name of the method being called and wrote it out in a message to the Debug console. But you could do so much more. Now let's take a look at another kind of advisor called an interceptor. As it's name implies, an interceptor has the ability to do something instead of the method that was invoked. Here's a simple method interceptor class that doesn't do something different at all. Instead, it just snaps a timer on each side of the method call and logs theduration of the call to the Debug console:
using System;
using System.Diagnostics;
using AopAlliance.Intercept;
namespace ObjectFactoryBenchmark.Advice
{
public class MethodTimerAdvice : IMethodInterceptor
{
public object Invoke( IMethodInvocation invocation )
{
long before = DateTime.Now.Ticks;
object result = invocation.Proceed();
double seconds = ((double)(DateTime.Now.Ticks - before)) / 10000000d;
Debug.WriteLine( String.Format(
"Method {0} took {1} seconds to execute.",
invocation.Method.Name, seconds ) );
return result;
}
}
}
Notice that the call to the Proceed() method is wrapped by some timer code to calculate the number of seconds that the call takes. As its name implies, the Proceed() method simply proceeds to the method that was invoked. But you don't have to do that. You could call a completely different method or simply make up some return data on the spot. Through the invocation parameter, you can also switch or add parameters to the call, too. Using Spring.NET, I have written interceptors that update Windows performance counters, make web service calls, inject hidden parameters and so forth. It's pretty powerful stuff.
Composition Versus Inheritance-Based Proxies
OK, so there's one loose end here. What about the property on ProxyFactoryObject called ProxyTargetType that I mentioned above? Why was that Boolean property set to true in my configuration file? In Spring.NET, there are two basic types of proxies: composition and inheritance. The latter type is new to Spring.NET 1.1 which was released not too long before this article. With the older type of composition-based proxies, Spring.NET essentially wrapped the actual type being created with a lightweight object of another type and exposed the interfaces implemented on the wrapped type with some fancy footwork in the Reflection.Emit namespace. This worked OK but the reference that the user got back when using composition-based proxies wasn't of the type that was requested. In our example above, a composition-based proxy for the Worker class would not have an IS-A relationship with the Worker type. It would implement all of the interfaces declared on the type but it wouldn't actually be of the type requested.
In Spring.NET 1.1, we now have inheritance-based proxies in addition to the older composition type. Setting the ProxyTargetType property to true in the configuration above is the signal to the ProxyFactoryObject singleton to use inheritance when the proxy class is created. The key difference with inheritance proxies is that when the factory returns the reference to the object that's created, it can be cast to the type you requested because it IS-A instance of that type. The other key difference that you should know about inheritance-based proxies is that Spring.NET doesn't implement just the known interfaces for the type. Because it's using inheritance instead of composition, all of the virtual methods and properties on the target type are proxied in the object that's returned.
Conclusion
Being able to create proxies is really at the heart of AOP in Spring.NET because it's via those proxies that the aspects we're coding (or configuring) get injected. Before and after advisors, interceptors, etc. all depend on the proxy classes to do their work. In fact, if you look at the type names for the objects that are emitted by the ProxyFactoryObject when inheritance is being used, you'll see that the classes all begin with the prefix DecoratorAopProxy. The term decorator in the name implies that the purpose of the proxy is to decorate the original objects with newly injected behaviors like point-cut advisors and interceptors as shown above.
In the next installment in this series, we'll look at the performance penalties you might pay for using something so much cooler than an iPhone. The sneak peak is that it's faster and lighter-weight than you think it might be with some caveats. Tune in soon for more.