partial fix for SPRNET-1221 (avoid duplicate advice invocations for nested proxies)

This commit is contained in:
eeichinger
2009-06-25 10:17:11 +00:00
parent 26555e09a0
commit adb63f8e6d
10 changed files with 452 additions and 146 deletions

View File

@@ -42,12 +42,24 @@ namespace Spring.Aop.Framework
/// <author>Aleksandar Seovic (.NET)</author>
public sealed class AopUtils
{
// This is a leaky abstraction as we have hardcoded known IAopProxyFactory implementations.
private const string COMPOSITION_PROXY_TYPE_NAME = "CompositionAopProxy";
private const string DECORATOR_PROXY_TYPE_NAME = "DecoratorAopProxy";
/// <summary>
/// Is the supplied <paramref name="objectType"/> an AOP proxy?
/// </summary>
/// <remarks>
/// Return whether the given type is either a composition-based or a decorator-based proxy type.
/// </remarks>
/// <param name="objectType">The type to be checked.</param>
/// <returns><see langword="true"/> if the supplied <paramref name="objectType"/> is an AOP proxy type.</returns>
public static bool IsAopProxyType(Type objectType)
{
return IsCompositionAopProxyType(objectType) || IsDecoratorAopProxyType(objectType);
}
/// <summary>
/// Is the supplied <paramref name="instance"/> an AOP proxy?
/// </summary>
@@ -75,7 +87,20 @@ namespace Spring.Aop.Framework
/// </returns>
public static bool IsCompositionAopProxy(Object instance)
{
return ((instance != null) && instance.GetType().FullName.StartsWith(COMPOSITION_PROXY_TYPE_NAME));
return ((instance != null) && IsCompositionAopProxyType(instance.GetType()));
}
/// <summary>
/// Is the supplied <paramref name="objectType"/> a composition based AOP proxy type?
/// </summary>
/// <remarks>
/// Return whether the given type is a composition-based proxy type.
/// </remarks>
/// <param name="objectType">The type to be checked.</param>
/// <returns><see langword="true"/> if the supplied <paramref name="objectType"/> is a composition based AOP proxy type.</returns>
public static bool IsCompositionAopProxyType(Type objectType)
{
return ((objectType != null) && objectType.FullName.StartsWith(COMPOSITION_PROXY_TYPE_NAME));
}
/// <summary>
@@ -88,7 +113,20 @@ namespace Spring.Aop.Framework
/// </returns>
public static bool IsDecoratorAopProxy(Object instance)
{
return ((instance != null) && instance.GetType().FullName.StartsWith(DECORATOR_PROXY_TYPE_NAME));
return ((instance != null) && IsDecoratorAopProxyType(instance.GetType()));
}
/// <summary>
/// Is the supplied <paramref name="objectType"/> a composition based AOP proxy type?
/// </summary>
/// <remarks>
/// Return whether the given type is a composition-based proxy type.
/// </remarks>
/// <param name="objectType">The type to be checked.</param>
/// <returns><see langword="true"/> if the supplied <paramref name="objectType"/> is a composition based AOP proxy type.</returns>
public static bool IsDecoratorAopProxyType(Type objectType)
{
return ((objectType != null) && objectType.FullName.StartsWith(DECORATOR_PROXY_TYPE_NAME));
}
/// <summary>

View File

@@ -1,7 +1,7 @@
#region License
#region License
/*
* Copyright <20> 2002-2007 the original author or authors.
* Copyright <20> 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,104 +14,128 @@
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
#region Imports
*/
#endregion
using System;
using System.Collections;
using System.Reflection;
using Spring.Proxy;
using Spring.Aop.Support;
using Spring.Proxy;
using Spring.Aop.Target;
using Spring.Util;
#endregion
namespace Spring.Aop.Framework.DynamicProxy
{
/// <summary>
/// Default implementation of the
/// <see cref="Spring.Aop.Framework.IAopProxyFactory"/> interface,
/// either creating a decorator-based dynamic proxy or
/// a composition-based dynamic proxy.
/// </summary>
/// <remarks>
/// <p>
/// Creates a decorator-base proxy if one the following is true :
/// - the "ProxyTargetType" property is set
/// - no interfaces have been specified
/// </p>
/// <p>
/// In general, specify "ProxyTargetType" to enforce a decorator-based proxy,
/// or specify one or more interfaces to use a composition-based proxy.
/// </p>
/// </remarks>
/// <author>Rod Johnson</author>
/// <author>Aleksandar Seovic (.NET)</author>
/// <author>Bruno Baia (.NET)</author>
/// <seealso cref="Spring.Aop.Framework.IAopProxyFactory"/>
[Serializable]
public class DefaultAopProxyFactory : IAopProxyFactory
{
using Spring.Util;
namespace Spring.Aop.Framework.DynamicProxy
{
/// <summary>
/// Default implementation of the
/// <see cref="Spring.Aop.Framework.IAopProxyFactory"/> interface,
/// either creating a decorator-based dynamic proxy or
/// a composition-based dynamic proxy.
/// </summary>
/// <remarks>
/// <p>
/// Creates a decorator-base proxy if one the following is true :
/// - the "ProxyTargetType" property is set
/// - no interfaces have been specified
/// </p>
/// <p>
/// In general, specify "ProxyTargetType" to enforce a decorator-based proxy,
/// or specify one or more interfaces to use a composition-based proxy.
/// </p>
/// </remarks>
/// <author>Rod Johnson</author>
/// <author>Aleksandar Seovic (.NET)</author>
/// <author>Bruno Baia (.NET)</author>
/// <author>Erich Eichinger (.NET)</author>
/// <seealso cref="Spring.Aop.Framework.IAopProxyFactory"/>
[Serializable]
public class DefaultAopProxyFactory : IAopProxyFactory
{
/// <summary>
/// Force transient assemblies to be resolvable by <see cref="Assembly.Load(string)"/>.
/// </summary>
static DefaultAopProxyFactory()
/// </summary>
static DefaultAopProxyFactory()
{
SystemUtils.RegisterLoadedAssemblyResolver();
}
/// <summary>
/// Creates an <see cref="Spring.Aop.Framework.IAopProxy"/> for the
/// supplied <paramref name="advisedSupport"/> configuration.
/// </summary>
/// <param name="advisedSupport">The AOP configuration.</param>
/// <returns>An <see cref="Spring.Aop.Framework.IAopProxy"/>.</returns>
/// <exception cref="AopConfigException">
/// If the supplied <paramref name="advisedSupport"/> configuration is
/// invalid.
/// </exception>
/// <seealso cref="Spring.Aop.Framework.IAopProxyFactory.CreateAopProxy"/>
public virtual IAopProxy CreateAopProxy(AdvisedSupport advisedSupport)
{
if (advisedSupport == null)
{
throw new AopConfigException("Cannot create IAopProxy with null ProxyConfig");
}
if (advisedSupport.Advisors.Length == 0 && advisedSupport.TargetSource == EmptyTargetSource.Empty)
{
throw new AopConfigException("Cannot create IAopProxy with no advisors and no target source");
}
if (advisedSupport.ProxyType == null)
{
IProxyTypeBuilder typeBuilder;
if ((advisedSupport.ProxyTargetType) ||
(advisedSupport.Interfaces.Length == 0))
{
typeBuilder = new DecoratorAopProxyTypeBuilder(advisedSupport);
}
else
{
typeBuilder = new CompositionAopProxyTypeBuilder(advisedSupport);
}
advisedSupport.ProxyType = BuildProxyType(typeBuilder);
advisedSupport.ProxyConstructor = advisedSupport.ProxyType.GetConstructor(new Type[] { typeof(IAdvised) });
}
return (IAopProxy)advisedSupport.ProxyConstructor.Invoke(new object[] { advisedSupport });
}
/// <summary>
/// Generates the proxy type.
/// </summary>
/// <param name="typeBuilder">
/// The <see cref="Spring.Proxy.IProxyTypeBuilder"/> to use
/// </param>
/// <returns>The generated proxy class.</returns>
protected virtual Type BuildProxyType(IProxyTypeBuilder typeBuilder)
{
return typeBuilder.BuildProxyType();
}
}
}
/// <summary>
/// Creates an <see cref="Spring.Aop.Framework.IAopProxy"/> for the
/// supplied <paramref name="advisedSupport"/> configuration.
/// </summary>
/// <param name="advisedSupport">The AOP configuration.</param>
/// <returns>An <see cref="Spring.Aop.Framework.IAopProxy"/>.</returns>
/// <exception cref="AopConfigException">
/// If the supplied <paramref name="advisedSupport"/> configuration is
/// invalid.
/// </exception>
/// <seealso cref="Spring.Aop.Framework.IAopProxyFactory.CreateAopProxy"/>
public virtual IAopProxy CreateAopProxy(AdvisedSupport advisedSupport)
{
if (advisedSupport == null)
{
throw new AopConfigException("Cannot create IAopProxy with null ProxyConfig");
}
if (advisedSupport.Advisors.Length == 0 && advisedSupport.TargetSource == EmptyTargetSource.Empty)
{
throw new AopConfigException("Cannot create IAopProxy with no advisors and no target source");
}
if (advisedSupport.ProxyType == null)
{
IProxyTypeBuilder typeBuilder;
if ((advisedSupport.ProxyTargetType) ||
(advisedSupport.Interfaces.Length == 0))
{
typeBuilder = new DecoratorAopProxyTypeBuilder(advisedSupport);
}
else
{
typeBuilder = new CompositionAopProxyTypeBuilder(advisedSupport);
}
advisedSupport.ProxyType = BuildProxyType(typeBuilder);
advisedSupport.ProxyConstructor = advisedSupport.ProxyType.GetConstructor(new Type[] { typeof(IAdvised) });
}
if (advisedSupport.TargetSource is SingletonTargetSource
&& AopUtils.IsAopProxyType(advisedSupport.TargetSource.TargetType))
{
IAdvised innerProxy = (IAdvised)advisedSupport.TargetSource.GetTarget();
// eliminate duplicate advisors
ArrayList thisAdvisors = new ArrayList(advisedSupport.Advisors);
foreach (IAdvisor innerAdvisor in innerProxy.Advisors)
{
foreach (IAdvisor thisAdvisor in thisAdvisors)
{
if (ReferenceEquals(thisAdvisor, innerAdvisor)
|| (thisAdvisor.GetType() == typeof(DefaultPointcutAdvisor)
&& ((DefaultPointcutAdvisor)thisAdvisor).Equals(innerAdvisor)
)
)
{
advisedSupport.RemoveAdvisor(thisAdvisor);
}
}
}
// elimination of duplicate introductions is not necessary
// since they do not propagate to nested proxy anyway
}
return (IAopProxy)advisedSupport.ProxyConstructor.Invoke(new object[] { advisedSupport });
}
/// <summary>
/// Generates the proxy type.
/// </summary>
/// <param name="typeBuilder">
/// The <see cref="Spring.Proxy.IProxyTypeBuilder"/> to use
/// </param>
/// <returns>The generated proxy class.</returns>
protected virtual Type BuildProxyType(IProxyTypeBuilder typeBuilder)
{
return typeBuilder.BuildProxyType();
}
}
}

View File

@@ -33,7 +33,6 @@ namespace Spring.Aop.Support
{
private IAdvice advice;
/// <summary>
/// Return the advice part of this advisor.
/// </summary>
@@ -47,7 +46,24 @@ namespace Spring.Aop.Support
set { this.advice = value; }
}
///<summary>
/// 2 <see cref="AbstractGenericPointcutAdvisor"/>s are considered equals, if
/// a) their pointcuts are equal
/// b) their advices are equal
///</summary>
public override bool Equals(object obj)
{
return base.Equals(obj as AbstractGenericPointcutAdvisor);
}
/// <summary>
/// Calculates a unique hashcode based on advice + pointcut
/// </summary>
public override int GetHashCode()
{
return base.GetHashCode();
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current
/// <see cref="System.Object"/>.

View File

@@ -126,8 +126,18 @@ namespace Spring.Aop.Support
if (!(o is AbstractPointcutAdvisor))
{
return false;
}
if (ReferenceEquals(this, o))
{
return true;
}
AbstractPointcutAdvisor otherAdvisor = (AbstractPointcutAdvisor)o;
if (this.Order != otherAdvisor.Order)
{
return false;
}
IPointcutAdvisor otherAdvisor = (IPointcutAdvisor)o;
if (otherAdvisor.Advice == null && otherAdvisor.Pointcut == null)
{
return (this.Advice == null && this.Pointcut == null);
@@ -157,7 +167,8 @@ namespace Spring.Aop.Support
{
return 0 // (SPRNET-847) base.GetHashCode()
+ 13 * (Pointcut == null ? 0 : Pointcut.GetHashCode())
+ 27 * (Advice == null ? 0 : Advice.GetHashCode());
+ 27 * (Advice == null ? 0 : Advice.GetHashCode())
+ 31 * Order.GetHashCode();
}
#endregion

View File

@@ -37,8 +37,8 @@ namespace Spring.Aop.Support
[Serializable]
public class DefaultIntroductionAdvisor : IIntroductionAdvisor, ITypeFilter
{
private IAdvice _introduction;
private ISet _interfaces = new HybridSet();
private readonly IAdvice _introduction;
private readonly ISet _interfaces = new HybridSet();
/// <summary>
/// Creates a new instance of the
@@ -236,5 +236,45 @@ namespace Spring.Aop.Support
throw new ArgumentException("Type [" + intf.FullName + "] is not an interface; cannot be used in an introduction.");
}
}
/// <summary>
/// 2 IntroductionAdvisors are considered equal if
/// a) they are of the same type
/// b) their introduction advices are equal
/// c) they introduce the same interfaces
/// </summary>
public bool Equals(DefaultIntroductionAdvisor other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
if (other.GetType() != this.GetType())
return false;
return Equals(other._introduction, _introduction) && Equals(other._interfaces, _interfaces);
}
/// <summary>
/// 2 IntroductionAdvisors are considered equal if
/// a) they are of the same type
/// b) their introduction advices are equal
/// c) they introduce the same interfaces
/// </summary>
public override bool Equals(object obj)
{
return Equals(obj as DefaultIntroductionAdvisor);
}
/// <summary>
/// 2 IntroductionAdvisors are considered equal if
/// a) they are of the same type
/// b) their introduction advices are equal
/// c) they introduce the same interfaces
/// </summary>
public override int GetHashCode()
{
unchecked
{
return ((_introduction != null ? _introduction.GetHashCode() : 0)*397) ^ (_interfaces != null ? _interfaces.GetHashCode() : 0);
}
}
}
}

