SPRNET-1318 introduce local cache of merged ObjectDefinitions to reduce impact of AppContext.Dispose() calls

This commit is contained in:
Steve Bohlen
2012-12-11 16:50:57 -05:00
parent c1e9dd63e3
commit d1520682df
6 changed files with 661 additions and 237 deletions

View File

@@ -257,7 +257,7 @@ namespace Spring.Objects.Factory.Support
ApplyPropertyValues(name, definition, new ObjectWrapper(instance), definition.PropertyValues);
}
}
/// <summary>
/// Apply the property values of the object definition with the supplied
/// <paramref name="name"/> to the supplied <paramref name="instance"/>.
@@ -480,8 +480,8 @@ namespace Spring.Objects.Factory.Support
//have to clone the collection before iterating it to avoid arbitrary code in the objects' Dispose()
// that might permit re-entering the DisposableInnerObjects collections during the iteration to destroy them
// see https://jira.springframework.org/browse/SPRNET-1334
ISet clone = (ISet) DisposableInnerObjects.Clone();
ISet clone = (ISet)DisposableInnerObjects.Clone();
foreach (object o in clone)
{
@@ -792,44 +792,44 @@ namespace Spring.Objects.Factory.Support
ignoredDependencyInterfaces.Add(type);
}
// /// <summary>
// /// Create an object instance for the given object definition.
// /// </summary>
// /// <param name="name">The name of the object.</param>
// /// <param name="definition">
// /// The object definition for the object that is to be instantiated.
// /// </param>
// /// <param name="arguments">
// /// The arguments to use if creating a prototype using explicit arguments to
// /// a static factory method. It is invalid to use a non-<see langword="null"/> arguments value
// /// in any other case.
// /// </param>
// /// <returns>
// /// A new instance of the object.
// /// </returns>
// /// <exception cref="Spring.Objects.ObjectsException">
// /// In case of errors.
// /// </exception>
// /// <remarks>
// /// <p>
// /// Delegates to the
// /// <see cref="Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory.CreateObject (string,RootObjectDefinition,object[],bool)"/>
// /// method version with the <c>allowEagerCaching</c> parameter set to <b>true</b>.
// /// </p>
// /// <p>
// /// The object definition will already have been merged with the parent
// /// definition in case of a child definition.
// /// </p>
// /// <p>
// /// All the other methods in this class invoke this method, although objects
// /// may be cached after being instantiated by this method. All object
// /// instantiation within this class is performed by this method.
// /// </p>
// /// </remarks>
// protected internal override object CreateObject(string name, RootObjectDefinition definition, object[] arguments)
// {
// return CreateObject(name, definition, arguments, true, false);
// }
// /// <summary>
// /// Create an object instance for the given object definition.
// /// </summary>
// /// <param name="name">The name of the object.</param>
// /// <param name="definition">
// /// The object definition for the object that is to be instantiated.
// /// </param>
// /// <param name="arguments">
// /// The arguments to use if creating a prototype using explicit arguments to
// /// a static factory method. It is invalid to use a non-<see langword="null"/> arguments value
// /// in any other case.
// /// </param>
// /// <returns>
// /// A new instance of the object.
// /// </returns>
// /// <exception cref="Spring.Objects.ObjectsException">
// /// In case of errors.
// /// </exception>
// /// <remarks>
// /// <p>
// /// Delegates to the
// /// <see cref="Spring.Objects.Factory.Support.AbstractAutowireCapableObjectFactory.CreateObject (string,RootObjectDefinition,object[],bool)"/>
// /// method version with the <c>allowEagerCaching</c> parameter set to <b>true</b>.
// /// </p>
// /// <p>
// /// The object definition will already have been merged with the parent
// /// definition in case of a child definition.
// /// </p>
// /// <p>
// /// All the other methods in this class invoke this method, although objects
// /// may be cached after being instantiated by this method. All object
// /// instantiation within this class is performed by this method.
// /// </p>
// /// </remarks>
// protected internal override object CreateObject(string name, RootObjectDefinition definition, object[] arguments)
// {
// return CreateObject(name, definition, arguments, true, false);
// }
/// <summary>
/// Create an object instance for the given object definition.
@@ -1221,7 +1221,7 @@ namespace Spring.Objects.Factory.Support
if (unsatisfiedDependencies.Count > 0)
{
throw new UnsatisfiedDependencyException(definition.ResourceDescription, name, unsatisfiedDependencies[0].Name,
"Set this property value or disable dependency checking for this object.");
"Set this property value or disable dependency checking for this object.");
}
}
@@ -1438,32 +1438,10 @@ namespace Spring.Objects.Factory.Support
/// </param>
protected override void DestroyObject(string name, object target)
{
log.Debug(m => m("Destroying dependant objects for object '{0}", name));
DestroyDependantObjects(name);
ApplyObjectPostProcessBeforeDestruction(target, name);
if (target is IDisposable)
using (new DisposableObjectAdapter(target, name, GetMergedObjectDefinition(name, true), ObjectPostProcessors))
{
log.Debug(m => m(string.Format(CultureInfo.InvariantCulture, "Calling Dispose() on object with name '{0}'.", name)));
try
{
((IDisposable)target).Dispose();
}
catch (Exception ex)
{
log.Error("Destroy() on object with name '" + name + "' threw an exception.", ex);
}
}
RootObjectDefinition rootDefinition = GetMergedObjectDefinition(name, false);
if (rootDefinition != null && StringUtils.HasText(rootDefinition.DestroyMethodName))
{
log.Debug(m => m("Calling custom destroy method '{0}' on object with name '{1}'.", rootDefinition.DestroyMethodName, name));
InvokeCustomDestroyMethod(name, target, rootDefinition.DestroyMethodName);
log.Debug(m => m("Destroying dependant objects for object '{0}", name));
DestroyDependantObjects(name);
}
}
@@ -1533,123 +1511,123 @@ namespace Spring.Objects.Factory.Support
///// <param name="argumentValue">
///// The value of the property that is being resolved.
///// </param>
// protected object ResolveValueIfNecessary(string name, RootObjectDefinition definition, string argumentName, object argumentValue)
// {
// object resolvedValue = null;
//
// AssertUtils.ArgumentNotNull(resolvedValue, "test");
//
// // we must check the argument value to see whether it requires a runtime
// // reference to another object to be resolved.
// // if it does, we'll attempt to instantiate the object and set the reference.
// if (RemotingServices.IsTransparentProxy(argumentValue))
// {
// resolvedValue = argumentValue;
// }
// else if (argumentValue is ObjectDefinitionHolder)
// {
// // contains an IObjectDefinition with name and aliases...
// ObjectDefinitionHolder holder = (ObjectDefinitionHolder)argumentValue;
// resolvedValue = ResolveInnerObjectDefinition(name, holder.ObjectName, argumentName, holder.ObjectDefinition, definition.IsSingleton);
// }
// else if (argumentValue is IObjectDefinition)
// {
// // resolve plain IObjectDefinition, without contained name: use dummy name...
// IObjectDefinition def = (IObjectDefinition)argumentValue;
// resolvedValue = ResolveInnerObjectDefinition(name, "(inner object)", argumentName, def, definition.IsSingleton);
//
// }
// else if (argumentValue is RuntimeObjectReference)
// {
// RuntimeObjectReference roref = (RuntimeObjectReference)argumentValue;
// resolvedValue = ResolveReference(definition, name, argumentName, roref);
// }
// else if (argumentValue is ExpressionHolder)
// {
// ExpressionHolder expHolder = (ExpressionHolder)argumentValue;
// object context = null;
// IDictionary variables = null;
//
// if (expHolder.Properties != null)
// {
// PropertyValue contextProperty = expHolder.Properties.GetPropertyValue("Context");
// context = contextProperty == null
// ? null
// : ResolveValueIfNecessary2(name, definition, "Context",
// contextProperty.Value);
// PropertyValue variablesProperty = expHolder.Properties.GetPropertyValue("Variables");
// object vars = (variablesProperty == null
// ? null
// : ResolveValueIfNecessary2(name, definition, "Variables",
// variablesProperty.Value));
// if (vars is IDictionary)
// {
// variables = (IDictionary)vars;
// }
// else
// {
// if (vars != null) throw new ArgumentException("'Variables' must resolve to an IDictionary");
// }
// }
//
// if (variables == null) variables = CollectionsUtil.CreateCaseInsensitiveHashtable();
// // add 'this' objectfactory reference to variables
// variables.Add(Expression.ReservedVariableNames.CurrentObjectFactory, this);
//
// resolvedValue = expHolder.Expression.GetValue(context, variables);
// }
// else if (argumentValue is IManagedCollection)
// {
// resolvedValue =
// ((IManagedCollection)argumentValue).Resolve(name, definition, argumentName,
// new ManagedCollectionElementResolver(ResolveValueIfNecessary2));
// }
// else if (argumentValue is TypedStringValue)
// {
// TypedStringValue tsv = (TypedStringValue)argumentValue;
// try
// {
// Type resolvedTargetType = ResolveTargetType(tsv);
// if (resolvedTargetType != null)
// {
// resolvedValue = TypeConversionUtils.ConvertValueIfNecessary(tsv.TargetType, tsv.Value, null);
// }
// else
// {
// resolvedValue = tsv.Value;
// }
// }
// catch (Exception ex)
// {
// throw new ObjectCreationException(definition.ResourceDescription, name,
// "Error converted typed String value for " + argumentName, ex);
// }
//
// }
// else
// {
// // no need to resolve value...
// resolvedValue = argumentValue;
// }
// return resolvedValue;
// }
// protected object ResolveValueIfNecessary(string name, RootObjectDefinition definition, string argumentName, object argumentValue)
// {
// object resolvedValue = null;
//
// AssertUtils.ArgumentNotNull(resolvedValue, "test");
//
// // we must check the argument value to see whether it requires a runtime
// // reference to another object to be resolved.
// // if it does, we'll attempt to instantiate the object and set the reference.
// if (RemotingServices.IsTransparentProxy(argumentValue))
// {
// resolvedValue = argumentValue;
// }
// else if (argumentValue is ObjectDefinitionHolder)
// {
// // contains an IObjectDefinition with name and aliases...
// ObjectDefinitionHolder holder = (ObjectDefinitionHolder)argumentValue;
// resolvedValue = ResolveInnerObjectDefinition(name, holder.ObjectName, argumentName, holder.ObjectDefinition, definition.IsSingleton);
// }
// else if (argumentValue is IObjectDefinition)
// {
// // resolve plain IObjectDefinition, without contained name: use dummy name...
// IObjectDefinition def = (IObjectDefinition)argumentValue;
// resolvedValue = ResolveInnerObjectDefinition(name, "(inner object)", argumentName, def, definition.IsSingleton);
//
// }
// else if (argumentValue is RuntimeObjectReference)
// {
// RuntimeObjectReference roref = (RuntimeObjectReference)argumentValue;
// resolvedValue = ResolveReference(definition, name, argumentName, roref);
// }
// else if (argumentValue is ExpressionHolder)
// {
// ExpressionHolder expHolder = (ExpressionHolder)argumentValue;
// object context = null;
// IDictionary variables = null;
//
// if (expHolder.Properties != null)
// {
// PropertyValue contextProperty = expHolder.Properties.GetPropertyValue("Context");
// context = contextProperty == null
// ? null
// : ResolveValueIfNecessary2(name, definition, "Context",
// contextProperty.Value);
// PropertyValue variablesProperty = expHolder.Properties.GetPropertyValue("Variables");
// object vars = (variablesProperty == null
// ? null
// : ResolveValueIfNecessary2(name, definition, "Variables",
// variablesProperty.Value));
// if (vars is IDictionary)
// {
// variables = (IDictionary)vars;
// }
// else
// {
// if (vars != null) throw new ArgumentException("'Variables' must resolve to an IDictionary");
// }
// }
//
// if (variables == null) variables = CollectionsUtil.CreateCaseInsensitiveHashtable();
// // add 'this' objectfactory reference to variables
// variables.Add(Expression.ReservedVariableNames.CurrentObjectFactory, this);
//
// resolvedValue = expHolder.Expression.GetValue(context, variables);
// }
// else if (argumentValue is IManagedCollection)
// {
// resolvedValue =
// ((IManagedCollection)argumentValue).Resolve(name, definition, argumentName,
// new ManagedCollectionElementResolver(ResolveValueIfNecessary2));
// }
// else if (argumentValue is TypedStringValue)
// {
// TypedStringValue tsv = (TypedStringValue)argumentValue;
// try
// {
// Type resolvedTargetType = ResolveTargetType(tsv);
// if (resolvedTargetType != null)
// {
// resolvedValue = TypeConversionUtils.ConvertValueIfNecessary(tsv.TargetType, tsv.Value, null);
// }
// else
// {
// resolvedValue = tsv.Value;
// }
// }
// catch (Exception ex)
// {
// throw new ObjectCreationException(definition.ResourceDescription, name,
// "Error converted typed String value for " + argumentName, ex);
// }
//
// }
// else
// {
// // no need to resolve value...
// resolvedValue = argumentValue;
// }
// return resolvedValue;
// }
///// <summary>
///// Resolve the target type of the passed <see cref="TypedStringValue"/>.
///// </summary>
///// <param name="value">The <see cref="TypedStringValue"/> who's target type is to be resolved</param>
///// <returns>The resolved target type, if any. <see lang="null" /> otherwise.</returns>
// protected virtual Type ResolveTargetType(TypedStringValue value)
// {
// if (value.HasTargetType)
// {
// return value.TargetType;
// }
// else
// {
// return null;
// }
// }
// protected virtual Type ResolveTargetType(TypedStringValue value)
// {
// if (value.HasTargetType)
// {
// return value.TargetType;
// }
// else
// {
// return null;
// }
// }
///// <summary>
///// Resolves an inner object definition.
@@ -1674,30 +1652,30 @@ namespace Spring.Objects.Factory.Support
///// <returns>
///// The resolved object as defined by the inner object definition.
///// </returns>
// protected object ResolveInnerObjectDefinition(string name, string innerObjectName, string argumentName, IObjectDefinition definition,
// bool singletonOwner)
// {
// RootObjectDefinition mod = GetMergedObjectDefinition(innerObjectName, definition);
// mod.IsSingleton = singletonOwner;
// object instance;
// object result;
// try
// {
// instance = InstantiateObject(innerObjectName, mod, ObjectUtils.EmptyObjects, false, false);
// result = GetObjectForInstance(innerObjectName, instance);
// }
// catch (ObjectsException ex)
// {
// throw ObjectCreationException.GetObjectCreationException(ex, name, argumentName, definition.ResourceDescription, innerObjectName);
// }
// if (singletonOwner && instance is IDisposable)
// {
// // keep a reference to the inner object instance, to be able to destroy
// // it on factory shutdown...
// DisposableInnerObjects.Add(instance);
// }
// return result;
// }
// protected object ResolveInnerObjectDefinition(string name, string innerObjectName, string argumentName, IObjectDefinition definition,
// bool singletonOwner)
// {
// RootObjectDefinition mod = GetMergedObjectDefinition(innerObjectName, definition);
// mod.IsSingleton = singletonOwner;
// object instance;
// object result;
// try
// {
// instance = InstantiateObject(innerObjectName, mod, ObjectUtils.EmptyObjects, false, false);
// result = GetObjectForInstance(innerObjectName, instance);
// }
// catch (ObjectsException ex)
// {
// throw ObjectCreationException.GetObjectCreationException(ex, name, argumentName, definition.ResourceDescription, innerObjectName);
// }
// if (singletonOwner && instance is IDisposable)
// {
// // keep a reference to the inner object instance, to be able to destroy
// // it on factory shutdown...
// DisposableInnerObjects.Add(instance);
// }
// return result;
// }
/// <summary>
/// Resolve a reference to another object in the factory.
@@ -1897,7 +1875,6 @@ namespace Spring.Objects.Factory.Support
return instance;
}
/// <summary>
/// Applies the <code>PostProcessAfterInitialization</code> callback of all
/// registered IObjectPostProcessors, giving them a chance to post-process

