/// Executes after target
@@ -78,35 +91,54 @@ namespace Spring.Aspects.Cache
///
public void AfterReturning(object returnValue, MethodInfo method, object[] arguments, object target)
{
- ParameterInfo[] parameters = method.GetParameters();
- for (int i = 0; i < parameters.Length; i++)
+ bool isLogDebugEnabled = logger.IsDebugEnabled;
+
+ CacheParameterInfo cpi = GetCacheParameterInfo(method);
+ CacheParameterAttribute[][] cacheParameterAttributes = cpi.CacheParameterAttributes;
+
+ for (int i = 0; i < cacheParameterAttributes.Length; i++)
{
- ParameterInfo p = parameters[i];
- CacheParameterAttribute[] paramInfoArray =
- (CacheParameterAttribute[])p.GetCustomAttributes(typeof(CacheParameterAttribute), false);
-
- bool isLogDebugEnabled = logger.IsDebugEnabled;
- foreach (CacheParameterAttribute paramInfo in paramInfoArray)
+ foreach (CacheParameterAttribute paramInfo in cacheParameterAttributes[i])
{
if (EvalCondition(paramInfo.Condition, paramInfo.ConditionExpression, arguments[i], null))
{
ICache cache = GetCache(paramInfo.CacheName);
- AssertUtils.ArgumentNotNull(cache, "CacheName",
- "Parameter cache with the specified name [" + paramInfo.CacheName +
- "] does not exist.");
+ if (cache == null)
+ {
+ throw new ArgumentNullException("CacheName", string.Format("Parameter cache with the specified name [{0}] does not exist.", paramInfo.CacheName));
+ }
object key = paramInfo.KeyExpression.GetValue(arguments[i]);
- #region Instrumentation
+ #region Instrumentation
if (isLogDebugEnabled)
{
- logger.Debug("Caching parameter for key [" + key + "].");
+ logger.Debug(string.Format("Caching parameter for key [{0}].", key));
}
-
#endregion
+
cache.Insert(key, arguments[i], paramInfo.TimeToLiveTimeSpan);
}
}
}
}
+
+ private CacheParameterInfo GetCacheParameterInfo(MethodInfo method)
+ {
+ CacheParameterInfo cpi = (CacheParameterInfo) _cacheParameterInfoCache[method];
+ if (cpi == null)
+ {
+ ParameterInfo[] parameters = method.GetParameters();
+ CacheParameterAttribute[][] parameterInfos = new CacheParameterAttribute[parameters.Length][];
+ for (int i = 0; i < parameters.Length; i++)
+ {
+ ParameterInfo p = parameters[i];
+ CacheParameterAttribute[] paramInfoArray = (CacheParameterAttribute[])GetCustomAttributes(p, typeof(CacheParameterAttribute));
+ parameterInfos[i] = paramInfoArray;
+ }
+ cpi = new CacheParameterInfo(parameters, parameterInfos);
+ _cacheParameterInfoCache[method] = cpi;
+ }
+ return cpi;
+ }
}
}
\ No newline at end of file
diff --git a/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvice.cs b/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvice.cs
index ca5f087b..2e3aca93 100644
--- a/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvice.cs
+++ b/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvice.cs
@@ -1,5 +1,5 @@
-#region License
-
+#region License
+
/*
* Copyright © 2002-2006 the original author or authors.
*
@@ -14,213 +14,236 @@
* 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
-
-using System.Collections;
-using System.Reflection;
-using AopAlliance.Intercept;
-using Common.Logging;
-using Spring.Caching;
-using Spring.Expressions;
-using Spring.Util;
-
-#endregion
-
-namespace Spring.Aspects.Cache
-{
- ///
- /// Implementation of a result caching advice.
- ///
- ///
- ///
- /// This advice can be used to cache the return value of the method.
- ///
- ///
- /// Parameters that determine where, how and for how long the return value
- /// will be cached are retrieved from the and/or
- /// that are defined on the pointcut.
- ///
- ///
- ///
- ///
- /// Aleksandar Seovic
- public class CacheResultAdvice : BaseCacheAdvice, IMethodInterceptor
- {
- // shared logger instance
- private static readonly ILog logger = LogManager.GetLogger(typeof (CacheResultAdvice));
-
- // NullValue
- private static readonly object NullValue = new object();
-
- ///
- /// Applies caching around a method invocation.
- ///
- ///
- ///
- /// This method tries to retrieve an object from the cache, using the supplied
- /// to generate a cache key. If an object is found
- /// in the cache, the cached value is returned and the method call does not
- /// proceed any further down the invocation chain.
- ///
- ///
- /// If object does not exist in the cache, the advised method is called (using
- /// )
- /// and any return value is cached for the next method invocation.
- ///
- ///
- ///
- /// The method invocation that is being intercepted.
- ///
- ///
- /// A cached object or the result of the
- /// call.
- ///
- ///
- /// If any of the interceptors in the chain or the target object itself
- /// throws an exception.
- ///
- public object Invoke(IMethodInvocation invocation)
- {
- CacheResultAttribute resultInfo =
- (CacheResultAttribute) GetCustomAttribute(invocation.Method, typeof (CacheResultAttribute));
- CacheResultItemsAttribute[] itemInfoArray =
- (CacheResultItemsAttribute[])invocation.Method.GetCustomAttributes(typeof(CacheResultItemsAttribute), false);
-
- bool cacheHit = false;
- object returnValue = GetReturnValue(invocation, resultInfo, out cacheHit);
-
- if (!cacheHit && itemInfoArray.Length > 0 && returnValue is IEnumerable)
- {
- CacheResultItems((IEnumerable)returnValue, itemInfoArray);
- }
-
- return returnValue;
- }
-
- ///
- /// Obtains return value either from cache or by invoking target method
- /// and caches it if necessary.
- ///
- ///
- /// The method invocation that is being intercepted.
- ///
- ///
- /// Attribute specifying where and how to cache return value. Can be null,
- /// in which case no caching of the result as a whole will be performed
- /// (if the result is collection, individual items could still be cached separately).
- ///
- ///
- /// Returns true if the return value was found in cache, false otherwise.
- ///
- ///
- /// Return value for the specified .
- ///
- private object GetReturnValue(IMethodInvocation invocation, CacheResultAttribute resultInfo, out bool cacheHit)
- {
- if (resultInfo != null)
- {
- object returnValue = null;
- bool isLogDebugEnabled = logger.IsDebugEnabled;
-
+ */
+
+#endregion
+
+#region Imports
+
+using System.Collections;
+using System.Reflection;
+using AopAlliance.Intercept;
+using Common.Logging;
+using Spring.Caching;
+using Spring.Expressions;
+using Spring.Util;
+
+#endregion
+
+namespace Spring.Aspects.Cache
+{
+ ///
+ /// Implementation of a result caching advice.
+ ///
+ ///
+ ///
+ /// This advice can be used to cache the return value of the method.
+ ///
+ ///
+ /// Parameters that determine where, how and for how long the return value
+ /// will be cached are retrieved from the and/or
+ /// that are defined on the pointcut.
+ ///
+ ///
+ ///
+ ///
+ /// Aleksandar Seovic
+ public class CacheResultAdvice : BaseCacheAdvice, IMethodInterceptor
+ {
+ // NullValue
+ private static readonly object NullValue = new object();
+
+ private class CacheResultInfo
+ {
+ public readonly CacheResultAttribute ResultInfo;
+ public readonly CacheResultItemsAttribute[] ItemInfoArray;
+
+ public CacheResultInfo(CacheResultAttribute resultInfo, CacheResultItemsAttribute[] itemInfoArray)
+ {
+ ResultInfo = resultInfo;
+ ItemInfoArray = itemInfoArray;
+ }
+ }
+
+ private readonly Hashtable _cacheResultAttributeCache = new Hashtable();
+
+ ///
+ /// Applies caching around a method invocation.
+ ///
+ ///
+ ///
+ /// This method tries to retrieve an object from the cache, using the supplied
+ /// to generate a cache key. If an object is found
+ /// in the cache, the cached value is returned and the method call does not
+ /// proceed any further down the invocation chain.
+ ///
+ ///
+ /// If object does not exist in the cache, the advised method is called (using
+ /// )
+ /// and any return value is cached for the next method invocation.
+ ///
+ ///
+ ///
+ /// The method invocation that is being intercepted.
+ ///
+ ///
+ /// A cached object or the result of the
+ /// call.
+ ///
+ ///
+ /// If any of the interceptors in the chain or the target object itself
+ /// throws an exception.
+ ///
+ public object Invoke(IMethodInvocation invocation)
+ {
+ CacheResultInfo cacheResultInfo = GetCacheResultInfo(invocation.Method);
+
+ bool cacheHit = false;
+ object returnValue = GetReturnValue(invocation, cacheResultInfo.ResultInfo, out cacheHit);
+
+ if (!cacheHit && cacheResultInfo.ItemInfoArray.Length > 0 && returnValue is IEnumerable)
+ {
+ CacheResultItems((IEnumerable)returnValue, cacheResultInfo.ItemInfoArray);
+ }
+
+ return returnValue;
+ }
+
+ ///
+ /// Obtains return value either from cache or by invoking target method
+ /// and caches it if necessary.
+ ///
+ ///
+ /// The method invocation that is being intercepted.
+ ///
+ ///
+ /// Attribute specifying where and how to cache return value. Can be null,
+ /// in which case no caching of the result as a whole will be performed
+ /// (if the result is collection, individual items could still be cached separately).
+ ///
+ ///
+ /// Returns true if the return value was found in cache, false otherwise.
+ ///
+ ///
+ /// Return value for the specified .
+ ///
+ private object GetReturnValue(IMethodInvocation invocation, CacheResultAttribute resultInfo, out bool cacheHit)
+ {
+ if (resultInfo != null)
+ {
+ object returnValue = null;
+ bool isLogDebugEnabled = logger.IsDebugEnabled;
+
IDictionary vars = PrepareVariables(invocation.Method, invocation.Arguments);
AssertUtils.ArgumentNotNull(resultInfo.KeyExpression, "KeyExpression",
- "The cache attribute is missing the key definition.");
- object resultKey = resultInfo.KeyExpression.GetValue(null, vars);
- ICache cache = GetCache(resultInfo.CacheName);
- AssertUtils.ArgumentNotNull(cache, "CacheName",
- "Result cache with the specified name [" + resultInfo.CacheName +
- "] does not exist.");
-
- returnValue = cache.Get(resultKey);
- cacheHit = (returnValue != null);
- if (!cacheHit)
- {
- #region Instrumentation
-
- if (isLogDebugEnabled)
- {
- logger.Debug("Object for key [" + resultKey + "] was not found in cache. Proceeding...");
- }
-
- #endregion
-
- returnValue = invocation.Proceed();
- if (EvalCondition(resultInfo.Condition, resultInfo.ConditionExpression, returnValue, vars))
- {
- #region Instrumentation
-
- if (isLogDebugEnabled)
- {
- logger.Debug("Caching object for key [" + resultKey + "].");
- }
-
- #endregion
- cache.Insert(resultKey, (returnValue==null)?NullValue:returnValue, resultInfo.TimeToLiveTimeSpan);
- }
- }
- else
- {
- #region Instrumentation
-
- if (isLogDebugEnabled)
- {
- logger.Debug("Cache hit for [" + resultKey + "]. Aborting invocation...");
- }
-
- #endregion
- }
-
- return (returnValue==NullValue)?null:returnValue;
- }
-
- cacheHit = false;
- return invocation.Proceed();
- }
-
- ///
- /// Caches each item from the collection returned by target method.
- ///
- ///
- /// A collection of items to cache.
- ///
- ///
- /// Attributes specifying where and how to cache each item from the collection.
- ///
- private void CacheResultItems(IEnumerable items, CacheResultItemsAttribute[] itemInfoArray)
- {
- foreach (CacheResultItemsAttribute itemInfo in itemInfoArray)
- {
- ICache cache = GetCache(itemInfo.CacheName);
- AssertUtils.ArgumentNotNull(cache, "CacheName",
- "Result item cache with the specified name [" + itemInfo.CacheName +
+ "The cache attribute is missing the key definition.");
+ object resultKey = resultInfo.KeyExpression.GetValue(null, vars);
+ ICache cache = GetCache(resultInfo.CacheName);
+ AssertUtils.ArgumentNotNull(cache, "CacheName",
+ "Result cache with the specified name [" + resultInfo.CacheName +
+ "] does not exist.");
+
+ returnValue = cache.Get(resultKey);
+ cacheHit = (returnValue != null);
+ if (!cacheHit)
+ {
+ #region Instrumentation
+
+ if (isLogDebugEnabled)
+ {
+ logger.Debug("Object for key [" + resultKey + "] was not found in cache. Proceeding...");
+ }
+
+ #endregion
+
+ returnValue = invocation.Proceed();
+ if (EvalCondition(resultInfo.Condition, resultInfo.ConditionExpression, returnValue, vars))
+ {
+ #region Instrumentation
+
+ if (isLogDebugEnabled)
+ {
+ logger.Debug("Caching object for key [" + resultKey + "].");
+ }
+
+ #endregion
+ cache.Insert(resultKey, (returnValue == null) ? NullValue : returnValue, resultInfo.TimeToLiveTimeSpan);
+ }
+ }
+ else
+ {
+ #region Instrumentation
+
+ if (isLogDebugEnabled)
+ {
+ logger.Debug("Cache hit for [" + resultKey + "]. Aborting invocation...");
+ }
+
+ #endregion
+ }
+
+ return (returnValue == NullValue) ? null : returnValue;
+ }
+
+ cacheHit = false;
+ return invocation.Proceed();
+ }
+
+ ///
+ /// Caches each item from the collection returned by target method.
+ ///
+ ///
+ /// A collection of items to cache.
+ ///
+ ///
+ /// Attributes specifying where and how to cache each item from the collection.
+ ///
+ private void CacheResultItems(IEnumerable items, CacheResultItemsAttribute[] itemInfoArray)
+ {
+ foreach (CacheResultItemsAttribute itemInfo in itemInfoArray)
+ {
+ ICache cache = GetCache(itemInfo.CacheName);
+ AssertUtils.ArgumentNotNull(cache, "CacheName",
+ "Result item cache with the specified name [" + itemInfo.CacheName +
"] does not exist.");
AssertUtils.ArgumentNotNull(itemInfo.KeyExpression, "KeyExpression",
- "The cache attribute is missing the key definition.");
-
- bool isDebugEnabled = logger.IsDebugEnabled;
- foreach (object item in items)
- {
- if (EvalCondition(itemInfo.Condition, itemInfo.ConditionExpression, item, null))
- {
- object itemKey = itemInfo.KeyExpression.GetValue(item);
- #region Instrumentation
-
- if (isDebugEnabled)
- {
- logger.Debug("Caching collection item for key [" + itemKey + "].");
- }
-
- #endregion
- cache.Insert(itemKey, (item==null?NullValue:item), itemInfo.TimeToLiveTimeSpan);
- }
- }
- }
- }
- }
+ "The cache attribute is missing the key definition.");
+
+ bool isDebugEnabled = logger.IsDebugEnabled;
+ foreach (object item in items)
+ {
+ if (EvalCondition(itemInfo.Condition, itemInfo.ConditionExpression, item, null))
+ {
+ object itemKey = itemInfo.KeyExpression.GetValue(item);
+ #region Instrumentation
+
+ if (isDebugEnabled)
+ {
+ logger.Debug("Caching collection item for key [" + itemKey + "].");
+ }
+
+ #endregion
+ cache.Insert(itemKey, (item == null ? NullValue : item), itemInfo.TimeToLiveTimeSpan);
+ }
+ }
+ }
+ }
+
+ private CacheResultInfo GetCacheResultInfo(MethodInfo method)
+ {
+ CacheResultInfo cacheResultInfo = (CacheResultInfo)_cacheResultAttributeCache[method];
+ // no need for locking here - last one wins
+ if (cacheResultInfo == null)
+ {
+ CacheResultAttribute resultInfo = (CacheResultAttribute)GetCustomAttribute(method, typeof(CacheResultAttribute));
+ CacheResultItemsAttribute[] itemInfoArray = (CacheResultItemsAttribute[])GetCustomAttributes(method, typeof(CacheResultItemsAttribute));
+
+ cacheResultInfo = new CacheResultInfo(resultInfo, itemInfoArray);
+ _cacheResultAttributeCache[method] = cacheResultInfo;
+ }
+ return cacheResultInfo;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Spring/Spring.Aop/Aspects/Cache/InvalidateCacheAdvice.cs b/src/Spring/Spring.Aop/Aspects/Cache/InvalidateCacheAdvice.cs
index 37be5eda..f6c708ea 100644
--- a/src/Spring/Spring.Aop/Aspects/Cache/InvalidateCacheAdvice.cs
+++ b/src/Spring/Spring.Aop/Aspects/Cache/InvalidateCacheAdvice.cs
@@ -55,8 +55,7 @@ namespace Spring.Aspects.Cache
/// Aleksandar Seovic
public class InvalidateCacheAdvice : BaseCacheAdvice, IAfterReturningAdvice
{
- // shared logger instance
- //private static readonly ILog logger = LogManager.GetLogger(typeof(InvalidateCacheAdvice));
+ private readonly Hashtable _invalidateCacheAttributeCache = new Hashtable();
///
/// Executes after
@@ -81,8 +80,7 @@ namespace Spring.Aspects.Cache
///
public void AfterReturning(object returnValue, MethodInfo method, object[] arguments, object target)
{
- InvalidateCacheAttribute[] cacheInfoArray =
- (InvalidateCacheAttribute[]) method.GetCustomAttributes(typeof(InvalidateCacheAttribute), false);
+ InvalidateCacheAttribute[] cacheInfoArray = GetInvalidateCacheInfo(method);
if (cacheInfoArray.Length > 0)
{
@@ -115,6 +113,17 @@ namespace Spring.Aspects.Cache
}
}
}
+ }
+
+ private InvalidateCacheAttribute[] GetInvalidateCacheInfo(MethodInfo method)
+ {
+ InvalidateCacheAttribute[] cacheInfoArray = (InvalidateCacheAttribute[]) _invalidateCacheAttributeCache[method];
+ if (cacheInfoArray == null)
+ {
+ cacheInfoArray = (InvalidateCacheAttribute[])GetCustomAttributes(method, typeof(InvalidateCacheAttribute));
+ _invalidateCacheAttributeCache[method] = cacheInfoArray;
+ }
+ return cacheInfoArray;
}
}
}
\ No newline at end of file
diff --git a/src/Spring/Spring.Core/Caching/BaseCacheAttribute.cs b/src/Spring/Spring.Core/Caching/BaseCacheAttribute.cs
index 09e2f76d..62a25e00 100644
--- a/src/Spring/Spring.Core/Caching/BaseCacheAttribute.cs
+++ b/src/Spring/Spring.Core/Caching/BaseCacheAttribute.cs
@@ -170,6 +170,5 @@ namespace Spring.Caching
get { return timeToLiveTimeSpan; }
}
- #endregion
- }
+ #endregion
}
}
\ No newline at end of file
diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/AbstractFallbackTransactionAttributeSource.cs b/src/Spring/Spring.Data/Transaction/Interceptor/AbstractFallbackTransactionAttributeSource.cs
index 69cb04d8..6d4e4e47 100644
--- a/src/Spring/Spring.Data/Transaction/Interceptor/AbstractFallbackTransactionAttributeSource.cs
+++ b/src/Spring/Spring.Data/Transaction/Interceptor/AbstractFallbackTransactionAttributeSource.cs
@@ -77,7 +77,7 @@ namespace Spring.Transaction.Interceptor
///
/// Cache of s, keyed by method and target class.
///
- private IDictionary _transactionAttibuteCache = new Hashtable();
+ private readonly IDictionary _transactionAttibuteCache = new Hashtable();
///
/// Creates a new instance of the
@@ -135,11 +135,10 @@ namespace Spring.Transaction.Interceptor
public ITransactionAttribute ReturnTransactionAttribute(MethodInfo method, Type targetType)
{
object cacheKey = getCacheKey(method, targetType);
- object cached = null;
- lock (_transactionAttibuteCache)
+ lock (_transactionAttibuteCache)
{
- cached = _transactionAttibuteCache[cacheKey];
+ object cached = _transactionAttibuteCache[cacheKey];
if (cached != null)
{
@@ -228,7 +227,7 @@ namespace Spring.Transaction.Interceptor
private object getCacheKey(MethodBase method, Type targetType)
{
- return targetType + String.Empty + method.GetHashCode();
+ return string.Intern(targetType.AssemblyQualifiedName + "." + method);
}
private ITransactionAttribute computeTransactionAttribute(MethodInfo method, Type targetType)
diff --git a/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheResultAdviceTests.cs b/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheResultAdviceTests.cs
index 00628c22..47da50eb 100644
--- a/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheResultAdviceTests.cs
+++ b/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheResultAdviceTests.cs
@@ -375,14 +375,13 @@ namespace Spring.Aspects.Cache
private void ExpectAttributeRetrieval( MethodInfo method )
{
- mockInvocation.ExpectAndReturn( "Method", method );
- mockInvocation.ExpectAndReturn( "Method", method );
+ mockInvocation.SetValue( "Method", method );
}
private void ExpectCacheKeyGeneration( MethodInfo method, params object[] arguments )
{
- mockInvocation.ExpectAndReturn( "Method", method );
- mockInvocation.ExpectAndReturn( "Arguments", arguments );
+// mockInvocation.ExpectAndReturn( "Method", method );
+ mockInvocation.SetValue( "Arguments", arguments );
}
private void ExpectCacheInstanceRetrieval( string cacheName, ICache cache )