XML constructor-arg element allows for specifying a constructor argument by name now, with target argument names read from the class file via ASM or from Java 6's @ConstructorProperties annotation (SPR-3313)
This commit is contained in:
@@ -139,13 +139,27 @@ public class ConstructorArgumentValues {
|
||||
* @return the ValueHolder for the argument, or <code>null</code> if none set
|
||||
*/
|
||||
public ValueHolder getIndexedArgumentValue(int index, Class requiredType) {
|
||||
return getIndexedArgumentValue(index, requiredType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get argument value for the given index in the constructor argument list.
|
||||
* @param index the index in the constructor argument list
|
||||
* @param requiredType the type to match (can be <code>null</code> to match
|
||||
* untyped values only)
|
||||
* @param requiredName the type to match (can be <code>null</code> to match
|
||||
* unnamed values only)
|
||||
* @return the ValueHolder for the argument, or <code>null</code> if none set
|
||||
*/
|
||||
public ValueHolder getIndexedArgumentValue(int index, Class requiredType, String requiredName) {
|
||||
Assert.isTrue(index >= 0, "Index must not be negative");
|
||||
ValueHolder valueHolder = this.indexedArgumentValues.get(index);
|
||||
if (valueHolder != null) {
|
||||
if (valueHolder.getType() == null ||
|
||||
(requiredType != null && requiredType.getName().equals(valueHolder.getType()))) {
|
||||
return valueHolder;
|
||||
}
|
||||
if (valueHolder != null &&
|
||||
(valueHolder.getType() == null ||
|
||||
(requiredType != null && requiredType.getName().equals(valueHolder.getType()))) &&
|
||||
(valueHolder.getName() == null ||
|
||||
(requiredName != null && requiredName.equals(valueHolder.getName())))) {
|
||||
return valueHolder;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -163,7 +177,7 @@ public class ConstructorArgumentValues {
|
||||
/**
|
||||
* Add generic argument value to be matched by type.
|
||||
* <p>Note: A single generic argument value will just be used once,
|
||||
* rather than matched multiple times (as of Spring 1.1).
|
||||
* rather than matched multiple times.
|
||||
* @param value the argument value
|
||||
*/
|
||||
public void addGenericArgumentValue(Object value) {
|
||||
@@ -173,7 +187,7 @@ public class ConstructorArgumentValues {
|
||||
/**
|
||||
* Add generic argument value to be matched by type.
|
||||
* <p>Note: A single generic argument value will just be used once,
|
||||
* rather than matched multiple times (as of Spring 1.1).
|
||||
* rather than matched multiple times.
|
||||
* @param value the argument value
|
||||
* @param type the type of the constructor argument
|
||||
*/
|
||||
@@ -184,7 +198,7 @@ public class ConstructorArgumentValues {
|
||||
/**
|
||||
* Add generic argument value to be matched by type.
|
||||
* <p>Note: A single generic argument value will just be used once,
|
||||
* rather than matched multiple times (as of Spring 1.1).
|
||||
* rather than matched multiple times.
|
||||
* @param newValue the argument value in the form of a ValueHolder
|
||||
* <p>Note: Identical ValueHolder instances will only be registered once,
|
||||
* to allow for merging and re-merging of argument value definitions. Distinct
|
||||
@@ -199,12 +213,21 @@ public class ConstructorArgumentValues {
|
||||
|
||||
/**
|
||||
* Look for a generic argument value that matches the given type.
|
||||
* @param requiredType the type to match (can be <code>null</code> to find
|
||||
* an arbitrary next generic argument value)
|
||||
* @param requiredType the type to match
|
||||
* @return the ValueHolder for the argument, or <code>null</code> if none set
|
||||
*/
|
||||
public ValueHolder getGenericArgumentValue(Class requiredType) {
|
||||
return getGenericArgumentValue(requiredType, null);
|
||||
return getGenericArgumentValue(requiredType, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for a generic argument value that matches the given type.
|
||||
* @param requiredType the type to match
|
||||
* @param requiredName the name to match
|
||||
* @return the ValueHolder for the argument, or <code>null</code> if none set
|
||||
*/
|
||||
public ValueHolder getGenericArgumentValue(Class requiredType, String requiredName) {
|
||||
return getGenericArgumentValue(requiredType, requiredName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,16 +236,23 @@ public class ConstructorArgumentValues {
|
||||
* resolution process.
|
||||
* @param requiredType the type to match (can be <code>null</code> to find
|
||||
* an arbitrary next generic argument value)
|
||||
* @param requiredName the type to match (can be <code>null</code> to match
|
||||
* unnamed values only)
|
||||
* @param usedValueHolders a Set of ValueHolder objects that have already been used
|
||||
* in the current resolution process and should therefore not be returned again
|
||||
* @return the ValueHolder for the argument, or <code>null</code> if none found
|
||||
*/
|
||||
public ValueHolder getGenericArgumentValue(Class requiredType, Set usedValueHolders) {
|
||||
public ValueHolder getGenericArgumentValue(Class requiredType, String requiredName, Set<ValueHolder> usedValueHolders) {
|
||||
for (ValueHolder valueHolder : this.genericArgumentValues) {
|
||||
if (usedValueHolders == null || !usedValueHolders.contains(valueHolder)) {
|
||||
if (requiredType != null) {
|
||||
// Check matching type.
|
||||
if (valueHolder.getType() != null) {
|
||||
if (valueHolder.getName() != null) {
|
||||
if (valueHolder.getName().equals(requiredName)) {
|
||||
return valueHolder;
|
||||
}
|
||||
}
|
||||
else if (valueHolder.getType() != null) {
|
||||
if (valueHolder.getType().equals(requiredType.getName())) {
|
||||
return valueHolder;
|
||||
}
|
||||
@@ -260,7 +290,19 @@ public class ConstructorArgumentValues {
|
||||
* @return the ValueHolder for the argument, or <code>null</code> if none set
|
||||
*/
|
||||
public ValueHolder getArgumentValue(int index, Class requiredType) {
|
||||
return getArgumentValue(index, requiredType, null);
|
||||
return getArgumentValue(index, requiredType, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for an argument value that either corresponds to the given index
|
||||
* in the constructor argument list or generically matches by type.
|
||||
* @param index the index in the constructor argument list
|
||||
* @param requiredType the type to match
|
||||
* @param requiredName the name to match
|
||||
* @return the ValueHolder for the argument, or <code>null</code> if none set
|
||||
*/
|
||||
public ValueHolder getArgumentValue(int index, Class requiredType, String requiredName) {
|
||||
return getArgumentValue(index, requiredType, requiredName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,11 +317,11 @@ public class ConstructorArgumentValues {
|
||||
* in case of multiple generic argument values of the same type)
|
||||
* @return the ValueHolder for the argument, or <code>null</code> if none set
|
||||
*/
|
||||
public ValueHolder getArgumentValue(int index, Class requiredType, Set usedValueHolders) {
|
||||
public ValueHolder getArgumentValue(int index, Class requiredType, String requiredName, Set<ValueHolder> usedValueHolders) {
|
||||
Assert.isTrue(index >= 0, "Index must not be negative");
|
||||
ValueHolder valueHolder = getIndexedArgumentValue(index, requiredType);
|
||||
ValueHolder valueHolder = getIndexedArgumentValue(index, requiredType, requiredName);
|
||||
if (valueHolder == null) {
|
||||
valueHolder = getGenericArgumentValue(requiredType, usedValueHolders);
|
||||
valueHolder = getGenericArgumentValue(requiredType, requiredName, usedValueHolders);
|
||||
}
|
||||
return valueHolder;
|
||||
}
|
||||
@@ -365,6 +407,8 @@ public class ConstructorArgumentValues {
|
||||
|
||||
private String type;
|
||||
|
||||
private String name;
|
||||
|
||||
private Object source;
|
||||
|
||||
private boolean converted = false;
|
||||
@@ -389,10 +433,20 @@ public class ConstructorArgumentValues {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ValueHolder for the given value, type and name.
|
||||
* @param value the argument value
|
||||
* @param type the type of the constructor argument
|
||||
* @param name the name of the constructor argument
|
||||
*/
|
||||
public ValueHolder(Object value, String type, String name) {
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value for the constructor argument.
|
||||
* Only necessary for manipulating a registered value,
|
||||
* for example in BeanFactoryPostProcessors.
|
||||
* @see PropertyPlaceholderConfigurer
|
||||
*/
|
||||
public void setValue(Object value) {
|
||||
@@ -408,9 +462,6 @@ public class ConstructorArgumentValues {
|
||||
|
||||
/**
|
||||
* Set the type of the constructor argument.
|
||||
* Only necessary for manipulating a registered value,
|
||||
* for example in BeanFactoryPostProcessors.
|
||||
* @see PropertyPlaceholderConfigurer
|
||||
*/
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
@@ -423,6 +474,20 @@ public class ConstructorArgumentValues {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the constructor argument.
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the constructor argument.
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the configuration source <code>Object</code> for this metadata element.
|
||||
* <p>The exact type of the object will depend on the configuration mechanism used.
|
||||
@@ -487,7 +552,7 @@ public class ConstructorArgumentValues {
|
||||
* ValueHolder instance with the same contents.
|
||||
*/
|
||||
public ValueHolder copy() {
|
||||
ValueHolder copy = new ValueHolder(this.value, this.type);
|
||||
ValueHolder copy = new ValueHolder(this.value, this.type, this.name);
|
||||
copy.setSource(this.source);
|
||||
return copy;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ import org.springframework.beans.factory.config.InstantiationAwareBeanPostProces
|
||||
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -108,8 +109,12 @@ import org.springframework.util.StringUtils;
|
||||
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
|
||||
implements AutowireCapableBeanFactory {
|
||||
|
||||
/** Strategy for creating bean instances */
|
||||
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
|
||||
|
||||
/** Resolver strategy for method parameter names */
|
||||
private ParameterNameDiscoverer parameterNameDiscoverer;
|
||||
|
||||
/** Whether to automatically try to resolve circular references between beans */
|
||||
private boolean allowCircularReferences = true;
|
||||
|
||||
@@ -176,6 +181,25 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
return this.instantiationStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ParameterNameDiscoverer to use for resolving method parameter
|
||||
* names if needed (e.g. for constructor names).
|
||||
* <p>Default is none. A typical candidate is
|
||||
* {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer},
|
||||
* which implies an ASM dependency and hence isn't set as the default.
|
||||
*/
|
||||
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||
this.parameterNameDiscoverer = parameterNameDiscoverer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ParameterNameDiscoverer to use for resolving method parameter
|
||||
* names if needed.
|
||||
*/
|
||||
protected ParameterNameDiscoverer getParameterNameDiscoverer() {
|
||||
return this.parameterNameDiscoverer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to allow circular references between beans - and automatically
|
||||
* try to resolve them.
|
||||
@@ -822,9 +846,9 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
}
|
||||
|
||||
// Shortcut when re-creating the same bean...
|
||||
if (mbd.resolvedConstructorOrFactoryMethod != null) {
|
||||
if (mbd.resolvedConstructorOrFactoryMethod != null && args == null) {
|
||||
if (mbd.constructorArgumentsResolved) {
|
||||
return autowireConstructor(beanName, mbd, null, args);
|
||||
return autowireConstructor(beanName, mbd, null, null);
|
||||
}
|
||||
else {
|
||||
return instantiateBean(beanName, mbd);
|
||||
@@ -901,9 +925,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
protected BeanWrapper instantiateUsingFactoryMethod(
|
||||
String beanName, RootBeanDefinition mbd, Object[] explicitArgs) {
|
||||
|
||||
ConstructorResolver constructorResolver =
|
||||
new ConstructorResolver(this, this, getInstantiationStrategy(), getCustomTypeConverter());
|
||||
return constructorResolver.instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
|
||||
return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -923,9 +945,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
protected BeanWrapper autowireConstructor(
|
||||
String beanName, RootBeanDefinition mbd, Constructor[] ctors, Object[] explicitArgs) {
|
||||
|
||||
ConstructorResolver constructorResolver =
|
||||
new ConstructorResolver(this, this, getInstantiationStrategy(), getCustomTypeConverter());
|
||||
return constructorResolver.autowireConstructor(beanName, mbd, ctors, explicitArgs);
|
||||
return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.beans.factory.support;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -38,12 +39,12 @@ 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.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MethodInvoker;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -66,29 +67,20 @@ import org.springframework.util.ReflectionUtils;
|
||||
*/
|
||||
class ConstructorResolver {
|
||||
|
||||
private final AbstractBeanFactory beanFactory;
|
||||
private static final String CONSTRUCTOR_PROPERTIES_CLASS_NAME = "java.beans.ConstructorProperties";
|
||||
|
||||
private final AutowireCapableBeanFactory autowireFactory;
|
||||
private static final boolean constructorPropertiesAnnotationAvailable =
|
||||
ClassUtils.isPresent(CONSTRUCTOR_PROPERTIES_CLASS_NAME, ConstructorResolver.class.getClassLoader());
|
||||
|
||||
private final InstantiationStrategy instantiationStrategy;
|
||||
|
||||
private final TypeConverter typeConverter;
|
||||
private final AbstractAutowireCapableBeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
public ConstructorResolver(AbstractAutowireCapableBeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
this.autowireFactory = autowireFactory;
|
||||
this.instantiationStrategy = instantiationStrategy;
|
||||
this.typeConverter = typeConverter;
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +154,7 @@ class ConstructorResolver {
|
||||
int minTypeDiffWeight = Integer.MAX_VALUE;
|
||||
|
||||
for (int i = 0; i < candidates.length; i++) {
|
||||
Constructor candidate = candidates[i];
|
||||
Constructor<?> candidate = candidates[i];
|
||||
Class[] paramTypes = candidate.getParameterTypes();
|
||||
|
||||
if (constructorToUse != null && argsToUse.length > paramTypes.length) {
|
||||
@@ -174,17 +166,26 @@ class ConstructorResolver {
|
||||
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)");
|
||||
"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");
|
||||
}
|
||||
|
||||
ArgumentsHolder args;
|
||||
List<Exception> causes = null;
|
||||
|
||||
if (resolvedValues != null) {
|
||||
// Try to resolve arguments for current constructor.
|
||||
try {
|
||||
String[] paramNames = null;
|
||||
if (constructorPropertiesAnnotationAvailable) {
|
||||
paramNames = ConstructorPropertiesChecker.evaluateAnnotation(candidate, paramTypes.length);
|
||||
}
|
||||
if (paramNames == null) {
|
||||
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
|
||||
if (pnd != null) {
|
||||
paramNames = pnd.getParameterNames(candidate);
|
||||
}
|
||||
}
|
||||
args = createArgumentArray(
|
||||
beanName, mbd, resolvedValues, bw, paramTypes, candidate, autowiring);
|
||||
beanName, mbd, resolvedValues, bw, paramTypes, paramNames, candidate, autowiring);
|
||||
}
|
||||
catch (UnsatisfiedDependencyException ex) {
|
||||
if (this.beanFactory.logger.isTraceEnabled()) {
|
||||
@@ -238,7 +239,7 @@ class ConstructorResolver {
|
||||
}
|
||||
|
||||
try {
|
||||
Object beanInstance = this.instantiationStrategy.instantiate(
|
||||
Object beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(
|
||||
mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
|
||||
bw.setWrappedInstance(beanInstance);
|
||||
return bw;
|
||||
@@ -385,10 +386,15 @@ class ConstructorResolver {
|
||||
ArgumentsHolder args;
|
||||
|
||||
if (resolvedValues != null) {
|
||||
// Resolved contructor arguments: type conversion and/or autowiring necessary.
|
||||
// Resolved constructor arguments: type conversion and/or autowiring necessary.
|
||||
try {
|
||||
String[] paramNames = null;
|
||||
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
|
||||
if (pnd != null) {
|
||||
paramNames = pnd.getParameterNames(candidate);
|
||||
}
|
||||
args = createArgumentArray(
|
||||
beanName, mbd, resolvedValues, bw, paramTypes, candidate, autowiring);
|
||||
beanName, mbd, resolvedValues, bw, paramTypes, paramNames, candidate, autowiring);
|
||||
}
|
||||
catch (UnsatisfiedDependencyException ex) {
|
||||
if (this.beanFactory.logger.isTraceEnabled()) {
|
||||
@@ -451,7 +457,7 @@ class ConstructorResolver {
|
||||
}
|
||||
|
||||
try {
|
||||
Object beanInstance = this.instantiationStrategy.instantiate(
|
||||
Object beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(
|
||||
mbd, beanName, this.beanFactory, factoryBean, factoryMethodToUse, argsToUse);
|
||||
if (beanInstance == null) {
|
||||
return null;
|
||||
@@ -473,9 +479,10 @@ class ConstructorResolver {
|
||||
String beanName, RootBeanDefinition mbd, BeanWrapper bw,
|
||||
ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) {
|
||||
|
||||
TypeConverter converterToUse = (this.typeConverter != null ? this.typeConverter : bw);
|
||||
TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ?
|
||||
this.beanFactory.getCustomTypeConverter() : bw);
|
||||
BeanDefinitionValueResolver valueResolver =
|
||||
new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converterToUse);
|
||||
new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
|
||||
|
||||
int minNrOfArgs = cargs.getArgumentCount();
|
||||
|
||||
@@ -496,7 +503,7 @@ class ConstructorResolver {
|
||||
Object resolvedValue =
|
||||
valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
|
||||
ConstructorArgumentValues.ValueHolder resolvedValueHolder =
|
||||
new ConstructorArgumentValues.ValueHolder(resolvedValue, valueHolder.getType());
|
||||
new ConstructorArgumentValues.ValueHolder(resolvedValue, valueHolder.getType(), valueHolder.getName());
|
||||
resolvedValueHolder.setSource(valueHolder);
|
||||
resolvedValues.addIndexedArgumentValue(index, resolvedValueHolder);
|
||||
}
|
||||
@@ -510,7 +517,7 @@ class ConstructorResolver {
|
||||
Object resolvedValue =
|
||||
valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
|
||||
ConstructorArgumentValues.ValueHolder resolvedValueHolder =
|
||||
new ConstructorArgumentValues.ValueHolder(resolvedValue, valueHolder.getType());
|
||||
new ConstructorArgumentValues.ValueHolder(resolvedValue, valueHolder.getType(), valueHolder.getName());
|
||||
resolvedValueHolder.setSource(valueHolder);
|
||||
resolvedValues.addGenericArgumentValue(resolvedValueHolder);
|
||||
}
|
||||
@@ -525,11 +532,12 @@ class ConstructorResolver {
|
||||
*/
|
||||
private ArgumentsHolder createArgumentArray(
|
||||
String beanName, RootBeanDefinition mbd, ConstructorArgumentValues resolvedValues,
|
||||
BeanWrapper bw, Class[] paramTypes, Object methodOrCtor, boolean autowiring)
|
||||
throws UnsatisfiedDependencyException {
|
||||
BeanWrapper bw, Class[] paramTypes, String[] paramNames, Object methodOrCtor,
|
||||
boolean autowiring) throws UnsatisfiedDependencyException {
|
||||
|
||||
String methodType = (methodOrCtor instanceof Constructor ? "constructor" : "factory method");
|
||||
TypeConverter converter = (this.typeConverter != null ? this.typeConverter : bw);
|
||||
TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ?
|
||||
this.beanFactory.getCustomTypeConverter() : bw);
|
||||
|
||||
ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
|
||||
Set<ConstructorArgumentValues.ValueHolder> usedValueHolders =
|
||||
@@ -538,15 +546,16 @@ class ConstructorResolver {
|
||||
boolean resolveNecessary = false;
|
||||
|
||||
for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
|
||||
Class paramType = paramTypes[paramIndex];
|
||||
Class<?> paramType = paramTypes[paramIndex];
|
||||
String paramName = (paramNames != null ? paramNames[paramIndex] : null);
|
||||
// Try to find matching constructor argument value, either indexed or generic.
|
||||
ConstructorArgumentValues.ValueHolder valueHolder =
|
||||
resolvedValues.getArgumentValue(paramIndex, paramType, usedValueHolders);
|
||||
resolvedValues.getArgumentValue(paramIndex, paramType, paramName, 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);
|
||||
valueHolder = resolvedValues.getGenericArgumentValue(null, null, usedValueHolders);
|
||||
}
|
||||
if (valueHolder != null) {
|
||||
// We found a potential match - let's give it a try.
|
||||
@@ -637,7 +646,8 @@ class ConstructorResolver {
|
||||
Class[] paramTypes = (methodOrCtor instanceof Method ?
|
||||
((Method) methodOrCtor).getParameterTypes() : ((Constructor) methodOrCtor).getParameterTypes());
|
||||
Object[] argsToResolve = mbd.preparedConstructorArguments;
|
||||
TypeConverter converter = (this.typeConverter != null ? this.typeConverter : bw);
|
||||
TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ?
|
||||
this.beanFactory.getCustomTypeConverter() : bw);
|
||||
BeanDefinitionValueResolver valueResolver =
|
||||
new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
|
||||
Object[] resolvedArgs = new Object[argsToResolve.length];
|
||||
@@ -654,7 +664,7 @@ class ConstructorResolver {
|
||||
else if (argValue instanceof String) {
|
||||
argValue = this.beanFactory.evaluateBeanDefinitionString((String) argValue, mbd);
|
||||
}
|
||||
Class paramType = paramTypes[argIndex];
|
||||
Class<?> paramType = paramTypes[argIndex];
|
||||
try {
|
||||
resolvedArgs[argIndex] = converter.convertIfNecessary(argValue, paramType, methodParam);
|
||||
}
|
||||
@@ -676,7 +686,7 @@ class ConstructorResolver {
|
||||
protected Object resolveAutowiredArgument(
|
||||
MethodParameter param, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) {
|
||||
|
||||
return this.autowireFactory.resolveDependency(
|
||||
return this.beanFactory.resolveDependency(
|
||||
new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
|
||||
}
|
||||
|
||||
@@ -722,4 +732,26 @@ class ConstructorResolver {
|
||||
private static class AutowiredArgumentMarker {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to avoid a Java 6 dependency.
|
||||
*/
|
||||
private static class ConstructorPropertiesChecker {
|
||||
|
||||
public static String[] evaluateAnnotation(Constructor<?> candidate, int paramCount) {
|
||||
ConstructorProperties cp = candidate.getAnnotation(ConstructorProperties.class);
|
||||
if (cp != null) {
|
||||
String[] names = cp.value();
|
||||
if (names.length != paramCount) {
|
||||
throw new IllegalStateException("Constructor annotated with @ConstructorProperties but not " +
|
||||
"corresponding to actual number of parameters (" + paramCount + "): " + candidate);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,7 +48,6 @@ import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -100,9 +99,6 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
/** Whether to allow eager class loading even for lazy-init beans */
|
||||
private boolean allowEagerClassLoading = true;
|
||||
|
||||
/** Resolver strategy for method parameter names */
|
||||
private ParameterNameDiscoverer parameterNameDiscoverer;
|
||||
|
||||
/** Resolver to use for checking if a bean definition is an autowire candidate */
|
||||
private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();
|
||||
|
||||
@@ -177,17 +173,6 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
this.allowEagerClassLoading = allowEagerClassLoading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ParameterNameDiscoverer to use for resolving method parameter
|
||||
* names if needed (e.g. for default qualifier values on autowired methods).
|
||||
* <p>Default is none. A typical candidate is
|
||||
* {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer},
|
||||
* which implies an ASM dependency and hence isn't set as the default.
|
||||
*/
|
||||
public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||
this.parameterNameDiscoverer = parameterNameDiscoverer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a custom autowire candidate resolver for this BeanFactory to use
|
||||
* when deciding whether a bean definition should be considered as a
|
||||
@@ -452,7 +437,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd, DependencyDescriptor descriptor) {
|
||||
resolveBeanClass(mbd, beanName);
|
||||
if (mbd.isFactoryMethodUnique && mbd.resolvedConstructorOrFactoryMethod == null) {
|
||||
new ConstructorResolver(this, null, null, null).resolveFactoryMethodIfPossible(mbd);
|
||||
new ConstructorResolver(this).resolveFactoryMethodIfPossible(mbd);
|
||||
}
|
||||
return getAutowireCandidateResolver().isAutowireCandidate(
|
||||
new BeanDefinitionHolder(mbd, beanName, getAliases(beanName)), descriptor);
|
||||
@@ -622,8 +607,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
public Object resolveDependency(DependencyDescriptor descriptor, String beanName,
|
||||
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
|
||||
|
||||
descriptor.initParameterNameDiscovery(this.parameterNameDiscoverer);
|
||||
Class type = descriptor.getDependencyType();
|
||||
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
|
||||
Class<?> type = descriptor.getDependencyType();
|
||||
|
||||
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
|
||||
if (value != null) {
|
||||
|
||||
@@ -756,6 +756,7 @@ public class BeanDefinitionParserDelegate {
|
||||
public void parseConstructorArgElement(Element ele, BeanDefinition bd) {
|
||||
String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE);
|
||||
String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE);
|
||||
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
|
||||
if (StringUtils.hasLength(indexAttr)) {
|
||||
try {
|
||||
int index = Integer.parseInt(indexAttr);
|
||||
@@ -770,6 +771,9 @@ public class BeanDefinitionParserDelegate {
|
||||
if (StringUtils.hasLength(typeAttr)) {
|
||||
valueHolder.setType(typeAttr);
|
||||
}
|
||||
if (StringUtils.hasLength(nameAttr)) {
|
||||
valueHolder.setName(nameAttr);
|
||||
}
|
||||
valueHolder.setSource(extractSource(ele));
|
||||
bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder);
|
||||
}
|
||||
@@ -790,6 +794,9 @@ public class BeanDefinitionParserDelegate {
|
||||
if (StringUtils.hasLength(typeAttr)) {
|
||||
valueHolder.setType(typeAttr);
|
||||
}
|
||||
if (StringUtils.hasLength(nameAttr)) {
|
||||
valueHolder.setName(nameAttr);
|
||||
}
|
||||
valueHolder.setSource(extractSource(ele));
|
||||
bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2009 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.
|
||||
@@ -168,15 +168,13 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume
|
||||
// Resolve system properties: e.g. "${user.dir}"
|
||||
location = SystemPropertyUtils.resolvePlaceholders(location);
|
||||
|
||||
Set<Resource> actualResources = new LinkedHashSet<Resource>(4);
|
||||
if (ResourcePatternUtils.isUrl(location)) {
|
||||
try {
|
||||
Set<Resource> actualResources = new LinkedHashSet<Resource> (4);
|
||||
int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
|
||||
}
|
||||
Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
|
||||
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
|
||||
}
|
||||
catch (BeanDefinitionStoreException ex) {
|
||||
getReaderContext().error(
|
||||
@@ -186,22 +184,23 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume
|
||||
else {
|
||||
// No URL -> considering resource location as relative to the current file.
|
||||
try {
|
||||
Resource relativeResource = getReaderContext().getResource().createRelative(location);
|
||||
int importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
|
||||
String baseLocation = getReaderContext().getResource().getURL().toString();
|
||||
int importCount = getReaderContext().getReader().loadBeanDefinitions(
|
||||
StringUtils.applyRelativePath(baseLocation, location), actualResources);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
|
||||
}
|
||||
getReaderContext().fireImportProcessed(location, new Resource[] {relativeResource}, extractSource(ele));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
getReaderContext().error(
|
||||
"Invalid relative resource location [" + location + "] to import bean definitions from", ele, ex);
|
||||
getReaderContext().error("Failed to resolve current resource location", ele, ex);
|
||||
}
|
||||
catch (BeanDefinitionStoreException ex) {
|
||||
getReaderContext().error(
|
||||
"Failed to import bean definitions from relative location [" + location + "]", ele, ex);
|
||||
getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
|
||||
ele, ex);
|
||||
}
|
||||
}
|
||||
Resource[] actResArray = actualResources.toArray(new Resource[actualResources.size()]);
|
||||
getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user