/// <summary> /// Create an array of arguments to invoke a constructor or static factory method, /// given the resolved constructor arguments values. /// </summary> /// <remarks>When return value is null the out parameter UnsatisfiedDependencyExceptionData will contain /// information for use in throwing a UnsatisfiedDependencyException by the caller. This avoids using /// exceptions for flow control as in the original implementation.</remarks> private ArgumentsHolder CreateArgumentArray(string objectName, RootObjectDefinition rod, ConstructorArgumentValues resolvedValues, ObjectWrapper wrapper, Type[] paramTypes, MethodBase methodOrCtorInfo, bool autowiring, out UnsatisfiedDependencyExceptionData unsatisfiedDependencyExceptionData) { string methodType = (methodOrCtorInfo is ConstructorInfo) ? "constructor" : "factory method"; unsatisfiedDependencyExceptionData = null; ArgumentsHolder args = new ArgumentsHolder(paramTypes.Length); ISet usedValueHolders = new HybridSet(); IList autowiredObjectNames = new LinkedList(); bool resolveNecessary = false; ParameterInfo[] argTypes = methodOrCtorInfo.GetParameters(); for (int paramIndex = 0; paramIndex < paramTypes.Length; paramIndex++) { Type paramType = paramTypes[paramIndex]; string parameterName = argTypes[paramIndex].Name; // If we couldn't find a direct match and are not supposed to autowire, // let's try the next generic, untyped argument value as fallback: // it could match after type conversion (for example, String -> int). ConstructorArgumentValues.ValueHolder valueHolder = null; if (resolvedValues.GetNamedArgumentValue(parameterName) != null) { valueHolder = resolvedValues.GetArgumentValue(parameterName, paramType, usedValueHolders); } else { valueHolder = resolvedValues.GetArgumentValue(paramIndex, paramType, usedValueHolders); } if (valueHolder == null && !autowiring) { valueHolder = resolvedValues.GetGenericArgumentValue(null, usedValueHolders); } if (valueHolder != null) { // We found a potential match - let's give it a try. // Do not consider the same value definition multiple times! usedValueHolders.Add(valueHolder); args.rawArguments[paramIndex] = valueHolder.Value; try { object originalValue = valueHolder.Value; object convertedValue = TypeConversionUtils.ConvertValueIfNecessary(paramType, originalValue, null); args.arguments[paramIndex] = convertedValue; //? args.preparedArguments[paramIndex] = convertedValue; } catch (TypeMismatchException ex) { //To avoid using exceptions for flow control, this is not a cost in Java as stack trace is lazily created. string errorMessage = String.Format(CultureInfo.InvariantCulture, "Could not convert {0} argument value [{1}] to required type [{2}] : {3}", methodType, valueHolder.Value, paramType, ex.Message); unsatisfiedDependencyExceptionData = new UnsatisfiedDependencyExceptionData(paramIndex, paramType, errorMessage); return null; } } else { // No explicit match found: we're either supposed to autowire or // have to fail creating an argument array for the given constructor. if (!autowiring) { string errorMessage = String.Format(CultureInfo.InvariantCulture, "Ambiguous {0} argument types - " + "Did you specify the correct object references as {0} arguments?", methodType); unsatisfiedDependencyExceptionData = new UnsatisfiedDependencyExceptionData(paramIndex, paramType, errorMessage); return null; } try { MethodParameter param = MethodParameter.ForMethodOrConstructor(methodOrCtorInfo, paramIndex); object autowiredArgument = ResolveAutoWiredArgument(param, objectName, autowiredObjectNames); args.rawArguments[paramIndex] = autowiredArgument; args.arguments[paramIndex] = autowiredArgument; args.preparedArguments[paramIndex] = new AutowiredArgumentMarker(); resolveNecessary = true; } catch (ObjectsException ex) { unsatisfiedDependencyExceptionData = new UnsatisfiedDependencyExceptionData(paramIndex, paramType, ex.Message); return null; } } } foreach (string autowiredObjectName in autowiredObjectNames) { if (log.IsDebugEnabled) { log.Debug("Autowiring by type from object name '" + objectName + "' via " + methodType + " to object named '" + autowiredObjectName + "'"); } } return args; }
/// <summary> /// Gets the constructor instantiation info given the object definition. /// </summary> /// <param name="objectName">Name of the object.</param> /// <param name="rod">The RootObjectDefinition</param> /// <param name="chosenCtors">The explicitly chosen ctors.</param> /// <param name="explicitArgs">The explicit chose ctor args.</param> /// <returns>A ConstructorInstantiationInfo containg the specified constructor in the RootObjectDefinition or /// one based on type matching.</returns> public ConstructorInstantiationInfo GetConstructorInstantiationInfo(string objectName, RootObjectDefinition rod, ConstructorInfo[] chosenCtors, object[] explicitArgs) { ObjectWrapper wrapper = new ObjectWrapper(); ConstructorInfo constructorToUse = null; object[] argsToUse = null; if (explicitArgs != null) { argsToUse = explicitArgs; } else { //TODO performance optmization on cached ctors. } // Need to resolve the constructor. bool autowiring = (chosenCtors != null || rod.ResolvedAutowireMode == AutoWiringMode.Constructor); ConstructorArgumentValues resolvedValues = null; int minNrOfArgs = 0; if (explicitArgs != null) { minNrOfArgs = explicitArgs.Length; } else { ConstructorArgumentValues cargs = rod.ConstructorArgumentValues; resolvedValues = new ConstructorArgumentValues(); minNrOfArgs = ResolveConstructorArguments(objectName, rod, wrapper, cargs, resolvedValues); } // Take specified constructors, if any. ConstructorInfo[] candidates = (chosenCtors != null ? chosenCtors : AutowireUtils.GetConstructors(rod, 0)); AutowireUtils.SortConstructors(candidates); int minTypeDiffWeight = Int32.MaxValue; for (int i = 0; i < candidates.Length; i++) { ConstructorInfo candidate = candidates[i]; Type[] paramTypes = ReflectionUtils.GetParameterTypes(candidate.GetParameters()); if (constructorToUse != null && argsToUse.Length > paramTypes.Length) { // already found greedy constructor that can be satisfied, so // don't look any further, there are only less greedy constructors left... break; } if (paramTypes.Length < minNrOfArgs) { throw new ObjectCreationException(rod.ResourceDescription, objectName, string.Format(CultureInfo.InvariantCulture, "'{0}' constructor arguments specified but no matching constructor found " + "in object '{1}' (hint: specify argument indexes, names, or " + "types to avoid ambiguities).", minNrOfArgs, objectName)); } ArgumentsHolder args = null; if (resolvedValues != null) { UnsatisfiedDependencyExceptionData unsatisfiedDependencyExceptionData = null; // Try to resolve arguments for current constructor //need to check for null as indicator of no ctor arg match instead of using exceptions for flow //control as in the Java implementation args = CreateArgumentArray(objectName, rod, resolvedValues, wrapper, paramTypes, candidate, autowiring, out unsatisfiedDependencyExceptionData); if (args == null) { if (i == candidates.Length - 1 && constructorToUse == null) { throw new UnsatisfiedDependencyException(rod.ResourceDescription, objectName, unsatisfiedDependencyExceptionData.ParameterIndex, unsatisfiedDependencyExceptionData.ParameterType, unsatisfiedDependencyExceptionData.ErrorMessage); } // try next constructor... continue; } } else { // Explicit arguments given -> arguments length must match exactly if (paramTypes.Length != explicitArgs.Length) { continue; } args = new ArgumentsHolder(explicitArgs); } int typeDiffWeight = args.GetTypeDifferenceWeight(paramTypes); // Choose this constructor if it represents the closest match. if (typeDiffWeight < minTypeDiffWeight) { constructorToUse = candidate; argsToUse = args.arguments; minTypeDiffWeight = typeDiffWeight; } } if (constructorToUse == null) { throw new ObjectCreationException(rod.ResourceDescription, objectName, "Could not resolve matching constructor."); } return new ConstructorInstantiationInfo(constructorToUse, argsToUse); }
/// <summary> /// Instantiate an object instance using a named factory method. /// </summary> /// <remarks> /// <p> /// The method may be static, if the <paramref name="definition"/> /// parameter specifies a class, rather than a /// <see cref="Spring.Objects.Factory.IFactoryObject"/> instance, or an /// instance variable on a factory object itself configured using Dependency /// Injection. /// </p> /// <p> /// Implementation requires iterating over the static or instance methods /// with the name specified in the supplied <paramref name="definition"/> /// (the method may be overloaded) and trying to match with the parameters. /// We don't have the types attached to constructor args, so trial and error /// is the only way to go here. /// </p> /// </remarks> /// <param name="name"> /// The name associated with the supplied <paramref name="definition"/>. /// </param> /// <param name="definition"> /// The definition describing the instance that is to be instantiated. /// </param> /// <param name="arguments"> /// Any arguments to the factory method that is to be invoked. /// </param> /// <returns> /// The result of the factory method invocation (the instance). /// </returns> public virtual IObjectWrapper InstantiateUsingFactoryMethod(string name, RootObjectDefinition definition, object[] arguments) { ObjectWrapper wrapper = new ObjectWrapper(); Type factoryClass = null; bool isStatic = true; ConstructorArgumentValues cargs = definition.ConstructorArgumentValues; ConstructorArgumentValues resolvedValues = new ConstructorArgumentValues(); int expectedArgCount = 0; // we don't have arguments passed in programmatically, so we need to resolve the // arguments specified in the constructor arguments held in the object definition... if (arguments == null || arguments.Length == 0) { expectedArgCount = cargs.ArgumentCount; ResolveConstructorArguments(name, definition, wrapper, cargs, resolvedValues); } else { // if we have constructor args, don't need to resolve them... expectedArgCount = arguments.Length; } if (StringUtils.HasText(definition.FactoryObjectName)) { // it's an instance method on the factory object's class... factoryClass = objectFactory.GetObject(definition.FactoryObjectName).GetType(); isStatic = false; } else { // it's a static factory method on the object class... factoryClass = definition.ObjectType; } GenericArgumentsHolder genericArgsInfo = new GenericArgumentsHolder(definition.FactoryMethodName); IList <MethodInfo> factoryMethodCandidates = FindMethods(genericArgsInfo.GenericMethodName, expectedArgCount, isStatic, factoryClass); bool autowiring = (definition.AutowireMode == AutoWiringMode.Constructor); // try all matching methods to see if they match the constructor arguments... for (int i = 0; i < factoryMethodCandidates.Count; i++) { MethodInfo factoryMethodCandidate = factoryMethodCandidates[i]; if (genericArgsInfo.ContainsGenericArguments) { string[] unresolvedGenericArgs = genericArgsInfo.GetGenericArguments(); if (factoryMethodCandidate.GetGenericArguments().Length != unresolvedGenericArgs.Length) { continue; } Type[] paramTypes = new Type[unresolvedGenericArgs.Length]; for (int j = 0; j < unresolvedGenericArgs.Length; j++) { paramTypes[j] = TypeResolutionUtils.ResolveType(unresolvedGenericArgs[j]); } factoryMethodCandidate = factoryMethodCandidate.MakeGenericMethod(paramTypes); } if (arguments == null || arguments.Length == 0) { Type[] paramTypes = ReflectionUtils.GetParameterTypes(factoryMethodCandidate.GetParameters()); // try to create the required arguments... UnsatisfiedDependencyExceptionData unsatisfiedDependencyExceptionData = null; ArgumentsHolder args = CreateArgumentArray(name, definition, resolvedValues, wrapper, paramTypes, factoryMethodCandidate, autowiring, out unsatisfiedDependencyExceptionData); if (args == null) { arguments = null; // if we failed to match this method, keep // trying new overloaded factory methods... continue; } else { arguments = args.arguments; } } // if we get here, we found a usable candidate factory method - check, if arguments match //arguments = (arguments.Length == 0 ? null : arguments); if (ReflectionUtils.GetMethodByArgumentValues(new MethodInfo[] { factoryMethodCandidate }, arguments) == null) { continue; } object objectInstance = instantiationStrategy.Instantiate(definition, name, objectFactory, factoryMethodCandidate, arguments); wrapper.WrappedInstance = objectInstance; #region Instrumentation if (log.IsDebugEnabled) { log.Debug(string.Format(CultureInfo.InvariantCulture, "Object '{0}' instantiated via factory method [{1}].", name, factoryMethodCandidate)); } #endregion return(wrapper); } // if we get here, we didn't match any method... throw new ObjectDefinitionStoreException( string.Format(CultureInfo.InvariantCulture, "Cannot find matching factory method '{0} on Type [{1}].", definition.FactoryMethodName, factoryClass)); }
/// <summary> /// Create an array of arguments to invoke a constructor or static factory method, /// given the resolved constructor arguments values. /// </summary> /// <remarks>When return value is null the out parameter UnsatisfiedDependencyExceptionData will contain /// information for use in throwing a UnsatisfiedDependencyException by the caller. This avoids using /// exceptions for flow control as in the original implementation.</remarks> private ArgumentsHolder CreateArgumentArray(string objectName, RootObjectDefinition rod, ConstructorArgumentValues resolvedValues, ObjectWrapper wrapper, Type[] paramTypes, MethodBase methodOrCtorInfo, bool autowiring, out UnsatisfiedDependencyExceptionData unsatisfiedDependencyExceptionData) { string methodType = (methodOrCtorInfo is ConstructorInfo) ? "constructor" : "factory method"; unsatisfiedDependencyExceptionData = null; ArgumentsHolder args = new ArgumentsHolder(paramTypes.Length); ISet usedValueHolders = new HybridSet(); IList autowiredObjectNames = new LinkedList(); bool resolveNecessary = false; ParameterInfo[] argTypes = methodOrCtorInfo.GetParameters(); for (int paramIndex = 0; paramIndex < paramTypes.Length; paramIndex++) { Type paramType = paramTypes[paramIndex]; string parameterName = argTypes[paramIndex].Name; // If we couldn't find a direct match and are not supposed to autowire, // let's try the next generic, untyped argument value as fallback: // it could match after type conversion (for example, String -> int). ConstructorArgumentValues.ValueHolder valueHolder = null; if (resolvedValues.GetNamedArgumentValue(parameterName) != null) { valueHolder = resolvedValues.GetArgumentValue(parameterName, paramType, usedValueHolders); } else { valueHolder = resolvedValues.GetArgumentValue(paramIndex, paramType, usedValueHolders); } if (valueHolder == null && !autowiring) { valueHolder = resolvedValues.GetGenericArgumentValue(null, usedValueHolders); } if (valueHolder != null) { // We found a potential match - let's give it a try. // Do not consider the same value definition multiple times! usedValueHolders.Add(valueHolder); args.rawArguments[paramIndex] = valueHolder.Value; try { object originalValue = valueHolder.Value; object convertedValue = TypeConversionUtils.ConvertValueIfNecessary(paramType, originalValue, null); args.arguments[paramIndex] = convertedValue; //? args.preparedArguments[paramIndex] = convertedValue; } catch (TypeMismatchException ex) { //To avoid using exceptions for flow control, this is not a cost in Java as stack trace is lazily created. string errorMessage = String.Format(CultureInfo.InvariantCulture, "Could not convert {0} argument value [{1}] to required type [{2}] : {3}", methodType, valueHolder.Value, paramType, ex.Message); unsatisfiedDependencyExceptionData = new UnsatisfiedDependencyExceptionData(paramIndex, paramType, errorMessage); return(null); } } else { // No explicit match found: we're either supposed to autowire or // have to fail creating an argument array for the given constructor. if (!autowiring) { string errorMessage = String.Format(CultureInfo.InvariantCulture, "Ambiguous {0} argument types - " + "Did you specify the correct object references as {0} arguments?", methodType); unsatisfiedDependencyExceptionData = new UnsatisfiedDependencyExceptionData(paramIndex, paramType, errorMessage); return(null); } try { MethodParameter param = MethodParameter.ForMethodOrConstructor(methodOrCtorInfo, paramIndex); object autowiredArgument = ResolveAutoWiredArgument(param, objectName, autowiredObjectNames); args.rawArguments[paramIndex] = autowiredArgument; args.arguments[paramIndex] = autowiredArgument; args.preparedArguments[paramIndex] = new AutowiredArgumentMarker(); resolveNecessary = true; } catch (ObjectsException ex) { unsatisfiedDependencyExceptionData = new UnsatisfiedDependencyExceptionData(paramIndex, paramType, ex.Message); return(null); } } } foreach (string autowiredObjectName in autowiredObjectNames) { if (log.IsDebugEnabled) { log.Debug("Autowiring by type from object name '" + objectName + "' via " + methodType + " to object named '" + autowiredObjectName + "'"); } } return(args); }
/// <summary> /// Gets the constructor instantiation info given the object definition. /// </summary> /// <param name="objectName">Name of the object.</param> /// <param name="rod">The RootObjectDefinition</param> /// <param name="chosenCtors">The explicitly chosen ctors.</param> /// <param name="explicitArgs">The explicit chose ctor args.</param> /// <returns>A ConstructorInstantiationInfo containg the specified constructor in the RootObjectDefinition or /// one based on type matching.</returns> public ConstructorInstantiationInfo GetConstructorInstantiationInfo(string objectName, RootObjectDefinition rod, ConstructorInfo[] chosenCtors, object[] explicitArgs) { ObjectWrapper wrapper = new ObjectWrapper(); ConstructorInfo constructorToUse = null; object[] argsToUse = null; if (explicitArgs != null) { argsToUse = explicitArgs; } else { //TODO performance optmization on cached ctors. } // Need to resolve the constructor. bool autowiring = (chosenCtors != null || rod.ResolvedAutowireMode == AutoWiringMode.Constructor); ConstructorArgumentValues resolvedValues = null; int minNrOfArgs = 0; if (explicitArgs != null) { minNrOfArgs = explicitArgs.Length; } else { ConstructorArgumentValues cargs = rod.ConstructorArgumentValues; resolvedValues = new ConstructorArgumentValues(); minNrOfArgs = ResolveConstructorArguments(objectName, rod, wrapper, cargs, resolvedValues); } // Take specified constructors, if any. ConstructorInfo[] candidates = (chosenCtors != null ? chosenCtors : AutowireUtils.GetConstructors(rod, 0)); AutowireUtils.SortConstructors(candidates); int minTypeDiffWeight = Int32.MaxValue; for (int i = 0; i < candidates.Length; i++) { ConstructorInfo candidate = candidates[i]; Type[] paramTypes = ReflectionUtils.GetParameterTypes(candidate.GetParameters()); if (constructorToUse != null && argsToUse.Length > paramTypes.Length) { // already found greedy constructor that can be satisfied, so // don't look any further, there are only less greedy constructors left... break; } if (paramTypes.Length < minNrOfArgs) { throw new ObjectCreationException(rod.ResourceDescription, objectName, string.Format(CultureInfo.InvariantCulture, "'{0}' constructor arguments specified but no matching constructor found " + "in object '{1}' (hint: specify argument indexes, names, or " + "types to avoid ambiguities).", minNrOfArgs, objectName)); } ArgumentsHolder args = null; if (resolvedValues != null) { UnsatisfiedDependencyExceptionData unsatisfiedDependencyExceptionData = null; // Try to resolve arguments for current constructor //need to check for null as indicator of no ctor arg match instead of using exceptions for flow //control as in the Java implementation args = CreateArgumentArray(objectName, rod, resolvedValues, wrapper, paramTypes, candidate, autowiring, out unsatisfiedDependencyExceptionData); if (args == null) { if (i == candidates.Length - 1 && constructorToUse == null) { throw new UnsatisfiedDependencyException(rod.ResourceDescription, objectName, unsatisfiedDependencyExceptionData.ParameterIndex, unsatisfiedDependencyExceptionData.ParameterType, unsatisfiedDependencyExceptionData.ErrorMessage); } // try next constructor... continue; } } else { // Explicit arguments given -> arguments length must match exactly if (paramTypes.Length != explicitArgs.Length) { continue; } args = new ArgumentsHolder(explicitArgs); } int typeDiffWeight = args.GetTypeDifferenceWeight(paramTypes); // Choose this constructor if it represents the closest match. if (typeDiffWeight < minTypeDiffWeight) { constructorToUse = candidate; argsToUse = args.arguments; minTypeDiffWeight = typeDiffWeight; } } if (constructorToUse == null) { throw new ObjectCreationException(rod.ResourceDescription, objectName, "Could not resolve matching constructor."); } return(new ConstructorInstantiationInfo(constructorToUse, argsToUse)); }