Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / XML

AOP Using Spring.NET - Part 1

4.80/5 (30 votes)
6 Jun 2008CPOL7 min read 1   1.7K  
Aspect Oriented Programming and how Spring.NET supports this.

Introduction

In my previous article, I discussed about Spring.NET's Dependency Injection technique and how it can be used in a real time scenario. This article will focus on another feature called Aspect Oriented Programming and how Spring.NET supports this. This will be the first part of a series of AOP articles that I have planned.

Aspect Oriented Programming (AOP)

AOP is a design principle that helps to separate concerns while developing a multi-layered system. AOP is built using Object Oriented Programming concepts. AOP is implemented using Reflection in modern frameworks/runtimes like CLR and JRE.

Since I mentioned about separation of concerns, it's now time to talk about that. In the AOP world, a concern could be defined as fulfilling a business requirement. For example, fetching a list of customers from a database could be a concern. There are two types of concerns - core and cross-cutting concerns.

  • Core concern: This defines the main business requirement. For example, fetching a list of customers, updating customer information etc.
  • Cross-cutting concern: This defines the other activities that should happen while performing the core concern. This could span across multiple layers and classes. For example, checking the time taken to retrieve a list of customers for monitoring performance, logging the customer update activity for auditing purposes etc.

Seperation of concerns

Why do we need separation of concerns?

Well, at the high level, we need to have a clean way of doing things. In order to fulfill a business requirement, we are creating software (class/component/method), and we are also creating additional software like logging, exception handling, performance monitoring etc., which is not the actual business requirement. The additional software is mandatory in some areas (like Development, QA environment) and optional in some areas (Production). So, how do we implement the software that is mandatory in some stage and optional in some other stage? The answer is - Separation of concerns.

We separate the actual logic (core concern) and the optional logic (cross-cutting concern) through AOP concepts.

At the low level, it boils down to the separation of the actual logic and the infrastructural logic. We create the class/method that performs the actual logic, and we create a separate class/method that performs the infrastructural logic, and glue them together at runtime based on configuration. In a QA environment, this configuration can be enabled so they are added, and in Production, they can be disabled. The actual logic will not have any infrastructural code, and it can concentrate on what it is supposed to do. All infrastructure code (cross-cutting concern logic) is developed separately.

Following are some of the benefits:

  • The core logic will not care about the infrastructural logic
  • The infrastructural logic can be plugged in at runtime, enabled/disabled, based on the environment
  • Any changes, bug fixes, new versions to the infrastructural logic will not affect the core logic
  • Loosely coupled system

AOP terminology

We need to be aware of some AOP terms before jumping further deep.

  • Advice: The infrastructure code or extra code that we write to accomplish a cross-cutting concern is created as an Advice and applied to the core concern.
  • Point-cut: This is the point at which the Advice needs to be applied to the code. If we wish to write a trace message before the execution of a data access layer method call, then the Point-cut is defined as before executing the data access layer method call.
  • Aspect: The combination of an Advice and a Point-cut is termed as an Aspect.

AOP using Spring.NET

AOP is one of the key features offered by Spring.NET. This section explains the steps involved in doing the same.

Scenario

The scenario is very similar to my previous article. I have three layers - Presentation, Business Logic Layer (BLL), and Data Access Layer (DAL). The UI layer implements the MVP pattern in which I created views (WinForms), Presenter classes, and DataTables as Models.

The UI calls the Presenter, which interacts with the BLL, the BLL interacts with the DAL and retrieves data from the Northwind database.

Logical architecture

The following diagram shows the layers of the application:

LogicalArchitecture.JPG

The Data Access Layer has an Around Advice applied to trace the method calls. Basically, we want to write "begin" and "end" trace messages for the Data Access Layer. Without AOP, this is how I would have done to accomplish the tracing requirement:

C#
public DataSet GetAll()
{
    TracingComponent.Write("Begin of GetAll()");
    
    // Retrive the data from database. Perform the actual logic
    DataSet dataSet = null;// get data from database;
    
    TracingComponent.Write("End of GetAll()");
    return dataSet;
}

In the above piece of code snippet, we are using the TracingComponent (a fictitious component) in the code directly. Any changes to the TracingComponent will affect our Data Access Layer code. Using AOP, things have changed for better, which is what we will be focusing on now.

Steps involved in AOP using Spring.NET

  • Create an Advice, and implement the infrastructural code in the Advice
  • Configure the Advice, and target objects
  • Use the Spring application context to get the object instance
  • Call the target method

Having seen the high level steps, it's now time to dive deep into the code to understand how to put things in place.

Creating the Advice

Step 1: Create an Advice. Spring.NET offers many types of Advices. They are the Before Advice, After Advice, Around Advice, and Throws Advice. The names are self-explanatory, and I would leave that learning to you.

