Spring.net Logging Example using aop
Asked Answered
P

1

1

I'm learning Spring.Net and am trying something simple, which is not working. I want to log any method calls decorated with LogCall

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            Test();
            InitializeComponent();
        }

        [LogCall]
        public void Test()
        {
        }
    }

    public class LogCallInterceptor : IMethodBeforeAdvice
    {
        public void Before(MethodInfo method, object[] args, object target)
        {
            Debug.Write(method.Name);
        }
    }

    [Serializable]
    [AttributeUsage(AttributeTargets.Method)]
    public class LogCallAttribute : Attribute
    {
    }
}

And here's the App.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <spring>
    <objects xmlns="http://www.springframework.net">
      <object id="TestLogAdvice" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop">
        <property name="advice">
          <object type="WpfApplication1.LogCallInterceptor, WpfApplication1" />
        </property>
        <property name="attribute" value="WpfApplication1.LogCallAttribute, WpfApplication1" />
      </object>
    </objects>
  </spring>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Spring.Core" publicKeyToken="65e474d141e25e07" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-1.3.2.40943" newVersion="1.3.2.40943" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Spring.Aop" publicKeyToken="65e474d141e25e07" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-1.3.2.40943" newVersion="1.3.2.40943" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

I'm really new to all this so I'm not even sure if this is a valid approach.

Based on the first answer, I reworked my example. Still not working? Am I getting warm?

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            var someClass = new SomeClass();
            someClass.Test();
            InitializeComponent();
        }
    }

    public class SomeClass
    {
        [LogCall]
        public void Test()
        {
        }
    }

    public class LogCallInterceptor : IMethodBeforeAdvice
    {
        public void Before(MethodInfo method, object[] args, object target)
        {
            Debug.Write(method.Name);
        }
    }

    [Serializable]
    [AttributeUsage(AttributeTargets.Method)]
    public class LogCallAttribute : Attribute
    {
    }
}

And the new app.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <spring>
    <objects xmlns="http://www.springframework.net">
      <object id="TestLogAdvice" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop">
        <property name="advice">
          <object type="WpfApplication1.LogCallInterceptor, WpfApplication1" />
        </property>
        <property name="attribute" value="WpfApplication1.LogCallAttribute, WpfApplication1" />
      </object>
    </objects>
    <object id="mySomeClass" type="Spring.Aop.Framework.ProxyFactoryObject">
      <property name="target">
        <object id="mySomeClassTarget" type="WpfApplication1.SomeClass"/>
      </property>
      <property name="interceptorNames">
        <list>
          <value>TestLogAdvice</value>
        </list>
      </property>
    </object>  
  </spring>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Spring.Core" publicKeyToken="65e474d141e25e07" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-1.3.2.40943" newVersion="1.3.2.40943" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Spring.Aop" publicKeyToken="65e474d141e25e07" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-1.3.2.40943" newVersion="1.3.2.40943" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
Psi answered 6/11, 2011 at 18:49 Comment(3)
I've updated my answer, you're getting close.Convincing
There's an error in your xml: </objects> should be moved.Convincing
If this is your complete app.config, it won't work either, I'll put a note below my answer.Convincing
C
3

You are using spring aop to set up logging, and this is a valid approach. There are a couple of things you have to consider:

Spring AOP uses a dynamic proxy to decorate a class with (logging) advices. This proxy intercepts calls to your object and applies the logging advice. In your class, you call the Test method from within the class itself. This way the dynamic proxy can never intercept the call and no logging will take place.

From your config I read that you define which advice has to run (your LogCallInterceptor) and where (methods matching your attribute), but I don't see where you define your proxy factory. Spring has to create a proxy and you have to tell it where to do it.

The aop quickstart is a good place to find out how to do this. In fact, one of the first examples is a logging example, which is very applicable to your question. I'm guessing that after reading the first part of the quickstart (chapter 38.2.1.) you'll know what to do to get this working.

Spring AOP is a powerful technique, but can be a bit hard to master at first. You're well on your way already.

Edit 1

I see you've updated your question. You're almost there, I think.

Now you're creating a SomeClass instance directly from code. This way, Spring again doesn't get a chance to create it's proxy. You have to delegate the creation of SomeClass to the spring container:

public MainWindow()
{
  // normally speaking, we should not create the container here,
  // but that's another subject
  var ctx = ContexRegistry.GetContext(); // init spring container
  var someClass = (SomeClass) ctx["mySomeClass"];
  someClass.Test();
  InitializeComponent();
}

This way, someClass will hold the proxy instead of the target.

After this, there's one problem remaining (hint).

Edit 2

Your're Test method has to be virtual, otherwise spring can't create an inheritance based proxy. (or your class has to implement one or more interfaces).

Configuration using auto proxy

The following app.config uses an DefaultAdvisorAutoProxyCreator. This will make sure you don't have to create a proxy factory for each and every class you want to apply your logging advisor to. The DefaultAdvisorAutoProxyCreator will find all objects with LogCallAttributes and create a proxy for them.

<?xml version="1.0" encoding="utf-8"?>

<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.springframework.net">

      <object id="TestLogAdvice" type="Spring.Aop.Support.AttributeMatchMethodPointcutAdvisor, Spring.Aop">
        <property name="advice">
          <object type="q8029460.LogCallInterceptor, q8029460" />
        </property>
        <property name="attribute" value="q8029460.LogCallAttribute, q8029460" />
      </object>

      <object id="ProxyCreator" type="Spring.Aop.Framework.AutoProxy.DefaultAdvisorAutoProxyCreator, Spring.Aop"/>

      <object id="mySomeClass" type="q8029460.MyClass, q8029460" />

    </objects>
  </spring>

</configuration>
Convincing answered 6/11, 2011 at 19:40 Comment(7)
After reading the quick start I can see what the proxy does. Do I have to declare a proxy for every class that uses this attribute?Psi
That's an approach, but you can also create an AutoProxy. I suggest you first try to get it to work for one class and then find out more about the AutoProxy. For instance, this answer of mine uses an auto proxy: #6658574Convincing
Docs on AutoProxy: springframework.net/doc-latest/reference/html/…Convincing
Ah, light bulb moment. I fell back to the example of creating a proxy factory and adding the advice. Then getting the proxy and executing test. I added an ISomeClass interface to use a composition proxy. It makes sense now. Thanks for your help.Psi
Great you got it working! You also could have made your Test method virtual, so that spring could create an inheritance based proxy.Convincing
ref: github.com/serra/stackoverflow/tree/master/spring-questions/…Convincing
(psst, if you see a user called "Spammy Product spammer" and they add an answer in which they spam their Spammy Product product, don't waste your time editing; just flag as spam/offensive and we'll nuke the user from orbit. Thanks for the flag, but I don't want you wasting your own time trying to redeem a spammer's post!)Dedededen

© 2022 - 2024 — McMap. All rights reserved.