View File

@@ -29,6 +29,7 @@ using System.ComponentModel;
using Common.Logging;
using Spring.Collections;
using Spring.Collections.Generic;
using Spring.Core;
using Spring.Core.TypeConversion;
using Spring.Objects.Factory.Config;
@@ -144,6 +145,27 @@ namespace Spring.Objects.Factory.Support
/// </summary>
private readonly Dictionary<string, object> factoryObjectProductCache = new Dictionary<string, object>();
/// <summary>
/// Disposable object instances: object name --> disposable instance
/// </summary>
private readonly Dictionary<string, object> disposableObjects = new Dictionary<string, object>();
/// <summary>
/// root object definitons: object name --> Root Object Definition
/// </summary>
protected SynchronizedHashtable mergedObjectDefinitions = new Spring.Collections.SynchronizedHashtable();
/// <summary>
/// Whether to cache object metadata or rather reobtain it for every access
/// </summary>
protected bool cacheObjectMetadata = true;
/// <summary>
/// Names of object that have already been created at least once
/// </summary>
private Spring.Collections.Generic.ISet<string> alreadyCreated = new SynchronizedSet<string>(new HashedSet<string>());
#region Constructor (s) / Destructor
/// <summary>
@@ -623,11 +645,26 @@ namespace Spring.Objects.Factory.Support
/// Return a <see cref="Spring.Objects.Factory.Support.RootObjectDefinition"/>,
/// even by traversing parent if the parameter is a child definition.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="od">The od.</param>
/// <returns>
/// A merged <see cref="Spring.Objects.Factory.Support.RootObjectDefinition"/>
/// with overridden properties.
/// </returns>
protected internal virtual RootObjectDefinition GetMergedObjectDefinition(string name, IObjectDefinition od)
{
return GetMergedLocalObjectDefinition(name) ?? GetMergedObjectDefinitionInternal(name, od);
}
/// <summary>
/// Return a <see cref="Spring.Objects.Factory.Support.RootObjectDefinition"/>,
/// even by traversing parent if the parameter is a child definition.
/// </summary>
/// <returns>
/// A merged <see cref="Spring.Objects.Factory.Support.RootObjectDefinition"/>
/// with overridden properties.
/// </returns>
protected internal virtual RootObjectDefinition GetMergedObjectDefinitionInternal(string name, IObjectDefinition od)
{
if (od == null)
{
@@ -636,40 +673,99 @@ namespace Spring.Objects.Factory.Support
RootObjectDefinition mod;
if (od.ParentName == null)
// Check with full lock now in order to enforce the same merged instance.
lock (this.mergedObjectDefinitions.SyncRoot)
{
mod = CreateRootObjectDefinition(od);
}
else
{
// IObjectDefinition childDefinition = definition;
IObjectDefinition pod = null;
if (!name.Equals(od.ParentName))
mod = this.mergedObjectDefinitions[name] as RootObjectDefinition;
if (null != mod)
{
pod = GetMergedObjectDefinition(TransformedObjectName(od.ParentName), true);
return mod;
}
if (od.ParentName == null)
{
mod = CreateRootObjectDefinition(od);
}
else
{
if (ParentObjectFactory is AbstractObjectFactory)
IObjectDefinition pod = null;
if (!name.Equals(od.ParentName))
{
pod = ((AbstractObjectFactory)ParentObjectFactory).GetMergedObjectDefinition(od.ParentName, true);
pod = GetMergedObjectDefinition(TransformedObjectName(od.ParentName), true);
}
else
{
if (ParentObjectFactory is AbstractObjectFactory)
{
pod = ((AbstractObjectFactory)ParentObjectFactory).GetMergedObjectDefinition(
od.ParentName, true);
}
}
if (pod == null)
{
throw new NoSuchObjectDefinitionException(od.ParentName,
string.Format(
"Parent name '{0}' is equal to object name '{1}' - "
+
"cannot be resolved without an AbstractObjectFactory parent.",
od.ParentName, name));
}
mod = CreateRootObjectDefinition(pod);
mod.OverrideFrom(od);
}
if (pod == null)
// Only cache the merged bean definition if we're already about to create an
// instance of the object, or at least have already created an instance before.
if (CacheObjectMetadata && IsObjectEligibleForMetadataCaching(name))
{
throw new NoSuchObjectDefinitionException(od.ParentName,
string.Format(
"Parent name '{0}' is equal to object name '{1}' - "
+
"cannot be resolved without an AbstractObjectFactory parent.",
od.ParentName, name));
this.mergedObjectDefinitions.Remove(name);
this.mergedObjectDefinitions.Add(name, mod);
}
mod = CreateRootObjectDefinition(pod);
mod.OverrideFrom(od);
return mod;
} //release the lock scope
}
/// <summary>
/// Gets the merged local object definition.
/// </summary>
/// <param name="objectName">Name of the object.</param>
/// <returns>
/// Merged RootBeanDefinition, traversing the parent bean definition
/// if the specified bean corresponds to a child bean definition.
/// </returns>
protected RootObjectDefinition GetMergedLocalObjectDefinition(string objectName)
{
// Quick check on the concurrent map first, with minimal locking.
RootObjectDefinition mbd = null;
if (this.mergedObjectDefinitions.ContainsKey(objectName))
{
mbd = this.mergedObjectDefinitions[objectName] as RootObjectDefinition;
}
return mod;
return mbd; // ?? GetMergedObjectDefinition(objectName, GetObjectDefinition(objectName));
}
/// <summary>
/// Determines whether the metadata for the specified object name is eligible for caching.
/// </summary>
/// <param name="beanName">Name of the bean.</param>
/// <returns>
/// <c>true</c> if [is object eligible for metadata caching] [the specified bean name]; otherwise, <c>false</c>.
/// </returns>
protected bool IsObjectEligibleForMetadataCaching(String beanName)
{
return this.alreadyCreated.Contains(beanName);
}
protected bool CacheObjectMetadata
{
get { return this.cacheObjectMetadata; }
}
/// <summary>
@@ -1233,8 +1329,8 @@ namespace Spring.Objects.Factory.Support
/// </summary>
/// <remarks>More specifically, check whether a GetObject call for the given name
/// would return an object that is assignable to the specified target type.
/// Translates aliases back to the corresponding canonical bean name.
/// Will ask the parent factory if the bean cannot be found in this factory instance.
/// Translates aliases back to the corresponding canonical instance name.
/// Will ask the parent factory if the instance cannot be found in this factory instance.
/// </remarks>
/// <param name="name">The name of the object to query.</param>
/// <param name="targetType">Type of the target to match against.</param>
@@ -1314,8 +1410,8 @@ namespace Spring.Objects.Factory.Support
/// </summary>
/// <remarks>More specifically, check whether a GetObject call for the given name
/// would return an object that is assignable to the specified target type.
/// Translates aliases back to the corresponding canonical bean name.
/// Will ask the parent factory if the bean cannot be found in this factory instance.
/// Translates aliases back to the corresponding canonical instance name.
/// Will ask the parent factory if the instance cannot be found in this factory instance.
/// </remarks>
/// <param name="name">The name of the object to query.</param>
/// <typeparam name="T">Type of the target to match against.</typeparam>
@@ -1624,7 +1720,7 @@ namespace Spring.Objects.Factory.Support
private OrderedDictionary singletonLocks;
/// <summary>
/// Set of registered singletons, containing the bean names in registration order
/// Set of registered singletons, containing the instance names in registration order
/// </summary>
private HashSet<string> registeredSingletons = new HashSet<string>();
@@ -1664,7 +1760,7 @@ namespace Spring.Objects.Factory.Support
}
/// <summary>
/// Determines whether the local object factory contains a bean of the given name,
/// Determines whether the local object factory contains a instance of the given name,
/// ignoring object defined in ancestor contexts.
/// This is an alternative to <code>ContainsObject</code>, ignoring an object
/// of the given name from an ancestor object factory.
@@ -2194,6 +2290,15 @@ namespace Spring.Objects.Factory.Support
}
}
try
{
RegisterDisposableObjectIfNecessary(name, instance, mergedObjectDefinition);
}
catch (ObjectDefinitionValidationException ex)
{
throw new ObjectCreationException(mergedObjectDefinition.ResourceDescription, name, "Invalid destruction signature", ex);
}
return EnsureObjectIsOfRequiredType(name, instance, requiredType);
}
catch
@@ -2237,6 +2342,34 @@ namespace Spring.Objects.Factory.Support
}
}
protected void RegisterDisposableObjectIfNecessary(string name, object instance, RootObjectDefinition od)
{
if (od.IsSingleton && RequiresDestruction(instance, od))
{
// Register a DisposableObject implementation that performs all destruction
// work for the given object: DestructionAwareObjectPostProcessors,
// DisposableObject interface, custom destroy method.
RegisterDisposableObject(name,
new DisposableObjectAdapter(instance, name, od, objectPostProcessors));
}
}
/// <summary>
/// Requireses the destruction.
/// </summary>
/// <param name="instance">The instance to check.</param>
/// <param name="od">The corresponding instance definition.</param>
/// <returns>
/// Boolean indicating whether destruction is required.
/// </returns>
protected bool RequiresDestruction(object instance, RootObjectDefinition od)
{
return (instance != null &&
(instance is IDisposable || od.DestroyMethodName != null || HasDestructionAwareBeanPostProcessors));
}
private int nestingCount;
/// <summary>
@@ -2507,17 +2640,17 @@ namespace Spring.Objects.Factory.Support
if (name == alias)
{
#region Instrumentation
if (log.IsDebugEnabled)
{
log.Debug(string.Format("Ignoring attempt to Register alias '{0}' for object with name '{1}' because name and alias would be the same value.", alias, name));
}
#endregion
return;
}
#region Instrumentation
if (log.IsDebugEnabled)
@@ -2597,7 +2730,7 @@ namespace Spring.Objects.Factory.Support
/// <remarks>
/// <para>
/// Only checks already instantiated singletons; does not return names
/// for singleton bean definitions which have not been instantiated yet.
/// for singleton instance definitions which have not been instantiated yet.
/// </para>
/// <para>
/// The main purpose of this method is to check manually registered singletons
@@ -2666,6 +2799,23 @@ namespace Spring.Objects.Factory.Support
#endregion
/// <summary>
/// Registers the disposable object.
/// </summary>
/// <param name="objectName">Name of the instance.</param>
/// <param name="instance">The instance.</param>
/// Add the given instance to the list of disposable beans in this registry.
/// Disposable beans usually correspond to registered singletons,
/// matching the instance name but potentially being a different instance
/// (for example, a DisposableBean adapter for a singleton that does not
/// naturally implement <see cref="IDisposable"/>).
public void RegisterDisposableObject(String objectName, IDisposable instance)
{
disposableObjects.Add(objectName, instance);
}
/// <summary>
/// Determines whether the given object name is already in use within this factory,
/// i.e. whether there is a local object or alias registered under this name or

