Add autowire by attribute for fields, properties, methods and constructors

This commit is contained in:
Thomas Trageser
2012-08-23 13:14:12 +01:00
parent e5c8a0396a
commit 773f3146da
14 changed files with 2501 additions and 4 deletions

View File

@@ -136,5 +136,13 @@ namespace Spring.Core
{
get { return constructorInfo; }
}
public Attribute[] GetParameterAttributes()
{
if (methodInfo != null)
return Attribute.GetCustomAttributes(methodInfo.GetParameters()[parameterIndex]);
else
return Attribute.GetCustomAttributes(constructorInfo.GetParameters()[parameterIndex]);
}
}
}

View File

@@ -0,0 +1,74 @@
#region License
/*
* Copyright © 2010-2011 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
{
/// <summary>
/// Marks a constructor, field, propery or config method as to be
/// autowired by Spring's dependency injection facilities.
///
/// Only one constructor (at max) of any given bean class may carry this
/// annotation, indicating the constructor to autowire when used as a Spring
/// bean. Such a constructor does not have to be public.
///
/// Fields are injected right after construction of a object, before any
/// config methods are invoked. Such a config field does not have to be public.
///
/// Config methods may have an arbitrary name and any number of arguments;
/// each of those arguments will be autowired with a matching bean in the
/// Spring container. Object property setter methods are effectively just
/// a special case of such a general config method. Such config methods
/// do not have to be public.
///
/// In the case of multiple argument methods, the 'required' parameter is
/// applicable for all arguments.
///
/// In case of a {@link java.util.Collection} or {@link java.util.Map}
/// dependency type, the container will autowire all beans matching the
/// declared value type. In case of a Map, the keys must be declared as
/// type String and will be resolved to the corresponding bean names.
///
/// Note that actual injection is performed through a
/// {@link org.springframework.beans.factory.config.BeanPostProcessor
/// BeanPostProcessor} which in turn means that you <em>cannot</em>
/// use {@code @Autowired} to inject references into
/// {@link org.springframework.beans.factory.config.BeanPostProcessor
/// BeanPostProcessor} or
/// {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessor}
/// types. Please consult the javadoc for the {@link AutowiredAnnotationBeanPostProcessor}
/// class (which, by default, checks for the presence of this annotation).
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Constructor)]
public class AutowiredAttribute : Attribute
{
private bool _required = true;
/// <summary>
/// Defines it Autowired PostProcessor should fail if object is not set
/// </summary>
public bool Required
{
get { return _required; }
set { _required = value; }
}
}
}

View File

@@ -0,0 +1,639 @@
#region License
/*
* Copyright © 2010-2011 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;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Linq;
using Spring.Collections;
using Spring.Core;
using Spring.Objects.Factory.Config;
using Common.Logging;
using Spring.Objects.Factory.Support;
namespace Spring.Objects.Factory.Attributes
{
/// <summary>
/// <see cref="IInstantiationAwareObjectPostProcessor"/> implementation
/// that autowires annotated fields, properties and arbitrary config methods.
/// Such members to be injected are detected through an attribute: by default,
/// Spring's <see cref="AutowiredAttribute"/>.
///
/// Only one constructor (at max) of any given bean class may carry this
/// annotation with the 'required' parameter set to <code>true</code>,
/// indicating <i>the</i> constructor to autowire when used as a Spring bean.
/// If multiple <i>non-required</i> constructors carry the annotation, they
/// will be considered as candidates for autowiring. The constructor with
/// the greatest number of dependencies that can be satisfied by matching
/// beans in the Spring container will be chosen. If none of the candidates
/// can be satisfied, then a default constructor (if present) will be used.
/// An annotated constructor does not have to be public.
///
/// Fields are injected right after construction of a bean, before any
/// config methods are invoked. Such a config field does not have to be public.
///
/// Config methods may have an arbitrary name and any number of arguments; each of
/// those arguments will be autowired with a matching bean in the Spring container.
/// Bean property setter methods are effectively just a special case of such a
/// general config method. Config methods do not have to be public.
///
/// Note: A default AutowiredAttributeObjectPostProcessor will be registered
/// by the "context:annotation-config" and "context:component-scan" XML tags.
/// Remove or turn off the default annotation configuration there if you intend
/// to specify a custom AutowiredAnnotationBeanPostProcessor bean definition.
/// <b>NOTE:</b> Annotation injection will be performed <i>before</i> XML injection;
/// thus the latter configuration will override the former for properties wired through
/// both approaches.
/// </summary>
public class AutowiredAttributeObjectPostProcessor : InstantiationAwareObjectPostProcessorAdapter,
IObjectFactoryAware, IOrdered
{
private static readonly ILog Logger = LogManager.GetLogger<AutowiredAttributeObjectPostProcessor>();
private int _order = int.MaxValue - 2;
private static IConfigurableListableObjectFactory _objectFactory;
private SynchronizedHashtable _candidateConstructorsCache = new SynchronizedHashtable();
private readonly IDictionary<Type, InjectionMetadata> _injectionMetadataCache =
new Dictionary<Type, InjectionMetadata>();
private Type _autowiredPropertyType = typeof (AutowiredAttribute);
/// <summary>
/// Return the order value of this object, where a higher value means greater in
/// terms of sorting.
/// </summary>
/// <remarks>
/// <p>Normally starting with 0 or 1, with <see cref="F:System.Int32.MaxValue"/> indicating
/// greatest. Same order values will result in arbitrary positions for the affected
/// objects.
/// </p><p>Higher value can be interpreted as lower priority, consequently the first object
/// has highest priority.
/// </p>
/// </remarks>
/// <returns>
/// The order value.
/// </returns>
public int Order
{
get { return _order; }
private set { _order = value; }
}
/// <summary>
/// Callback that supplies the owning factory to an object instance.
/// </summary>
/// <value>
/// Owning <see cref="T:Spring.Objects.Factory.IObjectFactory"/>
/// (may not be <see langword="null"/>). The object can immediately
/// call methods on the factory.
/// </value>
/// <remarks>
/// <p>Invoked after population of normal object properties but before an init
/// callback like <see cref="T:Spring.Objects.Factory.IInitializingObject"/>'s
/// <see cref="M:Spring.Objects.Factory.IInitializingObject.AfterPropertiesSet"/>
/// method or a custom init-method.
/// </p>
/// </remarks>
/// <exception cref="T:Spring.Objects.ObjectsException">In case of initialization errors.
/// </exception>
public IObjectFactory ObjectFactory
{
set { _objectFactory = (IConfigurableListableObjectFactory) value; }
}
/// <summary>
/// Sets the used AutowiredAttributeType during the scan
/// </summary>
public Type AutowiredAttributeType
{
get { return _autowiredPropertyType; }
set { _autowiredPropertyType = value; }
}
/// <summary>
/// Determines the candidate constructors to use for the given object.
/// </summary>
/// <param name="objectType">The raw Type of the object.</param>
/// <param name="objectName">Name of the object.</param>
/// <returns>The candidate constructors, or <code>null</code> if none specified</returns>
/// <exception cref="ObjectsException">in case of errors</exception>
public override ConstructorInfo[] DetermineCandidateConstructors(Type objectType, string objectName)
{
// Quick check on the concurrent map first, with minimal locking.
ConstructorInfo[] candidateConstructors = _candidateConstructorsCache.ContainsKey(objectType)
? (ConstructorInfo[])_candidateConstructorsCache[objectType]
: null;
if (candidateConstructors == null)
{
lock (_candidateConstructorsCache)
{
candidateConstructors = _candidateConstructorsCache.ContainsKey(objectType)
? (ConstructorInfo[])_candidateConstructorsCache[objectType]
: null;
if (candidateConstructors == null)
{
ConstructorInfo[] rawCandidates = objectType.GetConstructors();
IList<ConstructorInfo> candidates = new List<ConstructorInfo>(rawCandidates.Length);
ConstructorInfo requiredConstructor = null;
ConstructorInfo defaultConstructor = null;
foreach(var candidate in rawCandidates)
{
AutowiredAttribute attr =
Attribute.GetCustomAttribute(candidate, typeof (AutowiredAttribute)) as AutowiredAttribute;
if (attr != null)
{
if (requiredConstructor != null) {
throw new ObjectCreationException("Invalid autowire-marked constructor: " + candidate +
". Found another constructor with 'required' Autowired annotation: " +
requiredConstructor);
}
if (candidate.GetParameters().Length == 0)
{
throw new InvalidOperationException("Autowired annotation requires at least one argument: " + candidate);
}
if (attr.Required)
{
if (candidates.Count > 0)
{
throw new ObjectCreationException(
"Invalid autowire-marked constructors: " + candidates +
". Found another constructor with 'required' Autowired annotation: " +
requiredConstructor);
}
requiredConstructor = candidate;
}
candidates.Add(candidate);
}
else if (candidate.GetParameters().Length == 0)
{
defaultConstructor = candidate;
}
}
if (candidates.Count > 0)
{
// Add default constructor to list of optional constructors, as fallback.
if (requiredConstructor == null && defaultConstructor != null) {
candidates.Add(defaultConstructor);
}
candidateConstructors = candidates.ToArray();
}
else {
candidateConstructors = new ConstructorInfo[0];
}
_candidateConstructorsCache.Add(objectType, candidateConstructors);
}
}
}
return (candidateConstructors.Length > 0 ? candidateConstructors : null);
}
/// <summary>
/// Finds autowire candidates and verifies them
/// </summary>
/// <param name="objectType">
/// The <see cref="System.Type"/> of the target object that is to be
/// instantiated.
/// </param>
/// <param name="objectName">
/// The name of the target object.
/// </param>
/// <returns>
/// The object to expose instead of a default instance of the target
/// object.
/// </returns>
/// <exception cref="Spring.Objects.ObjectsException">
/// In the case of any errors.
/// </exception>
/// <seealso cref="Spring.Objects.Factory.Support.AbstractObjectDefinition.HasObjectType"/>
/// <seealso cref="Spring.Objects.Factory.Support.IConfigurableObjectDefinition.FactoryMethodName"/>
public override object PostProcessBeforeInstantiation(Type objectType, string objectName)
{
var objectDefinition = _objectFactory.GetObjectDefinition(objectName) as RootObjectDefinition;
if (objectType != null)
{
var metadata = FindAutowiringMetadata(objectType);
metadata.CheckConfigMembers(objectDefinition);
}
return null;
}
/// <summary>
/// Injects autoried annotated properties, fields, methods into objectInstance
/// </summary>
/// <param name="pvs"></param>
/// <param name="pis"></param>
/// <param name="objectInstance"></param>
/// <param name="objectName">Name of the object.</param>
/// <returns>The actual property values to apply to the given object (can be the
/// passed-in PropertyValues instances0 or null to skip property population.</returns>
public override IPropertyValues PostProcessPropertyValues(IPropertyValues pvs, IList<PropertyInfo> pis,
object objectInstance, string objectName)
{
var metadata = FindAutowiringMetadata(objectInstance.GetType());
try
{
metadata.Inject(objectInstance, objectName, pvs);
}
catch (Exception ex)
{
throw new ObjectCreationException(objectName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
private InjectionMetadata FindAutowiringMetadata(Type objectType)
{
// Quick check on the concurrent map first, with minimal locking.
InjectionMetadata metadata = null;
if (_injectionMetadataCache.ContainsKey(objectType))
metadata = _injectionMetadataCache[objectType];
if (metadata == null)
{
lock (_injectionMetadataCache)
{
if (!_injectionMetadataCache.ContainsKey(objectType))
{
metadata = BuildAutowiringMetadata(objectType);
_injectionMetadataCache.Add(objectType, metadata);
}
}
}
return metadata;
}
private InjectionMetadata BuildAutowiringMetadata(Type objectType)
{
var elements = new List<InjectionMetadata.InjectedElement>();
do
{
var currElements = new List<InjectionMetadata.InjectedElement>();
foreach (
var property in
objectType.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance))
{
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)
{
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));
}
}
elements.InsertRange(0, currElements);
objectType = objectType.BaseType;
} while (objectType != null && objectType != typeof (Object));
return new InjectionMetadata(objectType, elements);
}
/// <summary>
/// Register the specified bean as dependent on the autowired beans.
/// </summary>
private static void RegisterDependentObjects(string objectName, IList autowiredObjectNames)
{
if (objectName == null) return;
var objectDefinition = _objectFactory.GetObjectDefinition(objectName) as RootObjectDefinition;
IList<string> dependsOn = new List<string>(objectDefinition.DependsOn);
foreach (var name in autowiredObjectNames)
{
var autowiredObjectName = name as string;
if (!dependsOn.Contains(autowiredObjectName))
{
dependsOn.Add(autowiredObjectName);
Logger.Debug(
m =>
m("Autowiring by type from object name '{0}' to object named '{1}'", objectName,
autowiredObjectName));
}
}
objectDefinition.DependsOn = dependsOn;
}
private static object ResolvedCachedArgument(String objectName, Object cachedArgument)
{
if (cachedArgument is DependencyDescriptor)
{
var descriptor = (DependencyDescriptor) cachedArgument;
return _objectFactory.ResolveDependency(descriptor, objectName, null);
}
else if (cachedArgument is RuntimeObjectReference)
{
return _objectFactory.GetObject(((RuntimeObjectReference) cachedArgument).ObjectName);
}
else
{
return cachedArgument;
}
}
/// <summary>
/// Class representing injection information about an annotated field.
/// </summary>
private class AutowiredPropertyElement : InjectionMetadata.InjectedElement
{
private readonly bool _required;
private bool _cached = false;
private Object _cachedFieldValue;
public AutowiredPropertyElement(PropertyInfo property, bool required)
: base(property)
{
_required = required;
}
public override void Inject(Object instance, String objectName, IPropertyValues pvs)
{
var property = (PropertyInfo) _member;
try
{
Object value;
if (_cached)
{
value = ResolvedCachedArgument(objectName, _cachedFieldValue);
}
else
{
var descriptor = new DependencyDescriptor(property, _required);
IList autowiredObjectNames = new ArrayList();
value = _objectFactory.ResolveDependency(descriptor, objectName, autowiredObjectNames);
lock (this)
{
if (!_cached)
{
if (value != null || _required)
{
_cachedFieldValue = descriptor;
RegisterDependentObjects(objectName, autowiredObjectNames);
if (autowiredObjectNames.Count == 1)
{
var autowiredBeanName = autowiredObjectNames[0] as string;
if (_objectFactory.ContainsObject(autowiredBeanName))
{
if (_objectFactory.IsTypeMatch(autowiredBeanName, property.GetType()))
{
_cachedFieldValue = new RuntimeObjectReference(autowiredBeanName);
}
}
}
}
else
{
_cachedFieldValue = null;
}
_cached = true;
}
}
}
if (value != null)
{
property.SetValue(instance, value, null);
}
}
catch (Exception ex)
{
throw new ObjectCreationException("Could not autowire property: " + property, ex);
}
}
}
/// <summary>
/// Class representing injection information about an annotated field.
/// </summary>
private class AutowiredFieldElement : InjectionMetadata.InjectedElement
{
private readonly bool _required;
private bool _cached = false;
private Object _cachedFieldValue;
public AutowiredFieldElement(FieldInfo field, bool required)
: base(field)
{
_required = required;
}
public override void Inject(Object instance, String objectName, IPropertyValues pvs)
{
var field = (FieldInfo) _member;
try
{
Object value;
if (_cached)
{
value = ResolvedCachedArgument(objectName, _cachedFieldValue);
}
else
{
var descriptor = new DependencyDescriptor(field, _required);
IList autowiredObjectNames = new ArrayList();
value = _objectFactory.ResolveDependency(descriptor, objectName, autowiredObjectNames);
lock (this)
{
if (!_cached)
{
if (value != null || _required)
{
_cachedFieldValue = descriptor;
RegisterDependentObjects(objectName, autowiredObjectNames);
if (autowiredObjectNames.Count == 1)
{
var autowiredBeanName = autowiredObjectNames[0] as string;
if (_objectFactory.ContainsObject(autowiredBeanName))
{
if (_objectFactory.IsTypeMatch(autowiredBeanName, field.GetType()))
{
_cachedFieldValue = new RuntimeObjectReference(autowiredBeanName);
}
}
}
}
else
{
_cachedFieldValue = null;
}
_cached = true;
}
}
}
if (value != null)
{
field.SetValue(instance, value);
}
}
catch (Exception ex)
{
throw new ObjectCreationException("Could not autowire field: " + field, ex);
}
}
}
///
/// Class representing injection information about an annotated method.
///
private class AutowiredMethodElement : InjectionMetadata.InjectedElement
{
private readonly bool _required;
private bool _cached = false;
private volatile Object[] _cachedMethodArguments;
public AutowiredMethodElement(MethodInfo method, bool required)
: base(method)
{
_required = required;
}
public override void Inject(Object target, string objectName, IPropertyValues pvs)
{
MethodInfo method = _member as MethodInfo;
try
{
Object[] arguments;
if (_cached)
{
arguments = ResolveCachedArguments(objectName);
}
else
{
Type[] paramTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();
arguments = new Object[paramTypes.Length];
var descriptors = new DependencyDescriptor[paramTypes.Length];
IList autowiredBeanNames = new ArrayList();
for (int i = 0; i < arguments.Length; i++)
{
MethodParameter methodParam = new MethodParameter(method, i);
descriptors[i] = new DependencyDescriptor(methodParam, _required);
arguments[i] = _objectFactory.ResolveDependency(descriptors[i], objectName,
autowiredBeanNames);
if (arguments[i] == null && !_required)
{
arguments = null;
break;
}
}
lock (this)
{
if (!_cached)
{
if (arguments != null)
{
_cachedMethodArguments = new Object[arguments.Length];
for (int i = 0; i < arguments.Length; i++)
{
_cachedMethodArguments[i] = descriptors[i];
}
RegisterDependentObjects(objectName, autowiredBeanNames);
if (autowiredBeanNames.Count == paramTypes.Length)
{
for (int i = 0; i < paramTypes.Length; i++)
{
string autowiredBeanName = autowiredBeanNames[i] as string;
if (_objectFactory.ContainsObject(autowiredBeanName))
{
if (_objectFactory.IsTypeMatch(autowiredBeanName, paramTypes[i]))
{
_cachedMethodArguments[i] =
new RuntimeObjectReference(autowiredBeanName);
}
}
}
}
}
else
{
_cachedMethodArguments = null;
}
_cached = true;
}
}
}
if (arguments != null)
{
method.Invoke(target, arguments);
}
}
catch (Exception ex)
{
throw new ObjectCreationException("Could not autowire method: " + method, ex);
}
}
private Object[] ResolveCachedArguments(string objectName)
{
if (_cachedMethodArguments == null)
{
return null;
}
Object[] arguments = new Object[_cachedMethodArguments.Length];
for (int i = 0; i < arguments.Length; i++)
{
arguments[i] = ResolvedCachedArgument(objectName, _cachedMethodArguments[i]);
}
return arguments;
}
}
}
}

View File

@@ -0,0 +1,112 @@
#region License
/*
* Copyright © 2010-2011 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.Reflection;
using Common.Logging;
using Spring.Objects;
using Spring.Objects.Factory.Support;
namespace Spring.Objects.Factory.Attributes
{
/// <summary>
/// Internal class for managing injection metadata.
/// Not intended for direct use in applications.
/// </summary>
public class InjectionMetadata
{
private static readonly ILog Logger = LogManager.GetLogger<InjectionMetadata>();
private readonly IList<InjectedElement> _injectedElements;
/// <summary>
/// </summary>
/// <param name="targetType"></param>
/// <param name="elements"></param>
public InjectionMetadata(Type targetType, IList<InjectedElement> elements)
{
_injectedElements = new List<InjectedElement>();
if (elements.Count > 0)
{
foreach (var element in elements)
{
Logger.Debug(m => m("Found injected element on class [" + targetType.Name + "]: " + element));
_injectedElements.Add(element);
}
}
}
/// <summary>
/// </summary>
/// <param name="objectDefinition"></param>
public void CheckConfigMembers(RootObjectDefinition objectDefinition)
{
}
/// <summary>
/// Inject values for members into object instance
/// </summary>
/// <param name="instance"></param>
/// <param name="objectName"></param>
/// <param name="pvs"></param>
public void Inject(Object instance, string objectName, IPropertyValues pvs)
{
if (_injectedElements.Count == 0)
return;
foreach(var element in _injectedElements)
{
Logger.Debug(m => m("Processing injected method of bean '{0}': {1}", objectName, element));
element.Inject(instance, objectName, pvs);
}
}
/// <summary>
/// Represents an element that needs to be injected
/// </summary>
public abstract class InjectedElement
{
/// <summary>
/// The Property, field, method or constructor info
/// </summary>
protected readonly MemberInfo _member;
/// <summary>
/// Instantiates a new inject element
/// </summary>
/// <param name="member"></param>
protected InjectedElement(MemberInfo member)
{
_member = member;
}
/// <summary>
/// Ececuted to inject value to associated memeber info
/// </summary>
/// <param name="target"></param>
/// <param name="requestingObjectName"></param>
/// <param name="pvs"></param>
public abstract void Inject(object target, string requestingObjectName, IPropertyValues pvs);
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Spring.Objects.Factory.Attributes
{
/// <summary>
/// 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.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public class QualifierAttribute : Attribute
{
private readonly string _name;
/// <summary>
/// Instantiate a new qualifier type
/// </summary>
/// <param name="name">name to use as qualifier</param>
public QualifierAttribute(string name)
{
_name = name;
}
/// <summary>
/// Gets the name associated with this qualifier
/// </summary>
public string Name { get { return _name; } }
}
}

View File

@@ -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
/// </summary>
/// <param name="methodParameter">The MethodParameter to wrap.</param>
/// <param name="required">if set to <c>true</c> if the dependency is required.</param>
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;
}
/// <summary>
/// Create a new descriptor for a property.
/// Considers the dependency as 'eager'.
/// <param name="property">property to wrap</param>
/// <param name="required">required whether the dependency is required</param>
/// </summary>
public DependencyDescriptor(PropertyInfo property, bool required)
: this(property, required, true)
{
}
/// <summary>
/// Create a new descriptor for a property.
/// <param name="property">property to wrap</param>
/// <param name="required ">whether the dependency is required</param>
/// <param name="eager">whether this dependency is 'eager' in the sense of</param>
/// eagerly resolving potential target beans for type matching
/// </summary>
public DependencyDescriptor(PropertyInfo property, bool required, bool eager)
{
this.property = property;
this.required = required;
this.eager = eager;
}
/// <summary>
/// Create a new descriptor for a field.
/// Considers the dependency as 'eager'.
/// <param name="field">field to wrap</param>
/// <param name="required">whether the dependency is required</param>
/// </summary>
public DependencyDescriptor(FieldInfo field, bool required)
: this(field, required, true)
{
}
/// <summary>
/// Create a new descriptor for a field.
/// <param name="field">field to wrap</param>
/// <param name="required ">whether the dependency is required</param>
/// <param name="eager">whether this dependency is 'eager' in the sense of</param>
/// eagerly resolving potential target beans for type matching
/// </summary>
public DependencyDescriptor(FieldInfo field, bool required, bool eager)
{
this.field = field;
this.required = required;
this.eager = eager;
}
/// <summary>
/// Gets a value indicating whether this dependency is required.
@@ -79,7 +135,17 @@ namespace Spring.Objects.Factory.Config
/// <value>The type of the dependency (never <code>null</code></value>
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;
}
}
/// <summary>
@@ -101,5 +167,83 @@ namespace Spring.Objects.Factory.Config
{
get { return methodParameter; }
}
public string Name
{
get
{
if (methodParameter != null)
return methodParameter.ParameterName();
if (property != null)
return property.Name;
if (field != null)
return field.Name;
return "";
}
}
/// <summary>
/// Determine whether the given dependency carries a value annotation.
/// </summary>
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;
}
/// <summary>
/// Get the qualifier name if exists
/// </summary>
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);
}
}
}

View File

@@ -1111,6 +1111,21 @@ 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;
if (type.IsArray)
{
@@ -1133,6 +1148,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.
@@ -1142,6 +1189,20 @@ 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)
@@ -1155,9 +1216,8 @@ namespace Spring.Objects.Factory.Support
}
if (matchingObjects.Count > 1)
{
throw new NoSuchObjectDefinitionException(type,
"expected single matching object but found " + matchingObjects.Count + ": " + matchingObjects);
"expected single matching object but found " + matchingObjects.Count + ": " + matchingObjects);
}
DictionaryEntry entry = (DictionaryEntry)ObjectUtils.EnumerateFirstElement(matchingObjects);
if (autowiredObjectNames != null)
@@ -1201,6 +1261,7 @@ namespace Spring.Objects.Factory.Support
}
}
}
for (int i = 0; i < candidateNames.Count; i++)
{
string candidateName = candidateNames[i];

View File

@@ -675,6 +675,10 @@
<Compile Include="Globalization\Resource.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Objects\Factory\Attributes\AutowiredAttribute.cs" />
<Compile Include="Objects\Factory\Attributes\AutowiredAttributeObjectPostProcessor.cs" />
<Compile Include="Objects\Factory\Attributes\InjectionMetadata.cs" />
<Compile Include="Objects\Factory\Attributes\QualifierAttribute.cs" />
<Compile Include="Objects\Factory\Attributes\RequiredAttribute.cs" />
<Compile Include="Objects\Factory\Attributes\RequiredAttributeObjectPostProcessor.cs" />
<Compile Include="Objects\Factory\Config\AbstractConfigurer.cs" />

View File

@@ -0,0 +1,160 @@
#region License
/*
* Copyright © 2002-2011 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.Collections.Generic;
using NUnit.Framework;
using Spring.Context.Support;
using Spring.Objects.Factory.Config;
using Spring.Objects.Factory.Support;
namespace Spring.Objects.Factory.Attributes
{
[TestFixture]
public class AutowireAttributeCollectionTests
{
private GenericApplicationContext _applicationContext;
[SetUp]
public void Setup()
{
_applicationContext = new GenericApplicationContext();
var objDef = new RootObjectDefinition(typeof(AutowiredAttributeObjectPostProcessor));
objDef.Role = ObjectRole.ROLE_INFRASTRUCTURE;
_applicationContext.RegisterObjectDefinition("AutowiredAttributeObjectPostProcessor", objDef);
objDef = new RootObjectDefinition(typeof(ColFoo1));
_applicationContext.RegisterObjectDefinition("Foo1", objDef);
objDef = new RootObjectDefinition(typeof(ColFoo2));
_applicationContext.RegisterObjectDefinition("Foo2", objDef);
objDef = new RootObjectDefinition(typeof(ColTestObject1));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("ColTestObject1", objDef);
objDef = new RootObjectDefinition(typeof(ColTestObject2));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("ColTestObject2", objDef);
objDef = new RootObjectDefinition(typeof(ColTestObject3));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("ColTestObject3", objDef);
objDef = new RootObjectDefinition(typeof(ColTestObject4));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("ColTestObject4", objDef);
_applicationContext.Refresh();
}
[Test]
public void InjectAListOfObjects()
{
var testObj = (ColTestObject1)_applicationContext.GetObject("ColTestObject1");
var objDef = _applicationContext.ObjectFactory.GetObjectDefinition("ColTestObject1");
Assert.That(testObj.Count, Is.EqualTo(2));
Assert.That(objDef.DependsOn.Count, Is.EqualTo(2));
}
[Test]
public void InjectASetOfObjects()
{
var testObj = (ColTestObject2)_applicationContext.GetObject("ColTestObject2");
Assert.That(testObj.Count, Is.EqualTo(2));
}
[Test]
public void InjectADictionaryOfObjects()
{
var testObj = (ColTestObject3)_applicationContext.GetObject("ColTestObject3");
Assert.That(testObj.Count, Is.EqualTo(2));
}
[Test]
public void InjectAnArrayOfObjects()
{
var testObj = (ColTestObject4)_applicationContext.GetObject("ColTestObject4");
Assert.That(testObj.Count, Is.EqualTo(2));
}
}
#region Test Objects
public interface IColFoo
{
string Name();
}
public class ColFoo1 : IColFoo
{
public string Name()
{
return "Foo1";
}
}
public class ColFoo2 : IColFoo
{
public string Name()
{
return "Foo2";
}
}
public class ColTestObject1
{
[Autowired]
private IList<IColFoo> _col;
public int Count { get { return _col.Count; } }
}
public class ColTestObject2
{
[Autowired]
private Spring.Collections.Generic.ISet<IColFoo> _col;
public int Count { get { return _col.Count; } }
}
public class ColTestObject3
{
[Autowired]
private IDictionary<string, IColFoo> _col;
public int Count { get { return _col.Count; } }
}
public class ColTestObject4
{
[Autowired]
private IColFoo[] _col;
public int Count { get { return _col.Length; } }
}
#endregion
}

View File

@@ -0,0 +1,343 @@
#region License
/*
* Copyright © 2002-2011 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.Linq;
using System.Text;
using NUnit.Framework;
using Spring.Context.Support;
using Spring.Objects.Factory.Config;
using Spring.Objects.Factory.Support;
namespace Spring.Objects.Factory.Attributes
{
[TestFixture]
public class AutowireAttributeConstructorTest
{
private GenericApplicationContext _applicationContext;
[SetUp]
public void Setup()
{
_applicationContext = new GenericApplicationContext();
var objDef = new RootObjectDefinition(typeof(AutowiredAttributeObjectPostProcessor));
objDef.Role = ObjectRole.ROLE_INFRASTRUCTURE;
_applicationContext.RegisterObjectDefinition("AutowiredAttributeObjectPostProcessor", objDef);
objDef = new RootObjectDefinition(typeof(ConsSimple));
_applicationContext.ObjectFactory.RegisterObjectDefinition("ConsSimple", objDef);
objDef = new RootObjectDefinition(typeof(ConsAdvanced));
_applicationContext.ObjectFactory.RegisterObjectDefinition("ConsAdvanced", objDef);
objDef = new RootObjectDefinition(typeof(ConsHello));
_applicationContext.ObjectFactory.RegisterObjectDefinition("ConsHello", objDef);
objDef = new RootObjectDefinition(typeof(ConsCiao));
_applicationContext.ObjectFactory.RegisterObjectDefinition("ConsCiao", objDef);
objDef = new RootObjectDefinition(typeof(ConsTestObject1));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("ConsTestObject1", objDef);
objDef = new RootObjectDefinition(typeof(ConsTestObject2));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("ConsTestObject2", objDef);
objDef = new RootObjectDefinition(typeof(ConsTestObject3));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("ConsTestObject3", objDef);
objDef = new RootObjectDefinition(typeof(ConsTestObject4));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("ConsTestObject4", objDef);
objDef = new RootObjectDefinition(typeof(ConsTestObject5));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("ConsTestObject5", objDef);
objDef = new RootObjectDefinition(typeof(ConsTestObject6));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("ConsTestObject6", objDef);
objDef = new RootObjectDefinition(typeof(ConsTestObject7));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("ConsTestObject7", objDef);
_applicationContext.Refresh();
}
[Test]
public void ConstructorWithInjectParameter()
{
var testObj = (ConsTestObject1)_applicationContext.GetObject("ConsTestObject1");
Assert.That(testObj.IsSet(), Is.True);
}
[Test]
public void SelectInjectConstructorOverDefault()
{
var testObj = (ConsTestObject2)_applicationContext.GetObject("ConsTestObject2");
Assert.That(testObj.IsSet(), Is.True);
}
[Test]
public void SelectConstructorWithMostParamters()
{
var testObj = (ConsTestObject3)_applicationContext.GetObject("ConsTestObject3");
Assert.That(testObj.IsSet(), Is.True);
}
[Test]
public void FailIfParameterAreRequired()
{
Exception ex = null;
try
{
var testObj = (ConsTestObject4)_applicationContext.GetObject("ConsTestObject4");
}
catch (Exception e) { ex = e; }
Assert.That(ex, Is.Not.Null, "Exception should be thrown");
Assert.That(ex.Message, Is.StringContaining("Unsatisfied dependency expressed"));
}
[Test]
public void InjectCollection()
{
var testObj = (ConsTestObject5)_applicationContext.GetObject("ConsTestObject5");
Assert.That(testObj.IsSet(), Is.True);
Assert.That(testObj.ObjectCount(), Is.EqualTo(2));
}
[Test]
public void SetParameterByParameterName()
{
var testObj = (ConsTestObject6)_applicationContext.GetObject("ConsTestObject6");
Assert.That(testObj.IsSet(), Is.True);
}
[Test]
public void SetParameterByQualifier()
{
var testObj = (ConsTestObject7)_applicationContext.GetObject("ConsTestObject7");
Assert.That(testObj.IsSet(), Is.True);
Assert.That(testObj.CorrectObject(), Is.True);
}
}
#region Test Objects
public interface IConsSimple
{
string Message();
}
public class ConsSimple : IConsSimple
{
public string Message()
{
return "ok";
}
}
public interface IConsAdvanced
{
string Message();
}
public class ConsAdvanced : IConsAdvanced
{
public string Message()
{
return "ok";
}
}
public interface INotSet
{
}
public interface IConsCol
{
string Message();
}
public class ConsHello : IConsCol
{
public string Message()
{
return "hello";
}
}
public class ConsCiao : IConsCol
{
public string Message()
{
return "ciao";
}
}
public class ConsTestObject1
{
private IConsSimple _consSimple;
[Autowired]
public ConsTestObject1(IConsSimple consSimple)
{
_consSimple = consSimple;
}
public bool IsSet()
{
return _consSimple != null;
}
}
public class ConsTestObject2
{
private IConsSimple _consSimple;
public ConsTestObject2()
{
}
[Autowired]
public ConsTestObject2(IConsSimple consSimple)
{
_consSimple = consSimple;
}
public bool IsSet()
{
return _consSimple != null;
}
}
public class ConsTestObject3
{
private IConsSimple _consSimple;
private IConsAdvanced _consAdvanced;
[Autowired(Required = false)]
public ConsTestObject3(IConsSimple consSimple)
{
_consSimple = consSimple;
}
[Autowired(Required = false)]
public ConsTestObject3(IConsSimple consSimple, IConsAdvanced consAdvanced)
{
_consSimple = consSimple;
_consAdvanced = consAdvanced;
}
public bool IsSet()
{
return _consSimple != null && _consAdvanced != null;
}
}
public class ConsTestObject4
{
private INotSet _notSet;
[Autowired]
public ConsTestObject4(INotSet notSet)
{
_notSet = notSet;
}
public bool IsSet()
{
return _notSet != null;
}
}
public class ConsTestObject5
{
private IDictionary<string,IConsCol> _consCol;
[Autowired]
public ConsTestObject5(IDictionary<string, IConsCol> consCol)
{
_consCol = consCol;
}
public bool IsSet()
{
return _consCol != null;
}
public int ObjectCount()
{
return _consCol.Count;
}
}
public class ConsTestObject6
{
private IConsCol _consHello;
[Autowired]
public ConsTestObject6(IConsCol consHello)
{
_consHello = consHello;
}
public bool IsSet()
{
return _consHello != null;
}
}
public class ConsTestObject7
{
private IConsCol _consHello;
[Autowired]
public ConsTestObject7([Qualifier("ConsCiao")] IConsCol consHello)
{
_consHello = consHello;
}
public bool IsSet()
{
return _consHello != null;
}
public bool CorrectObject()
{
return _consHello.Message() == "ciao";
}
}
#endregion
}

View File

@@ -0,0 +1,290 @@
#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 NUnit.Framework;
using Spring.Context.Support;
using Spring.Objects.Factory.Config;
using Spring.Objects.Factory.Support;
namespace Spring.Objects.Factory.Attributes
{
[TestFixture]
public class AutowireAttributeFieldTest
{
private GenericApplicationContext _applicationContext;
[SetUp]
public void Setup()
{
_applicationContext = new GenericApplicationContext();
var objDef = new RootObjectDefinition(typeof(AutowiredAttributeObjectPostProcessor));
objDef.Role = ObjectRole.ROLE_INFRASTRUCTURE;
_applicationContext.RegisterObjectDefinition("AutowiredAttributeObjectPostProcessor", objDef);
objDef = new RootObjectDefinition(typeof(FldFooImpl));
_applicationContext.RegisterObjectDefinition("FldFoo", objDef);
objDef = new RootObjectDefinition(typeof(FldHello));
_applicationContext.RegisterObjectDefinition("FldHello", objDef);
objDef = new RootObjectDefinition(typeof(FldCioa));
_applicationContext.RegisterObjectDefinition("FldCioa", objDef);
objDef = new RootObjectDefinition(typeof(FldHola));
_applicationContext.RegisterObjectDefinition("FldHola", objDef);
objDef = new RootObjectDefinition(typeof(FldTestObject1));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("FldTestObject1", objDef);
objDef = new RootObjectDefinition(typeof(FldTestObject2));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("FldTestObject2", objDef);
objDef = new RootObjectDefinition(typeof(FldTestObject3));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("FldTestObject3", objDef);
objDef = new RootObjectDefinition(typeof(FldTestObject4));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("FldTestObject4", objDef);
objDef = new RootObjectDefinition(typeof(FldTestObject5));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("FldTestObject5", objDef);
objDef = new RootObjectDefinition(typeof(FldTestObject6));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("FldTestObject6", objDef);
objDef = new RootObjectDefinition(typeof(FldTestObject7));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("FldTestObject7", objDef);
_applicationContext.Refresh();
}
[Test]
public void InjectPropertyBasedOnFieldType()
{
var testObj = (FldTestObject1)_applicationContext.GetObject("FldTestObject1");
var objectDefinition = _applicationContext.ObjectFactory.GetObjectDefinition("FldTestObject1");
Assert.That(testObj.Test(), Is.EqualTo("foo"));
Assert.That(objectDefinition.DependsOn.Count, Is.EqualTo(1));
}
[Test]
public void WithTwoTypesRegisteredAndNoNameShouldFail()
{
Assert.That(delegate { var testObj = (FldTestObject2)_applicationContext.GetObject("FldTestObject2"); }, Throws.Exception.TypeOf<ObjectCreationException>());
}
[Test]
public void WithQualifierName()
{
var testObj = (FldTestObject3)_applicationContext.GetObject("FldTestObject3");
Assert.That(testObj.Say(), Is.EqualTo("cioa"));
}
[Test]
public void WithTwoTypesAndNoQualifierUsePopertyName()
{
var testObj = (FldTestObject4)_applicationContext.GetObject("FldTestObject4");
Assert.That(testObj.Say(), Is.EqualTo("cioa"));
}
[Test]
public void FailIfTypeCantBeResolved()
{
Exception ex = null;
try
{
var testObj = (FldTestObject5)_applicationContext.GetObject("FldTestObject5");
}
catch (Exception e) { ex = e; }
Assert.That(ex, Is.Not.Null, "Should throw an exception");
Assert.That(ex.Message, Is.StringContaining("Injection of autowired dependencies failed"));
}
[Test]
public void InjectedObjectAssignedToTwoInterfaces()
{
var testObj = (FldTestObject6)_applicationContext.GetObject("FldTestObject6");
Assert.That(testObj.Test(), Is.EqualTo("test"));
}
[Test]
public void IsNotRequired()
{
var testObj = (FldTestObject7)_applicationContext.GetObject("FldTestObject7");
Assert.That(testObj.IsNull(), Is.True);
}
}
#region Test Objects
public interface IFldNotAnObject
{
}
public interface IFldAnotherOne
{
string Quite();
}
public interface IFldFoo
{
string Test();
}
public class FldFooImpl : IFldFoo
{
public string Test()
{
return "foo";
}
}
public interface IFldSay
{
string Say();
}
public class FldHello : IFldSay
{
public string Say()
{
return "hello";
}
}
public class FldCioa : IFldSay
{
public string Say()
{
return "cioa";
}
}
public class FldHola : IFldFoo, IFldAnotherOne
{
public string Quite()
{
return "test";
}
public string Test()
{
return "test";
}
}
public class FldTestObject1
{
[Autowired]
private IFldFoo _fldFoo;
public string Test()
{
return _fldFoo.Test();
}
}
// object with 2 possibilities should fail
public class FldTestObject2
{
[Autowired]
private IFldSay _wrongName;
public string Say()
{
return _wrongName.Say();
}
}
// should not fail but inject Cioa object
public class FldTestObject3
{
[Autowired]
[Qualifier("FldCioa")]
private IFldSay _fldCioa;
public string Say()
{
return _fldCioa.Say();
}
}
// should not fail but inject Hello via Propertyname
public class FldTestObject4
{
[Autowired]
private IFldSay _fldCioa;
public string Say()
{
return _fldCioa.Say();
}
}
// should not fail but inject Hello via Propertyname
public class FldTestObject5
{
[Autowired]
private IFldNotAnObject _ohhh;
}
public class FldTestObject6
{
[Autowired]
private IFldAnotherOne _hola;
public string Test()
{
return _hola.Quite();
}
}
public class FldTestObject7
{
[Autowired(Required = false)]
private IFldNotAnObject _nono;
public bool IsNull()
{
return (_nono == null);
}
}
#endregion
}

View File

@@ -0,0 +1,371 @@
#region License
/*
* Copyright © 2002-2011 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 NUnit.Framework;
using Spring.Context.Support;
using Spring.Objects.Factory.Config;
using Spring.Objects.Factory.Support;
namespace Spring.Objects.Factory.Attributes
{
[TestFixture]
public class AutowireAttributeMethodsTests
{
private GenericApplicationContext _applicationContext;
[SetUp]
public void Setup()
{
_applicationContext = new GenericApplicationContext();
var objDef = new RootObjectDefinition(typeof(AutowiredAttributeObjectPostProcessor));
objDef.Role = ObjectRole.ROLE_INFRASTRUCTURE;
_applicationContext.RegisterObjectDefinition("AutowiredAttributeObjectPostProcessor", objDef);
objDef = new RootObjectDefinition(typeof(Simple));
_applicationContext.ObjectFactory.RegisterObjectDefinition("Simple", objDef);
objDef = new RootObjectDefinition(typeof(MethodHello));
_applicationContext.ObjectFactory.RegisterObjectDefinition("MethodHello", objDef);
objDef = new RootObjectDefinition(typeof(MethodCiao));
_applicationContext.ObjectFactory.RegisterObjectDefinition("MethodCiao", objDef);
objDef = new RootObjectDefinition(typeof(MethodTestObject1));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("MethodTestObject1", objDef);
objDef = new RootObjectDefinition(typeof(MethodTestObject2));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("MethodTestObject2", objDef);
objDef = new RootObjectDefinition(typeof(MethodTestObject3));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("MethodTestObject3", objDef);
objDef = new RootObjectDefinition(typeof(MethodTestObject4));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("MethodTestObject4", objDef);
objDef = new RootObjectDefinition(typeof(MethodTestObject5));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("MethodTestObject5", objDef);
objDef = new RootObjectDefinition(typeof(MethodTestObject6));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("MethodTestObject6", objDef);
objDef = new RootObjectDefinition(typeof(MethodTestObject7));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("MethodTestObject7", objDef);
objDef = new RootObjectDefinition(typeof(MethodTestObject8));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("MethodTestObject8", objDef);
objDef = new RootObjectDefinition(typeof(MethodTestObject9));
objDef.Scope = "prototype";
_applicationContext.ObjectFactory.RegisterObjectDefinition("MethodTestObject9", objDef);
_applicationContext.Refresh();
}
[Test]
public void InObjectByType()
{
var testObj = (MethodTestObject1)_applicationContext.GetObject("MethodTestObject1");
var objectDefinition = _applicationContext.ObjectFactory.GetObjectDefinition("MethodTestObject1");
Assert.That(testObj.GetObject(), Is.Not.Null);
Assert.That(objectDefinition.DependsOn.Count, Is.EqualTo(1));
}
[Test]
public void InjectByParamterName()
{
var testObj = (MethodTestObject2)_applicationContext.GetObject("MethodTestObject2");
Assert.That(testObj.GetObject(), Is.Not.Null);
}
[Test]
public void InjectByQualifier()
{
var testObj = (MethodTestObject3)_applicationContext.GetObject("MethodTestObject3");
Assert.That(testObj.GetObject(), Is.Not.Null);
}
[Test]
public void FailIfTypeCantBeResolved()
{
Exception ex = null;
try
{
var testObj = (FldTestObject5)_applicationContext.GetObject("MethodTestObject4");
}
catch (Exception e) { ex = e; }
Assert.That(ex, Is.Not.Null, "Should throw an exception");
Assert.That(ex.Message, Is.StringContaining("Injection of autowired dependencies failed"));
}
[Test]
public void InjectListOfObjects()
{
var testObj = (MethodTestObject5)_applicationContext.GetObject("MethodTestObject5");
Assert.That(testObj.GetObject(), Is.Not.Null);
Assert.That(testObj.GetObject().Count, Is.EqualTo(2));
}
[Test]
public void InjectSeveralParameters()
{
var testObj = (MethodTestObject6)_applicationContext.GetObject("MethodTestObject6");
var objectDefinition = _applicationContext.ObjectFactory.GetObjectDefinition("MethodTestObject6");
Assert.That(testObj.GetObject(), Is.Not.Null);
Assert.That(testObj.GetSimple(), Is.Not.Null);
Assert.That(objectDefinition.DependsOn.Count, Is.EqualTo(2));
}
[Test]
public void FailIfObjecNotAvailable()
{
Assert.That(delegate { var testObj = (MethodTestObject7)_applicationContext.GetObject("MethodTestObject7"); },
Throws.Exception.TypeOf<ObjectCreationException>());
}
[Test]
public void PassIfObjectNotavailableButNotRequired()
{
var testObj1 = (MethodTestObject8)_applicationContext.GetObject("MethodTestObject8");
var objectDefinition = _applicationContext.ObjectFactory.GetObjectDefinition("MethodTestObject8");
Assert.That(testObj1.GetSimple(), Is.Null);
Assert.That(objectDefinition.DependsOn.Count, Is.EqualTo(0));
var testObj2 = (MethodTestObject9)_applicationContext.GetObject("MethodTestObject9");
objectDefinition = _applicationContext.ObjectFactory.GetObjectDefinition("MethodTestObject9");
Assert.That(testObj2.GetrAdvanced(), Is.Null);
Assert.That(objectDefinition.DependsOn.Count, Is.EqualTo(0));
}
}
#region Test Objects
public interface ISimple
{
string Foo();
}
public class Simple : ISimple
{
public string Foo()
{
return "simple";
}
}
public interface IAdvanced
{
}
public interface IMethodFoo
{
string Foo();
}
public class MethodHello : IMethodFoo
{
public string Foo()
{
return "hello";
}
}
public class MethodCiao : IMethodFoo
{
public string Foo()
{
return "ciao";
}
}
public class MethodTestObject1
{
private ISimple _impl;
[Autowired]
public void Prepare(ISimple impl)
{
_impl = impl;
}
public object GetObject()
{
return _impl;
}
}
public class MethodTestObject2
{
private IMethodFoo _methodCiao;
[Autowired]
public void Prepare(IMethodFoo methodCiao)
{
_methodCiao = methodCiao;
}
public object GetObject()
{
return _methodCiao;
}
}
public class MethodTestObject3
{
private IMethodFoo _impl;
[Autowired]
public void Prepare([Qualifier("MethodHello")] IMethodFoo impl)
{
_impl = impl;
}
public object GetObject()
{
return _impl;
}
}
public class MethodTestObject4
{
private IMethodFoo _impl;
[Autowired]
public void Prepare([Qualifier("MethodHello")] IMethodFoo impl, string test)
{
_impl = impl;
}
public object GetObject()
{
return _impl;
}
}
public class MethodTestObject5
{
private IList<IMethodFoo> _impl;
[Autowired]
public void Prepare(IList<IMethodFoo> impl)
{
_impl = impl;
}
public IList<IMethodFoo> GetObject()
{
return _impl;
}
}
public class MethodTestObject6
{
private IMethodFoo _impl;
private ISimple _simple;
[Autowired]
public void Prepare(IMethodFoo methodHello, ISimple simple)
{
_impl = methodHello;
_simple = simple;
}
public object GetObject()
{
return _impl;
}
public object GetSimple()
{
return _simple;
}
}
public class MethodTestObject7
{
private ISimple _simple;
[Autowired]
public void Prepare([Qualifier("NotAvailable")] ISimple simple)
{
_simple = simple;
}
public object GetSimple()
{
return _simple;
}
}
public class MethodTestObject8
{
private ISimple _simple;
[Autowired(Required = false)]
public void Prepare([Qualifier("NotAvailable")] ISimple simple)
{
_simple = simple;
}
public object GetSimple()
{
return _simple;
}
}
public class MethodTestObject9
{
private IAdvanced _advanced;
[Autowired(Required = false)]
public void Prepare(IAdvanced advanced)
{
_advanced = advanced;
}
public object GetrAdvanced()
{
return _advanced;
}
}
#endregion
}

View File

@@ -0,0 +1,254 @@
#region License
/*
* Copyright © 2002-2011 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 NUnit.Framework;
using Spring.Context.Support;
using Spring.Objects.Factory.Config;
using Spring.Objects.Factory.Support;
namespace Spring.Objects.Factory.Attributes
{
[TestFixture]
public class AutowireAttributePropertyTest
{
private GenericApplicationContext _applicationContext;
[SetUp]
public void Setup()
{
_applicationContext = new GenericApplicationContext();
var objDef = new RootObjectDefinition(typeof(AutowiredAttributeObjectPostProcessor));
objDef.Role = ObjectRole.ROLE_INFRASTRUCTURE;
_applicationContext.RegisterObjectDefinition("AutowiredAttributeObjectPostProcessor", objDef);
objDef = new RootObjectDefinition(typeof(PropFooImpl));
_applicationContext.RegisterObjectDefinition("PropFoo", objDef);
objDef = new RootObjectDefinition(typeof(PropHello));
_applicationContext.RegisterObjectDefinition("PropHello", objDef);
objDef = new RootObjectDefinition(typeof(PropCioa));
_applicationContext.RegisterObjectDefinition("PropCioa", objDef);
objDef = new RootObjectDefinition(typeof(PropTestObject1));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("PropTestObject1", objDef);
objDef = new RootObjectDefinition(typeof(PropTestObject2));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("PropTestObject2", objDef);
objDef = new RootObjectDefinition(typeof(PropTestObject3));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("PropTestObject3", objDef);
objDef = new RootObjectDefinition(typeof(PropTestObject4));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("PropTestObject4", objDef);
objDef = new RootObjectDefinition(typeof(PropTestObject5));
objDef.Scope = "prototype";
_applicationContext.RegisterObjectDefinition("PropTestObject5", objDef);
_applicationContext.Refresh();
}
[Test]
public void InjectPropertyBasedOnPropertyType()
{
var testObj = (PropTestObject1)_applicationContext.GetObject("PropTestObject1");
var objectDefinition = _applicationContext.ObjectFactory.GetObjectDefinition("PropTestObject1");
Assert.That(testObj.Foo, Is.Not.Null);
Assert.That(testObj.Test(), Is.EqualTo("foo"));
Assert.That(objectDefinition.DependsOn.Count, Is.EqualTo(1), "Should have one Dependant Object");
}
[Test]
public void WithTwoTypesRegisteredAndNoNameShouldFail()
{
Assert.That(delegate { var testObj = (PropTestObject2)_applicationContext.GetObject("PropTestObject2"); }, Throws.Exception.TypeOf<ObjectCreationException>());
}
[Test]
public void WithQualifierName()
{
var testObj = (PropTestObject3)_applicationContext.GetObject("PropTestObject3");
Assert.That(testObj.Cioa, Is.Not.Null);
Assert.That(testObj.Say(), Is.EqualTo("cioa"));
}
[Test]
public void WithTwoTypesAndNoQualifierUsePopertyName()
{
var testObj = (PropTestObject4)_applicationContext.GetObject("PropTestObject4");
Assert.That(testObj.PropCioa, Is.Not.Null);
Assert.That(testObj.Say(), Is.EqualTo("cioa"));
}
[Test]
public void FailIfTypeCantBeResolved()
{
Exception ex = null;
try
{
var testObj = (FldTestObject5)_applicationContext.GetObject("PropTestObject5");
}
catch (Exception e) { ex = e; }
Assert.That(ex, Is.Not.Null, "Should throw an exception");
Assert.That(ex.Message, Is.StringContaining("Injection of autowired dependencies failed"));
}
}
#region Test Objects
public interface IPropNotAnObject
{
}
public interface IPropFoo
{
string Test();
}
public class PropFooImpl : IPropFoo
{
public string Test()
{
return "foo";
}
}
public interface IPropSay
{
string PropSay();
}
public class PropHello : IPropSay
{
public string PropSay()
{
return "hello";
}
}
public class PropCioa : IPropSay
{
public string PropSay()
{
return "cioa";
}
}
public class PropTestObject1
{
private IPropFoo _foo;
[Autowired]
public IPropFoo Foo
{
get { return _foo; }
set { _foo = value; }
}
public string Test()
{
return _foo.Test();
}
}
public class PropTestObject2
{
private IPropSay _hello;
[Autowired]
public IPropSay WrongName
{
get { return _hello; }
set { _hello = value; }
}
public string Say()
{
return _hello.PropSay();
}
}
// should not fail but inject Cioa object
public class PropTestObject3
{
private IPropSay _cioa;
[Autowired]
[Qualifier("PropCioa")]
public IPropSay Cioa
{
get { return _cioa; }
set { _cioa = value; }
}
public string Say()
{
return _cioa.PropSay();
}
}
// should not fail but inject Hello via Propertyname
public class PropTestObject4
{
private IPropSay _obj;
[Autowired]
public IPropSay PropCioa
{
get { return _obj; }
set { _obj = value; }
}
public string Say()
{
return _obj.PropSay();
}
}
// should not fail but inject Hello via Propertyname
public class PropTestObject5
{
private IPropNotAnObject _obj;
[Autowired]
public IPropNotAnObject Ohhh
{
get { return _obj; }
set { _obj = value; }
}
}
#endregion
}

View File

@@ -318,6 +318,11 @@
</Compile>
<Compile Include="HookableContextHandler.cs" />
<Compile Include="Objects\ExpressionTestObject.cs" />
<Compile Include="Objects\Factory\Attributes\AutowireAttributeCollectionTests.cs" />
<Compile Include="Objects\Factory\Attributes\AutowireAttributeConstructorTest.cs" />
<Compile Include="Objects\Factory\Attributes\AutowireAttributeFieldTest.cs" />
<Compile Include="Objects\Factory\Attributes\AutowireAttributeMethodsTests.cs" />
<Compile Include="Objects\Factory\Attributes\AutowireAttributePropertyTest.cs" />
<Compile Include="Objects\Factory\Attributes\MyRequiredAttribute.cs" />
<Compile Include="Objects\Factory\Attributes\RequiredAttributeObjectPostProcessorTests.cs" />
<Compile Include="Objects\Factory\Attributes\RequiredTestObject.cs" />