In general, users should take care to prevent overlaps with other
+ /// metadata attributes by using fully-qualified names, perhaps using
+ /// class or package names as prefix.
+ ///
+ /// the unique attribute key
+ /// the attribute value to be attached
+ void SetAttribute(string name, object value);
+
+ ///
+ /// Get the value of the attribute identified by name.
+ /// Return null if the attribute doesn't exist.
+ ///
+ /// the unique attribute key
+ /// the current value of the attribute, if any
+ object GetAttribute(string name);
+
+ ///
+ /// Remove the attribute identified by name and return its value.
+ /// Return null if no attribute under name is found.
+ ///
+ /// the unique attribute key
+ /// The last value of the attribute, if any
+ object RemoveAttribute(string name);
+
+ ///
+ /// Checks weather a specific attributes exists
+ ///
+ /// The unique attribute key
+ ///
+ /// true if the attribute identified by name exists.
+ /// Otherwise return false
+ ///
+ bool HasAttribute(string name);
+
+ ///
+ /// Return the names of all attributes.
+ ///
+ String[] AttributeNames { get; }
+
+ }
+}
diff --git a/src/Spring/Spring.Core/Core/MethodParameter.cs b/src/Spring/Spring.Core/Core/MethodParameter.cs
index 1a923378..7d5c8f5c 100644
--- a/src/Spring/Spring.Core/Core/MethodParameter.cs
+++ b/src/Spring/Spring.Core/Core/MethodParameter.cs
@@ -39,6 +39,7 @@ namespace Spring.Core
private ConstructorInfo constructorInfo;
private readonly int parameterIndex;
+
private Type parameterType;
///
@@ -137,12 +138,33 @@ namespace Spring.Core
get { return constructorInfo; }
}
- public Attribute[] GetParameterAttributes()
+ ///
+ /// Return the annotations associated with the specific method/constructor parameter.
+ ///
+ public Attribute[] ParameterAttributes
{
- if (methodInfo != null)
- return Attribute.GetCustomAttributes(methodInfo.GetParameters()[parameterIndex]);
- else
- return Attribute.GetCustomAttributes(constructorInfo.GetParameters()[parameterIndex]);
+ get
+ {
+ if (methodInfo != null)
+ return Attribute.GetCustomAttributes(methodInfo.GetParameters()[parameterIndex]);
+ else
+ return Attribute.GetCustomAttributes(constructorInfo.GetParameters()[parameterIndex]);
+ }
}
+
+ ///
+ /// Return the annotations associated with the target method/constructor itself.
+ ///
+ public Attribute[] MethodAttributes
+ {
+ get
+ {
+ if (methodInfo != null)
+ return Attribute.GetCustomAttributes(methodInfo);
+ else
+ return Attribute.GetCustomAttributes(constructorInfo);
+ }
+ }
+
}
}
\ No newline at end of file
diff --git a/src/Spring/Spring.Core/Objects/Factory/Attributes/AutowiredAttributeObjectPostProcessor.cs b/src/Spring/Spring.Core/Objects/Factory/Attributes/AutowiredAttributeObjectPostProcessor.cs
index 07c74ea8..38ad3593 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Attributes/AutowiredAttributeObjectPostProcessor.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Attributes/AutowiredAttributeObjectPostProcessor.cs
@@ -79,7 +79,7 @@ namespace Spring.Objects.Factory.Attributes
private readonly IDictionary _injectionMetadataCache =
new Dictionary();
- private Type _autowiredPropertyType = typeof (AutowiredAttribute);
+ private IList _autowiredPropertyTypes = new List();
///
/// Return the order value of this object, where a higher value means greater in
@@ -125,16 +125,25 @@ namespace Spring.Objects.Factory.Attributes
set { _objectFactory = (IConfigurableListableObjectFactory) value; }
}
-
///
- /// Sets the used AutowiredAttributeType during the scan
+ /// Add a Autowired Attribute Type
///
- public Type AutowiredAttributeType
+ public void AddAutowiredType(Type attributeType)
{
- get { return _autowiredPropertyType; }
- set { _autowiredPropertyType = value; }
+ if (!_autowiredPropertyTypes.Contains(attributeType))
+ _autowiredPropertyTypes.Add(attributeType);
}
+ ///
+ /// Create a new instance of an Autowire Post Processor
+ /// with standard attributes of
+ /// and
+ ///
+ public AutowiredAttributeObjectPostProcessor()
+ {
+ _autowiredPropertyTypes.Add(typeof(AutowiredAttribute));
+ _autowiredPropertyTypes.Add(typeof(ValueAttribute));
+ }
///
/// Determines the candidate constructors to use for the given object.
@@ -296,46 +305,59 @@ namespace Spring.Objects.Factory.Attributes
do
{
- var currElements = new List();
- foreach (
- var property in
- objectType.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance))
+ foreach (var autowiredType in _autowiredPropertyTypes)
{
- var attr = Attribute.GetCustomAttribute(property, _autowiredPropertyType) as AutowiredAttribute;
- if (attr != null && property.DeclaringType == objectType)
- currElements.Add(new AutowiredPropertyElement(property, attr.Required));
- }
- foreach (
- var field in
- objectType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance))
- {
- var attr = Attribute.GetCustomAttribute(field, _autowiredPropertyType) as AutowiredAttribute;
- if (attr != null && field.DeclaringType == objectType)
- currElements.Add(new AutowiredFieldElement(field, attr.Required));
- }
- foreach (
- var method in
- objectType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance))
- {
- var attr = Attribute.GetCustomAttribute(method, _autowiredPropertyType) as AutowiredAttribute;
- if (attr != null && method.DeclaringType == objectType)
+ var currElements = new List();
+ foreach (
+ var property in
+ objectType.GetProperties(BindingFlags.NonPublic | BindingFlags.Public |
+ BindingFlags.Instance))
{
- if (method.IsStatic)
- {
- Logger.Warn(
- m => m("Autowired annotation is not supported on static methods: " + method.Name));
- continue;
- }
- if (method.IsGenericMethod)
- {
- Logger.Warn(
- m => m("Autowired annotation is not supported on generic methods: " + method.Name));
- continue;
- }
- currElements.Add(new AutowiredMethodElement(method, attr.Required));
+ var required = true;
+ var attr = Attribute.GetCustomAttribute(property, autowiredType);
+ if (attr is AutowiredAttribute)
+ required = ((AutowiredAttribute)attr).Required;
+ if (attr != null && property.DeclaringType == objectType)
+ currElements.Add(new AutowiredPropertyElement(property, required));
}
+ foreach (
+ var field in
+ objectType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance))
+ {
+ var required = true;
+ var attr = Attribute.GetCustomAttribute(field, autowiredType);
+ if (attr is AutowiredAttribute)
+ required = ((AutowiredAttribute) attr).Required;
+ if (attr != null && field.DeclaringType == objectType)
+ currElements.Add(new AutowiredFieldElement(field, required));
+ }
+ foreach (
+ var method in
+ objectType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance))
+ {
+ var required = true;
+ var attr = Attribute.GetCustomAttribute(method, autowiredType);
+ if (attr is AutowiredAttribute)
+ required = ((AutowiredAttribute)attr).Required;
+ if (attr != null && method.DeclaringType == objectType)
+ {
+ if (method.IsStatic)
+ {
+ Logger.Warn(
+ m => m("Autowired annotation is not supported on static methods: " + method.Name));
+ continue;
+ }
+ if (method.IsGenericMethod)
+ {
+ Logger.Warn(
+ m => m("Autowired annotation is not supported on generic methods: " + method.Name));
+ continue;
+ }
+ currElements.Add(new AutowiredMethodElement(method, required));
+ }
+ }
+ elements.InsertRange(0, currElements);
}
- elements.InsertRange(0, currElements);
objectType = objectType.BaseType;
} while (objectType != null && objectType != typeof (Object));
diff --git a/src/Spring/Spring.Core/Objects/Factory/Attributes/QualifierAnnotationAutowireCandidateResolver.cs b/src/Spring/Spring.Core/Objects/Factory/Attributes/QualifierAnnotationAutowireCandidateResolver.cs
new file mode 100644
index 00000000..e521ac5f
--- /dev/null
+++ b/src/Spring/Spring.Core/Objects/Factory/Attributes/QualifierAnnotationAutowireCandidateResolver.cs
@@ -0,0 +1,295 @@
+#region License
+
+/*
+ * Copyright 2002-2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Spring.Core;
+using Spring.Core.TypeConversion;
+using Spring.Objects.Factory.Config;
+using Spring.Objects.Factory.Support;
+using Spring.Util;
+
+namespace Spring.Objects.Factory.Attributes
+{
+ ///
+ /// implementation that matches bean definition qualifier
+ /// against on the field or parameter to be autowired.
+ /// Also supports suggested expression values through a attribute.
+ ///
+ public class QualifierAnnotationAutowireCandidateResolver : IAutowireCandidateResolver, IObjectFactoryAware
+ {
+ private IObjectFactory _objectFactory;
+
+ private ISet _qualifierTypes = new HashSet();
+
+ private Type _valueAttributeType = typeof(ValueAttribute);
+
+ public IObjectFactory ObjectFactory
+ {
+ set { _objectFactory = value; }
+ }
+
+ public Type ValueAttributeType
+ {
+ set { _valueAttributeType = value; }
+ }
+
+ ///
+ /// Create a new QualifierAnnotationAutowireCandidateResolver
+ /// for Spring's standard attribute.
+ ///
+ public QualifierAnnotationAutowireCandidateResolver()
+ {
+ _qualifierTypes.Add(typeof(QualifierAttribute));
+ }
+
+ ///
+ /// Create a new QualifierAnnotationAutowireCandidateResolver
+ /// for the given qualifier attribute type.
+ ///
+ /// the qualifier attribute to look for
+ public QualifierAnnotationAutowireCandidateResolver(Type qualifierType)
+ {
+ Trace.Assert(qualifierType != null, "'qualifierType' must not be null");
+ _qualifierTypes.Add(qualifierType);
+ }
+
+ ///
+ /// Create a new QualifierAnnotationAutowireCandidateResolver
+ /// for the given qualifier attribute types.
+ ///
+ /// the qualifier annotations to look for
+ public QualifierAnnotationAutowireCandidateResolver(ISet qualifierTypes) {
+ Trace.Assert(qualifierTypes != null, "'qualifierTypes' must not be null");
+ _qualifierTypes.UnionWith(qualifierTypes);
+ }
+
+ ///
+ /// Register the given type to be used as a qualifier when autowiring.
+ ///
This identifies qualifier annotations for direct use (on fields,
+ /// method parameters and constructor parameters) as well as meta
+ /// annotations that in turn identify actual qualifier annotations.
+ ///
This implementation only supports annotations as qualifier types.
+ /// The default is Spring's attribute which serves
+ /// as a qualifier for direct use and also as a meta attribute.
+ ///
+ /// the attribute type to register
+ public void AddQualifierType(Type qualifierType) {
+ _qualifierTypes.Add(qualifierType);
+ }
+
+ ///
+ /// Determine whether the provided object definition is an autowire candidate.
+ ///
To be considered a candidate the object's autowire-candidate
+ /// attribute must not have been set to 'false'. Also, if an attribute on
+ /// the field or parameter to be autowired is recognized by this bean factory
+ /// as a qualifier, the object must 'match' against the attribute as
+ /// well as any attributes it may contain. The bean definition must contain
+ /// the same qualifier or match by meta attributes. A "value" attribute will
+ /// fallback to match against the bean name or an alias if a qualifier or
+ /// attribute does not match.
+ ///
+ public bool IsAutowireCandidate(ObjectDefinitionHolder odHolder, DependencyDescriptor descriptor)
+ {
+ if (!odHolder.ObjectDefinition.IsAutowireCandidate)
+ {
+ // if explicitly false, do not proceed with qualifier check
+ return false;
+ }
+ if (descriptor == null) {
+ // no qualification necessaryodHolder
+ return true;
+ }
+ bool match = CheckQualifiers(odHolder, descriptor.Attributes);
+ if (match)
+ {
+ MethodParameter methodParam = descriptor.MethodParameter;
+ if (methodParam != null)
+ {
+ var method = methodParam.MethodInfo;
+ if (method == null || method.ReturnType == typeof(void)) {
+ match = CheckQualifiers(odHolder, methodParam.MethodAttributes);
+ }
+ }
+ }
+ return match;
+ }
+
+ ///
+ /// Match the given qualifier annotations against the candidate bean definition.
+ ///
+ protected bool CheckQualifiers(ObjectDefinitionHolder odHolder, Attribute[] annotationsToSearch)
+ {
+ if (annotationsToSearch == null || annotationsToSearch.Length == 0) {
+ return true;
+ }
+ foreach (var attribute in annotationsToSearch)
+ {
+ if (IsQualifier(attribute.GetType()))
+ {
+ if (!CheckQualifier(odHolder, attribute))
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ ///
+ /// Checks whether the given attribute type is a recognized qualifier type.
+ ///
+ protected bool IsQualifier(Type attributeType)
+ {
+ foreach (Type qualifierType in _qualifierTypes)
+ {
+ if (IsSubTypeOf(attributeType, qualifierType))
+ return true;
+ }
+ return false;
+ }
+
+ private bool IsSubTypeOf(Type actual, Type requested)
+ {
+ do
+ {
+ if (actual == requested)
+ return true;
+
+ actual = actual.BaseType;
+ } while (actual != typeof(Object));
+
+ return false;
+ }
+
+ ///
+ /// Match the given qualifier attribute against the candidate bean definition.
+ ///
+ protected bool CheckQualifier(ObjectDefinitionHolder odHolder, Attribute attribute)
+ {
+ Type type = attribute.GetType();
+ RootObjectDefinition od = (RootObjectDefinition) odHolder.ObjectDefinition;
+ AutowireCandidateQualifier qualifier = od.GetQualifier(type.FullName);
+ if (qualifier == null) {
+ qualifier = od.GetQualifier(type.Name);
+ }
+ if (qualifier == null) {
+ Attribute targetAttribute = null;
+ // TODO: Get the resolved factory method
+ //if (od.GetResolvedFactoryMethod() != null) {
+ // targetAttribute = Attribute.GetCustomAttribute(od.GetResolvedFactoryMethod(), type);
+ //}
+ if (targetAttribute == null) {
+ // look for matching attribute on the target class
+ if (_objectFactory != null) {
+ Type objectType = od.ObjectType;
+ if (objectType != null)
+ {
+ targetAttribute = Attribute.GetCustomAttribute(objectType, type);
+ }
+ }
+ if (targetAttribute == null && od.ObjectType != null) {
+ targetAttribute = Attribute.GetCustomAttribute(od.ObjectType, type);
+ }
+ }
+ if (targetAttribute != null && targetAttribute.Equals(attribute)) {
+ return true;
+ }
+ }
+
+ IDictionary attributes = AttributeUtils.GetAttributeProperties(attribute);
+ if (attributes.Count == 0 && qualifier == null) {
+ // if no attributes, the qualifier must be present
+ return false;
+ }
+ foreach(var entry in attributes)
+ {
+ string propertyName = entry.Key;
+ object expectedValue = entry.Value;
+ object actualValue = null;
+ // check qualifier first
+ if (qualifier != null)
+ {
+ actualValue = qualifier.GetAttribute(propertyName);
+ }
+ if (actualValue == null)
+ {
+ // fall back on bean definition attribute
+ actualValue = od.GetAttribute(propertyName);
+ }
+ if (actualValue == null && propertyName.Equals(AutowireCandidateQualifier.VALUE_KEY) &&
+ expectedValue is string && odHolder.MatchesName((string) expectedValue))
+ {
+ // fall back on bean name (or alias) match
+ continue;
+ }
+ if (actualValue == null && qualifier != null)
+ {
+ // fall back on default, but only if the qualifier is present
+ actualValue = AttributeUtils.GetDefaultValue(attribute, propertyName);
+ }
+ if (actualValue != null)
+ {
+ actualValue = TypeConversionUtils.ConvertValueIfNecessary(expectedValue.GetType(), actualValue, null);
+ }
+ if (!expectedValue.Equals(actualValue)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ///
+ /// Determine whether the given dependency carries a value attribute.
+ ///
+ public Object GetSuggestedValue(DependencyDescriptor descriptor)
+ {
+ Object value = FindValue(descriptor.Attributes);
+ if (value == null)
+ {
+ MethodParameter methodParam = descriptor.MethodParameter;
+ if (methodParam != null)
+ {
+ value = FindValue(methodParam.MethodAttributes);
+ }
+ }
+ return value;
+ }
+
+ ///
+ /// Determine a suggested value from any of the given candidate annotations.
+ ///
+ protected Object FindValue(Attribute[] annotationsToSearch) {
+ foreach(var attribute in annotationsToSearch) {
+ if (_valueAttributeType == attribute.GetType())
+ {
+ Object value = ((ValueAttribute)attribute).Expression;
+ if (value == null)
+ {
+ throw new InvalidOperationException("Value attribute must have a value attribute");
+ }
+ return value;
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/Spring/Spring.Core/Objects/Factory/Attributes/QualifierAttribute.cs b/src/Spring/Spring.Core/Objects/Factory/Attributes/QualifierAttribute.cs
index bf2e34ba..c6ef6f43 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Attributes/QualifierAttribute.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Attributes/QualifierAttribute.cs
@@ -13,20 +13,52 @@ namespace Spring.Objects.Factory.Attributes
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public class QualifierAttribute : Attribute
{
- private readonly string _name;
+ private readonly string _value;
///
- /// Instantiate a new qualifier type
+ /// Instantiate a new qualifier with an empty name
///
- /// name to use as qualifier
- public QualifierAttribute(string name)
+ public QualifierAttribute()
{
- _name = name;
+ _value = "";
+ }
+
+ ///
+ /// Instantiate a new qualifier with a givin name
+ ///
+ /// name to use as qualifier
+ public QualifierAttribute(string value)
+ {
+ _value = value;
}
///
/// Gets the name associated with this qualifier
///
- public string Name { get { return _name; } }
+ public string Value { get { return _value; } }
+
+
+ ///
+ /// Checks weather the attribute is the same
+ ///
+ ///
+ ///
+ public override bool Equals(object obj)
+ {
+ if (obj == null || GetType() != obj.GetType())
+ {
+ return false;
+ }
+
+ var o1 = obj as QualifierAttribute;
+ if (_value != o1._value) return false;
+
+ return true;
+ }
+
+ public override int GetHashCode()
+ {
+ return _value != null ? _value.GetHashCode() : 0;
+ }
}
}
diff --git a/src/Spring/Spring.Core/Objects/Factory/Attributes/ValueAttribute.cs b/src/Spring/Spring.Core/Objects/Factory/Attributes/ValueAttribute.cs
new file mode 100644
index 00000000..62155b4c
--- /dev/null
+++ b/src/Spring/Spring.Core/Objects/Factory/Attributes/ValueAttribute.cs
@@ -0,0 +1,37 @@
+#region License
+
+/*
+ * Copyright 2002-2010 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#endregion
+
+using System;
+
+namespace Spring.Objects.Factory.Attributes
+{
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
+ public class ValueAttribute : Attribute
+ {
+ private string _expression;
+
+ public ValueAttribute(string expression)
+ {
+ _expression = expression;
+ }
+
+ public string Expression { get { return _expression; } }
+ }
+}
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/DependencyDescriptor.cs b/src/Spring/Spring.Core/Objects/Factory/Config/DependencyDescriptor.cs
index cecbebe7..f891798a 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/DependencyDescriptor.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/DependencyDescriptor.cs
@@ -168,7 +168,28 @@ namespace Spring.Objects.Factory.Config
get { return methodParameter; }
}
- public string Name
+ ///
+ /// Gets the Attributes assigned to Field, Property or Paramater
+ ///
+ public Attribute[] Attributes
+ {
+ get
+ {
+ if (methodParameter != null)
+ return methodParameter.ParameterAttributes;
+ if (property != null)
+ return Attribute.GetCustomAttributes(property);
+ if (field != null)
+ return Attribute.GetCustomAttributes(field);
+
+ return new Attribute[0];
+ }
+ }
+
+ ///
+ /// Gets the name of the member info
+ ///
+ public string DependencyName
{
get
{
@@ -182,68 +203,5 @@ namespace Spring.Objects.Factory.Config
return "";
}
}
-
- ///
- /// Determine whether the given dependency carries a value annotation.
- ///
- public Object GetSuggestedValue()
- {
- Object value = null;
-
- if (methodParameter != null)
- value = ConvertFieldName(methodParameter.ParameterName());
- if (property != null)
- value = property.Name;
- if (field != null)
- value = ConvertFieldName(field.Name);
-
- return value;
- }
-
- ///
- /// Get the qualifier name if exists
- ///
- public string GetQualifierName()
- {
- string value = null;
-
- if (methodParameter != null)
- value = FindValue(methodParameter.GetParameterAttributes());
- if (property != null)
- value = FindValue(Attribute.GetCustomAttributes(property));
- if (field != null)
- value = FindValue(Attribute.GetCustomAttributes(field));
-
- return value;
- }
-
- /**
- * Determine a suggested value from any of the given candidate annotations.
- */
-
- private string FindValue(Attribute[] attributesToSearch)
- {
- foreach (Attribute attribute in attributesToSearch)
- {
- if (attribute is QualifierAttribute)
- {
- var qualifierAttribute = attribute as QualifierAttribute;
- return qualifierAttribute.Name;
- }
- }
- return null;
- }
-
- private string ConvertFieldName(string fieldName)
- {
- if (string.IsNullOrEmpty(fieldName))
- return string.Empty;
-
- char[] letters = fieldName.TrimStart('_').ToCharArray();
- letters[0] = char.ToUpper(letters[0]);
-
- return new string(letters);
- }
-
}
}
\ No newline at end of file
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/IConfigurableObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/Config/IConfigurableObjectFactory.cs
index 52678387..e81168a5 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/IConfigurableObjectFactory.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/IConfigurableObjectFactory.cs
@@ -22,6 +22,7 @@
using System;
using System.ComponentModel;
+using Spring.Util;
#endregion
@@ -163,5 +164,17 @@ namespace Spring.Objects.Factory.Config
///
void RegisterCustomConverter(Type requiredType, TypeConverter converter);
+ ///
+ /// Add a String resolver for embedded values such as annotation attributes.
+ ///
+ /// the String resolver to apply to embedded values
+ void AddEmbeddedValueResolver(IStringValueResolver valueResolver);
+
+ ///
+ /// Resolve the given embedded value, e.g. an annotation attribute.
+ ///
+ /// the value to resolve
+ /// the resolved value (may be the original value as-is)
+ string ResolveEmbeddedValue(string value);
}
}
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/IObjectDefinition.cs b/src/Spring/Spring.Core/Objects/Factory/Config/IObjectDefinition.cs
index 81f19b3d..fbc180ca 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/IObjectDefinition.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/IObjectDefinition.cs
@@ -226,5 +226,12 @@ namespace Spring.Objects.Factory.Config
/// true if this instance is autowire candidate; otherwise, false.
///
bool IsAutowireCandidate { get; }
+
+ ///
+ /// Return whether this bean is a primary autowire candidate.
+ /// If this value is true for exactly one bean among multiple
+ /// matching candidates, it will serve as a tie-breaker.
+ ///
+ bool IsPrimary { get; }
}
}
\ No newline at end of file
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/ObjectDefinitionHolder.cs b/src/Spring/Spring.Core/Objects/Factory/Config/ObjectDefinitionHolder.cs
index 4fa884b9..ff010302 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/ObjectDefinitionHolder.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/ObjectDefinitionHolder.cs
@@ -132,6 +132,17 @@ namespace Spring.Objects.Factory.Config
get { return aliases; }
}
+ ///
+ /// Checks wether a givin candidate name has a defined object or alias
+ ///
+ /// name to check if exists
+ ///
+ public bool MatchesName(string candidateName)
+ {
+ return (!string.IsNullOrEmpty(candidateName) &&
+ (candidateName.Equals(ObjectName) || Aliases.Contains(candidateName)));
+ }
+
#endregion
}
}
\ No newline at end of file
diff --git a/src/Spring/Spring.Core/Objects/Factory/Config/PropertyPlaceholderConfigurer.cs b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyPlaceholderConfigurer.cs
index 017d5f40..a0c862cd 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/PropertyPlaceholderConfigurer.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/PropertyPlaceholderConfigurer.cs
@@ -28,6 +28,7 @@ using System.Globalization;
using Common.Logging;
using Spring.Collections;
+using Spring.Util;
#endregion
@@ -247,13 +248,10 @@ namespace Spring.Objects.Factory.Config
definition.ResourceDescription, name, ex.Message);
}
}
+
+ factory.AddEmbeddedValueResolver(resolveAdapter);
}
-
-
-
-
-
///
/// Parse values recursively to be able to resolve cross-references between
/// placeholder values.
@@ -401,7 +399,7 @@ namespace Spring.Objects.Factory.Config
#region Helper class
- private class PlaceholderResolveHandlerAdapter
+ private class PlaceholderResolveHandlerAdapter : IStringValueResolver
{
private readonly PropertyPlaceholderConfigurer ppc;
private readonly NameValueCollection props;
diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinition.cs b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinition.cs
index e12ba0ae..598a6dd8 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinition.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectDefinition.cs
@@ -22,13 +22,15 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Globalization;
+using System.Linq;
using System.Reflection;
using System.Text;
-
using Spring.Core.TypeResolution;
using Spring.Objects.Factory.Config;
using Spring.Util;
+using Spring.Collections.Generic;
#endregion
@@ -44,7 +46,7 @@ namespace Spring.Objects.Factory.Support
/// Juergen Hoeller
/// Rick Evans (.NET)
[Serializable]
- public abstract class AbstractObjectDefinition : IConfigurableObjectDefinition
+ public abstract class AbstractObjectDefinition : ObjectMetadataAttributeAccessor, IConfigurableObjectDefinition
{
private static readonly string SCOPE_SINGLETON = "singleton";
private static readonly string SCOPE_PROTOTYPE = "prototype";
@@ -135,6 +137,8 @@ namespace Spring.Objects.Factory.Support
InitMethodName = other.InitMethodName;
DestroyMethodName = other.DestroyMethodName;
IsAutowireCandidate = other.IsAutowireCandidate;
+ IsPrimary = other.IsPrimary;
+ CopyQualifiersFrom(aod);
DependsOn = new List(other.DependsOn);
FactoryMethodName = other.FactoryMethodName;
FactoryObjectName = other.FactoryObjectName;
@@ -541,6 +545,67 @@ namespace Spring.Objects.Factory.Support
set { autowireCandidate = value;}
}
+
+ ///
+ /// Set whether this bean is a primary autowire candidate.
+ /// If this value is true for exactly one bean among multiple
+ /// matching candidates, it will serve as a tie-breaker.
+ ///
+ public bool IsPrimary
+ {
+ get { return primary; }
+ set { primary = value; }
+ }
+
+ ///
+ /// Register a qualifier to be used for autowire candidate resolution,
+ /// keyed by the qualifier's type name.
+ ///
+ ///
+ public void AddQualifier(AutowireCandidateQualifier qualifier)
+ {
+ qualifiers.Add(qualifier.TypeName, qualifier);
+ }
+
+ ///
+ /// Return whether this bean has the specified qualifier.
+ ///
+ public bool HasQualifier(string typeName)
+ {
+ return qualifiers.ContainsKey(typeName);
+ }
+
+ ///
+ /// Return the qualifier mapped to the provided type name.
+ ///
+ public AutowireCandidateQualifier GetQualifier(string typeName)
+ {
+ return qualifiers.ContainsKey(typeName) ? qualifiers[typeName] : null;
+ }
+
+ ///
+ /// Return all registered qualifiers.
+ ///
+ /// the Set of objects.
+ public Set GetQualifiers()
+ {
+ return new OrderedSet(qualifiers.Values);
+ }
+
+ ///
+ /// Copy the qualifiers from the supplied AbstractBeanDefinition to this bean definition.
+ ///
+ /// the AbstractBeanDefinition to copy from
+ public void CopyQualifiersFrom(AbstractObjectDefinition source)
+ {
+ Trace.Assert(source != null, "Source must not be null");
+ foreach (var qualifier in source.qualifiers)
+ {
+ if (!qualifiers.Contains(qualifier))
+ qualifiers.Add(qualifier);
+ }
+ }
+
///
/// The name of the initializer method.
///
@@ -748,6 +813,7 @@ namespace Spring.Objects.Factory.Support
}
AutowireMode = other.AutowireMode;
ResourceDescription = other.ResourceDescription;
+ IsPrimary = other.IsPrimary;
AbstractObjectDefinition aod = other as AbstractObjectDefinition;
if (aod != null)
@@ -759,6 +825,7 @@ namespace Spring.Objects.Factory.Support
MethodOverrides.AddAll(aod.MethodOverrides);
DependencyCheck = aod.DependencyCheck;
+ CopyQualifiersFrom(aod);
}
}
@@ -779,6 +846,7 @@ namespace Spring.Objects.Factory.Support
buffer.Append("; Singleton = ").Append(IsSingleton);
buffer.Append("; LazyInit = ").Append(IsLazyInit);
buffer.Append("; Autowire = ").Append(AutowireMode);
+ buffer.Append("; Primary = ").Append(IsPrimary);
buffer.Append("; DependencyCheck = ").Append(DependencyCheck);
buffer.Append("; InitMethodName = ").Append(InitMethodName);
buffer.Append("; DestroyMethodName = ").Append(DestroyMethodName);
@@ -811,6 +879,12 @@ namespace Spring.Objects.Factory.Support
private DependencyCheckingMode dependencyCheck = DependencyCheckingMode.None;
private IList dependsOn;
private bool autowireCandidate = true;
+
+ private bool primary;
+
+ private readonly IDictionary qualifiers =
+ new Dictionary();
+
private string initMethodName = null;
private string destroyMethodName = null;
private string factoryMethodName = null;
diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectFactory.cs
index 446336bd..72c66b3d 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectFactory.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Support/AbstractObjectFactory.cs
@@ -1603,6 +1603,11 @@ namespace Spring.Objects.Factory.Support
///
private ISet objectPostProcessors = new SortedSet(new ObjectOrderComparator());
+ ///
+ /// String Resolver applied to Autowired value injections
+ ///
+ private ISet embeddedValueResolvers = new SortedSet();
+
///
/// Indicates whether any IInstantiationAwareBeanPostProcessors have been registered
///
@@ -2385,6 +2390,30 @@ namespace Spring.Objects.Factory.Support
return this.singletonsInCreation.Contains(name);
}
+ ///
+ /// Add a String resolver for embedded values such as annotation attributes.
+ ///
+ /// the String resolver to apply to embedded values
+ public void AddEmbeddedValueResolver(IStringValueResolver valueResolver)
+ {
+ embeddedValueResolvers.Add(valueResolver);
+ }
+
+ ///
+ /// Resolve the given embedded value, e.g. an annotation attribute.
+ ///
+ /// the value to resolve
+ /// the resolved value (may be the original value as-is)
+ public string ResolveEmbeddedValue(string value)
+ {
+ string result = value;
+ foreach(IStringValueResolver resolver in embeddedValueResolvers)
+ {
+ result = resolver.ParseAndResolveVariables(result);
+ }
+ return result;
+ }
+
///
/// Add a new
/// that will get applied to objects created by this factory.
diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/AutowireCandidateQualifier.cs b/src/Spring/Spring.Core/Objects/Factory/Support/AutowireCandidateQualifier.cs
new file mode 100644
index 00000000..790c9151
--- /dev/null
+++ b/src/Spring/Spring.Core/Objects/Factory/Support/AutowireCandidateQualifier.cs
@@ -0,0 +1,102 @@
+#region License
+
+/*
+/// Copyright 2002-2010 the original author or authors.
+ *
+/// Licensed under the Apache License, Version 2.0 (the "License");
+/// you may not use this file except in compliance with the License.
+/// You may obtain a copy of the License at
+ *
+/// http://www.apache.org/licenses/LICENSE-2.0
+ *
+/// Unless required by applicable law or agreed to in writing, software
+/// distributed under the License is distributed on an "AS IS" BASIS,
+/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+/// See the License for the specific language governing permissions and
+/// limitations under the License.
+ */
+
+#endregion
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+
+namespace Spring.Objects.Factory.Support
+{
+ ///
+ /// Qualifier for resolving autowire candidates. A bean definition that
+ /// includes one or more such qualifiers enables fine-grained matching
+ /// against annotations on a field or parameter to be autowired.
+ ///
+ public class AutowireCandidateQualifier : ObjectMetadataAttributeAccessor
+ {
+ public static string VALUE_KEY = "Value";
+
+ private readonly string _typeName;
+
+
+ ///
+ /// Construct a qualifier to match against an annotation of the
+ /// given type.
+ ///
+ /// type the annotation type
+ public AutowireCandidateQualifier(Type type) : this(type.Name)
+ {
+ }
+
+ ///
+ /// Construct a qualifier to match against an annotation of the
+ /// given type name.
+ ///
The type name may match the fully-qualified class name of
+ /// the annotation or the short class name (without the package).
+ ///
+ /// the name of the annotation type
+ public AutowireCandidateQualifier(string typeName)
+ {
+ Trace.Assert(typeName != null, "Type name must not be null");
+ _typeName = typeName;
+ }
+
+ ///
+ /// Construct a qualifier to match against an annotation of the
+ /// given type whose value attribute also matches
+ /// the specified value.
+ ///
+ /// the annotation type
+ /// the annotation value to match
+ public AutowireCandidateQualifier(Type type, object value) : this(type.Name, value)
+ {
+ }
+
+ ///
+ /// Construct a qualifier to match against an annotation of the
+ /// given type name whose value attribute also matches
+ /// the specified value.
+ ///
The type name may match the fully-qualified class name of
+ /// the annotation or the short class name (without the package).
+ ///
+ /// the name of the annotation type
+ /// the annotation value to match
+ public AutowireCandidateQualifier(string typeName, object value)
+ {
+ Trace.Assert(typeName != null, "Type name must not be null");
+ _typeName = typeName;
+ SetAttribute(VALUE_KEY, value);
+ }
+
+
+ ///
+ /// Retrieve the type name. This value will be the same as the
+ /// type name provided to the constructor or the fully-qualified
+ /// class name if a Class instance was provided to the constructor.
+ ///
+ public String TypeName
+ {
+ get { return _typeName; }
+ }
+
+ }
+}
diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/AutowireUtils.cs b/src/Spring/Spring.Core/Objects/Factory/Support/AutowireUtils.cs
index b68e6747..70d6734e 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Support/AutowireUtils.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Support/AutowireUtils.cs
@@ -27,6 +27,7 @@ using System.Reflection;
using Spring.Collections;
using Spring.Core;
+using Spring.Objects.Factory.Attributes;
using Spring.Objects.Factory.Config;
using Spring.Util;
@@ -348,7 +349,7 @@ namespace Spring.Objects.Factory.Support
/// A SimpleAutowireCandidateResolver
public static IAutowireCandidateResolver CreateAutowireCandidateResolver()
{
- return new SimpleAutowireCandidateResolver();
+ return new QualifierAnnotationAutowireCandidateResolver();
}
///
diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/DefaultListableObjectFactory.cs b/src/Spring/Spring.Core/Objects/Factory/Support/DefaultListableObjectFactory.cs
index f9b33de6..bad5a33d 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Support/DefaultListableObjectFactory.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Support/DefaultListableObjectFactory.cs
@@ -25,6 +25,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
+using System.Linq;
using Common.Logging;
@@ -32,6 +33,8 @@ using Spring.Core;
using Spring.Core.TypeConversion;
using Spring.Objects.Factory.Config;
using Spring.Util;
+using Spring.Expressions;
+using Spring.Context.Support;
#endregion
@@ -324,7 +327,7 @@ namespace Spring.Objects.Factory.Support
///
/// IDictionary from dependency type to corresponding autowired value
///
- private readonly IDictionary resolvableDependencies = new Hashtable();
+ private readonly IDictionary resolvableDependencies = new Dictionary();
#endregion
@@ -523,7 +526,7 @@ namespace Spring.Objects.Factory.Support
{
AssertUtils.IsTrue((autowiredValue is IObjectFactory) || dependencyType.IsInstanceOfType(autowiredValue),
"Value [" + autowiredValue + "] does not implement specified type [" + dependencyType.Name + "]");
- if (!resolvableDependencies.Contains(dependencyType))
+ if (!resolvableDependencies.ContainsKey(dependencyType))
{
this.resolvableDependencies.Add(dependencyType, autowiredValue);
}
@@ -1111,22 +1114,20 @@ namespace Spring.Objects.Factory.Support
public override object ResolveDependency(DependencyDescriptor descriptor, string objectName,
IList autowiredObjectNames)
{
- string qualifierName = descriptor.GetQualifierName();
- if (!string.IsNullOrEmpty(qualifierName))
- {
- if (ContainsObject(qualifierName))
- {
- autowiredObjectNames.Add(qualifierName);
- return GetObject(qualifierName);
- }
- else
- {
- if (descriptor.Required)
- throw new NoSuchObjectDefinitionException(qualifierName, "no object found with this name");
- return null;
- }
- }
Type type = descriptor.DependencyType;
+ Object value = AutowireCandidateResolver.GetSuggestedValue(descriptor);
+ if (value != null)
+ {
+ if (value is string)
+ {
+ object valueBefore = value;
+ value = ResolveEmbeddedValue((string) value);
+ if (valueBefore.Equals(value))
+ value = ExpressionEvaluator.GetValue(null, (string) value);
+ }
+ return TypeConversionUtils.ConvertValueIfNecessary(type, value, null);
+ }
+
if (type.IsArray)
{
Type elementType = type.GetElementType();
@@ -1189,20 +1190,6 @@ namespace Spring.Objects.Factory.Support
else
{
IDictionary matchingObjects = FindAutowireCandidates(objectName, type, descriptor);
- if (matchingObjects.Count == 0 || matchingObjects.Count > 1)
- {
- Object value = descriptor.GetSuggestedValue();
- if (value is string)
- {
- string matchingObject = value as string;
- if (ContainsObject(matchingObject))
- {
- matchingObjects.Clear();
- matchingObjects.Add(matchingObject, GetObject(matchingObject));
- }
- }
- }
-
if (matchingObjects.Count == 0)
{
if (descriptor.Required)
@@ -1216,8 +1203,17 @@ namespace Spring.Objects.Factory.Support
}
if (matchingObjects.Count > 1)
{
- throw new NoSuchObjectDefinitionException(type,
- "expected single matching object but found " + matchingObjects.Count + ": " + matchingObjects);
+ string primaryObjecName = DeterminePrimaryCandidate(matchingObjects, descriptor);
+ if (primaryObjecName == null)
+ {
+ throw new NoSuchObjectDefinitionException(type,
+ "expected single matching object but found " + matchingObjects.Count + ": " + matchingObjects);
+ }
+ if (autowiredObjectNames != null)
+ {
+ autowiredObjectNames.Add(primaryObjecName);
+ }
+ return matchingObjects[primaryObjecName];
}
DictionaryEntry entry = (DictionaryEntry)ObjectUtils.EnumerateFirstElement(matchingObjects);
if (autowiredObjectNames != null)
@@ -1228,7 +1224,75 @@ namespace Spring.Objects.Factory.Support
}
}
+ ///
+ /// Determine the primary autowire candidate in the given set of beans.
+ ///
+ /// a Map of candidate names and candidate instances
+ /// that match the required type
+ /// the target dependency to match against
+ /// the name of the primary candidate, or null if none found
+ private string DeterminePrimaryCandidate(IDictionary candidateObjects, DependencyDescriptor descriptor) {
+ string primaryObjectName = null;
+ string fallbackObjectName = null;
+ foreach(DictionaryEntry entry in candidateObjects)
+ {
+ string candidateBeanName = entry.Key as string;
+ object objectInstance = entry.Value;
+ if (IsPrimary(candidateBeanName, objectInstance))
+ {
+ if (primaryObjectName != null)
+ {
+ bool candidateLocal = ContainsObjectDefinition(candidateBeanName);
+ bool primaryLocal = ContainsObjectDefinition(primaryObjectName);
+ if (candidateLocal == primaryLocal)
+ {
+ throw new NoSuchObjectDefinitionException(descriptor.DependencyType,
+ "more than one 'primary' bean found among candidates: " + candidateObjects);
+ }
+ if (candidateLocal && !primaryLocal)
+ {
+ primaryObjectName = candidateBeanName;
+ }
+ }
+ else
+ {
+ primaryObjectName = candidateBeanName;
+ }
+ }
+ if (primaryObjectName == null &&
+ (resolvableDependencies.Values.Contains(objectInstance) ||
+ MatchesObjectName(candidateBeanName, descriptor.DependencyName)))
+ {
+ fallbackObjectName = candidateBeanName;
+ }
+ }
+ return (primaryObjectName != null ? primaryObjectName : fallbackObjectName);
+ }
+ ///
+ /// Return whether the object definition for the given object name has been
+ /// marked as a primary object.
+ ///
+ /// the name of the bean
+ /// the corresponding bean instance
+ /// whether the given bean qualifies as primary
+ private bool IsPrimary(string objectName, object objectInstance) {
+ if (ContainsObjectDefinition(objectName)) {
+ return GetMergedObjectDefinition(objectName, true).IsPrimary;
+ }
+ return (ParentObjectFactory is DefaultListableObjectFactory &&
+ ((DefaultListableObjectFactory)ParentObjectFactory).IsPrimary(objectName, objectInstance));
+ }
+
+ ///
+ /// Determine whether the given candidate name matches the bean name or the aliases
+ ///stored in this bean definition.
+ ///
+ protected bool MatchesObjectName(string objectName, string candidateName)
+ {
+ return (candidateName != null &&
+ (candidateName.Equals(objectName) || GetAliases(objectName).Contains(candidateName)));
+ }
///
/// Raises the no such object definition exception for an unresolvable dependency
@@ -1248,9 +1312,9 @@ namespace Spring.Objects.Factory.Support
ObjectFactoryUtils.ObjectNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.Eager);
IDictionary result = new OrderedDictionary(candidateNames.Count);
- foreach (DictionaryEntry entry in resolvableDependencies)
+ foreach (var entry in resolvableDependencies)
{
- Type autoWiringType = (Type)entry.Key;
+ Type autoWiringType = entry.Key;
if (autoWiringType.IsAssignableFrom(requiredType))
{
object autowiringValue = this.resolvableDependencies[autoWiringType];
diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/IAutowireCandidateResolver.cs b/src/Spring/Spring.Core/Objects/Factory/Support/IAutowireCandidateResolver.cs
index 199a4a08..4dfeff37 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Support/IAutowireCandidateResolver.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Support/IAutowireCandidateResolver.cs
@@ -18,6 +18,7 @@
#endregion
+using System;
using Spring.Objects.Factory.Config;
namespace Spring.Objects.Factory.Support
@@ -41,5 +42,16 @@ namespace Spring.Objects.Factory.Support
/// true if the object definition qualifies as autowire candidate; otherwise, false.
///
bool IsAutowireCandidate(ObjectDefinitionHolder odHolder, DependencyDescriptor descriptor);
+
+
+ ///
+ /// Determine whether a default value is suggested for the given dependency.
+ ///
+ /// The descriptor for the target method parameter or field
+ /// The value suggested (typically an expression String),
+ /// or null if none found
+ ///
+ Object GetSuggestedValue(DependencyDescriptor descriptor);
+
}
}
\ No newline at end of file
diff --git a/src/Spring/Spring.Core/Objects/Factory/Support/SimpleAutowireCandidateResolver.cs b/src/Spring/Spring.Core/Objects/Factory/Support/SimpleAutowireCandidateResolver.cs
index 52551da3..9df13e0f 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Support/SimpleAutowireCandidateResolver.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Support/SimpleAutowireCandidateResolver.cs
@@ -45,5 +45,18 @@ namespace Spring.Objects.Factory.Support
{
return odHolder.ObjectDefinition.IsAutowireCandidate;
}
+
+
+ ///
+ /// Determine whether a default value is suggested for the given dependency.
+ ///
+ /// The descriptor for the target method parameter or field
+ /// The value suggested (typically an expression String),
+ /// or null if none found
+ ///
+ public object GetSuggestedValue(DependencyDescriptor descriptor)
+ {
+ return null;
+ }
}
}
\ No newline at end of file
diff --git a/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionConstants.cs b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionConstants.cs
index 064e02e9..51c9835e 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionConstants.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Xml/ObjectDefinitionConstants.cs
@@ -298,6 +298,11 @@ namespace Spring.Objects.Factory.Xml
///