/* * 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. */ package org.springframework.beans.factory.support; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.BeansException; import org.springframework.beans.TypeConverter; import org.springframework.beans.TypeMismatchException; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.UnsatisfiedDependencyException; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.config.TypedStringValue; import org.springframework.core.GenericTypeResolver; import org.springframework.core.JdkVersion; import org.springframework.core.MethodParameter; import org.springframework.util.MethodInvoker; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; /** * Helper class for resolving constructors and factory methods. * Performs constructor resolution through argument matching. * * <p>Operates on an {@link AbstractBeanFactory} and an {@link InstantiationStrategy}. * Used by {@link AbstractAutowireCapableBeanFactory}. * * @author Juergen Hoeller * @author Rob Harrop * @author Mark Fisher * @since 2.0 * @see #autowireConstructor * @see #instantiateUsingFactoryMethod * @see AbstractAutowireCapableBeanFactory */ class ConstructorResolver { private final AbstractBeanFactory beanFactory; private final AutowireCapableBeanFactory autowireFactory; private final InstantiationStrategy instantiationStrategy; private final TypeConverter typeConverter; /** * Create a new ConstructorResolver for the given factory and instantiation strategy. * @param beanFactory the BeanFactory to work with * @param autowireFactory the BeanFactory as AutowireCapableBeanFactory * @param instantiationStrategy the instantiate strategy for creating bean instances * @param typeConverter the TypeConverter to use (or <code>null</code> for using the default) */ public ConstructorResolver(AbstractBeanFactory beanFactory, AutowireCapableBeanFactory autowireFactory, InstantiationStrategy instantiationStrategy, TypeConverter typeConverter) { this.beanFactory = beanFactory; this.autowireFactory = autowireFactory; this.instantiationStrategy = instantiationStrategy; this.typeConverter = typeConverter; } /** * "autowire constructor" (with constructor arguments by type) behavior. * Also applied if explicit constructor argument values are specified, * matching all remaining arguments with beans from the bean factory. * <p>This corresponds to constructor injection: In this mode, a Spring * bean factory is able to host components that expect constructor-based * dependency resolution. * @param beanName the name of the bean * @param mbd the merged bean definition for the bean * @param chosenCtors chosen candidate constructors (or <code>null</code> if none) * @param explicitArgs argument values passed in programmatically via the getBean method, * or <code>null</code> if none (-> use constructor argument values from bean definition) * @return a BeanWrapper for the new instance */ protected BeanWrapper autowireConstructor( String beanName, RootBeanDefinition mbd, Constructor[] chosenCtors, Object[] explicitArgs) { BeanWrapperImpl bw = new BeanWrapperImpl(); this.beanFactory.initBeanWrapper(bw); Constructor constructorToUse = null; Object[] argsToUse = null; if (explicitArgs != null) { argsToUse = explicitArgs; } else { constructorToUse = (Constructor) mbd.resolvedConstructorOrFactoryMethod; if (constructorToUse != null) { // Found a cached constructor... argsToUse = mbd.resolvedConstructorArguments; if (argsToUse == null) { Class[] paramTypes = constructorToUse.getParameterTypes(); Object[] argsToResolve = mbd.preparedConstructorArguments; TypeConverter converter = (this.typeConverter != null ? this.typeConverter : bw); BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter); argsToUse = new Object[argsToResolve.length]; for (int i = 0; i < argsToResolve.length; i++) { Object argValue = argsToResolve[i]; MethodParameter methodParam = new MethodParameter(constructorToUse, i); if (JdkVersion.isAtLeastJava15()) { GenericTypeResolver.resolveParameterType(methodParam, constructorToUse.getDeclaringClass()); } if (argValue instanceof AutowiredArgumentMarker) { argValue = resolveAutowiredArgument(methodParam, beanName, null, converter); } else if (argValue instanceof BeanMetadataElement) { argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue); } argsToUse[i] = converter.convertIfNecessary(argValue, paramTypes[i], methodParam); } } } } if (constructorToUse == null) { // Need to resolve the constructor. boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); ConstructorArgumentValues resolvedValues = null; int minNrOfArgs = 0; if (explicitArgs != null) { minNrOfArgs = explicitArgs.length; } else { ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues(); resolvedValues = new ConstructorArgumentValues(); minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } // Take specified constructors, if any. Constructor[] candidates = (chosenCtors != null ? chosenCtors : mbd.getBeanClass().getDeclaredConstructors()); AutowireUtils.sortConstructors(candidates); int minTypeDiffWeight = Integer.MAX_VALUE; List causes = null; for (int i = 0; i < candidates.length; i++) { Constructor candidate = candidates[i]; Class[] paramTypes = candidate.getParameterTypes(); if (constructorToUse != null && argsToUse.length > paramTypes.length) { // Already found greedy constructor that can be satisfied -> // do not look any further, there are only less greedy constructors left. break; } if (paramTypes.length < minNrOfArgs) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, minNrOfArgs + " constructor arguments specified but no matching constructor found in bean '" + beanName + "' " + "(hint: specify index and/or type arguments for simple parameters to avoid type ambiguities)"); } ArgumentsHolder args = null; if (resolvedValues != null) { // Try to resolve arguments for current constructor. try { args = createArgumentArray( beanName, mbd, resolvedValues, bw, paramTypes, candidate, autowiring); } catch (UnsatisfiedDependencyException ex) { if (this.beanFactory.logger.isTraceEnabled()) { this.beanFactory.logger.trace( "Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex); } if (i == candidates.length - 1 && constructorToUse == null) { if (causes != null) { for (Iterator it = causes.iterator(); it.hasNext();) { this.beanFactory.onSuppressedException((Exception) it.next()); } } throw ex; } else { // Swallow and try next constructor. if (causes == null) { causes = new LinkedList(); } causes.add(ex); 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 BeanCreationException( mbd.getResourceDescription(), beanName, "Could not resolve matching constructor"); } if (explicitArgs == null) { mbd.resolvedConstructorOrFactoryMethod = constructorToUse; } } try { Object beanInstance = this.instantiationStrategy.instantiate( mbd, beanName, this.beanFactory, constructorToUse, argsToUse); bw.setWrappedInstance(beanInstance); return bw; } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); } } /** * Instantiate the bean using a named factory method. The method may be static, if the * bean definition parameter specifies a class, rather than a "factory-bean", or * an instance variable on a factory object itself configured using Dependency Injection. * <p>Implementation requires iterating over the static or instance methods with the * name specified in the RootBeanDefinition (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. The explicitArgs array may contain * argument values passed in programmatically via the corresponding getBean method. * @param beanName the name of the bean * @param mbd the merged bean definition for the bean * @param explicitArgs argument values passed in programmatically via the getBean * method, or <code>null</code> if none (-> use constructor argument values from bean definition) * @return a BeanWrapper for the new instance */ public BeanWrapper instantiateUsingFactoryMethod(String beanName, RootBeanDefinition mbd, Object[] explicitArgs) { BeanWrapperImpl bw = new BeanWrapperImpl(); this.beanFactory.initBeanWrapper(bw); Class factoryClass = null; Object factoryBean = null; boolean isStatic = true; String factoryBeanName = mbd.getFactoryBeanName(); if (factoryBeanName != null) { if (factoryBeanName.equals(beanName)) { throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName, "factory-bean reference points back to the same bean definition"); } factoryBean = this.beanFactory.getBean(factoryBeanName); if (factoryBean == null) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "factory-bean '" + factoryBeanName + "' returned null"); } factoryClass = factoryBean.getClass(); isStatic = false; } else { // It's a static factory method on the bean class. factoryClass = mbd.getBeanClass(); } Method factoryMethodToUse = null; Object[] argsToUse = null; if (explicitArgs != null) { argsToUse = explicitArgs; } else { factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod; if (factoryMethodToUse != null) { // Found a cached factory method... argsToUse = mbd.resolvedConstructorArguments; if (argsToUse == null) { Class[] paramTypes = factoryMethodToUse.getParameterTypes(); Object[] argsToResolve = mbd.preparedConstructorArguments; TypeConverter converter = (this.typeConverter != null ? this.typeConverter : bw); BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter); argsToUse = new Object[argsToResolve.length]; for (int i = 0; i < argsToResolve.length; i++) { Object argValue = argsToResolve[i]; MethodParameter methodParam = new MethodParameter(factoryMethodToUse, i); if (JdkVersion.isAtLeastJava15()) { GenericTypeResolver.resolveParameterType(methodParam, factoryClass); } if (argValue instanceof AutowiredArgumentMarker) { argValue = resolveAutowiredArgument(methodParam, beanName, null, converter); } else if (argValue instanceof BeanMetadataElement) { argValue = valueResolver.resolveValueIfNecessary("factory method argument", argValue); } argsToUse[i] = converter.convertIfNecessary(argValue, paramTypes[i], methodParam); } } } } if (factoryMethodToUse == null) { // Need to determine the factory method... // Try all methods with this name to see if they match the given arguments. Method[] candidates = ReflectionUtils.getAllDeclaredMethods(factoryClass); boolean autowiring = (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); int minTypeDiffWeight = Integer.MAX_VALUE; ConstructorArgumentValues resolvedValues = null; int minNrOfArgs = 0; if (explicitArgs != null) { minNrOfArgs = explicitArgs.length; } else { // We don't have arguments passed in programmatically, so we need to resolve the // arguments specified in the constructor arguments held in the bean definition. ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues(); resolvedValues = new ConstructorArgumentValues(); minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } List causes = null; for (int i = 0; i < candidates.length; i++) { Method candidate = candidates[i]; Class[] paramTypes = candidate.getParameterTypes(); if (Modifier.isStatic(candidate.getModifiers()) == isStatic && candidate.getName().equals(mbd.getFactoryMethodName()) && paramTypes.length >= minNrOfArgs) { ArgumentsHolder args = null; if (resolvedValues != null) { // Resolved contructor arguments: type conversion and/or autowiring necessary. try { args = createArgumentArray( beanName, mbd, resolvedValues, bw, paramTypes, candidate, autowiring); } catch (UnsatisfiedDependencyException ex) { if (this.beanFactory.logger.isTraceEnabled()) { this.beanFactory.logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex); } if (i == candidates.length - 1 && factoryMethodToUse == null) { if (causes != null) { for (Iterator it = causes.iterator(); it.hasNext();) { this.beanFactory.onSuppressedException((Exception) it.next()); } } throw ex; } else { // Swallow and try next overloaded factory method. if (causes == null) { causes = new LinkedList(); } causes.add(ex); 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) { factoryMethodToUse = candidate; argsToUse = args.arguments; minTypeDiffWeight = typeDiffWeight; } } } if (factoryMethodToUse == null) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "No matching factory method found: " + (mbd.getFactoryBeanName() != null ? "factory bean '" + mbd.getFactoryBeanName() + "'; " : "") + "factory method '" + mbd.getFactoryMethodName() + "'. " + "Check that a method of the specified name exists and that it is " + (isStatic ? "static" : "non-static") + "."); } if (void.class.equals(factoryMethodToUse.getReturnType())) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid factory method '" + mbd.getFactoryMethodName() + "': needs to have a non-void return type!"); } if (explicitArgs == null) { mbd.resolvedConstructorOrFactoryMethod = factoryMethodToUse; } } try { Object beanInstance = this.instantiationStrategy.instantiate( mbd, beanName, this.beanFactory, factoryBean, factoryMethodToUse, argsToUse); if (beanInstance == null) { return null; } bw.setWrappedInstance(beanInstance); return bw; } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); } } /** * Resolve the constructor arguments for this bean into the resolvedValues object. * This may involve looking up other beans. * This method is also used for handling invocations of static factory methods. */ private int resolveConstructorArguments( String beanName, RootBeanDefinition mbd, BeanWrapper bw, ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) { TypeConverter converterToUse = (this.typeConverter != null ? this.typeConverter : bw); BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converterToUse); int minNrOfArgs = cargs.getArgumentCount(); for (Iterator it = cargs.getIndexedArgumentValues().entrySet().iterator(); it.hasNext();) { Map.Entry entry = (Map.Entry) it.next(); int index = ((Integer) entry.getKey()).intValue(); if (index < 0) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid constructor argument index: " + index); } if (index > minNrOfArgs) { minNrOfArgs = index + 1; } ConstructorArgumentValues.ValueHolder valueHolder = (ConstructorArgumentValues.ValueHolder) entry.getValue(); if (valueHolder.isConverted()) { resolvedValues.addIndexedArgumentValue(index, valueHolder); } else { Object resolvedValue = valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue()); ConstructorArgumentValues.ValueHolder resolvedValueHolder = new ConstructorArgumentValues.ValueHolder(resolvedValue, valueHolder.getType()); resolvedValueHolder.setSource(valueHolder); resolvedValues.addIndexedArgumentValue(index, resolvedValueHolder); } } for (Iterator it = cargs.getGenericArgumentValues().iterator(); it.hasNext();) { ConstructorArgumentValues.ValueHolder valueHolder = (ConstructorArgumentValues.ValueHolder) it.next(); if (valueHolder.isConverted()) { resolvedValues.addGenericArgumentValue(valueHolder); } else { Object resolvedValue = valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue()); ConstructorArgumentValues.ValueHolder resolvedValueHolder = new ConstructorArgumentValues.ValueHolder(resolvedValue, valueHolder.getType()); resolvedValueHolder.setSource(valueHolder); resolvedValues.addGenericArgumentValue(resolvedValueHolder); } } return minNrOfArgs; } /** * Create an array of arguments to invoke a constructor or factory method, * given the resolved constructor argument values. */ private ArgumentsHolder createArgumentArray( String beanName, RootBeanDefinition mbd, ConstructorArgumentValues resolvedValues, BeanWrapper bw, Class[] paramTypes, Object methodOrCtor, boolean autowiring) throws UnsatisfiedDependencyException { String methodType = (methodOrCtor instanceof Constructor ? "constructor" : "factory method"); TypeConverter converter = (this.typeConverter != null ? this.typeConverter : bw); ArgumentsHolder args = new ArgumentsHolder(paramTypes.length); Set usedValueHolders = new HashSet(paramTypes.length); Set autowiredBeanNames = new LinkedHashSet(4); boolean resolveNecessary = false; for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) { Class paramType = paramTypes[paramIndex]; // Try to find matching constructor argument value, either indexed or generic. ConstructorArgumentValues.ValueHolder valueHolder = resolvedValues.getArgumentValue(paramIndex, paramType, usedValueHolders); // 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). 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.getValue(); if (valueHolder.isConverted()) { Object convertedValue = valueHolder.getConvertedValue(); args.arguments[paramIndex] = convertedValue; args.preparedArguments[paramIndex] = convertedValue; } else { try { Object originalValue = valueHolder.getValue(); Object convertedValue = converter.convertIfNecessary(originalValue, paramType, MethodParameter.forMethodOrConstructor(methodOrCtor, paramIndex)); args.arguments[paramIndex] = convertedValue; ConstructorArgumentValues.ValueHolder sourceHolder = (ConstructorArgumentValues.ValueHolder) valueHolder.getSource(); Object sourceValue = sourceHolder.getValue(); if (originalValue == sourceValue || sourceValue instanceof TypedStringValue) { // Either a converted value or still the original one: store converted value. sourceHolder.setConvertedValue(convertedValue); args.preparedArguments[paramIndex] = convertedValue; } else { resolveNecessary = true; args.preparedArguments[paramIndex] = sourceValue; } } catch (TypeMismatchException ex) { throw new UnsatisfiedDependencyException( mbd.getResourceDescription(), beanName, paramIndex, paramType, "Could not convert " + methodType + " argument value of type [" + ObjectUtils.nullSafeClassName(valueHolder.getValue()) + "] to required type [" + paramType.getName() + "]: " + ex.getMessage()); } } } 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) { throw new UnsatisfiedDependencyException( mbd.getResourceDescription(), beanName, paramIndex, paramType, "Ambiguous " + methodType + " argument types - " + "did you specify the correct bean references as " + methodType + " arguments?"); } try { MethodParameter param = MethodParameter.forMethodOrConstructor(methodOrCtor, paramIndex); Object autowiredArgument = resolveAutowiredArgument(param, beanName, autowiredBeanNames, converter); args.rawArguments[paramIndex] = autowiredArgument; args.arguments[paramIndex] = autowiredArgument; args.preparedArguments[paramIndex] = new AutowiredArgumentMarker(); resolveNecessary = true; } catch (BeansException ex) { throw new UnsatisfiedDependencyException( mbd.getResourceDescription(), beanName, paramIndex, paramType, ex); } } } for (Iterator it = autowiredBeanNames.iterator(); it.hasNext();) { String autowiredBeanName = (String) it.next(); this.beanFactory.registerDependentBean(autowiredBeanName, beanName); if (this.beanFactory.logger.isDebugEnabled()) { this.beanFactory.logger.debug("Autowiring by type from bean name '" + beanName + "' via " + methodType + " to bean named '" + autowiredBeanName + "'"); } } if (resolveNecessary) { mbd.preparedConstructorArguments = args.preparedArguments; } else { mbd.resolvedConstructorArguments = args.arguments; } mbd.constructorArgumentsResolved = true; return args; } /** * Template method for resolving the specified argument which is supposed to be autowired. */ protected Object resolveAutowiredArgument( MethodParameter param, String beanName, Set autowiredBeanNames, TypeConverter typeConverter) { return this.autowireFactory.resolveDependency( new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter); } /** * Private inner class for holding argument combinations. */ private static class ArgumentsHolder { public Object rawArguments[]; public Object arguments[]; public Object preparedArguments[]; public ArgumentsHolder(int size) { this.rawArguments = new Object[size]; this.arguments = new Object[size]; this.preparedArguments = new Object[size]; } public ArgumentsHolder(Object[] args) { this.rawArguments = args; this.arguments = args; this.preparedArguments = args; } public int getTypeDifferenceWeight(Class[] paramTypes) { // If valid arguments found, determine type difference weight. // Try type difference weight on both the converted arguments and // the raw arguments. If the raw weight is better, use it. // Decrease raw weight by 1024 to prefer it over equal converted weight. int typeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.arguments); int rawTypeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.rawArguments) - 1024; return (rawTypeDiffWeight < typeDiffWeight ? rawTypeDiffWeight : typeDiffWeight); } } /** * Marker for autowired arguments in a cached argument array. */ private static class AutowiredArgumentMarker { } }