![]() |
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. The aspects in within Spring.Aop.dll are Caching, Exception Handling, Logging, Retry, and Parameter Validation. Other traditional advice types such as validation, security, and thread management, will be included in a future release. 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 The cache aspect is
The following attributes are available
Each
The Each
The values of the Priority enumeration are
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 = "0:1:0")] public Airport GetAirport(long id) { // implementation not shown... } The first parameter is the cache name. The second string parameter is the cache key and is a string expression that incorporates the argument passed into the method, the id. The cache key value cannot be null or an empty string (""). The method parameter names are exposed as variables to the key expression. The expression may also call out to other objects in the Spring container allowing for a more complex key algorithm to be encapsulated. The end result is that the Airport object is cached by id for 60 seconds in a cache named AspNetCache. The TimetoLive property could also have been specified on the configuration of the AspNetCache object. 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"> <property name="SlidingExpiration" value="true"/> <property name="Priority" value="Low"/> <property name="TimeToLive" value="00:02:00"/> </object> <!-- 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 In some cases existing code can be easily adopted to a simple error handling strategy that can perform one of the following actions
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 may be effectively applied in your code base. It is worth noting 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 exception name 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 exception name 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 exception name ArithmeticException replace System.InvalidOperationException or on exception name 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 exception name 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
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 exception name ArithmeticException swallow or on exception name ArithmeticException return 12 You may also simply log the exception on exception name ArithmeticException,ArgumentException log 'My Message, Method Name ' + #method.Name Here we see that a comma delimited list of exception names can be specified. The logging is performed using the Commons.Logging library 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 an exception handler is matched, those in the chain are no longer evaluated. Note, do not confuse this handler chain with 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. The exception handling DSL also supports the ability to provide a SpEL boolean expression to determine if the advice will apply instead of just filtering by the expression name. For example, the following is the equivalent to the first example based on exception names but compares the specific type of the exception thrown on exception (#e is T(System.ArithmeticException)) wrap System.InvalidOperationException
The syntax use is 'on exception (SpEL boolean expression)' and
inside the expression you have access to the variables of the calling
context listed before, i.e. method, args, target, and e. This can be
useful to implement a small amount of conditional logic, such as checking
for a specific error number in an exception, i.e. While the examples given above are toy examples, they could just as easily be changed to convert your application specific exceptions. If you find yourself pushing the limits of using SpEL expressions, you will likely be better off creating your own custom aspect class instead of a scripting approach. You can also configure the each of the Handlers individually based on the action keyword. For example, to configure the logging properties on the LogExceptionHandler. <object name="logExceptionHandler" type="Spring.Aspects.Exceptions.LogExceptionHandler, Spring.Aop"> <property name="LogName" value="Cms.Session.ExceptionHandler" /> <property name="LogLevel" value="Debug"/> <property name="LogMessageOnly" value="true"/> </object> <object name="exceptionHandlingAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop"> <property name="ExceptionHandlerDictionary"> <dictionary> <entry key="log" ref="logExceptionHandler"/> </dictionary> </property> <property name="ExceptionHandlers"> <list> <value>on exception name ArithmeticException,ArgumentException log 'My Message, Method Name ' + #method.Name</value> </list> </property> </object> You can also configure <object name="exceptionHandlingAdvice" type="Spring.Aspects.Exceptions.ExceptionHandlerAdvice, Spring.Aop"> <property name="exceptionHandlers"> <list> <object type="Spring.Aspects.Exceptions.LogExceptionHandler"> <property name="LogName" value="Cms.Session.ExceptionHandler" /> <property name="ConstraintExpressionText" value="#e is T(System.Threading.ThreadAbortException)" /> <property name="ActionExpressionText" value="#log.Fatal('Request Timeout occured', #e)" /> </object> </list> </property> </object> The configuration of the logger name, level, and weather or not to pass the thrown exception as the second argument to the log method will be supported in the DSL style in a future release. The general syntax of the language is
or
The exception names are required as well as the action. The valid actions are
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 from within the exception handling expression. The logging advice lets you log the information on method entry, exit and thrown exception (if any). The implementation is based on the logging library, Common.Logging, that provides portability across different logging libraries. There are a number of configuration options available, listed below
You declare the logging advice in IoC container with the following
XML fragment. Alternatively, you can use the class
<object name="loggingAdvice" type="Spring.Aspects.Logging.SimpleLoggingAdvice, Spring.Aop"> <property name="LogUniqueIdentifier" value="true"/> <property name="LogExecutionTime" value="true"/> <property name="LogMethodArguments" value="true"/> <property name="LogReturnValue" value="true"/> <property name="Separator" value=";"/> <property name="LogLevel" value="Info"/> <property name="HideProxyTypeNames" value="true"/> <property name="UseDynamicLogger" value="true"/> </object> The default values for You can set the name of the logger with the property
LoggerName, for example "DataAccessLayer" for a
logging advice that would be applied across the all the classes in the
data access layer. That works well when using a 'category' style of
logging. If you do not set the LoggerName property,
then the type name of the logging advice is used as the logging name.
Another approach to logging is to log based on the type of the object
being called, the target type. Since often this is a proxy class with a
relatively meaningless name, the property
HideProxyTypeNames can be set to true to show the
true target type and not the proxy type. The
To further extend the functionality of the
The default implementation to calculate a unique identifier is to
use a GUID. You can alter this behavior by overriding the method
As an example of the Logging advice's output, adding the advice to the method public int Bark(string message, int[] luckyNumbers) { return 4; } And calling Bark("hello", new int[]{1, 2, 3} ), results in the following output Entering Bark, 5d2bad47-62cd-435b-8de7-91f12b7f433e, message=hello; luckyNumbers=System.Int32[] Exiting Bark, 5d2bad47-62cd-435b-8de7-91f12b7f433e, 30453.125 ms, return=4 The method parameters values are obtained using the ToString() method. If you would like to have an alternate implementation, say to view some values in an array, override the method string GetMethodArgumentAsString(IMethodInvocation invocation). When making a distributed call it is often a common requirement to be able to retry the method invocation if there was an exception. Typically the exception will be due to a communication issue that is intermittent and retrying over a period of time will likely result in a successful invocation. When applying retry advice it is important to know if making two calls to the remote service will cause side effects. Generally speaking, the method being invoked should be idempotent, that is, it is safe to call multiple times. The retry advice is specified using a little language, i.e a DSL. A simple example is shown below on exception name ArithmeticException retry 3x delay 1s The meaning is: when an exception that has 'ArithmeticException' in its type name is thrown, retry the invocation up to 3 times and delay for 1 second between each retry event. You can also provide a SpEL (Spring Expression Language) expression that calculates the time interval to sleep between each retry event. The syntax for this is shown below on exception name ArithmeticException retry 3x rate (1*#n + 0.5) As with the exception handling advice, you may also specify a boolean SpEL that must evaluate to true in order for the advice to apply. For example on exception (#e is T(System.ArithmeticException)) retry 3x delay 1s on exception (#e is T(System.ArithmeticException)) retry 3x rate (1*#n + 0.5) The time specified after the delay keyword is converted to a TimeSpan object using Spring's TimeSpanConverter. This supports setting the time as an integer + time unit. Time units are (d, h, m, s, ms) representing (days, hours, minutes, seconds, and milliseconds). For example; 1d = 1day, 5h = 5 hours etc. You can not specify a string such as '1d 5h'. The value that is calculated from the expression after the rate keyword is interpreted as a number of seconds. The power of using SpEL for the rate expression is that you can easily specify some exponential retry rate (a bigger delay for each retry attempt) or call out to a custom function developed for this purpose. When using a SpEL expression for the filter condition or for the rate expression, the following variable are available
You declare the advice in IoC container with the
following XML fragment. Alternatively, you can use the
<object name="exceptionHandlingAdvice" type="Spring.Aspects.RetryAdvice, Spring.Aop"> <property name="retryExpression" value="on exception name ArithmeticException retry 3x delay 1s"/> </object> The general syntax of the language is
or
The transaction aspect is more fully described in the section on transaction management. Spring provides a UI-agnostic validation framework in which you can declare validation rules, both progammatically and declaratively, and have those rules evaluated against an arbitrary .NET object. Spring provides additional support for the rendering of validation errors within Spring's ASP.NET framework. (See the section on ASP.NET usage tips for more information.) However, validation is not confined to the UI tier. It is a common task that occurs across most, if not all, applications layers. Validation that is performed in the UI layer is often repeated in the service layer, in order to be proactive in case non UI-based clients invoke the service layer. Validation rules completely different from those used in the UI layer may also be used on the server side. To address some of the common needs for validation on the server
side, Spring provides parameter validation advice so that applies Spring's
validation rules to the method parameters. The class
public FlightSuggestions SuggestFlights( [Validated("tripValidator")] Trip trip) { // unmodified implementation goes here } The The configuration of the advice is to simply define the an instance
of the <object id="validationAdvice" type="Spring.Aspects.Validation.ParameterValidationAdvice, Spring.Aop"/> <object type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxyCreator, Spring.Aop"> <property name="ObjectNames"> <list> <value>bookingAgent</value> </list> </property> <property name="InterceptorNames"> <list> <value>validationAdvice</value> </list> </property> </object> When the advised method is invoked first the validation of each
method parameter is performed. If all validation succeeds, then the method
body is executed. If validation fails an exception of the type
|