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:
@@ -28,6 +28,7 @@
|
||||
<dependency org="org.apache.log4j" name="com.springsource.org.apache.log4j" rev="1.2.15" conf="test->runtime"/>
|
||||
<dependency org="org.junit" name="com.springsource.org.junit" rev="4.5.0" conf="test->runtime"/>
|
||||
<dependency org="org.easymock" name="com.springsource.org.easymock" rev="2.3.0" conf="test->compile"/>
|
||||
<dependency org="org.springframework" name="org.springframework.asm" rev="latest.integration" conf="compile->compile"/>
|
||||
</dependencies>
|
||||
|
||||
</ivy-module>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,8 @@ http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframewor
|
||||
http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd
|
||||
http\://www.springframework.org/schema/tool/spring-tool-2.5.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd
|
||||
http\://www.springframework.org/schema/tool/spring-tool-3.0.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
|
||||
http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-2.5.xsd
|
||||
http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-3.0.xsd
|
||||
http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd
|
||||
http\://www.springframework.org/schema/util/spring-util-2.5.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd
|
||||
http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-2.5.xsd
|
||||
http\://www.springframework.org/schema/util/spring-util-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
|
||||
http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd
|
||||
|
||||
@@ -556,6 +556,16 @@
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="name" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The exact name of the argument in the constructor argument list.
|
||||
Only needed to avoid ambiguities, e.g. in case of 2 arguments of
|
||||
the exact same type. Note: This requires debug symbols to be
|
||||
stored in the class file in order to introspect argument names!
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="ref" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/util"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||
targetNamespace="http://www.springframework.org/schema/util"
|
||||
elementFormDefault="qualified"
|
||||
attributeFormDefault="unqualified">
|
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans"/>
|
||||
<xsd:import namespace="http://www.springframework.org/schema/tool"/>
|
||||
|
||||
<xsd:element name="constant">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Reference a public, static field on a type and expose its value as
|
||||
a bean. For example <code><util:constant static-field="java.lang.Integer.MAX_VALUE"/></code>.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="id" type="xsd:ID"/>
|
||||
<xsd:attribute name="static-field" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="property-path">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
Reference a property on a bean (or as a nested value) and expose its values as
|
||||
a bean. For example <util:property-path path="order.customer.name"/>.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="id" type="xsd:ID"/>
|
||||
<xsd:attribute name="path" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="list">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.beans.factory.config.ListFactoryBean">
|
||||
Builds a List instance of the specified type, populated with the specified content.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="java.util.List"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:listOrSetType">
|
||||
<xsd:attribute name="id" type="xsd:ID"/>
|
||||
<xsd:attribute name="list-class" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:expected-type type="java.lang.Class"/>
|
||||
<tool:assignable-to type="java.util.List"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="scope" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The scope of this collection bean: typically "singleton" (one shared instance,
|
||||
which will be returned by all calls to getBean with the given id), or
|
||||
"prototype" (independent instance resulting from each call to getBean).
|
||||
Default is "singleton". Further scopes, such as "request" or "session",
|
||||
might be supported by extended bean factories (e.g. in a web environment).
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="set">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.beans.factory.config.SetFactoryBean">
|
||||
Builds a Set instance of the specified type, populated with the specified content.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="java.util.Set"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:listOrSetType">
|
||||
<xsd:attribute name="id" type="xsd:ID"/>
|
||||
<xsd:attribute name="set-class" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:expected-type type="java.lang.Class"/>
|
||||
<tool:assignable-to type="java.util.Set"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="scope" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The scope of this collection bean: typically "singleton" (one shared instance,
|
||||
which will be returned by all calls to getBean with the given id), or
|
||||
"prototype" (independent instance resulting from each call to getBean).
|
||||
Default is "singleton". Further scopes, such as "request" or "session",
|
||||
might be supported by extended bean factories (e.g. in a web environment).
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="map">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.beans.factory.config.MapFactoryBean">
|
||||
Builds a Map instance of the specified type, populated with the specified content.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="java.util.Map"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:mapType">
|
||||
<xsd:attribute name="id" type="xsd:ID"/>
|
||||
<xsd:attribute name="map-class" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:expected-type type="java.lang.Class"/>
|
||||
<tool:assignable-to type="java.util.Map"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="scope" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The scope of this collection bean: typically "singleton" (one shared instance,
|
||||
which will be returned by all calls to getBean with the given id), or
|
||||
"prototype" (independent instance resulting from each call to getBean).
|
||||
Default is "singleton". Further scopes, such as "request" or "session",
|
||||
might be supported by extended bean factories (e.g. in a web environment).
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="properties">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.beans.factory.config.PropertiesFactoryBean">
|
||||
Loads a Properties instance from the resource location specified by the '<code>location</code>' attribute.
|
||||
</xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="java.util.Properties"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:propsType">
|
||||
<xsd:attribute name="id" type="xsd:ID"/>
|
||||
<xsd:attribute name="location" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:expected-type type="org.springframework.core.io.Resource"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="local-override" type="xsd:boolean">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Specifies whether local properties override properties from files.
|
||||
Default is "false": properties from files override local defaults.
|
||||
If set to "true", local properties will override defaults from files.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="scope" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The scope of this collection bean: typically "singleton" (one shared instance,
|
||||
which will be returned by all calls to getBean with the given id), or
|
||||
"prototype" (independent instance resulting from each call to getBean).
|
||||
Default is "singleton". Further scopes, such as "request" or "session",
|
||||
might be supported by extended bean factories (e.g. in a web environment).
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
</xsd:schema>
|
||||
@@ -28,6 +28,7 @@ import test.beans.TestBean;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
/**
|
||||
@@ -106,6 +107,7 @@ public class FactoryMethodTests {
|
||||
@Test
|
||||
public void testFactoryMethodsWithNullValue() {
|
||||
DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
|
||||
xbf.setParameterNameDiscoverer(new LocalVariableTableParameterNameDiscoverer());
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(xbf);
|
||||
reader.loadBeanDefinitions(new ClassPathResource("factory-methods.xml", getClass()));
|
||||
|
||||
@@ -118,6 +120,11 @@ public class FactoryMethodTests {
|
||||
assertEquals(27, fm.getNum());
|
||||
assertEquals(null, fm.getName());
|
||||
assertEquals("Juergen", fm.getTestBean().getName());
|
||||
|
||||
fm = (FactoryMethods) xbf.getBean("fullWithNamedNull");
|
||||
assertEquals(27, fm.getNum());
|
||||
assertEquals(null, fm.getName());
|
||||
assertEquals("Juergen", fm.getTestBean().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -264,13 +271,13 @@ public class FactoryMethodTests {
|
||||
TestBean tbArg2 = new TestBean();
|
||||
tbArg2.setName("arg2");
|
||||
|
||||
FactoryMethods fm1 = (FactoryMethods) xbf.getBean("testBeanOnlyPrototype", new Object[] {tbArg});
|
||||
FactoryMethods fm1 = (FactoryMethods) xbf.getBean("testBeanOnlyPrototype", tbArg);
|
||||
assertEquals(0, fm1.getNum());
|
||||
assertEquals("default", fm1.getName());
|
||||
// This comes from the test bean
|
||||
assertEquals("arg1", fm1.getTestBean().getName());
|
||||
|
||||
FactoryMethods fm2 = (FactoryMethods) xbf.getBean("testBeanOnlyPrototype", new Object[] {tbArg2});
|
||||
FactoryMethods fm2 = (FactoryMethods) xbf.getBean("testBeanOnlyPrototype", tbArg2);
|
||||
assertEquals("arg2", fm2.getTestBean().getName());
|
||||
assertEquals(fm1.getNum(), fm2.getNum());
|
||||
assertEquals(fm2.getStringValue(), "testBeanOnlyPrototypeDISetterString");
|
||||
@@ -279,12 +286,12 @@ public class FactoryMethodTests {
|
||||
assertSame(fm2.getTestBean(), fm2.getTestBean());
|
||||
assertNotSame(fm1, fm2);
|
||||
|
||||
FactoryMethods fm3 = (FactoryMethods) xbf.getBean("testBeanOnlyPrototype", new Object[] {tbArg2, new Integer(1), "myName"});
|
||||
FactoryMethods fm3 = (FactoryMethods) xbf.getBean("testBeanOnlyPrototype", tbArg2, new Integer(1), "myName");
|
||||
assertEquals(1, fm3.getNum());
|
||||
assertEquals("myName", fm3.getName());
|
||||
assertEquals("arg2", fm3.getTestBean().getName());
|
||||
|
||||
FactoryMethods fm4 = (FactoryMethods) xbf.getBean("testBeanOnlyPrototype", new Object[] {tbArg});
|
||||
FactoryMethods fm4 = (FactoryMethods) xbf.getBean("testBeanOnlyPrototype", tbArg);
|
||||
assertEquals(0, fm4.getNum());
|
||||
assertEquals("default", fm4.getName());
|
||||
assertEquals("arg1", fm4.getTestBean().getName());
|
||||
@@ -296,7 +303,7 @@ public class FactoryMethodTests {
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(xbf);
|
||||
reader.loadBeanDefinitions(new ClassPathResource("factory-methods.xml", getClass()));
|
||||
try {
|
||||
xbf.getBean("testBeanOnly", new Object[] {new TestBean()});
|
||||
xbf.getBean("testBeanOnly", new TestBean());
|
||||
fail("Shouldn't allow args to be passed to a singleton");
|
||||
}
|
||||
catch (BeanDefinitionStoreException ex) {
|
||||
@@ -311,7 +318,7 @@ public class FactoryMethodTests {
|
||||
reader.loadBeanDefinitions(new ClassPathResource("factory-methods.xml", getClass()));
|
||||
xbf.getBean("testBeanOnly");
|
||||
try {
|
||||
xbf.getBean("testBeanOnly", new Object[] {new TestBean()});
|
||||
xbf.getBean("testBeanOnly", new TestBean());
|
||||
fail("Shouldn't allow args to be passed to a singleton");
|
||||
}
|
||||
catch (BeanDefinitionStoreException ex) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
|
||||
|
||||
<beans>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||||
|
||||
<bean id="default" class="org.springframework.beans.factory.xml.FactoryMethods"
|
||||
factory-method="defaultInstance">
|
||||
@@ -42,17 +42,24 @@
|
||||
</bean>
|
||||
|
||||
<bean id="fullWithNull" class="org.springframework.beans.factory.xml.FactoryMethods"
|
||||
factory-method="newInstance">
|
||||
factory-method="newInstance" lazy-init="true">
|
||||
<constructor-arg index="2" type="java.lang.Integer"><null/></constructor-arg>
|
||||
<constructor-arg index="0"><ref local="juergen"/></constructor-arg>
|
||||
<constructor-arg index="1"><value>27</value></constructor-arg>
|
||||
<constructor-arg index="2" type="java.lang.Integer"><null/></constructor-arg>
|
||||
</bean>
|
||||
|
||||
<bean id="fullWithGenericNull" class="org.springframework.beans.factory.xml.FactoryMethods"
|
||||
factory-method="newInstance">
|
||||
factory-method="newInstance" lazy-init="true">
|
||||
<constructor-arg type="java.lang.Integer"><null/></constructor-arg>
|
||||
<constructor-arg><ref local="juergen"/></constructor-arg>
|
||||
<constructor-arg type="int"><value>27</value></constructor-arg>
|
||||
<constructor-arg type="java.lang.Integer"><null/></constructor-arg>
|
||||
</bean>
|
||||
|
||||
<bean id="fullWithNamedNull" class="org.springframework.beans.factory.xml.FactoryMethods"
|
||||
factory-method="newInstance" lazy-init="true">
|
||||
<constructor-arg name="something"><null/></constructor-arg>
|
||||
<constructor-arg name="tb"><ref local="juergen"/></constructor-arg>
|
||||
<constructor-arg name="num"><value>27</value></constructor-arg>
|
||||
</bean>
|
||||
|
||||
<bean id="fullWithAutowire" class="org.springframework.beans.factory.xml.FactoryMethods"
|
||||
|
||||
@@ -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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.beans.factory.xml;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
@@ -23,7 +24,6 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
@@ -39,7 +39,6 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.support.MethodReplacer;
|
||||
|
||||
|
||||
/**
|
||||
* Types used by {@link XmlBeanFactoryTests} and its attendant XML config files.
|
||||
*
|
||||
@@ -55,11 +54,11 @@ final class XmlBeanFactoryTestTypes { }
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class ConstructorDependenciesBean implements Serializable {
|
||||
|
||||
|
||||
private int age;
|
||||
|
||||
|
||||
private String name;
|
||||
|
||||
|
||||
private TestBean spouse1;
|
||||
|
||||
private TestBean spouse2;
|
||||
@@ -83,6 +82,7 @@ class ConstructorDependenciesBean implements Serializable {
|
||||
this.spouse2 = spouse2;
|
||||
}
|
||||
|
||||
@ConstructorProperties({"spouse", "otherSpouse", "myAge"})
|
||||
public ConstructorDependenciesBean(TestBean spouse1, TestBean spouse2, int age) {
|
||||
this.spouse1 = spouse1;
|
||||
this.spouse2 = spouse2;
|
||||
@@ -122,7 +122,33 @@ class ConstructorDependenciesBean implements Serializable {
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SimpleConstructorArgBean {
|
||||
|
||||
private int age;
|
||||
|
||||
private String name;
|
||||
|
||||
public SimpleConstructorArgBean() {
|
||||
}
|
||||
|
||||
public SimpleConstructorArgBean(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public SimpleConstructorArgBean(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
|
||||
|
||||
<beans>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||||
|
||||
<bean id="rod1" class="org.springframework.beans.factory.xml.ConstructorDependenciesBean">
|
||||
<constructor-arg><ref bean="kerry2"/></constructor-arg>
|
||||
@@ -104,11 +104,14 @@
|
||||
</bean>
|
||||
|
||||
<bean id="rod16" class="org.springframework.beans.factory.xml.ConstructorDependenciesBean">
|
||||
<constructor-arg><ref bean="kerry2"/></constructor-arg>
|
||||
<constructor-arg><ref bean="kerry1"/></constructor-arg>
|
||||
<constructor-arg name="otherSpouse"><ref bean="kerry1"/></constructor-arg>
|
||||
<constructor-arg name="spouse"><ref bean="kerry2"/></constructor-arg>
|
||||
<constructor-arg><value>29</value></constructor-arg>
|
||||
</bean>
|
||||
|
||||
<bean id="rod17" class="org.springframework.beans.factory.xml.SimpleConstructorArgBean" scope="prototype">
|
||||
</bean>
|
||||
|
||||
<bean id="kerry1" class="org.springframework.beans.TestBean">
|
||||
<property name="name">
|
||||
<value>Kerry1</value>
|
||||
|
||||
@@ -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.
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
package org.springframework.beans.factory.xml;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -29,7 +26,11 @@ import java.net.URL;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
@@ -62,7 +63,6 @@ import org.springframework.core.io.support.EncodedResource;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.util.SerializationTestUtils;
|
||||
import org.springframework.util.StopWatch;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
/**
|
||||
* Miscellaneous tests for XML bean definitions.
|
||||
@@ -920,11 +920,11 @@ public final class XmlBeanFactoryTests {
|
||||
|
||||
ConstructorDependenciesBean rod9 = (ConstructorDependenciesBean) xbf.getBean("rod9");
|
||||
assertEquals(99, rod9.getAge());
|
||||
ConstructorDependenciesBean rod9a = (ConstructorDependenciesBean) xbf.getBean("rod9", new Object[] {new Integer(98)});
|
||||
ConstructorDependenciesBean rod9a = (ConstructorDependenciesBean) xbf.getBean("rod9", 98);
|
||||
assertEquals(98, rod9a.getAge());
|
||||
ConstructorDependenciesBean rod9b = (ConstructorDependenciesBean) xbf.getBean("rod9", new Object[] {"myName"});
|
||||
ConstructorDependenciesBean rod9b = (ConstructorDependenciesBean) xbf.getBean("rod9", "myName");
|
||||
assertEquals("myName", rod9b.getName());
|
||||
ConstructorDependenciesBean rod9c = (ConstructorDependenciesBean) xbf.getBean("rod9", new Object[] {new Integer(97)});
|
||||
ConstructorDependenciesBean rod9c = (ConstructorDependenciesBean) xbf.getBean("rod9", 97);
|
||||
assertEquals(97, rod9c.getAge());
|
||||
|
||||
ConstructorDependenciesBean rod10 = (ConstructorDependenciesBean) xbf.getBean("rod10");
|
||||
@@ -955,6 +955,20 @@ public final class XmlBeanFactoryTests {
|
||||
assertEquals(29, rod16.getAge());
|
||||
}
|
||||
|
||||
public @Test void testPrototypeWithExplicitArguments() {
|
||||
XmlBeanFactory xbf = new XmlBeanFactory(CONSTRUCTOR_ARG_CONTEXT);
|
||||
SimpleConstructorArgBean cd1 = (SimpleConstructorArgBean) xbf.getBean("rod17");
|
||||
assertEquals(0, cd1.getAge());
|
||||
SimpleConstructorArgBean cd2 = (SimpleConstructorArgBean) xbf.getBean("rod17", 98);
|
||||
assertEquals(98, cd2.getAge());
|
||||
SimpleConstructorArgBean cd3 = (SimpleConstructorArgBean) xbf.getBean("rod17", "myName");
|
||||
assertEquals("myName", cd3.getName());
|
||||
SimpleConstructorArgBean cd4 = (SimpleConstructorArgBean) xbf.getBean("rod17");
|
||||
assertEquals(0, cd4.getAge());
|
||||
SimpleConstructorArgBean cd5 = (SimpleConstructorArgBean) xbf.getBean("rod17", 97);
|
||||
assertEquals(97, cd5.getAge());
|
||||
}
|
||||
|
||||
public @Test void testConstructorArgWithSingleMatch() {
|
||||
XmlBeanFactory xbf = new XmlBeanFactory(CONSTRUCTOR_ARG_CONTEXT);
|
||||
File file = (File) xbf.getBean("file");
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
|
||||
|
||||
<beans>
|
||||
|
||||
<import resource="ClassPathXmlApplicationContextTests-resourceImport.xml"/>
|
||||
<import resource="ClassPathXmlApplicationContextTests-resourceIm*.xml"/>
|
||||
|
||||
<bean id="resource2" class="org.springframework.beans.ResourceTestBean">
|
||||
<constructor-arg index="0">
|
||||
<constructor-arg name="inputStream">
|
||||
<value>classpath:org/springframework/beans/factory/xml/test.properties</value>
|
||||
</constructor-arg>
|
||||
<constructor-arg index="1">
|
||||
<value>classpath:org/springframework/beans/factory/xml/test.properties</value>
|
||||
<constructor-arg name="resource">
|
||||
<value>test.properties</value>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
<bean id="resource1" class="org.springframework.beans.ResourceTestBean">
|
||||
<property name="resource">
|
||||
<value>classpath:org/springframework/beans/factory/xml/test.properties</value>
|
||||
<value>test.properties</value>
|
||||
</property>
|
||||
<property name="inputStream">
|
||||
<value>classpath:org/springframework/beans/factory/xml/test.properties</value>
|
||||
|
||||
@@ -322,7 +322,7 @@ public final class ClassPathXmlApplicationContextTests {
|
||||
ClassPathXmlApplicationContext ctx =
|
||||
new ClassPathXmlApplicationContext(RESOURCE_CONTEXT) {
|
||||
public Resource getResource(String location) {
|
||||
if (FQ_TEST_PROPERTIES.equals(location)) {
|
||||
if (TEST_PROPERTIES.equals(location)) {
|
||||
return new ClassPathResource(TEST_PROPERTIES, ClassPathXmlApplicationContextTests.class);
|
||||
}
|
||||
return super.getResource(location);
|
||||
@@ -336,13 +336,13 @@ public final class ClassPathXmlApplicationContextTests {
|
||||
assertEquals("contexttest", writer.toString());
|
||||
writer = new StringWriter();
|
||||
FileCopyUtils.copy(new InputStreamReader(resource1.getInputStream()), writer);
|
||||
assertEquals("contexttest", writer.toString());
|
||||
assertEquals("test", writer.toString());
|
||||
writer = new StringWriter();
|
||||
FileCopyUtils.copy(new InputStreamReader(resource2.getResource().getInputStream()), writer);
|
||||
assertEquals("contexttest", writer.toString());
|
||||
writer = new StringWriter();
|
||||
FileCopyUtils.copy(new InputStreamReader(resource2.getInputStream()), writer);
|
||||
assertEquals("contexttest", writer.toString());
|
||||
assertEquals("test", writer.toString());
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user