Unobtrusive AOP with Spring.Net

不羁的心 提交于 2019-12-17 20:26:38

问题


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?


回答1:


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 to DoStuff() 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.

  1. Use an InheritanceBasedAopConfigurer
  2. Declare methods you want to intercept as virtual
  3. Configure your interceptors

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 trick

To 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:

<!-- 
When not specifying an object id or name, 
spring will assign a name to it like [typename]#[0,1,2,..]  
-->  
<object type="MyApp.Controllers.HomeController, MyApp" 
        singleton="false" />

<object id="myInterceptor" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop">
  <property name="Attribute" value="MyApp.MyAttribute, MyApp" />
  <property name="Advice">
    <object type="MyApp.MyAdvice, MyApp" />
  </property>
</object>

<object type="Spring.Aop.Framework.AutoProxy.InheritanceBasedAopConfigurer, Spring.Aop">
  <property name="ObjectNames">
    <list>
      <value>*Controller#*</value>
    </list>
  </property>
  <property name="InterceptorNames">
    <list>
      <value>myInterceptor</value>
    </list>
  </property>
</object>

A working example is available on github. It is based on a standard mvc 3 application with Spring.Net Mvc3 support. Relevant files are:

  • xml configuration
  • HomeController
  • attribute and interceptor

References

  • Spring.net docs: proxying interfaces vs proxying classes vs using an inheritance based aop configurer
  • A similar question with regard to transaction interception is here: Asp.Net MVC Controller: declarative AOP with Spring.Net. Solution can be to delegate calls to injected services or to use an inheritance based proxy, see Mark's answer to the question mentioned above. In the latter case DoStuff() should be declared virtual.
  • spring.net forum post



回答2:


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 of MyController. 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.



来源:https://stackoverflow.com/questions/9114762/unobtrusive-aop-with-spring-net

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!