Normally starting with 0 or 1, with indicating
+ /// greatest. Same order values will result in arbitrary positions for the affected
+ /// objects.
+ ///
Higher value can be interpreted as lower priority, consequently the first object
+ /// has highest priority.
+ ///
+ ///
+ ///
+ /// The order value.
+ ///
+ public int Order
+ {
+ get { return _order; }
+ private set { _order = value; }
+ }
+
+
+ ///
+ /// Callback that supplies the owning factory to an object instance.
+ ///
+ ///
+ /// Owning
+ /// (may not be ). The object can immediately
+ /// call methods on the factory.
+ ///
+ ///
+ ///
Invoked after population of normal object properties but before an init
+ /// callback like 's
+ ///
+ /// method or a custom init-method.
+ ///
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
new file mode 100644
index 00000000..3fdb1ffb
--- /dev/null
+++ b/src/Spring/Spring.Core/Objects/Factory/Attributes/QualifierAttribute.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Spring.Objects.Factory.Attributes
+{
+ ///
+ /// This annotation may be used on a field or parameter as a qualifier for
+ /// candidate beans when autowiring. It may also be used to annotate other
+ /// custom annotations that can then in turn be used as qualifiers.
+ ///
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
+ public class QualifierAttribute : Attribute
+ {
+ private readonly string _value;
+
+ ///
+ /// Instantiate a new qualifier with an empty name
+ ///
+ public QualifierAttribute()
+ {
+ _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 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 39e8358c..f891798a 100644
--- a/src/Spring/Spring.Core/Objects/Factory/Config/DependencyDescriptor.cs
+++ b/src/Spring/Spring.Core/Objects/Factory/Config/DependencyDescriptor.cs
@@ -20,6 +20,8 @@
using System;
using Spring.Core;
+using System.Reflection;
+using Spring.Objects.Factory.Attributes;
namespace Spring.Objects.Factory.Config
{
@@ -34,6 +36,10 @@ namespace Spring.Objects.Factory.Config
{
private MethodParameter methodParameter;
+ private PropertyInfo property;
+
+ private FieldInfo field;
+
private readonly bool required;
private readonly bool eager;
@@ -45,7 +51,8 @@ namespace Spring.Objects.Factory.Config
///
/// The MethodParameter to wrap.
/// if set to true if the dependency is required.
- public DependencyDescriptor(MethodParameter methodParameter, bool required) : this(methodParameter, required, true)
+ public DependencyDescriptor(MethodParameter methodParameter, bool required)
+ : this(methodParameter, required, true)
{
}
@@ -63,6 +70,55 @@ namespace Spring.Objects.Factory.Config
this.eager = eager;
}
+ ///
+ /// Create a new descriptor for a property.
+ /// Considers the dependency as 'eager'.
+ /// property to wrap
+ /// required whether the dependency is required
+ ///
+ public DependencyDescriptor(PropertyInfo property, bool required)
+ : this(property, required, true)
+ {
+ }
+
+ ///
+ /// Create a new descriptor for a property.
+ /// property to wrap
+ /// whether the dependency is required
+ /// whether this dependency is 'eager' in the sense of
+ /// eagerly resolving potential target beans for type matching
+ ///
+ public DependencyDescriptor(PropertyInfo property, bool required, bool eager)
+ {
+ this.property = property;
+ this.required = required;
+ this.eager = eager;
+ }
+
+ ///
+ /// Create a new descriptor for a field.
+ /// Considers the dependency as 'eager'.
+ /// field to wrap
+ /// whether the dependency is required
+ ///
+ public DependencyDescriptor(FieldInfo field, bool required)
+ : this(field, required, true)
+ {
+ }
+
+ ///
+ /// Create a new descriptor for a field.
+ /// field to wrap
+ /// whether the dependency is required
+ /// whether this dependency is 'eager' in the sense of
+ /// eagerly resolving potential target beans for type matching
+ ///
+ public DependencyDescriptor(FieldInfo field, bool required, bool eager)
+ {
+ this.field = field;
+ this.required = required;
+ this.eager = eager;
+ }
///
/// Gets a value indicating whether this dependency is required.
@@ -79,7 +135,17 @@ namespace Spring.Objects.Factory.Config
/// The type of the dependency (never null
public Type DependencyType
{
- get { return methodParameter.ParameterType; }
+ get
+ {
+ if (methodParameter != null)
+ return methodParameter.ParameterType;
+ if (property != null)
+ return property.PropertyType;
+ if (field != null)
+ return field.FieldType;
+
+ return null;
+ }
}
///
@@ -101,5 +167,41 @@ namespace Spring.Objects.Factory.Config
{
get { return methodParameter; }
}
+
+ ///
+ /// 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
+ {
+ if (methodParameter != null)
+ return methodParameter.ParameterName();
+ if (property != null)
+ return property.Name;
+ if (field != null)
+ return field.Name;
+
+ return "";
+ }
+ }
}
}
\ 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..d0a0cd02 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..ac65c8a0 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 dbd12dea..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);
}
@@ -1112,6 +1115,19 @@ namespace Spring.Objects.Factory.Support
IList autowiredObjectNames)
{
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();
@@ -1133,6 +1149,38 @@ namespace Spring.Objects.Factory.Support
}
return TypeConversionUtils.ConvertValueIfNecessary(type, matchingObjects.Values, null);
}
+ else if (type.IsGenericType &&
+ (type.GetGenericTypeDefinition() == typeof(IList<>) || type.GetGenericTypeDefinition() == typeof(Spring.Collections.Generic.ISet<>) ||
+ type.GetGenericTypeDefinition() == typeof(IDictionary<,>)))
+ {
+ var isDictionary = (type.GetGenericTypeDefinition() == typeof (IDictionary<,>));
+ var elementType = isDictionary ? type.GetGenericArguments()[1] : type.GetGenericArguments()[0];
+
+ if (isDictionary && type.GetGenericArguments()[0] != typeof(string))
+ throw new NoSuchObjectDefinitionException(type,
+ "expected first generic to be a string but is " + type.GetGenericArguments()[0]);
+
+ IDictionary matchingObjects = FindAutowireCandidates(objectName, elementType, descriptor);
+ if (matchingObjects.Count == 0)
+ {
+ if (descriptor.Required)
+ {
+ RaiseNoSuchObjectDefinitionException(elementType, "dictionary/list/set of " + elementType.FullName, descriptor);
+ }
+ return null;
+ }
+ if (autowiredObjectNames != null)
+ {
+ foreach (DictionaryEntry matchingObject in matchingObjects)
+ {
+ autowiredObjectNames.Add(matchingObject.Key);
+ }
+ }
+
+ return isDictionary
+ ? TypeConversionUtils.ConvertValueIfNecessary(type, matchingObjects, null)
+ : TypeConversionUtils.ConvertValueIfNecessary(type, matchingObjects.Values, null);
+ }
else if (typeof(ICollection).IsAssignableFrom(type) && type.IsInterface)
{
//TODO - handle generic types.
@@ -1155,9 +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)
@@ -1168,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
@@ -1188,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];
@@ -1201,6 +1325,7 @@ namespace Spring.Objects.Factory.Support
}
}
}
+
for (int i = 0; i < candidateNames.Count; i++)
{
string candidateName = candidateNames[i];
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
///