View File

@@ -1287,7 +1287,7 @@ namespace Spring.Objects.Factory.Support
fallbackObjectName = candidateBeanName;
}
}
return (primaryObjectName != null ? primaryObjectName : fallbackObjectName);
return (primaryObjectName ?? fallbackObjectName);
}
/// <summary>

View File

@@ -0,0 +1,296 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Common.Logging;
using Spring.Collections;
using Spring.Objects.Factory.Config;
using Spring.Util;
namespace Spring.Objects.Factory.Support
{
public class DisposableObjectAdapter : IDisposable
{
private readonly ILog logger = LogManager.GetLogger(typeof(DisposableObjectAdapter));
private object instance;
private string objectName;
private bool invokeDisposableObject;
private string destroyMethodName;
private MethodInfo destroyMethod;
private List<IDestructionAwareObjectPostProcessor> objectPostProcessors;
/// <summary>
/// Disposables the bean adapter.
/// </summary>
/// <param name="instance">The bean.</param>
/// <param name="objectName">Name of the bean.</param>
/// <param name="objectDefinition">The bean definition.</param>
/// <param name="postProcessors">The post processors.</param>
/// <param name="acc">The acc.</param>
/// Create a new DisposableBeanAdapter for the given bean.
/// @param bean the bean instance (never
/// <code>null</code>
/// )
/// @param beanName the name of the bean
/// @param beanDefinition the merged bean definition
/// @param postProcessors the List of BeanPostProcessors
/// (potentially DestructionAwareBeanPostProcessor), if any
public DisposableObjectAdapter(object instance, string objectName, RootObjectDefinition objectDefinition, ISet postProcessors)
{
AssertUtils.ArgumentNotNull(instance, "Disposable object must not be null");
this.instance = instance;
this.objectName = objectName;
this.invokeDisposableObject = (this.instance is IDisposable); // && !beanDefinition.IsExternallyManagedDestroyMethod("destroy"));
if (null == objectDefinition)
{
return;
}
InferDestroyMethodIfNecessary(objectDefinition);
string definedDestroyMethodName = objectDefinition.DestroyMethodName;
if (definedDestroyMethodName != null && !(this.invokeDisposableObject && "Destroy".Equals(definedDestroyMethodName))) // && !beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName))
{
this.destroyMethodName = definedDestroyMethodName;
this.destroyMethod = DetermineDestroyMethod();
if (this.destroyMethod == null)
{
//TODO: add support for Enforcing Destroy Method
//if (beanDefinition.IsEnforceDestroyMethod()) {
// throw new BeanDefinitionValidationException("Couldn't find a destroy method named '" +
// destroyMethodName + "' on bean with name '" + beanName + "'");
//}
}
else
{
Type[] paramTypes = ReflectionUtils.GetParameterTypes(this.destroyMethod);
if (paramTypes.Length > 1)
{
throw new ObjectDefinitionValidationException("Method '" + definedDestroyMethodName + "' of object '" +
objectName + "' has more than one parameter - not supported as Destroy Method");
}
else if (paramTypes.Length == 1 && !(paramTypes[0] == typeof(bool)))
{
throw new ObjectDefinitionValidationException("Method '" + definedDestroyMethodName + "' of object '" +
objectName + "' has a non-boolean parameter - not supported as Destroy Method");
}
}
}
this.objectPostProcessors = FilterPostProcessors(postProcessors);
}
/**
* If the current value of the given beanDefinition's destroyMethodName property is
* {@link AbstractBeanDefinition#INFER_METHOD}, then attempt to infer a destroy method.
* Candidate methods are currently limited to public, no-arg methods named 'close'
* (whether declared locally or inherited). The given beanDefinition's
* destroyMethodName is updated to be null if no such method is found, otherwise set
* to the name of the inferred method. This constant serves as the default for the
* {@code @Bean#customDestroyMethod} attribute and the value of the constant may also be
* used in XML within the {@code <bean destroy-method="">} or {@code
* <beans default-destroy-method="">} attributes.
*/
private void InferDestroyMethodIfNecessary(RootObjectDefinition beanDefinition)
{
if ("(Inferred)".Equals(beanDefinition.DestroyMethodName))
{
try
{
MethodInfo candidate = ReflectionUtils.GetMethod(instance.GetType(), "Close", null);
if (candidate.IsPublic)
{
beanDefinition.DestroyMethodName = candidate.Name;
}
}
catch (MissingMethodException ex)
{
// no candidate destroy method found
beanDefinition.DestroyMethodName = null;
}
}
}
/// <summary>
/// Filters the post processors.
/// </summary>
/// <param name="postProcessors">The post processors.</param>
/// <returns></returns>
/// Search for all DestructionAwareBeanPostProcessors in the List.
/// @param postProcessors the List to search
/// @return the filtered List of DestructionAwareBeanPostProcessors
private List<IDestructionAwareObjectPostProcessor> FilterPostProcessors(ISet postProcessors)
{
List<IDestructionAwareObjectPostProcessor> filteredPostProcessors = null;
if (postProcessors != null && postProcessors.Count != 0)
{
filteredPostProcessors = new List<IDestructionAwareObjectPostProcessor>(postProcessors.Count);
filteredPostProcessors.AddRange(postProcessors.OfType<IDestructionAwareObjectPostProcessor>());
}
return filteredPostProcessors;
}
public void Dispose()
{
if (this.objectPostProcessors != null && this.objectPostProcessors.Count != 0)
{
foreach (IDestructionAwareObjectPostProcessor processor in this.objectPostProcessors)
{
try
{
processor.PostProcessBeforeDestruction(this.instance, this.objectName);
}
catch (Exception ex)
{
logger.ErrorFormat(
string.Format("Error during execution of {0}.PostProcessBeforeDestruction for object {1}",
processor.GetType().Name, this.objectName), ex);
}
}
}
if (this.invokeDisposableObject)
{
if (logger.IsDebugEnabled)
{
logger.Debug("Invoking Dispose() on object with name '" + this.objectName + "'");
}
try
{
((IDisposable)instance).Dispose();
}
catch (Exception ex)
{
string msg = "Invocation of Dispose method failed on object with name '" + this.objectName + "'";
if (logger.IsDebugEnabled)
{
logger.Warn(msg, ex);
}
else
{
logger.Warn(msg + ": " + ex);
}
}
}
if (this.destroyMethod != null)
{
InvokeCustomDestroyMethod(this.destroyMethod);
}
else if (this.destroyMethodName != null)
{
MethodInfo methodToCall = DetermineDestroyMethod();
if (methodToCall != null)
{
InvokeCustomDestroyMethod(methodToCall);
}
}
}
private MethodInfo DetermineDestroyMethod()
{
try
{
return FindDestroyMethod();
}
catch (ArgumentException ex)
{
throw new ObjectDefinitionValidationException("Couldn't find a unique Destroy Method on object with name '" +
this.objectName + ": " + ex.Message);
}
}
private MethodInfo FindDestroyMethod()
{
try
{
return ReflectionUtils.GetMethod(instance.GetType(), this.destroyMethodName, null);
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// Invokes the custom destroy method.
/// </summary>
/// <param name="customDestroyMethod">The custom destroy method.</param>
/// Invoke the specified custom destroy method on the given bean.
/// This implementation invokes a no-arg method if found, else checking
/// for a method with a single boolean argument (passing in "true",
/// assuming a "force" parameter), else logging an error.
private void InvokeCustomDestroyMethod(MethodInfo customDestroyMethod)
{
Type[] paramTypes = ReflectionUtils.GetParameterTypes(customDestroyMethod);
object[] args = new object[paramTypes.Length];
if (paramTypes.Length == 1)
{
args[0] = true;
}
if (logger.IsDebugEnabled)
{
logger.Debug("Invoking destroy method '" + this.destroyMethodName +
"' on object with name '" + this.objectName + "'");
}
try
{
customDestroyMethod.Invoke(instance, args);
}
catch (TargetInvocationException ex)
{
string msg = "Invocation of destroy method '" + this.destroyMethodName +
"' failed on object with name '" + this.objectName + "'";
if (logger.IsDebugEnabled)
{
logger.Warn(msg, ex.InnerException);
}
else
{
logger.Warn(msg + ": " + ex.InnerException);
}
}
catch (Exception ex)
{
logger.Error("Couldn't invoke destroy method '" + this.destroyMethodName +
"' on object with name '" + this.objectName + "'", ex);
}
}
/**
* Serializes a copy of the state of this class,
* filtering out non-serializable BeanPostProcessors.
*/
//protected Object writeReplace() {
// List<DestructionAwareBeanPostProcessor> serializablePostProcessors = null;
// if (this.objectPostProcessors != null) {
// serializablePostProcessors = new ArrayList<DestructionAwareBeanPostProcessor>();
// for (DestructionAwareBeanPostProcessor postProcessor : this.objectPostProcessors) {
// if (postProcessor instanceof Serializable) {
// serializablePostProcessors.add(postProcessor);
// }
// }
// }
// return new DisposableBeanAdapter(this.bean, this.beanName, this.invokeDisposableObject,
// this.nonPublicAccessAllowed, this.destroyMethodName, serializablePostProcessors);
//}
}
}

View File

@@ -723,6 +723,7 @@
<Compile Include="Objects\Factory\Parsing\ReaderContext.cs" />
<Compile Include="Objects\Factory\Support\AutowireCandidateQualifier.cs" />
<Compile Include="Objects\Factory\Support\DelegateInvokingFactoryObject.cs" />
<Compile Include="Objects\Factory\Support\DisposableObjectAdapter.cs" />
<Compile Include="Objects\Factory\Support\IObjectDefinitionRegistryPostProcessor.cs" />
<Compile Include="Objects\Factory\Support\ObjectScope.cs" />
<Compile Include="Objects\IObjectMetadataElement.cs" />

View File

@@ -160,9 +160,9 @@ namespace Spring.Util
Type[] parameterTypes = Array.ConvertAll<ParameterInfo, Type>(candidate.GetParameters(), delegate(ParameterInfo i) { return i.ParameterType; });
bool typesMatch = false;
bool zeroTypeArguments = argumentTypes.Length == 0;
bool zeroTypeArguments = null == argumentTypes || argumentTypes.Length == 0;
if (parameterTypes.Length == argumentTypes.Length && !zeroTypeArguments)
if (!zeroTypeArguments && parameterTypes.Length == argumentTypes.Length)
{
for (int i = 0; i < parameterTypes.Length; i++)
{