View File

@@ -41,7 +41,6 @@ namespace Spring.Aop.Support
[Serializable]
public class DefaultPointcutAdvisor : AbstractGenericPointcutAdvisor
{
private IPointcut pointcut = TruePointcut.True;
/// <summary>
@@ -90,12 +89,28 @@ namespace Spring.Aop.Support
{
get { return pointcut; }
set { pointcut = value;}
}
/// <summary>
}
///<summary>
/// 2 <see cref="DefaultPointcutAdvisor"/>s are considered equal, if
/// a) their pointcuts are equal
/// b) their advices are equal
///</summary>
public override bool Equals(object obj)
{
return base.Equals(obj as DefaultPointcutAdvisor);
}
/// <summary>
/// Calculates a unique hashcode based on advice + pointcut
/// </summary>
public override int GetHashCode()
{
return base.GetHashCode();
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents the current
/// <see cref="System.Object"/>.
/// </summary>

View File

@@ -66,10 +66,10 @@ namespace Spring.Aop.Framework.AutoProxy
}
[Test]
public void ProxyWithDoubleProxying()
public void ProxyWithDoubleProxyingInvokesInterceptorsOnceOnly()
{
ITestObject testObject = (ITestObject)ctx.GetObject("doubleProxy");
ProxyAssertions(testObject, 2);
ProxyAssertions(testObject, 1);
Assert.AreEqual("doubleProxy", testObject.Name);
}
@@ -138,7 +138,7 @@ namespace Spring.Aop.Framework.AutoProxy
int age = 5;
testObject.Age = age;
Assert.AreEqual(age, testObject.Age);
Assert.AreEqual(2 * nopInterceptorCount, nop.Count);
Assert.AreEqual(2*nopInterceptorCount, nop.Count);
}
private void DecoratorProxyAssertions(ITestObject testObject)

