diff --git a/org.springframework.beans/ivy.xml b/org.springframework.beans/ivy.xml index d26d757c53c..57077c1febb 100644 --- a/org.springframework.beans/ivy.xml +++ b/org.springframework.beans/ivy.xml @@ -28,6 +28,7 @@ + diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java index a5d03870b55..f3df62f97b0 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java @@ -139,13 +139,27 @@ public class ConstructorArgumentValues { * @return the ValueHolder for the argument, or null 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 null to match + * untyped values only) + * @param requiredName the type to match (can be null to match + * unnamed values only) + * @return the ValueHolder for the argument, or null 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. *

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. *

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. *

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 *

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 null to find - * an arbitrary next generic argument value) + * @param requiredType the type to match * @return the ValueHolder for the argument, or null 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 null 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 null to find * an arbitrary next generic argument value) + * @param requiredName the type to match (can be null 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 null if none found */ - public ValueHolder getGenericArgumentValue(Class requiredType, Set usedValueHolders) { + public ValueHolder getGenericArgumentValue(Class requiredType, String requiredName, Set 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 null 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 null 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 null if none set */ - public ValueHolder getArgumentValue(int index, Class requiredType, Set usedValueHolders) { + public ValueHolder getArgumentValue(int index, Class requiredType, String requiredName, Set 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 Object for this metadata element. *

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; } diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index ad5e09344d6..c7e5f8b15d7 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -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). + *

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); } /** diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index 37c7565ce00..86c3bd3f006 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -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 null 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 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 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 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; + } + } + } + } diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index e50cb973f95..a3eda4c784f 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -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). - *

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 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) { diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java index 0e858e46809..33dbfdd61bf 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/BeanDefinitionParserDelegate.java @@ -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); } diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java index f5a08e5bb58..8f6508623af 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java @@ -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 actualResources = new LinkedHashSet(4); if (ResourcePatternUtils.isUrl(location)) { try { - Set actualResources = new LinkedHashSet (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)); } /** diff --git a/org.springframework.beans/src/main/resources/META-INF/spring.schemas b/org.springframework.beans/src/main/resources/META-INF/spring.schemas index bccc5af1f86..e843e676fdb 100644 --- a/org.springframework.beans/src/main/resources/META-INF/spring.schemas +++ b/org.springframework.beans/src/main/resources/META-INF/spring.schemas @@ -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 diff --git a/org.springframework.beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-3.0.xsd b/org.springframework.beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-3.0.xsd index f5c5ff7d87c..f1bc731851d 100644 --- a/org.springframework.beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-3.0.xsd +++ b/org.springframework.beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-3.0.xsd @@ -556,6 +556,16 @@ ]]> + + + + + + + + + + + + + + + Reference a public, static field on a type and expose its value as + a bean. For example <util:constant static-field="java.lang.Integer.MAX_VALUE"/>. + + + + + + + + + + + + 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"/>. + + + + + + + + + + + + Builds a List instance of the specified type, populated with the specified content. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Builds a Set instance of the specified type, populated with the specified content. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Builds a Map instance of the specified type, populated with the specified content. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Loads a Properties instance from the resource location specified by the 'location' attribute. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.springframework.beans/src/test/java/org/springframework/beans/factory/xml/FactoryMethodTests.java b/org.springframework.beans/src/test/java/org/springframework/beans/factory/xml/FactoryMethodTests.java index 52d53b5d9f7..ed0933f079a 100644 --- a/org.springframework.beans/src/test/java/org/springframework/beans/factory/xml/FactoryMethodTests.java +++ b/org.springframework.beans/src/test/java/org/springframework/beans/factory/xml/FactoryMethodTests.java @@ -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) { diff --git a/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/factory-methods.xml b/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/factory-methods.xml index d7b2c24b4b3..f97701759b6 100644 --- a/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/factory-methods.xml +++ b/org.springframework.beans/src/test/resources/org/springframework/beans/factory/xml/factory-methods.xml @@ -1,7 +1,7 @@ - - - + @@ -42,17 +42,24 @@ + factory-method="newInstance" lazy-init="true"> + 27 - + factory-method="newInstance" lazy-init="true"> + 27 - + + + + + + 27 - - - + @@ -104,11 +104,14 @@ - - + + 29 + + + Kerry1 diff --git a/org.springframework.context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests.java b/org.springframework.context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests.java index 1ff8b72fabf..8d578b2541b 100644 --- a/org.springframework.context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests.java +++ b/org.springframework.context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests.java @@ -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"); diff --git a/org.springframework.context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests-resource.xml b/org.springframework.context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests-resource.xml index 141cb3f1d6d..e339891102f 100644 --- a/org.springframework.context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests-resource.xml +++ b/org.springframework.context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests-resource.xml @@ -1,16 +1,16 @@ - + - - - + - + classpath:org/springframework/beans/factory/xml/test.properties - - classpath:org/springframework/beans/factory/xml/test.properties + + test.properties diff --git a/org.springframework.context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests-resourceImport.xml b/org.springframework.context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests-resourceImport.xml index e5515885eff..0788f511084 100644 --- a/org.springframework.context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests-resourceImport.xml +++ b/org.springframework.context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests-resourceImport.xml @@ -5,7 +5,7 @@ - classpath:org/springframework/beans/factory/xml/test.properties + test.properties classpath:org/springframework/beans/factory/xml/test.properties diff --git a/org.springframework.context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests.java b/org.springframework.context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests.java index 6725bf281a9..1fdec145cc6 100644 --- a/org.springframework.context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests.java +++ b/org.springframework.context/src/test/java/org/springframework/context/support/ClassPathXmlApplicationContextTests.java @@ -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(); }