Chapter 14. Aspect Library

14.1. Introduction

Spring provides several aspects in the distribution. The most popular of which is transactional advice, located in the Spring.Data module. However, the aspects that are documented in this section are those contained within the Spring.Aop module itself. At the moment this consists of Caching and Exception Handling advice. Other traditional advice types, logging, performance counters, validation, security, retry, and thread management, will be included in the future.

14.2. Caching

Caching the return value of a method or the value of a method parameter is a common approach to increase application performance. Application performance is increased with effective use of caching since layers in the application that are closer to the user can return information within their own layer as compared to making more expensive calls to retrieve that information from a lower, and more slow, layer such as a database or a web service. Caching also can help in terms of application scalability, which is generally the more important concern.

The caching support in Spring.NET consists of base cache interfaces that can be used to specify a specific storage implementation of the cache and also an aspect that determines where to apply the caching functionality and its configuration.

The base cache interface that any cache implementation should implement is Spring.Caching.ICache located in Spring.Core. Two implementations are provided, Spring.Caching.AspNetCache located in Spring.Web which stores cache entries within an ASP.NET cache and a simple implementation, Spring.Caching.NonExpiringCache that stores cache entries in memory and never expires these entries. Custom implementation based on 3rd party implementations, such as Oracle Coherence, can be used by implementing the ICache interface.

The cache aspect is Spring.Aspects.Cache.CacheAspect located in Spring.Aop. It consists of three pieces of functionality, the ability to cache return values, method parameters, and explicit eviction of an item from the cache. The aspect currently relies on using attributes to specify the pointcut as well as the behavior, much like the transactional aspect. Future versions will allow for external configuration of the behavior so you can apply caching to a code base without needing to use attributes in the code.

The following attributes are available

  • CacheResult - used to cache the return value

  • CacheResultItems - used when returning a collection as a return value

  • CacheParameter - used to cache a method parameter

  • InvalidateCache - used to indicate one or more cache items should be invalidated.

Each CacheResult, CacheResultItems, and CacheParameter attributes define the following properties.

  • CacheName - the name of the cache implementation to use

  • Key - an string representing a Spring Expression Language (SpEL) expression used as the key in the cache.

  • Condition - a SpEL expression that should be evaluated in order to determine whether the item should be cached.

  • TimeToLive - The amount of time an object should remain in the cache (in seconds).

  • SlidingExperation - If this property value is set to true, every time the marked object is accessed it'sTimeToLive value is reset to its original value

  • Priority - the cache item priority controlling how likely an object is to be removed from an associated cache when the cache is being purged.

The values of the Priority enumeration are

  • Low - low likelihood of deletion when cache is purged.

  • Normal - default priority for deletion when cache is purged.

  • High - low likelihood of deletion when cache is purged.

  • NotRemovable - cache item not deleted when cache is purged.

The invalidateCache attribute has properties for the CacheName, the Key as well as the Condition, with the same meanings as listed previously.

An important element of the applying these attributes is the use of the expression language that allows for calling context information to drive the caching actions. Here is an example taken from the Spring Air sample application of the AirportDao implementation that implements an interface with the method GetAirport(long id).

        [CacheResult("AspNetCache", "'Airport.Id=' + #id", TimeToLive = 60, SlidingExpiration = true, Priority = CachePriority.Low)]
        public Airport GetAirport(long id)
        {
           // implementation not shown...
        }
        

The key used to cache the airport is a string expression that incorporates the argument passed into the method, the id. The method parameter names are exposed as variables to the key expression. If you do not specify a key, then all the parameter values will be used to cache the returned value. The expression may also call out to other objects in the spring container allowing for a more complex key algorithm to be encapsulated.

The configuration to enable the caching aspect is shown below

  <object id="CacheAspect" type="Spring.Aspects.Cache.CacheAspect, Spring.Aop"/>
  <object id="AspNetCache" type="Spring.Caching.AspNetCache, Spring.Web"/>


  <!-- Apply aspects to DAOs -->
  <object type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop">
    <property name="ObjectNames">
      <list>
        <value>*Dao</value>
      </list>
    </property>
    <property name="InterceptorNames">
      <list>
        <value>CacheAspect</value>
      </list>
    </property>
  </object>

in this example an ObjectNameAutoProxyCreator was used to apply this the cache aspect to objects that have Dao in their name,

14.3. Exception Handling

In some cases existing code can be easily adopted to a simple error handling strategy that can perform one of the following actions

  • translations - either wrap the thrown exception inside a new one or replace it with a new exception type (no inner exception is set).

  • return value - the exception is ignored and a return value for the method is provided instead

  • swallow - the exception is ignored.