View File

@@ -211,27 +211,27 @@ namespace Spring.Aop.Framework
pf.AddAdvice(nopInterceptor);
pf.AddAdvisor(new DefaultPointcutAdvisor(countingBeforeAdvice));
object proxy = pf.GetProxy();
ITestObject to = (ITestObject) proxy;
ITestObject to = (ITestObject)proxy;
Assert.AreEqual("Adam", to.Name);
Assert.AreEqual(1, countingBeforeAdvice.GetCalls());
}
[Test]
[ExpectedException(typeof (AopConfigException))]
[ExpectedException(typeof(AopConfigException))]
public void InstantiateWithNullTarget()
{
new ProxyFactory((object) null);
new ProxyFactory((object)null);
}
[Test]
[ExpectedException(typeof (ArgumentNullException))]
[ExpectedException(typeof(ArgumentNullException))]
public void AddNullInterface()
{
new ProxyFactory().AddInterface(null);
}
[Test]
[ExpectedException(typeof (AopConfigException))]
[ExpectedException(typeof(AopConfigException))]
public void AddInterfaceWhenConfigurationIsFrozen()
{
ProxyFactory factory = new ProxyFactory();
@@ -246,16 +246,16 @@ namespace Spring.Aop.Framework
ProxyFactory pf = new ProxyFactory(target);
NopInterceptor nop = new NopInterceptor();
IAdvisor advisor = new DefaultPointcutAdvisor(new CountingBeforeAdvice());
IAdvised advised = (IAdvised) pf.GetProxy();
IAdvised advised = (IAdvised)pf.GetProxy();
// Can use advised and ProxyFactory interchangeably
advised.AddAdvice(nop);
pf.AddAdvisor(advisor);
Assert.AreEqual(- 1, pf.IndexOf((IInterceptor) null));
Assert.AreEqual(- 1, pf.IndexOf(new NopInterceptor()));
Assert.AreEqual(-1, pf.IndexOf((IInterceptor)null));
Assert.AreEqual(-1, pf.IndexOf(new NopInterceptor()));
Assert.AreEqual(0, pf.IndexOf(nop));
Assert.AreEqual(- 1, advised.IndexOf((IAdvisor) null));
Assert.AreEqual(-1, advised.IndexOf((IAdvisor)null));
Assert.AreEqual(1, pf.IndexOf(advisor));
Assert.AreEqual(- 1, advised.IndexOf(new DefaultPointcutAdvisor(null)));
Assert.AreEqual(-1, advised.IndexOf(new DefaultPointcutAdvisor(null)));
}
[Test]
@@ -268,7 +268,7 @@ namespace Spring.Aop.Framework
IAdvisor advisor = new DefaultPointcutAdvisor(cba);
pf.AddAdvice(nop);
pf.AddAdvisor(advisor);
ITestObject proxied = (ITestObject) pf.GetProxy();
ITestObject proxied = (ITestObject)pf.GetProxy();
proxied.Age = 5;
Assert.AreEqual(1, cba.GetCalls());
Assert.AreEqual(1, nop.Count);
@@ -292,7 +292,7 @@ namespace Spring.Aop.Framework
pf.AddAdvisor(advisor);
NopInterceptor nop2 = new NopInterceptor(2); // make instance unique (see SPRNET-847)
pf.AddAdvice(nop2);
ITestObject proxied = (ITestObject) pf.GetProxy();
ITestObject proxied = (ITestObject)pf.GetProxy();
proxied.Age = 5;
Assert.AreEqual(1, cba.GetCalls());
Assert.AreEqual(1, nop.Count);
@@ -313,7 +313,7 @@ namespace Spring.Aop.Framework
// Check out of bounds
try
{
pf.RemoveAdvisor(- 1);
pf.RemoveAdvisor(-1);
Assert.Fail("Supposed to throw exception");
}
catch (AopConfigException)
@@ -338,14 +338,14 @@ namespace Spring.Aop.Framework
[Test]
public void TryRemoveNonProxiedInterface()
{
ProxyFactory factory = new ProxyFactory(new TestObject ());
ProxyFactory factory = new ProxyFactory(new TestObject());
Assert.IsFalse(factory.RemoveInterface(typeof(IServiceProvider)));
}
[Test]
public void RemoveProxiedInterface()
{
ProxyFactory factory = new ProxyFactory(new TestObject ());
ProxyFactory factory = new ProxyFactory(new TestObject());
Assert.IsTrue(factory.RemoveInterface(typeof(ITestObject)));
}
@@ -361,10 +361,10 @@ namespace Spring.Aop.Framework
IAdvisor advisor2 = new DefaultPointcutAdvisor(cba2);
pf.AddAdvisor(advisor1);
pf.AddAdvice(nop);
ITestObject proxied = (ITestObject) pf.GetProxy();
ITestObject proxied = (ITestObject)pf.GetProxy();
// Use the type cast feature
// Replace etc methods on advised should be same as on ProxyFactory
IAdvised advised = (IAdvised) proxied;
IAdvised advised = (IAdvised)proxied;
proxied.Age = 5;
Assert.AreEqual(1, cba1.GetCalls());
Assert.AreEqual(0, cba2.GetCalls());
@@ -427,9 +427,9 @@ namespace Spring.Aop.Framework
ProxyFactory pf = new ProxyFactory(tst);
// We've already implicitly added this interface.
// This call should be ignored without error
pf.AddInterface(typeof (ITimeStamped));
pf.AddInterface(typeof(ITimeStamped));
// All cool
ITimeStamped ts = (ITimeStamped) pf.GetProxy();
ITimeStamped ts = (ITimeStamped)pf.GetProxy();
}
internal class TestObjectSubclass : TestObject, IComparable
@@ -448,7 +448,7 @@ namespace Spring.Aop.Framework
ProxyFactory factory = new ProxyFactory(raw);
Assert.AreEqual(8, factory.Interfaces.Length, "Found correct number of interfaces");
//System.out.println("Proxied interfaces are " + StringUtils.arrayToDelimitedString(factory.getProxiedInterfaces(), ","));
ITestObject tb = (ITestObject) factory.GetProxy();
ITestObject tb = (ITestObject)factory.GetProxy();
Assert.IsTrue(tb is IOther, "Picked up secondary interface");
raw.Age = 25;
@@ -461,15 +461,15 @@ namespace Spring.Aop.Framework
//factory.addAdvisor(0, new DefaultIntroductionAdvisor(ti, typeof(ITimeStamped)));
factory.AddIntroduction(
new DefaultIntroductionAdvisor(ti, typeof (ITimeStamped))
new DefaultIntroductionAdvisor(ti, typeof(ITimeStamped))
);
Console.WriteLine(StringUtils.ArrayToDelimitedString(factory.Interfaces, "/"));
ITimeStamped ts = (ITimeStamped) factory.GetProxy();
ITimeStamped ts = (ITimeStamped)factory.GetProxy();
Assert.IsTrue(ts.TimeStamp == t);
// Shouldn't fail;
((IOther) ts).Absquatulate();
((IOther)ts).Absquatulate();
}
private class AnonymousClassInterceptor : IInterceptor
@@ -491,7 +491,7 @@ namespace Spring.Aop.Framework
}
// Check we can still use it
IOther other = (IOther) factory.GetProxy();
IOther other = (IOther)factory.GetProxy();
other.Absquatulate();
}
@@ -502,21 +502,21 @@ namespace Spring.Aop.Framework
NopInterceptor diUnused = new NopInterceptor(1); // // make instance unique (see SPRNET-847)
ProxyFactory factory = new ProxyFactory(new TestObject());
factory.AddAdvice(0, di);
ITestObject tb = (ITestObject) factory.GetProxy();
ITestObject tb = (ITestObject)factory.GetProxy();
Assert.IsTrue(factory.AdviceIncluded(di));
Assert.IsTrue(!factory.AdviceIncluded(diUnused));
Assert.IsTrue(factory.CountAdviceOfType(typeof (NopInterceptor)) == 1);
Assert.IsTrue(factory.CountAdviceOfType(typeof(NopInterceptor)) == 1);
factory.AddAdvice(0, diUnused);
Assert.IsTrue(factory.AdviceIncluded(diUnused));
Assert.IsTrue(factory.CountAdviceOfType(typeof (NopInterceptor)) == 2);
Assert.IsTrue(factory.CountAdviceOfType(typeof(NopInterceptor)) == 2);
}
[Test]
public void AddAdvisedSupportListener()
{
IDynamicMock mock = new DynamicMock(typeof(IAdvisedSupportListener));
IAdvisedSupportListener listener = (IAdvisedSupportListener) mock.Object;
IAdvisedSupportListener listener = (IAdvisedSupportListener)mock.Object;
mock.Expect("Activated");
ProxyFactory factory = new ProxyFactory(new TestObject());
@@ -529,7 +529,7 @@ namespace Spring.Aop.Framework
public void AdvisedSupportListenerMethodsAreCalledAppropriately()
{
IDynamicMock mock = new DynamicMock(typeof(IAdvisedSupportListener));
IAdvisedSupportListener listener = (IAdvisedSupportListener) mock.Object;
IAdvisedSupportListener listener = (IAdvisedSupportListener)mock.Object;
mock.Expect("Activated");
mock.Expect("AdviceChanged");
@@ -552,7 +552,7 @@ namespace Spring.Aop.Framework
public void AdvisedSupportListenerMethodsAre_NOT_CalledIfProxyHasNotBeenCreated()
{
IDynamicMock mock = new DynamicMock(typeof(IAdvisedSupportListener));
IAdvisedSupportListener listener = (IAdvisedSupportListener) mock.Object;
IAdvisedSupportListener listener = (IAdvisedSupportListener)mock.Object;
ProxyFactory factory = new ProxyFactory(new TestObject());
factory.AddListener(listener);
@@ -583,7 +583,7 @@ namespace Spring.Aop.Framework
public void RemoveAdvisedSupportListener()
{
IDynamicMock mock = new DynamicMock(typeof(IAdvisedSupportListener));
IAdvisedSupportListener listener = (IAdvisedSupportListener) mock.Object;
IAdvisedSupportListener listener = (IAdvisedSupportListener)mock.Object;
ProxyFactory factory = new ProxyFactory(new TestObject());
factory.AddListener(listener);
@@ -603,5 +603,73 @@ namespace Spring.Aop.Framework
factory.IsFrozen = true;
factory.RemoveAdvisor(null);
}
public interface IMultiProxyingTestInterface
{
string TestMethod(string arg);
}
public interface IMultiProxyingTestInterface2 : IMultiProxyingTestInterface { }
public class MultiProxyingTestClass : IMultiProxyingTestInterface2
{
public int InvocationCounter;
public string TestMethod(string arg)
{
InvocationCounter++;
return arg + "|" + arg;
}
}
public interface ICountingIntroduction
{
void Inc();
}
public class TestCountingIntroduction : ICountingIntroduction, IAdvice
{
public int Counter;
public void Inc()
{
Counter++;
}
}
[Test]
public void NestedProxiesDontInvokeSameAdviceOrIntroductionTwice()
{
MultiProxyingTestClass testObj = new MultiProxyingTestClass();
ProxyFactory pf1 = new ProxyFactory();
pf1.Target = testObj;
NopInterceptor di = new NopInterceptor();
NopInterceptor diUnused = new NopInterceptor(1); // // make instance unique (see SPRNET-847)
TestCountingIntroduction countingMixin = new TestCountingIntroduction();
pf1.AddAdvice(diUnused);
pf1.AddAdvisor(new DefaultPointcutAdvisor(di));
pf1.AddIntroduction(new DefaultIntroductionAdvisor(countingMixin));
object innerProxy = pf1.GetProxy();
ProxyFactory pf2 = new ProxyFactory();
pf2.Target = innerProxy;
pf2.AddAdvice(diUnused);
pf2.AddAdvisor(new DefaultPointcutAdvisor(di));
pf2.AddIntroduction(new DefaultIntroductionAdvisor(countingMixin));
object outerProxy = pf2.GetProxy();
// any advice instance is invoked once only
string result = ((IMultiProxyingTestInterface)outerProxy).TestMethod("arg");
Assert.AreEqual(1, testObj.InvocationCounter);
Assert.AreEqual("arg|arg", result);
Assert.AreEqual(1, di.Count);
// any introduction instance is invoked once only
((ICountingIntroduction)outerProxy).Inc();
Assert.AreEqual(1, countingMixin.Counter);
}
}
}

View File

@@ -0,0 +1,93 @@
#region License
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using AopAlliance.Aop;
using NUnit.Framework;
namespace Spring.Aop.Support
{
/// <summary>
/// </summary>
/// <author>Erich Eichinger</author>
[TestFixture]
public class DefaultIntroductionAdvisorTests
{
public interface IBaseInterface { }
public interface IDerivedInterface : IBaseInterface { }
private class TestIntroductionAdvice : IDerivedInterface, IAdvice
{
private object equalsToObject;
public void SetEqualsToObject(object other)
{
equalsToObject = other;
}
public override bool Equals(object obj)
{
return object.Equals(equalsToObject, obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
[Test]
public void EqualsOnAdviceEqualAndInterfacesEqual()
{
TestIntroductionAdvice to1 = new TestIntroductionAdvice();
TestIntroductionAdvice to2 = new TestIntroductionAdvice();
to1.SetEqualsToObject(to2);
to2.SetEqualsToObject(to1);
DefaultIntroductionAdvisor a1 = new DefaultIntroductionAdvisor(to1);
DefaultIntroductionAdvisor a2 = new DefaultIntroductionAdvisor(to2);
bool result = a1.Equals(a2);
Assert.IsTrue(result);
}
[Test]
[ExpectedException(typeof(ArgumentException), ExpectedMessage = "Type [Spring.Aop.Support.DefaultIntroductionAdvisorTests] is not an interface; cannot be used in an introduction.")]
public void BailsIfInterfaceTypeIsNotAnInterface()
{
DefaultIntroductionAdvisor a = new DefaultIntroductionAdvisor(new TestIntroductionAdvice(), this.GetType());
}
[Test]
[ExpectedException(typeof(ArgumentException), ExpectedMessage = "Introduction [Spring.Aop.Support.DefaultIntroductionAdvisorTests+TestIntroductionAdvice] does not implement interface 'System.ICloneable' specified in introduction advice.")]
public void IntroductionMustImplementIntroducedInterfaces()
{
DefaultIntroductionAdvisor advisor = new DefaultIntroductionAdvisor(new TestIntroductionAdvice(), typeof(ICloneable));
advisor.ValidateInterfaces();
}
[Test]
public void BaseInterfacesAreValid()
{
DefaultIntroductionAdvisor advisor = new DefaultIntroductionAdvisor(new TestIntroductionAdvice(), typeof(IBaseInterface));
advisor.ValidateInterfaces();
}
}
}

View File

@@ -210,6 +210,7 @@
<Compile Include="Aop\Support\ControlFlowPointcutTests.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Aop\Support\DefaultIntroductionAdvisorTests.cs" />
<Compile Include="Aop\Support\DelegatingIntroductionInterceptorTests.cs">
<SubType>Code</SubType>
</Compile>