+ ///
+ /// To expose the current proxy, set the
+ /// property on the controlling proxy to .
+ /// The default value for the property
+ /// is , for performance reasons.
+ ///
+ ///
/// The AOP framework does not expose proxies by default, as there is a
/// performance cost in doing so.
///
- ///
+ ///
/// The functionality in this class might be used by a target object that
/// needed access to resources on the invocation. However, this approach
/// should not be used when there is a reasonable alternative, as it makes
@@ -56,34 +63,21 @@ namespace Spring.Aop.Framework
/// Aleksandar Seovic (.NET)
public sealed class AopContext
{
- private const string CURRENTPROXY_SLOTNAME = "AopContext.CurrentProxySlotName";
+ [ThreadStatic]
+ private static Stack tls_ProxyStack;
///
- /// The AOP proxy associated with this thread.
+ /// The AOP proxy stack associated with this thread.
///
- ///
- ///
- /// Will be unless the
- /// property
- /// on the controlling proxy has been set to .
- ///
- ///
- /// The default value for the
- /// property
- /// is , for performance reasons.
- ///
- ///
private static Stack ProxyStack
{
get
{
- Stack proxyStack = LogicalThreadContext.GetData(CURRENTPROXY_SLOTNAME) as Stack;
- if (proxyStack == null)
+ if (tls_ProxyStack == null)
{
- proxyStack = new Stack();
- LogicalThreadContext.SetData(CURRENTPROXY_SLOTNAME, proxyStack);
+ tls_ProxyStack = new Stack();
}
- return proxyStack;
+ return tls_ProxyStack;
}
}
@@ -106,8 +100,7 @@ namespace Spring.Aop.Framework
{
get
{
- Stack proxyStack = LogicalThreadContext.GetData(CURRENTPROXY_SLOTNAME) as Stack;
- return (proxyStack != null && proxyStack.Count > 0);
+ return (tls_ProxyStack != null && tls_ProxyStack.Count > 0);
}
}
@@ -121,13 +114,14 @@ namespace Spring.Aop.Framework
{
get
{
- if (ProxyStack.Count == 0)
+ Stack proxyStack = ProxyStack;
+ if (proxyStack.Count == 0)
{
throw new AopConfigException(
"Cannot find proxy: Set the 'ExposeProxy' property " +
"to 'true' on IAdvised to make it available.");
}
- return ProxyStack.Peek();
+ return proxyStack.Peek();
}
}
@@ -163,12 +157,13 @@ namespace Spring.Aop.Framework
///
public static void PopProxy()
{
- if (ProxyStack.Count == 0)
+ Stack proxyStack = ProxyStack;
+ if (proxyStack.Count == 0)
{
throw new AopConfigException(
"Proxy stack empty. Always call 'PushProxy' before 'PopProxy'.");
}
- ProxyStack.Pop();
+ proxyStack.Pop();
}
#region Constructor (s) / Destructor
diff --git a/src/Spring/Spring.Aop/Aop/Framework/HashtableCachingAdvisorChainFactory.cs b/src/Spring/Spring.Aop/Aop/Framework/HashtableCachingAdvisorChainFactory.cs
index 1714ed8b..074719ea 100644
--- a/src/Spring/Spring.Aop/Aop/Framework/HashtableCachingAdvisorChainFactory.cs
+++ b/src/Spring/Spring.Aop/Aop/Framework/HashtableCachingAdvisorChainFactory.cs
@@ -22,82 +22,84 @@
using System;
using System.Collections;
+using System.Collections.Specialized;
using System.Reflection;
+using Spring.Collections;
#endregion
namespace Spring.Aop.Framework
{
- ///
- /// implementation
- /// that caches advisor chains on a per-advised-method basis.
- ///
- /// Rod Johnson
- /// Aleksandar Seovic (.NET)
- [Serializable]
- public sealed class HashtableCachingAdvisorChainFactory : IAdvisorChainFactory
- {
- private IDictionary methodCache = new Hashtable();
-
- ///
- /// Gets the list of and
- ///
- /// instances for the supplied .
- ///
- /// The proxy configuration object.
- /// The object proxy.
- ///
- /// The method for which the interceptors are to be evaluated.
- ///
- ///
- /// The of the target object.
- ///
- ///
- /// The list of and
- ///
- /// instances for the supplied .
- ///
- public IList GetInterceptors(IAdvised advised, object proxy, MethodInfo method, Type targetType)
- {
- IList cached = (IList) this.methodCache[method];
- if (cached == null)
- {
- // recalculate...
- cached = AdvisorChainFactoryUtils.CalculateInterceptors(advised, proxy, method, targetType);
+ ///
+ /// implementation
+ /// that caches advisor chains on a per-advised-method basis.
+ ///
+ /// Rod Johnson
+ /// Aleksandar Seovic (.NET)
+ [Serializable]
+ public sealed class HashtableCachingAdvisorChainFactory : IAdvisorChainFactory
+ {
+ private readonly IDictionary methodCache = new ListDictionary();
+
+ ///
+ /// Gets the list of and
+ ///
+ /// instances for the supplied .
+ ///
+ /// The proxy configuration object.
+ /// The object proxy.
+ ///
+ /// The method for which the interceptors are to be evaluated.
+ ///
+ ///
+ /// The of the target object.
+ ///
+ ///
+ /// The list of and
+ ///
+ /// instances for the supplied .
+ ///
+ public IList GetInterceptors(IAdvised advised, object proxy, MethodInfo method, Type targetType)
+ {
+ IList cached = (IList)this.methodCache[method];
+ if (cached == null)
+ {
+ // recalculate...
+ cached = AdvisorChainFactoryUtils.CalculateInterceptors(advised, proxy, method, targetType);
this.methodCache[method] = cached;
- }
- return cached;
- }
+ }
+ return cached;
+ }
- ///
- /// Invoked when the first proxy is created.
- ///
- ///
- /// The relevant source.
- ///
- public void Activated(AdvisedSupport source)
- {
- }
+ ///
+ /// Invoked when the first proxy is created.
+ ///
+ ///
+ /// The relevant source.
+ ///
+ public void Activated(AdvisedSupport source)
+ {
+ }
- ///
- /// Invoked when advice is changed after a proxy is created.
- ///
- ///
- /// The relevant source.
- ///
- public void AdviceChanged(AdvisedSupport source)
- {
- methodCache.Clear();
- }
+ ///
+ /// Invoked when advice is changed after a proxy is created.
+ ///
+ ///
+ /// The relevant source.
+ ///
+ public void AdviceChanged(AdvisedSupport source)
+ {
+ methodCache.Clear();
+ }
- ///
- /// Invoked when interfaces are changed after a proxy is created.
- ///
- ///
- /// The relevant source.
- ///
- public void InterfacesChanged(AdvisedSupport source)
- {
- }
- }
+ ///
+ /// Invoked when interfaces are changed after a proxy is created.
+ ///
+ ///
+ /// The relevant source.
+ ///
+ public void InterfacesChanged(AdvisedSupport source)
+ {
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Spring/Spring.Aop/Aop/Framework/ProxyConfig.cs b/src/Spring/Spring.Aop/Aop/Framework/ProxyConfig.cs
index 0a145c4a..eda0c092 100644
--- a/src/Spring/Spring.Aop/Aop/Framework/ProxyConfig.cs
+++ b/src/Spring/Spring.Aop/Aop/Framework/ProxyConfig.cs
@@ -59,7 +59,7 @@ namespace Spring.Aop.Framework
private IAopProxyFactory aopProxyFactory =
ObjectUtils.InstantiateType( typeof(ProxyConfig).Assembly, "Spring.Aop.Framework.DynamicProxy.CachedAopProxyFactory") as IAopProxyFactory;
private bool exposeProxy;
- private object syncRoot = new object();
+ private readonly object syncRoot = new object();
#endregion
#region Properites
diff --git a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicMethod.cs b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicMethod.cs
index 221dc15d..d5a51fbf 100644
--- a/src/Spring/Spring.Core/Reflection/Dynamic/DynamicMethod.cs
+++ b/src/Spring/Spring.Core/Reflection/Dynamic/DynamicMethod.cs
@@ -1,7 +1,7 @@
#region License
/*
- * Copyright © 2002-2007 the original author or authors.
+ * 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.
@@ -22,8 +22,9 @@
using System;
using System.Collections;
+using System.Collections.Specialized;
using System.Reflection;
-using System.Reflection.Emit;
+using Spring.Collections;
using Spring.Util;
#endregion
@@ -79,29 +80,23 @@ namespace Spring.Reflection.Dynamic
#if NET_2_0
#region Generated Function Cache
- private static readonly IDictionary methodCache = new Hashtable();
-
- ///
- /// Obtains cached property info or creates a new entry, if none is found.
- ///
- private static FunctionDelegate GetOrCreateDynamicMethod(MethodInfo methodInfo)
+ private class SafeMethodState
{
- FunctionDelegate method = (FunctionDelegate)methodCache[methodInfo];
- if (method == null)
+ public readonly FunctionDelegate method;
+ public readonly object[] nullArguments;
+
+ public SafeMethodState(FunctionDelegate method, object[] nullArguments)
{
- method = DynamicReflectionManager.CreateMethod(methodInfo);
- lock (methodCache)
- {
- methodCache[methodInfo] = method;
- }
+ this.method = method;
+ this.nullArguments = nullArguments;
}
- return method;
- }
+ }
+
+ private static readonly IDictionary stateCache = new HybridDictionary();
#endregion
- private readonly FunctionDelegate method;
- private readonly object[] nullArguments;
+ private readonly SafeMethodState state;
///
/// Creates a new instance of the safe method wrapper.
@@ -111,9 +106,15 @@ namespace Spring.Reflection.Dynamic
{
AssertUtils.ArgumentNotNull(methodInfo, "You cannot create a dynamic method for a null value.");
+ state = (SafeMethodState)stateCache[methodInfo];
+ if (state == null)
+ {
+ state = new SafeMethodState(DynamicReflectionManager.CreateMethod(methodInfo),
+ new object[methodInfo.GetParameters().Length]
+ );
+ stateCache[methodInfo] = state;
+ }
this.methodInfo = methodInfo;
- this.method = GetOrCreateDynamicMethod(methodInfo);
- this.nullArguments = new object[methodInfo.GetParameters().Length];
}
///
@@ -131,14 +132,15 @@ namespace Spring.Reflection.Dynamic
public object Invoke(object target, params object[] arguments)
{
// special case - when calling Invoke(null,null) it is undecidible if the second null is an argument or the argument array
- if (arguments==null && nullArguments.Length==1) arguments=nullArguments;
- int arglen = (arguments==null?0:arguments.Length);
- AssertUtils.IsTrue(
- nullArguments.Length == arglen
- , string.Format("Invalid number of arguments passed into method {0} - expected {1}, but was {2}", methodInfo.Name, nullArguments.Length, arglen)
- );
+ object[] nullArguments = state.nullArguments;
+ if (arguments == null && nullArguments.Length == 1) arguments = nullArguments;
+ int arglen = (arguments == null ? 0 : arguments.Length);
+ if (nullArguments.Length != arglen)
+ {
+ throw new ArgumentException(string.Format("Invalid number of arguments passed into method {0} - expected {1}, but was {2}", methodInfo.Name, nullArguments.Length, arglen));
+ }
- return this.method(target, arguments);
+ return this.state.method(target, arguments);
}
#else
private IDynamicMethod dynamicMethod;
@@ -206,7 +208,7 @@ namespace Spring.Reflection.Dynamic
}
#endregion
-
+
#if NET_2_0
///
/// Factory class for dynamic methods.
@@ -263,7 +265,7 @@ namespace Spring.Reflection.Dynamic
private static readonly CreateMethodCallback s_createMethodCallback = new CreateMethodCallback(CreateInternal);
- #region Create Method
+ #region Create Method
///
/// Creates dynamic method instance for the specified .
diff --git a/test/Spring/Spring.Aop.Tests/Aop/Framework/AopContextTests.cs b/test/Spring/Spring.Aop.Tests/Aop/Framework/AopContextTests.cs
index 96be81bd..71e59468 100644
--- a/test/Spring/Spring.Aop.Tests/Aop/Framework/AopContextTests.cs
+++ b/test/Spring/Spring.Aop.Tests/Aop/Framework/AopContextTests.cs
@@ -22,7 +22,7 @@
using System;
using System.Threading;
-
+using AopAlliance.Aop;
using NUnit.Framework;
using Spring.Objects;
@@ -34,58 +34,58 @@ using Spring.Util;
namespace Spring.Aop.Framework
{
- ///
- /// Unit tests for the AopContext class.
- ///
- /// Rick Evans
- [TestFixture]
- public sealed class AopContextTests
- {
- [SetUp]
- public void SetUp()
- {
- // makes sure the context is always empty before any unit test...
- try
- {
- do
- {
- AopContext.PopProxy();
- } while (true);
- }
- catch (AopConfigException)
- {
- }
- }
-
- [Test]
- [ExpectedException(typeof (AopConfigException))]
- public void CurrentProxyChokesIfNoAopProxyIsOnTheStack()
- {
- AopContext.CurrentProxy.ToString();
- }
-
- [Test]
- public void CurrentProxyStackJustPeeksItDoesntPop()
- {
- string foo = "Foo";
- AopContext.PushProxy(foo);
- object fooref = AopContext.CurrentProxy;
- Assert.IsTrue(ReferenceEquals(foo, fooref),
- "Not the exact same instance (must be).");
- // must not have been popped off the stack by looking at it...
- object foorefref = AopContext.CurrentProxy;
- Assert.IsTrue(ReferenceEquals(fooref, foorefref),
- "Not the exact same instance (must be).");
- }
-
- [Test]
- [ExpectedException(typeof (AopConfigException))]
- public void PopProxyWithNothingOnStack()
- {
- AopContext.PopProxy();
+ ///
+ /// Unit tests for the AopContext class.
+ ///
+ /// Rick Evans
+ [TestFixture]
+ public sealed class AopContextTests
+ {
+ [SetUp]
+ public void SetUp()
+ {
+ // makes sure the context is always empty before any unit test...
+ try
+ {
+ do
+ {
+ AopContext.PopProxy();
+ } while (true);
+ }
+ catch (AopConfigException)
+ {
+ }
}
[Test]
+ [ExpectedException(typeof(AopConfigException))]
+ public void CurrentProxyChokesIfNoAopProxyIsOnTheStack()
+ {
+ AopContext.CurrentProxy.ToString();
+ }
+
+ [Test]
+ public void CurrentProxyStackJustPeeksItDoesntPop()
+ {
+ string foo = "Foo";
+ AopContext.PushProxy(foo);
+ object fooref = AopContext.CurrentProxy;
+ Assert.IsTrue(ReferenceEquals(foo, fooref),
+ "Not the exact same instance (must be).");
+ // must not have been popped off the stack by looking at it...
+ object foorefref = AopContext.CurrentProxy;
+ Assert.IsTrue(ReferenceEquals(fooref, foorefref),
+ "Not the exact same instance (must be).");
+ }
+
+ [Test]
+ [ExpectedException(typeof(AopConfigException))]
+ public void PopProxyWithNothingOnStack()
+ {
+ AopContext.PopProxy();
+ }
+
+ [Test(Description = "SPRNET-1158")]
public void IsActiveMatchesStackState()
{
Assert.IsFalse(AopContext.IsActive);
@@ -97,11 +97,72 @@ namespace Spring.Aop.Framework
#region CurrentProxyIsThreadSafe
+ [Test, Explicit]
+ public void ProxyPerformanceTests()
+ {
+ int runs = 5000000;
+ StopWatch watch = new StopWatch();
+
+ ITestObject testObject = new ChainableTestObject(null);
+ using (watch.Start("Naked Duration: {0}"))
+ {
+ for (int i = 0; i < runs; i++)
+ {
+ object result = testObject.DoSomething(this);
+ }
+ }
+
+ ITestObject hardcodedWrapper = new ChainableTestObject(testObject);
+ using (watch.Start("Hardcoded Wrapper Duration: {0}"))
+ {
+ for (int i = 0; i < runs; i++)
+ {
+ object result = hardcodedWrapper.DoSomething(this);
+ }
+ }
+
+ PeformanceTestAopContextInterceptor interceptor = new PeformanceTestAopContextInterceptor();
+ ITestObject proxy = CreateProxy(testObject, interceptor, false);
+ using(watch.Start("Proxy Duration ('ExposeProxy'==false): {0}"))
+ {
+ for(int i=0;i