The applicability of general exception handling advice depends greatly on how tangled the code is regarding access to local variables that may form part of the exception. Once you get familiar with the feature set of Spring declarative exception handling advice you should evaluate where it maybe effectively applied in your code base. It is worth to note that you can still chain together multiple pieces of exception handling advice allowing you to mix the declarative approach shown in this section with the traditional inheritance based approach, i.e. implementing IThrowsAdvice or IMethodInterceptor.

Declarative exception handling is expressed in the form of a mini-language relevant to the domain at hand, exception handling. This could be referred to as a Domain Specific Language (DSL). Here is a simple example, which should hopefully be self explanatory.

<object name="exceptionHandlingAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop">
  <property name="exceptionHandlers">
    <list>
      <value>on ArithmeticException wrap System.InvalidOperationException</value>
    </list>
  </property>
</object>

What this is instructing the advice to do is the following bit of code when an ArithmeticException is thrown, throw new System.InvalidOperationException("Wrapped ArithmeticException", e). where e is the original ArithmeticException. The default message, "Wrapped ArithmethicException" is automatically appended. You may however specify the message used in the newly thrown exception as shown below

on ArithmeticException wrap System.InvalidOperationException 'My Message'

Similarly, if you would rather replace the exception, that is do not nest one inside the other, you can use the following syntax

on ArithmeticException replace System.InvalidOperationException

or

on ArithmeticException replace System.InvalidOperationException 'My Message'

Both wrap and replace are special cases of the more general translate action. An example of a translate expression is shown below

on ArithmeticException translate new System.InvalidOperationException('My Message, Method Name ' + #method.Name, #e)

What we see here after the translate keyword is text that will be passed into Spring's expression language (SpEL) for evaluation. Refer to the chapter on the expression language for more details. One important feature of the expression evaluation is the availability of variables relating to the calling context when the exception was thrown. These are

  • method - the MethodInfo object corresponding to the method that threw the exception

  • args - the argument array to the method that threw the exception, signature is object[]

  • target - the AOP target object instance.

  • e - the thrown exception

You can invoke methods on these variables, prefixed by a '#' in the expression. This gives you the flexibility to call special purpose constructors that can have any piece of information accessible via the above variables, or even other external data through the use of SpEL's ability to reference objects within the Spring container.

You may also choose to 'swallow' the exception or to return a specific return value, for example

on ArithmeticException swallow


or


on ArithmeticException return 12

You may also simply log the exception

on ArithmeticException log 'My Message, Method Name ' + #method.Name

Logging is performed using Commons.Logging that provides an abstraction over the underlying logging implementation. Logging is currently at the debug level with a logger name of "LogExceptionHandler" The ability to specify these values will be a future enhancement and likely via a syntax resembling a constructor for the action, i.e. log(Debug,"LoggerName").

Multiple exception handling statements can be specified within the <list> shown above. The processing flow is on exception, the name of the exception listed in the statement is compared to the thrown exception to see if there is a match. A comma separated list of exceptions can be used to group together the same action taken for different exception names. If the action to take is logging, then the logging action is performed and the search for other matching exception names continues. For all other actions, namely translate, wrap, replace, swallow, return, once can exception handler is matched, those in the chain are no longer evaluated. Note, do not confuse this handler chain wit the general advice AOP advice chain. For translate, wrap, and replace actions a SpEL expression is created and used to instantiate a new exception (in addition to any other processing that may occur when evaluating the expression) which is then thrown.

While the examples given above are toy examples, they could just as easily be changed to convert your application specific exceptions.

14.3.1. Reference

The general syntax of the language is

on [ExceptionName1,ExceptionName2,...] [action] [SpEL expression]

The exception names are requires as well as the action. The valid actions are

  • log

  • translate

  • wrap

  • replace

  • return

  • swallow

The form of the expression depends on the action. For logging, the entire string is taken as the SpEL expression to log. Translate expects an exception to be returned from evaluation the SpEL expression. wrap and replace are shorthand for the translate action. For wrap and replace you specify the exception name and the message to pass into the standard exception constructors (string, exception) and (string). The exception name can be a partial or fully qualified name. Spring will attempt to resolve the typename across all referenced assemblies. You may also register type aliases for use with SpEL in the standard manner with Spring.NET and those will be accessible form within the exception handling expression.

14.4. Transactions

The transaction aspect is more fully described in the section on transaction management.