In our case, we will create an Around advice. This will help us in intercepting before and after the target method call. We need to implement the interface IMethodInterceptor that is available in the "AopAlliance.Intercept" namespace. We need to provide an implementation for the method Invoke() of IMethodInterceptor. The signature is given below:

C#
public interface IMethodInterceptor 
{
    object Invoke(IMethodInvocation invocation);
}

In this, the IMethodInvocation will hold the reference to the actual object to which the method is called. We will create a class that implements this interface.

C#
using System.IO;
// Diagnostics namespace
using System.Diagnostics;

// Spring namespaces
using AopAlliance.Intercept;
using AOP.Argument;

public class TraceAroundAdvice : IMethodInterceptor
{
    private FileStream _stream = null;
    private TextWriterTraceListener _listener = null;

    public TraceAroundAdvice()
    {
        _stream = File.Open(@"C:\temp\Logs.txt", FileMode.Append);
        _listener = new TextWriterTraceListener(_stream);
        Trace.Listeners.Add(_listener);
    }
    
    public object Invoke(IMethodInvocation invocation)
    {
        // Write "begin" trace message
        string message = string.Format("[{0}] Begin method call {1}", 
                         DateTime.Now.ToString("M/dd/yy hh:m:ss"), 
                         invocation.Method.Name);
        Trace.WriteLine(message);
        Trace.Flush();
        
        // Call the actual method
        object returnValue = invocation.Proceed();
        
        // Write "end" trace message
        message = string.Format("[{0}] Completed method call {1}", 
                                DateTime.Now.ToString("M/dd/yy hh:m:ss"), 
                                invocation.Method.Name);
        Trace.WriteLine(message);
        Trace.Flush();
        
        // return the result
        return returnValue;
    }
}

The TraceAroundAdvice class constructor initializes the trace listener. The Invoke() method writes messages before and after the invocation.Proceed() call.

Spring configuration

Spring configuration is very similar to what we learnt in our DI article. We need to include a Spring configuration section, and define a context and object graph. We also need to define the Advices in the object graph.

The following figure shows the configuration for an Advice:

AdviceConfiguration.JPG

In the above figure, the CustomerDAL class is defined twice (for demo purpose) - one without the Advice "CustomerDAL" (which is similar to the DI article), and another with the Advice "CustomerDALWithAdvice". The object configuration "CustomerDALWithAdvice" has the Advice "TraceAroundAdvice" added to it. This Advice is defined in the "InterceptorNames" property. Note the type of the "CustomerDALWithAdvice" object property - it shows the "ProxyFactoryObject" class of the Spring framework. The Target property points to the actual object, which is the "DAL.Customer.CustomerDAL" class.

The TraceAroundAdvice itself has its own object configuration, which is given in the bottom of the figure. The configuration with the Advice can be walked through like this. When "CustomerDALWithAdvice" is used, the Spring factory (Spring.Aop.Framework.ProxyFactoryObject) class creates an object defined in the Target property. In our case, it is "DAL.Customer.CustomerDAL" which resides in the "DAL" assembly. While creating this instance, it adds the Advices defined in the InterceptorNames property to the execution pipeline. In our case, TraceAroundAdvice will be added.

Initialize Spring configuration and call the target method

The following code snippet shows the steps involved in initializing the Spring configuration and getting the IApplicationContext instance:

C#
// Include namespaces
using Spring.Context;
using Spring.Context.Support;

// Get the context
IApplicationContext applicationContext = ContextRegistry.GetContext();

// Get the instance through Spring configuration
IDAL _customerDAL = applicationContext["customerDALWithAdvice"];

// Call the target method
_customerBLL.GetAll();

Sample application

The sample application has three layers - UI, BLL, and DAL. Every layer is developed in a separate assembly. The UI layer is a Windows Forms application. Apart from these assemblies, the Advice is maintained in a different assembly called "AOP". The following figure shows the solution structure:

SolutionStructure.JPG

In the UI layer, the form creates a presenter using the DI. The presenter creates the BLL using the DI. The BLL creates the DAL using the DI (when "CustomerDAL" is used) or AOP (when "CustomerDALWithAdvice" is used). The configuration for the application is given below:

AppConfiguration.JPG

When the application is run, while using the Customer DAL methods, trace messages before and after the method call will be written to the "C:\Temp\Logs.txt" file.

Conclusion

This article gives an introduction to AOP concepts, the separation of concerns and terminology. We discussed about Spring.NET's AOP support, and the steps involved in creating an Advice and adding it to a target object. We also looked at a sample multi-layered application that uses Spring's AOP and DI techniques.

Happy reading! Happy coding!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)