Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
287 views
in Technique[技术] by (71.8m points)

.net - Unobtrusive AOP with Spring.Net

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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:

References


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...