提交 53333c3e 编写于 作者: J Juergen Hoeller

XML constructor-arg element allows for specifying a constructor argument by...

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)
上级 0a0b10b2
......@@ -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);
}
......
/*
* 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[
......
<?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>&lt;util:constant static-field=&quot;java.lang.Integer.MAX_VALUE&quot;/&gt;</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 &lt;util:property-path path=&quot;order.customer.name&quot;/&gt;.
</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) {
......
<?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"
......
/*
* 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;
}
}
......
<?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>
......
/*
* 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");
......
<?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();
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册