I'm trying to add logging to methods decorated with an attribute using Spring.Net for AOP.
Step 1: Reference 'Spring.Core', 'Spring.Aop', 'Common.Logging'
Step 2: Create an advice:
using AopAlliance.Intercept;
namespace MyApp.Aspects
{
public class LoggingAdvice : IMethodInterceptor
{
public object Invoke(IMethodInvocation invocation)
{
//todo: log started
object rval = invocation.Proceed();
return rval;
//todo: log finished
}
}
}
Step 3: Create an attribute:
using System;
namespace MyApp.Aspects
{
public class LoggingAttribute : Attribute
{
}
}
Step 4: Edit web.config
<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.springfrmework.net">
<object id="loggingAdvice" type="MyApp.Aspects.LoggingAdvice, MyApp"></object>
<object id="loggingAdvisor" type="Spring.Aop.Support.DefaultPointcutAdvisor, Spring.Aop">
<property name="Advice" ref="loggingAdvice" />
</object>
<object type="Spring.Aop.Framework.AutoProxy.AttributeAutoProxyCreator, Spring.Aop">
<property name="AttributeTypes" value="MyApp.Aspects.LoggingAttribute"/>
<property name="InterceptorNames" value="loggingAdvisor"/>
</object>
</objects>
</spring>
</configuration>
Step 5: Decorate a method with the attribute:
using System.Web.Mvc;
namespace MyApp.Controllers
{
public class MyController : Controller
{
[Logging]
public ActionResult DoStuff()
{
//todo: implement
}
}
}
The advice is never triggered. What am I missing?
This has to do with the fact that the controller is created and then actually calls
DoStuff()
on it self. The controller obviously does not hold a proxy to itself and therefore the call toDoStuff()
does not get intercepted by Spring.Net AOP.As tobsen mentions in his answer, you will have to get the controller from spring, otherwise interception will not take place. I assume you're using spring mvc support here to create controllers, but this does not clearly show from your question and you might have left it out.
How to intercept action methods on MVC 3 controllers
Summary
See below for details and an example.
InheritanceBasedAopConfigurer
Spring's default interception mechanism does not work ...
When a request is made to an MVC app, then from the request url a controller is chosen by the MVC framework. On this controller, the
Execute()
method is called, which in turn is responsible for invoking the action methods. It is important to realize that action methods are always called from within the controller.Spring.NET aop uses dynamic weaving. By default, at runtime a proxy is created for objects for which aop advisors are declared in the configuration. This proxy intercepts calls and forwards calls to the target instance. This is done when proxying interfaces and classes (using
proxy-target-type="true"
). When the target object invokes a method on it self, it will not do this through the spring proxy and the method does not get intercepted. This why the default aop mechanism doesn't work for mvc controllers.... but using an
InheritanceBasedAopConfigurer
does the trickTo intercept calls on action methods, you should use an
InheritanceBasedAopConfigurer
. This will create an inheritance based proxy that does not delegate to a target object, instead interception advice is added directly in the method body before invoking the base class method.Note that for this interception method to work, the methods have to be virtual.
The following xml config works:
A working example is available on github. It is based on a standard mvc 3 application with Spring.Net Mvc3 support. Relevant files are:
HomeController
References
DoStuff()
should be declared virtual.The way you are approaching may be correct, however, I can't see how you are retrieving instances of your proxied
MyController
class. As you indicated in the comment, you are not using spring to retrieve instances ofMyController
. Basically spring hasn't a chance to intercept that call and therfore cannot create a dynamic proxy (e.g. a adviced-wrapped instance around the original instance).I strongly suggest you have a look at the examples which are doing same thing you are trying to accomplish but are using a already built-in logging advice.
Also be sure to enable debug-logging in spring and read the log carefully. Spring writes in the log which types are proxied and if advices are wrapped around.
As for your problem, here is a wild guess: I am currently not sure but you might be missing the assembly of the Attribute in your declaration:
<property name="AttributeTypes" value="MyApp.Aspects.LoggingAttribute"/>
perhaps rewriting it to:<property name="AttributeTypes" value="MyApp.Aspects.LoggingAttribute, MyApp"/>
helps spring to find the attribute.