aop performance improvements

This commit is contained in:
eeichinger
2009-02-01 23:54:49 +00:00
parent ec6525f510
commit ff661e5893
6 changed files with 394 additions and 269 deletions

View File

@@ -76,7 +76,7 @@ namespace Spring.Aop.Framework
/// Array updated on changes to the advisors list, which is easier to
/// manipulate internally
/// </summary>
private IAdvisor[] _advisorsArray = new IAdvisor[] {};
private volatile IAdvisor[] _advisorsArray = new IAdvisor[] { };
/// <summary>
/// List of introductions.
@@ -88,7 +88,7 @@ namespace Spring.Aop.Framework
/// manipulate internally
/// </summary>
private IIntroductionAdvisor[] _introductionsArray
= new IIntroductionAdvisor[] {};
= new IIntroductionAdvisor[] { };
/// <summary>
/// Interface map specifying which object should interface methods be
@@ -100,7 +100,7 @@ namespace Spring.Aop.Framework
/// to the target object.
/// </p>
/// </remarks>
private IDictionary interfaceMap = new ListDictionary();
private readonly IDictionary interfaceMap = new ListDictionary();
/// <summary>
/// The <see cref="Spring.Aop.ITargetSource"/> for this instance.
@@ -120,7 +120,7 @@ namespace Spring.Aop.Framework
/// <summary>
/// The list of <see cref="Spring.Aop.Framework.AdvisedSupport"/> event listeners.
/// </summary>
private IList listeners = new ArrayList();
private readonly IList listeners = new ArrayList();
/// <summary>
/// The advisor chain factory.
@@ -149,7 +149,8 @@ namespace Spring.Aop.Framework
/// <exception cref="Spring.Aop.Framework.AopConfigException">
/// If this
/// </exception>
public AdvisedSupport(Type[] interfaces) : this()
public AdvisedSupport(Type[] interfaces)
: this()
{
if (interfaces != null)
{
@@ -181,14 +182,14 @@ namespace Spring.Aop.Framework
{
get
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
return this.advisorChainFactory;
}
}
set
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
if (this.advisorChainFactory != null)
{
@@ -216,12 +217,12 @@ namespace Spring.Aop.Framework
{
bool initialized = !(this.m_targetSource is EmptyTargetSource);
this.m_targetSource = value;
if (this.m_targetSource != null && !initialized && interfaceMap.Count == 0)
{
Type[] interfaces = ReflectionUtils.GetInterfaces(this.m_targetSource.TargetType);
foreach (Type intf in interfaces)
{
{
AddInterfaceInternal(intf);
}
}
@@ -240,23 +241,31 @@ namespace Spring.Aop.Framework
get
{
bool canBeSerialized = TargetSource.TargetType.IsSerializable;
if (canBeSerialized)
if (!canBeSerialized) return false;
lock (this.SyncRoot)
{
for (int i = 0; canBeSerialized && i < _advisorsArray.Length; i++)
IAdvisor[] advisorsArray = this._advisorsArray;
IIntroductionAdvisor[] introductionsArray = this._introductionsArray;
for (int i = 0; i < advisorsArray.Length; i++)
{
IAdvisor advisor = _advisorsArray[i];
IAdvisor advisor = advisorsArray[i];
canBeSerialized = advisor.GetType().IsSerializable
&& advisor.Advice.GetType().IsSerializable;
if (!canBeSerialized) return false;
}
for (int i = 0; canBeSerialized && i < _introductionsArray.Length; i++)
for (int i = 0; i < introductionsArray.Length; i++)
{
IIntroductionAdvisor advisor = _introductionsArray[i];
IIntroductionAdvisor advisor = introductionsArray[i];
canBeSerialized = advisor.GetType().IsSerializable
&& advisor.Advice.GetType().IsSerializable;
if (!canBeSerialized) return false;
}
}
return canBeSerialized;
return true;
}
}
@@ -273,7 +282,7 @@ namespace Spring.Aop.Framework
{
get
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
Type[] proxiedInterfaces = new Type[this.interfaceMap.Keys.Count];
this.interfaceMap.Keys.CopyTo(proxiedInterfaces, 0);
@@ -282,7 +291,7 @@ namespace Spring.Aop.Framework
}
set
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
this.interfaceMap.Clear();
for (int i = 0; i < value.Length; i++)
@@ -307,7 +316,7 @@ namespace Spring.Aop.Framework
{
get
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
return new Hashtable(this.interfaceMap);
}
@@ -332,7 +341,7 @@ namespace Spring.Aop.Framework
{
if (intf != null)
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
foreach (Type proxyInterface in this.interfaceMap.Keys)
{
@@ -359,9 +368,10 @@ namespace Spring.Aop.Framework
{
get
{
lock(this.SyncRoot)
// lock(this.SyncRoot)
{
return (IAdvisor[]) this._advisorsArray.Clone();
// return (IAdvisor[]) _advisorsArray.Clone();
return _advisorsArray;
}
}
}
@@ -387,9 +397,9 @@ namespace Spring.Aop.Framework
{
get
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
return (IIntroductionAdvisor[]) this._introductionsArray.Clone();
return (IIntroductionAdvisor[])this._introductionsArray.Clone();
}
}
}
@@ -461,7 +471,7 @@ namespace Spring.Aop.Framework
/// </returns>
public virtual int IndexOf(IAdvisor advisor)
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
return IndexOfInternal(advisor);
}
@@ -482,7 +492,7 @@ namespace Spring.Aop.Framework
/// </returns>
public virtual int IndexOf(IIntroductionAdvisor advisor)
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
return IndexOfInternal(advisor);
}
@@ -509,7 +519,7 @@ namespace Spring.Aop.Framework
bool wasRemoved = false;
if (advisor != null)
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
int index = IndexOf(advisor);
if (index == -1)
@@ -545,7 +555,7 @@ namespace Spring.Aop.Framework
public virtual void RemoveAdvisor(int index)
{
DieIfFrozen("Cannot remove advisor: config is frozen");
lock(this.SyncRoot)
lock (this.SyncRoot)
{
RemoveAdvisorInternal(index);
}
@@ -569,7 +579,7 @@ namespace Spring.Aop.Framework
/// </exception>
public bool RemoveAdvice(IAdvice advice)
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
int index = IndexOf(advice);
if (index == -1)
@@ -606,7 +616,7 @@ namespace Spring.Aop.Framework
bool wasRemoved = false;
if (introduction != null)
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
int index = IndexOf(introduction);
if (index == -1)
@@ -639,7 +649,7 @@ namespace Spring.Aop.Framework
public virtual void RemoveIntroduction(int index)
{
DieIfFrozen("Cannot remove introduction: config is frozen");
lock(this.SyncRoot)
lock (this.SyncRoot)
{
if (index < 0 || index >= _introductions.Count)
{
@@ -647,7 +657,7 @@ namespace Spring.Aop.Framework
"Introduction index " + index + " is out of bounds: Only have " + _introductions.Count +
" introductions.");
}
IIntroductionAdvisor advisor = (IIntroductionAdvisor) _introductions[index];
IIntroductionAdvisor advisor = (IIntroductionAdvisor)_introductions[index];
// remove all interfaces introduced by the advisor...
foreach (Type intf in advisor.Interfaces)
{
@@ -658,33 +668,33 @@ namespace Spring.Aop.Framework
}
}
//
// /// <summary>
// /// Removes the supplied <paramref name="interceptor"/> from the list of
// /// <see cref="Spring.Aop.Framework.AdvisedSupport.Advisors"/> for this
// /// proxy.
// /// </summary>
// /// <param name="interceptor">
// /// The <see cref="AopAlliance.Intercept.IInterceptor"/> to be removed.
// /// </param>
// /// <exception cref="AopConfigException">
// /// If this proxy configuration is frozen and the
// /// <paramref name="interceptor"/> cannot be added.
// /// </exception>
// public bool RemoveInterceptor(IInterceptor interceptor)
// {
// AssertFrozen("Cannot remove interceptor: config is frozen");
// int index = IndexOf(interceptor);
// if (index == -1)
// {
// return false;
// }
// else
// {
// RemoveAdvisor(index);
// return true;
// }
// }
//
// /// <summary>
// /// Removes the supplied <paramref name="interceptor"/> from the list of
// /// <see cref="Spring.Aop.Framework.AdvisedSupport.Advisors"/> for this
// /// proxy.
// /// </summary>
// /// <param name="interceptor">
// /// The <see cref="AopAlliance.Intercept.IInterceptor"/> to be removed.
// /// </param>
// /// <exception cref="AopConfigException">
// /// If this proxy configuration is frozen and the
// /// <paramref name="interceptor"/> cannot be added.
// /// </exception>
// public bool RemoveInterceptor(IInterceptor interceptor)
// {
// AssertFrozen("Cannot remove interceptor: config is frozen");
// int index = IndexOf(interceptor);
// if (index == -1)
// {
// return false;
// }
// else
// {
// RemoveAdvisor(index);
// return true;
// }
// }
/// <summary>
/// Adds the supplied <paramref name="advisor"/> to the list
@@ -705,12 +715,12 @@ namespace Spring.Aop.Framework
public virtual void AddAdvisor(int index, IAdvisor advisor)
{
DieIfFrozen("Cannot add advisor: config is frozen");
lock(this.SyncRoot)
lock (this.SyncRoot)
{
// advisor already in list (SPRNET-846)
if (_advisors.Contains(advisor)) return;
if(index == -1)
if (index == -1)
{
this._advisors.Add(advisor);
}
@@ -756,7 +766,7 @@ namespace Spring.Aop.Framework
{
if (advisor is IIntroductionAdvisor)
{
AddIntroduction((IIntroductionAdvisor) advisor);
AddIntroduction((IIntroductionAdvisor)advisor);
}
else
{
@@ -786,7 +796,7 @@ namespace Spring.Aop.Framework
DieIfFrozen("Cannot add introduction: config is frozen");
introductionAdvisor.ValidateInterfaces();
lock(this.SyncRoot)
lock (this.SyncRoot)
{
if (index < this._introductions.Count)
{
@@ -822,13 +832,13 @@ namespace Spring.Aop.Framework
public virtual void AddIntroduction(IIntroductionAdvisor introductionAdvisor)
{
Type introductionType = introductionAdvisor.Advice.GetType();
lock(this.SyncRoot)
lock (this.SyncRoot)
{
int pos = this._introductions.Count;
for (int i = 0; i < pos; i++)
{
IIntroductionAdvisor introduction
= (IIntroductionAdvisor) this._introductions[i];
= (IIntroductionAdvisor)this._introductions[i];
if (introduction.Advice.GetType() == introductionType)
{
pos = i;
@@ -858,14 +868,14 @@ namespace Spring.Aop.Framework
/// </exception>
public virtual void ReplaceIntroduction(int index, IIntroductionAdvisor introduction)
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
if(index < 0 || index >= _introductions.Count)
if (index < 0 || index >= _introductions.Count)
{
throw new AopConfigException(
"Introduction index " + index + " is out of bounds:" +
" there are currently " + _introductions.Count +
" introductions." );
" introductions.");
}
_introductions[index] = introduction;
@@ -897,7 +907,7 @@ namespace Spring.Aop.Framework
public bool ReplaceAdvisor(IAdvisor oldAdvisor, IAdvisor newAdvisor)
{
DieIfFrozen("Cannot replace advisor: config is frozen.");
lock(this.SyncRoot)
lock (this.SyncRoot)
{
int index = IndexOf(oldAdvisor);
if (index == -1 || newAdvisor == null)
@@ -920,7 +930,7 @@ namespace Spring.Aop.Framework
/// </returns>
public virtual string ToProxyConfigString()
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
return ToStringInternal();
}
@@ -1016,7 +1026,7 @@ namespace Spring.Aop.Framework
/// </param>
public virtual void AddListener(IAdvisedSupportListener listener)
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
this.listeners.Add(listener);
}
@@ -1031,7 +1041,7 @@ namespace Spring.Aop.Framework
/// </param>
public virtual void RemoveListener(IAdvisedSupportListener listener)
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
this.listeners.Remove(listener);
}
@@ -1052,10 +1062,10 @@ namespace Spring.Aop.Framework
/// </exception>
public virtual void AddInterface(Type intf)
{
DieIfFrozen("Cannot add interface: configuration is frozen.");
DieIfFrozen("Cannot add interface: configuration is frozen.");
AssertUtils.ArgumentNotNull(intf, "intf", "Cannot proxy a null interface.");
lock(this.SyncRoot)
lock (this.SyncRoot)
{
AddInterfaceInternal(intf);
InterfacesChanged();
@@ -1090,8 +1100,8 @@ namespace Spring.Aop.Framework
/// <see langword="true"/> if the interface was removed.</returns>
public virtual bool RemoveInterface(Type intf)
{
DieIfFrozen("Cannot remove interface: configuration is frozen.");
lock(this.SyncRoot)
DieIfFrozen("Cannot remove interface: configuration is frozen.");
lock (this.SyncRoot)
{
if (intf != null && this.interfaceMap.Contains(intf))
{
@@ -1125,7 +1135,7 @@ namespace Spring.Aop.Framework
/// </returns>
public virtual int IndexOf(IAdvice advice)
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
return IndexOfInternal(advice);
}
@@ -1158,7 +1168,7 @@ namespace Spring.Aop.Framework
{
for (int i = 0; i < this._advisors.Count; ++i)
{
IAdvisor advisor = (IAdvisor) this._advisors[i];
IAdvisor advisor = (IAdvisor)this._advisors[i];
if (advisor.Advice == advice)
{
return i;
@@ -1275,7 +1285,7 @@ namespace Spring.Aop.Framework
public int CountAdviceOfType(Type interceptorType)
{
int count = 0;
lock(this.SyncRoot)
lock (this.SyncRoot)
{
foreach (IAdvisor advisor in this._advisors)
{
@@ -1313,8 +1323,9 @@ namespace Spring.Aop.Framework
/// </summary>
private void UpdateAdvisorsArray()
{
this._advisorsArray = new IAdvisor[this._advisors.Count];
this._advisors.CopyTo(this._advisorsArray, 0);
IAdvisor[] advisorsArray = new IAdvisor[this._advisors.Count];
this._advisors.CopyTo(advisorsArray, 0);
this._advisorsArray = advisorsArray;
}
/// <summary>
@@ -1322,8 +1333,9 @@ namespace Spring.Aop.Framework
/// </summary>
private void UpdateIntroductionsArray()
{
this._introductionsArray = new IIntroductionAdvisor[this._introductions.Count];
this._introductions.CopyTo(this._introductionsArray, 0);
IIntroductionAdvisor[] introductionsArray = new IIntroductionAdvisor[this._introductions.Count];
this._introductions.CopyTo(introductionsArray, 0);
this._introductionsArray = introductionsArray;
}
/// <summary>
@@ -1454,7 +1466,7 @@ namespace Spring.Aop.Framework
/// </returns>
public override string ToString()
{
lock(this.SyncRoot)
lock (this.SyncRoot)
{
return ToStringInternal();
}

View File

@@ -20,6 +20,7 @@
#region Imports
using System;
using System.Collections;
using Spring.Threading;
@@ -40,11 +41,17 @@ namespace Spring.Aop.Framework
/// advice can use this to make advised calls. They can also use it to find
/// advice configuration.
/// </p>
/// <note>
/// <p>
/// To expose the current proxy, set the <see cref="Spring.Aop.Framework.ProxyConfig.ExposeProxy"/>
/// property on the controlling proxy to <see langword="true"/>.
/// The default value for the <see cref="Spring.Aop.Framework.ProxyConfig.ExposeProxy"/> property
/// is <see langword="false"/>, for performance reasons.
/// </p>
/// <note>
/// The AOP framework does not expose proxies by default, as there is a
/// performance cost in doing so.
/// </note>
/// <p>
/// <p>
/// 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
/// <author>Aleksandar Seovic (.NET)</author>
public sealed class AopContext
{
private const string CURRENTPROXY_SLOTNAME = "AopContext.CurrentProxySlotName";
[ThreadStatic]
private static Stack tls_ProxyStack;
/// <summary>
/// The AOP proxy associated with this thread.
/// The AOP proxy stack associated with this thread.
/// </summary>
/// <remarks>
/// <p>
/// Will be <cref lang="null"/> unless the
/// <see cref="Spring.Aop.Framework.ProxyConfig.ExposeProxy"/> property
/// on the controlling proxy has been set to <see langword="true"/>.
/// </p>
/// <p>
/// The default value for the
/// <see cref="Spring.Aop.Framework.ProxyConfig.ExposeProxy"/> property
/// is <see langword="false"/>, for performance reasons.
/// </p>
/// </remarks>
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
/// </exception>
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

View File

@@ -22,82 +22,84 @@
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Reflection;
using Spring.Collections;
#endregion
namespace Spring.Aop.Framework
{
/// <summary>
/// <see cref="Spring.Aop.Framework.IAdvisorChainFactory"/> implementation
/// that caches advisor chains on a per-advised-method basis.
/// </summary>
/// <author>Rod Johnson</author>
/// <author>Aleksandar Seovic (.NET)</author>
[Serializable]
public sealed class HashtableCachingAdvisorChainFactory : IAdvisorChainFactory
{
private IDictionary methodCache = new Hashtable();
/// <summary>
/// Gets the list of <see cref="AopAlliance.Intercept.IInterceptor"/> and
/// <see cref="Spring.Aop.Framework.InterceptorAndDynamicMethodMatcher"/>
/// instances for the supplied <paramref name="proxy"/>.
/// </summary>
/// <param name="advised">The proxy configuration object.</param>
/// <param name="proxy">The object proxy.</param>
/// <param name="method">
/// The method for which the interceptors are to be evaluated.
/// </param>
/// <param name="targetType">
/// The <see cref="System.Type"/> of the target object.
/// </param>
/// <returns>
/// The list of <see cref="AopAlliance.Intercept.IInterceptor"/> and
/// <see cref="Spring.Aop.Framework.InterceptorAndDynamicMethodMatcher"/>
/// instances for the supplied <paramref name="proxy"/>.
/// </returns>
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);
/// <summary>
/// <see cref="Spring.Aop.Framework.IAdvisorChainFactory"/> implementation
/// that caches advisor chains on a per-advised-method basis.
/// </summary>
/// <author>Rod Johnson</author>
/// <author>Aleksandar Seovic (.NET)</author>
[Serializable]
public sealed class HashtableCachingAdvisorChainFactory : IAdvisorChainFactory
{
private readonly IDictionary methodCache = new ListDictionary();
/// <summary>
/// Gets the list of <see cref="AopAlliance.Intercept.IInterceptor"/> and
/// <see cref="Spring.Aop.Framework.InterceptorAndDynamicMethodMatcher"/>
/// instances for the supplied <paramref name="proxy"/>.
/// </summary>
/// <param name="advised">The proxy configuration object.</param>
/// <param name="proxy">The object proxy.</param>
/// <param name="method">
/// The method for which the interceptors are to be evaluated.
/// </param>
/// <param name="targetType">
/// The <see cref="System.Type"/> of the target object.
/// </param>
/// <returns>
/// The list of <see cref="AopAlliance.Intercept.IInterceptor"/> and
/// <see cref="Spring.Aop.Framework.InterceptorAndDynamicMethodMatcher"/>
/// instances for the supplied <paramref name="proxy"/>.
/// </returns>
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;
}
/// <summary>
/// Invoked when the first proxy is created.
/// </summary>
/// <param name="source">
/// The relevant <see cref="Spring.Aop.Framework.AdvisedSupport"/> source.
/// </param>
public void Activated(AdvisedSupport source)
{
}
/// <summary>
/// Invoked when the first proxy is created.
/// </summary>
/// <param name="source">
/// The relevant <see cref="Spring.Aop.Framework.AdvisedSupport"/> source.
/// </param>
public void Activated(AdvisedSupport source)
{
}
/// <summary>
/// Invoked when advice is changed after a proxy is created.
/// </summary>
/// <param name="source">
/// The relevant <see cref="Spring.Aop.Framework.AdvisedSupport"/> source.
/// </param>
public void AdviceChanged(AdvisedSupport source)
{
methodCache.Clear();
}
/// <summary>
/// Invoked when advice is changed after a proxy is created.
/// </summary>
/// <param name="source">
/// The relevant <see cref="Spring.Aop.Framework.AdvisedSupport"/> source.
/// </param>
public void AdviceChanged(AdvisedSupport source)
{
methodCache.Clear();
}
/// <summary>
/// Invoked when interfaces are changed after a proxy is created.
/// </summary>
/// <param name="source">
/// The relevant <see cref="Spring.Aop.Framework.AdvisedSupport"/> source.
/// </param>
public void InterfacesChanged(AdvisedSupport source)
{
}
}
/// <summary>
/// Invoked when interfaces are changed after a proxy is created.
/// </summary>
/// <param name="source">
/// The relevant <see cref="Spring.Aop.Framework.AdvisedSupport"/> source.
/// </param>
public void InterfacesChanged(AdvisedSupport source)
{
}
}
}

View File

@@ -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

View File

@@ -1,7 +1,7 @@
#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.
@@ -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();
/// <summary>
/// Obtains cached property info or creates a new entry, if none is found.
/// </summary>
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;
/// <summary>
/// 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];
}
/// <summary>
@@ -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
/// <summary>
/// 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
/// <summary>
/// Creates dynamic method instance for the specified <see cref="MethodInfo"/>.

View File

@@ -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
{
/// <summary>
/// Unit tests for the AopContext class.
/// </summary>
/// <author>Rick Evans</author>
[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();
/// <summary>
/// Unit tests for the AopContext class.
/// </summary>
/// <author>Rick Evans</author>
[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<runs;i++)
{
object result = proxy.DoSomething(this);
}
}
Assert.AreEqual(runs, interceptor.Calls);
interceptor = new PeformanceTestAopContextInterceptor();
proxy = CreateProxy(testObject, interceptor, true);
using(watch.Start("Proxy Duration ('ExposeProxy'==true): {0}"))
{
for(int i=0;i<runs;i++)
{
object result = proxy.DoSomething(this);
}
}
Assert.AreEqual(runs, interceptor.Calls);
}
private class PeformanceTestAopContextInterceptor : IMethodInterceptor
{
public int Calls = 0;
public object Invoke(IMethodInvocation invocation)
{
Calls++;
Object ret = invocation.Proceed();
return ret;
}
}
[Test(Description = "http://opensource.atlassian.com/projects/spring/browse/SPRNET-341")]
public void CurrentProxyIsThreadSafe()
{
AsyncTestMethod t1 = new AsyncTestMethod(100, new ThreadStart(ProxyTestObjectAndExposeProxy));
AsyncTestMethod t2 = new AsyncTestMethod(100, new ThreadStart(ProxyTestObjectAndExposeProxy));
ProxyTestObjectAndExposeProxy();
AsyncTestMethod t1 = new AsyncTestMethod(1000, new ThreadStart(ProxyTestObjectAndExposeProxy));
AsyncTestMethod t2 = new AsyncTestMethod(1000, new ThreadStart(ProxyTestObjectAndExposeProxy));
t1.Start();
t2.Start();
@@ -109,32 +170,85 @@ namespace Spring.Aop.Framework
t2.AssertNoException();
}
public interface ITestObject
{
object DoSomething(object arg);
}
private class ChainableTestObject : ITestObject
{
private readonly ITestObject next;
public ChainableTestObject(ITestObject next)
{
this.next = next;
}
public virtual object DoSomething(object arg)
{
if (next != null)
{
return next.DoSomething(arg);
}
// simulate some sensible work
string rep = string.Format("{0} {1}", this.GetType(), arg.GetHashCode());
return arg;
}
}
private void ProxyTestObjectAndExposeProxy()
{
TestObject target = new TestObject();
target.Age = 26;
TestAopContextInterceptor interceptor = new TestAopContextInterceptor();
ITestObject proxy = CreateProxyChain(interceptor, true);
Assert.IsFalse(AopContext.IsActive);
Assert.AreEqual(this, proxy.DoSomething(this), "Incorrect return value");
Assert.IsFalse(AopContext.IsActive);
Assert.AreEqual(2, interceptor.Calls); // 2 interceptions on the way
}
private ITestObject CreateProxyChain(IAdvice interceptor, bool exposeProxy)
{
ITestObject first = new ChainableTestObject(null);
ITestObject firstProxy = CreateProxy(first, interceptor, exposeProxy);
Assert.IsNotNull(firstProxy);
ITestObject second = new ChainableTestObject(firstProxy);
ITestObject secondProxy = CreateProxy(second, interceptor, exposeProxy);
Assert.IsNotNull(secondProxy);
return secondProxy;
}
private ITestObject CreateProxy(object target, IAdvice interceptor, bool exposeProxy)
{
ProxyFactory pf = new ProxyFactory();
pf.ExposeProxy = true;
pf.ExposeProxy = exposeProxy;
pf.Target = target;
pf.AddAdvice(new TestAopContextInterceptor());
pf.AddAdvice(interceptor);
ITestObject proxy = pf.GetProxy() as ITestObject;
Assert.IsNotNull(proxy);
Assert.AreEqual(target.Age, proxy.Age, "Incorrect age");
return pf.GetProxy() as ITestObject;
}
private class TestAopContextInterceptor : IMethodInterceptor
{
public int Calls = 0;
public object Invoke(IMethodInvocation invocation)
{
Calls++;
Assert.IsTrue(AopContext.IsActive);
Assert.IsNotNull(AopContext.CurrentProxy);
Assert.AreSame(invocation.Proxy, AopContext.CurrentProxy);
Object ret = invocation.Proceed();
Assert.IsNotNull(AopContext.CurrentProxy);
Assert.IsTrue(AopContext.IsActive);
return ret;
}
}
#endregion
}
}
}