diff options
author | Emmanuel Bourg <ebourg@apache.org> | 2016-08-03 19:55:01 +0200 |
---|---|---|
committer | Emmanuel Bourg <ebourg@apache.org> | 2016-08-03 19:55:01 +0200 |
commit | 75a721d1019da2a2fa86e24ff439df4a224e5b19 (patch) | |
tree | 2c44c00ce2c8641cccad177177e5682e187a17ea /spring-beans | |
parent | 9eaca6a06af3cbceb3754de19d477be770614265 (diff) |
Imported Upstream version 4.3.2
Diffstat (limited to 'spring-beans')
82 files changed, 3653 insertions, 586 deletions
diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java index cf3ebd9a..f3e3bd14 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractNestablePropertyAccessor.java @@ -90,11 +90,11 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA private int autoGrowCollectionLimit = Integer.MAX_VALUE; - private Object wrappedObject; + Object wrappedObject; private String nestedPath = ""; - private Object rootObject; + Object rootObject; /** * Map with cached nested Accessors: nested path -> Accessor instance. @@ -914,11 +914,9 @@ public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyA return BeanUtils.instantiate(type); } } - catch (Exception ex) { - // TODO: Root cause exception context is lost here; just exception message preserved. - // Should we throw another exception type that preserves context instead? + catch (Throwable ex) { throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + name, - "Could not instantiate property type [" + type.getName() + "] to auto-grow nested property path: " + ex); + "Could not instantiate property type [" + type.getName() + "] to auto-grow nested property path", ex); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java index 07871217..216e5a4a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java @@ -148,7 +148,7 @@ public abstract class AbstractPropertyAccessor extends TypeConverterSupport impl * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyAccessException if the property was valid but the - * accessor method failed or a type mismatch occured + * accessor method failed or a type mismatch occurred */ @Override public abstract void setPropertyValue(String propertyName, Object value) throws BeansException; diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanInfoFactory.java b/spring-beans/src/main/java/org/springframework/beans/BeanInfoFactory.java index 1906fbfd..4c5fd27c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanInfoFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanInfoFactory.java @@ -24,7 +24,7 @@ import java.beans.IntrospectionException; * Can be used to plug in custom bean property resolution strategies (e.g. for other * languages on the JVM) or more efficient {@link BeanInfo} retrieval algorithms. * - * <p>BeanInfoFactories are are instantiated by the {@link CachedIntrospectionResults}, + * <p>BeanInfoFactories are instantiated by the {@link CachedIntrospectionResults}, * by using the {@link org.springframework.core.io.support.SpringFactoriesLoader} * utility class. * diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanInstantiationException.java b/spring-beans/src/main/java/org/springframework/beans/BeanInstantiationException.java index 09190f6f..d8f7d246 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanInstantiationException.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanInstantiationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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,9 @@ package org.springframework.beans; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + /** * Exception thrown when instantiation of a bean failed. * Carries the offending bean class. @@ -28,6 +31,10 @@ public class BeanInstantiationException extends FatalBeanException { private Class<?> beanClass; + private Constructor<?> constructor; + + private Method constructingMethod; + /** * Create a new BeanInstantiationException. @@ -49,12 +56,60 @@ public class BeanInstantiationException extends FatalBeanException { this.beanClass = beanClass; } + /** + * Create a new BeanInstantiationException. + * @param constructor the offending constructor + * @param msg the detail message + * @param cause the root cause + * @since 4.3 + */ + public BeanInstantiationException(Constructor<?> constructor, String msg, Throwable cause) { + super("Failed to instantiate [" + constructor.getDeclaringClass().getName() + "]: " + msg, cause); + this.beanClass = constructor.getDeclaringClass(); + this.constructor = constructor; + } /** - * Return the offending bean class. + * Create a new BeanInstantiationException. + * @param constructingMethod the delegate for bean construction purposes + * (typically, but not necessarily, a static factory method) + * @param msg the detail message + * @param cause the root cause + * @since 4.3 + */ + public BeanInstantiationException(Method constructingMethod, String msg, Throwable cause) { + super("Failed to instantiate [" + constructingMethod.getReturnType().getName() + "]: " + msg, cause); + this.beanClass = constructingMethod.getReturnType(); + this.constructingMethod = constructingMethod; + } + + + /** + * Return the offending bean class (never {@code null}). + * @return the class that was to be instantiated */ public Class<?> getBeanClass() { return this.beanClass; } + /** + * Return the offending constructor, if known. + * @return the constructor in use, or {@code null} in case of a + * factory method or in case of default instantiation + * @since 4.3 + */ + public Constructor<?> getConstructor() { + return this.constructor; + } + + /** + * Return the delegate for bean construction purposes, if known. + * @return the method in use (typically a static factory method), + * or {@code null} in case of constructor-based instantiation + * @since 4.3 + */ + public Method getConstructingMethod() { + return this.constructingMethod; + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java index 5c735f16..50c04c3b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java @@ -63,11 +63,10 @@ public abstract class BeanUtils { /** * Convenience method to instantiate a class using its no-arg constructor. - * As this method doesn't try to load classes by name, it should avoid - * class-loading issues. * @param clazz class to instantiate * @return the new instance * @throws BeanInstantiationException if the bean cannot be instantiated + * @see Class#newInstance() */ public static <T> T instantiate(Class<T> clazz) throws BeanInstantiationException { Assert.notNull(clazz, "Class must not be null"); @@ -87,13 +86,12 @@ public abstract class BeanUtils { /** * Instantiate a class using its no-arg constructor. - * As this method doesn't try to load classes by name, it should avoid - * class-loading issues. * <p>Note that this method tries to set the constructor accessible * if given a non-accessible (that is, non-public) constructor. * @param clazz class to instantiate * @return the new instance * @throws BeanInstantiationException if the bean cannot be instantiated + * @see Constructor#newInstance */ public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException { Assert.notNull(clazz, "Class must not be null"); @@ -110,18 +108,16 @@ public abstract class BeanUtils { /** * Instantiate a class using its no-arg constructor and return the new instance - * as the the specified assignable type. - * <p>Useful in cases where - * the type of the class to instantiate (clazz) is not available, but the type - * desired (assignableTo) is known. - * <p>As this method doesn't try to load classes by name, it should avoid - * class-loading issues. - * <p>Note that this method tries to set the constructor accessible - * if given a non-accessible (that is, non-public) constructor. + * as the specified assignable type. + * <p>Useful in cases where the type of the class to instantiate (clazz) is not + * available, but the type desired (assignableTo) is known. + * <p>Note that this method tries to set the constructor accessible if given a + * non-accessible (that is, non-public) constructor. * @param clazz class to instantiate * @param assignableTo type that clazz must be assignableTo * @return the new instance * @throws BeanInstantiationException if the bean cannot be instantiated + * @see Constructor#newInstance */ @SuppressWarnings("unchecked") public static <T> T instantiateClass(Class<?> clazz, Class<T> assignableTo) throws BeanInstantiationException { @@ -131,14 +127,13 @@ public abstract class BeanUtils { /** * Convenience method to instantiate a class using the given constructor. - * As this method doesn't try to load classes by name, it should avoid - * class-loading issues. - * <p>Note that this method tries to set the constructor accessible - * if given a non-accessible (that is, non-public) constructor. + * <p>Note that this method tries to set the constructor accessible if given a + * non-accessible (that is, non-public) constructor. * @param ctor the constructor to instantiate * @param args the constructor arguments to apply * @return the new instance * @throws BeanInstantiationException if the bean cannot be instantiated + * @see Constructor#newInstance */ public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException { Assert.notNull(ctor, "Constructor must not be null"); @@ -147,20 +142,16 @@ public abstract class BeanUtils { return ctor.newInstance(args); } catch (InstantiationException ex) { - throw new BeanInstantiationException(ctor.getDeclaringClass(), - "Is it an abstract class?", ex); + throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex); } catch (IllegalAccessException ex) { - throw new BeanInstantiationException(ctor.getDeclaringClass(), - "Is the constructor accessible?", ex); + throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex); } catch (IllegalArgumentException ex) { - throw new BeanInstantiationException(ctor.getDeclaringClass(), - "Illegal arguments for constructor", ex); + throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex); } catch (InvocationTargetException ex) { - throw new BeanInstantiationException(ctor.getDeclaringClass(), - "Constructor threw exception", ex.getTargetException()); + throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException()); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java index 6615661d..4d10dc0c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanWrapperImpl.java @@ -133,6 +133,19 @@ public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements } + /** + * Set a bean instance to hold, without any unwrapping of {@link java.util.Optional}. + * @param object the actual target object + * @since 4.3 + * @see #setWrappedInstance(Object) + */ + public void setBeanInstance(Object object) { + this.wrappedObject = object; + this.rootObject = object; + this.typeConverterDelegate = new TypeConverterDelegate(this, this.wrappedObject); + setIntrospectionClass(object.getClass()); + } + @Override public void setWrappedInstance(Object object, String nestedPath, Object rootObject) { super.setWrappedInstance(object, nestedPath, rootObject); diff --git a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java index 41c2f4c5..2efe9dab 100644 --- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java +++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -303,6 +303,24 @@ public class CachedIntrospectionResults { this.propertyDescriptorCache.put(pd.getName(), pd); } + // Explicitly check implemented interfaces for setter/getter methods as well, + // in particular for Java 8 default methods... + Class<?> clazz = beanClass; + while (clazz != null) { + Class<?>[] ifcs = clazz.getInterfaces(); + for (Class<?> ifc : ifcs) { + BeanInfo ifcInfo = Introspector.getBeanInfo(ifc, Introspector.IGNORE_ALL_BEANINFO); + PropertyDescriptor[] ifcPds = ifcInfo.getPropertyDescriptors(); + for (PropertyDescriptor pd : ifcPds) { + if (!this.propertyDescriptorCache.containsKey(pd.getName())) { + pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd); + this.propertyDescriptorCache.put(pd.getName(), pd); + } + } + } + clazz = clazz.getSuperclass(); + } + this.typeDescriptorCache = new ConcurrentReferenceHashMap<PropertyDescriptor, TypeDescriptor>(); } catch (IntrospectionException ex) { diff --git a/spring-beans/src/main/java/org/springframework/beans/NullValueInNestedPathException.java b/spring-beans/src/main/java/org/springframework/beans/NullValueInNestedPathException.java index e01fafbf..e15885bc 100644 --- a/spring-beans/src/main/java/org/springframework/beans/NullValueInNestedPathException.java +++ b/spring-beans/src/main/java/org/springframework/beans/NullValueInNestedPathException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -24,6 +24,7 @@ package org.springframework.beans; * spouse property of the target object has a null value. * * @author Rod Johnson + * @author Juergen Hoeller */ @SuppressWarnings("serial") public class NullValueInNestedPathException extends InvalidPropertyException { @@ -47,4 +48,16 @@ public class NullValueInNestedPathException extends InvalidPropertyException { super(beanClass, propertyName, msg); } + /** + * Create a new NullValueInNestedPathException. + * @param beanClass the offending bean class + * @param propertyName the offending property + * @param msg the detail message + * @param cause the root cause + * @since 4.3.2 + */ + public NullValueInNestedPathException(Class<?> beanClass, String propertyName, String msg, Throwable cause) { + super(beanClass, propertyName, msg, cause); + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java index 00068ea4..c7faf355 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -120,7 +120,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyAccessException if the property was valid but the - * accessor method failed or a type mismatch occured + * accessor method failed or a type mismatch occurred */ void setPropertyValue(String propertyName, Object value) throws BeansException; @@ -130,7 +130,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyAccessException if the property was valid but the - * accessor method failed or a type mismatch occured + * accessor method failed or a type mismatch occurred */ void setPropertyValue(PropertyValue pv) throws BeansException; @@ -144,7 +144,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. */ @@ -164,7 +164,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. * @see #setPropertyValues(PropertyValues, boolean, boolean) @@ -185,7 +185,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. * @see #setPropertyValues(PropertyValues, boolean, boolean) @@ -208,7 +208,7 @@ public interface PropertyAccessor { * @throws InvalidPropertyException if there is no such property or * if the property isn't writable * @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions - * occured for specific properties during the batch update. This exception bundles + * occurred for specific properties during the batch update. This exception bundles * all individual PropertyAccessExceptions. All other properties will have been * successfully updated. */ diff --git a/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java b/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java index f42d6c33..71825c38 100644 --- a/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java +++ b/spring-beans/src/main/java/org/springframework/beans/PropertyEditorRegistrySupport.java @@ -59,6 +59,7 @@ import org.springframework.beans.propertyeditors.FileEditor; import org.springframework.beans.propertyeditors.InputSourceEditor; import org.springframework.beans.propertyeditors.InputStreamEditor; import org.springframework.beans.propertyeditors.LocaleEditor; +import org.springframework.beans.propertyeditors.PathEditor; import org.springframework.beans.propertyeditors.PatternEditor; import org.springframework.beans.propertyeditors.PropertiesEditor; import org.springframework.beans.propertyeditors.ReaderEditor; @@ -87,11 +88,21 @@ import org.springframework.util.ClassUtils; */ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry { + private static Class<?> pathClass; + private static Class<?> zoneIdClass; static { + ClassLoader cl = PropertyEditorRegistrySupport.class.getClassLoader(); + try { + pathClass = ClassUtils.forName("java.nio.file.Path", cl); + } + catch (ClassNotFoundException ex) { + // Java 7 Path class not available + pathClass = null; + } try { - zoneIdClass = ClassUtils.forName("java.time.ZoneId", PropertyEditorRegistrySupport.class.getClassLoader()); + zoneIdClass = ClassUtils.forName("java.time.ZoneId", cl); } catch (ClassNotFoundException ex) { // Java 8 ZoneId class not available @@ -211,6 +222,9 @@ public class PropertyEditorRegistrySupport implements PropertyEditorRegistry { this.defaultEditors.put(InputStream.class, new InputStreamEditor()); this.defaultEditors.put(InputSource.class, new InputSourceEditor()); this.defaultEditors.put(Locale.class, new LocaleEditor()); + if (pathClass != null) { + this.defaultEditors.put(pathClass, new PathEditor()); + } this.defaultEditors.put(Pattern.class, new PatternEditor()); this.defaultEditors.put(Properties.class, new PropertiesEditor()); this.defaultEditors.put(Reader.class, new ReaderEditor()); diff --git a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java index dd63e8e9..908bfeaf 100644 --- a/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java +++ b/spring-beans/src/main/java/org/springframework/beans/TypeConverterDelegate.java @@ -186,8 +186,11 @@ class TypeConverterDelegate { if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) && convertedValue instanceof String) { TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor(); - if (elementTypeDesc != null && Enum.class.isAssignableFrom(elementTypeDesc.getType())) { - convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue); + if (elementTypeDesc != null) { + Class<?> elementType = elementTypeDesc.getType(); + if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) { + convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue); + } } } if (editor == null) { @@ -266,7 +269,7 @@ class TypeConverterDelegate { } else { // convertedValue == null - if (javaUtilOptionalEmpty != null && requiredType.equals(javaUtilOptionalEmpty.getClass())) { + if (javaUtilOptionalEmpty != null && requiredType == javaUtilOptionalEmpty.getClass()) { convertedValue = javaUtilOptionalEmpty; } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java index f0c2fa1d..75a38386 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -63,7 +63,7 @@ public class BeanCreationException extends FatalBeanException { * @param msg the detail message */ public BeanCreationException(String beanName, String msg) { - super("Error creating bean with name '" + beanName + "': " + msg); + super("Error creating bean" + (beanName != null ? " with name '" + beanName + "'" : "") + ": " + msg); this.beanName = beanName; } @@ -86,7 +86,7 @@ public class BeanCreationException extends FatalBeanException { * @param msg the detail message */ public BeanCreationException(String resourceDescription, String beanName, String msg) { - super("Error creating bean with name '" + beanName + "'" + + super("Error creating bean" + (beanName != null ? " with name '" + beanName + "'" : "") + (resourceDescription != null ? " defined in " + resourceDescription : "") + ": " + msg); this.resourceDescription = resourceDescription; this.beanName = beanName; @@ -123,7 +123,7 @@ public class BeanCreationException extends FatalBeanException { /** * Add a related cause to this bean creation exception, - * not being a direct cause of the failure but having occured + * not being a direct cause of the failure but having occurred * earlier in the creation of the same bean instance. * @param ex the related cause to add */ diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java index 3c6bd4c8..58b2fd97 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java @@ -188,12 +188,12 @@ public interface BeanFactory { * Return an instance, which may be shared or independent, of the specified bean. * <p>Allows for specifying explicit constructor arguments / factory method arguments, * overriding the specified default arguments (if any) in the bean definition. - * @param requiredType type the bean must match; can be an interface or superclass. - * {@code null} is disallowed. * <p>This method goes into {@link ListableBeanFactory} by-type lookup territory * but may also be translated into a conventional by-name lookup based on the name * of the given type. For more extensive retrieval operations across sets of beans, * use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}. + * @param requiredType type the bean must match; can be an interface or superclass. + * {@code null} is disallowed. * @param args arguments to use when creating a bean instance using explicit arguments * (only applied when creating a new instance as opposed to retrieving an existing one) * @return an instance of the bean diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanInitializationException.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanInitializationException.java index c52d0ae7..5c5ed79c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanInitializationException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanInitializationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -23,8 +23,8 @@ import org.springframework.beans.FatalBeanException; * factory-aware initialization code fails. BeansExceptions thrown by * bean factory methods themselves should simply be propagated as-is. * - * <p>Note that non-factory-aware initialization methods like afterPropertiesSet() - * or a custom "init-method" can throw any exception. + * <p>Note that {@code afterPropertiesSet()} or a custom "init-method" + * can throw any exception. * * @author Juergen Hoeller * @since 13.11.2003 diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java index bd6d33fb..6f98f435 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanNotOfRequiredTypeException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -45,8 +45,8 @@ public class BeanNotOfRequiredTypeException extends BeansException { * the expected type */ public BeanNotOfRequiredTypeException(String beanName, Class<?> requiredType, Class<?> actualType) { - super("Bean named '" + beanName + "' must be of type [" + requiredType.getName() + - "], but was actually of type [" + actualType.getName() + "]"); + super("Bean named '" + beanName + "' is expected to be of type [" + requiredType.getName() + + "] but was actually of type [" + actualType.getName() + "]"); this.beanName = beanName; this.requiredType = requiredType; this.actualType = actualType; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java b/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java new file mode 100644 index 00000000..0a3d55a9 --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java @@ -0,0 +1,167 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.beans.factory; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Field; +import java.lang.reflect.Member; + +import org.springframework.core.MethodParameter; +import org.springframework.util.Assert; + +/** + * A simple descriptor for an injection point, pointing to a method/constructor + * parameter or a field. Exposed by {@link UnsatisfiedDependencyException}. + * + * @author Juergen Hoeller + * @since 4.3 + * @see UnsatisfiedDependencyException#getInjectionPoint() + * @see org.springframework.beans.factory.config.DependencyDescriptor + */ +public class InjectionPoint { + + protected MethodParameter methodParameter; + + protected Field field; + + private volatile Annotation[] fieldAnnotations; + + + /** + * Create an injection point descriptor for a method or constructor parameter. + * @param methodParameter the MethodParameter to wrap + */ + public InjectionPoint(MethodParameter methodParameter) { + Assert.notNull(methodParameter, "MethodParameter must not be null"); + this.methodParameter = methodParameter; + } + + /** + * Create an injection point descriptor for a field. + * @param field the field to wrap + */ + public InjectionPoint(Field field) { + Assert.notNull(field, "Field must not be null"); + this.field = field; + } + + /** + * Copy constructor. + * @param original the original descriptor to create a copy from + */ + protected InjectionPoint(InjectionPoint original) { + this.methodParameter = (original.methodParameter != null ? + new MethodParameter(original.methodParameter) : null); + this.field = original.field; + this.fieldAnnotations = original.fieldAnnotations; + } + + /** + * Just available for serialization purposes in subclasses. + */ + protected InjectionPoint() { + } + + + /** + * Return the wrapped MethodParameter, if any. + * <p>Note: Either MethodParameter or Field is available. + * @return the MethodParameter, or {@code null} if none + */ + public MethodParameter getMethodParameter() { + return this.methodParameter; + } + + /** + * Return the wrapped Field, if any. + * <p>Note: Either MethodParameter or Field is available. + * @return the Field, or {@code null} if none + */ + public Field getField() { + return this.field; + } + + /** + * Obtain the annotations associated with the wrapped field or method/constructor parameter. + */ + public Annotation[] getAnnotations() { + if (this.field != null) { + if (this.fieldAnnotations == null) { + this.fieldAnnotations = this.field.getAnnotations(); + } + return this.fieldAnnotations; + } + else { + return this.methodParameter.getParameterAnnotations(); + } + } + + /** + * Return the type declared by the underlying field or method/constructor parameter, + * indicating the injection type. + */ + public Class<?> getDeclaredType() { + return (this.field != null ? this.field.getType() : this.methodParameter.getParameterType()); + } + + /** + * Returns the wrapped member, containing the injection point. + * @return the Field / Method / Constructor as Member + */ + public Member getMember() { + return (this.field != null ? this.field : this.methodParameter.getMember()); + } + + /** + * Return the wrapped annotated element. + * <p>Note: In case of a method/constructor parameter, this exposes + * the annotations declared on the method or constructor itself + * (i.e. at the method/constructor level, not at the parameter level). + * Use {@link #getAnnotations()} to obtain parameter-level annotations in + * such a scenario, transparently with corresponding field annotations. + * @return the Field / Method / Constructor as AnnotatedElement + */ + public AnnotatedElement getAnnotatedElement() { + return (this.field != null ? this.field : this.methodParameter.getAnnotatedElement()); + } + + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (getClass() != other.getClass()) { + return false; + } + InjectionPoint otherPoint = (InjectionPoint) other; + return (this.field != null ? this.field.equals(otherPoint.field) : + this.methodParameter.equals(otherPoint.methodParameter)); + } + + @Override + public int hashCode() { + return (this.field != null ? this.field.hashCode() : this.methodParameter.hashCode()); + } + + @Override + public String toString() { + return (this.field != null ? "field '" + this.field.getName() + "'" : this.methodParameter.toString()); + } + +} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/NoUniqueBeanDefinitionException.java b/spring-beans/src/main/java/org/springframework/beans/factory/NoUniqueBeanDefinitionException.java index 7db0bae6..6bb4f6db 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/NoUniqueBeanDefinitionException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/NoUniqueBeanDefinitionException.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -34,6 +34,8 @@ public class NoUniqueBeanDefinitionException extends NoSuchBeanDefinitionExcepti private int numberOfBeansFound; + private Collection<String> beanNamesFound; + /** * Create a new {@code NoUniqueBeanDefinitionException}. @@ -54,6 +56,7 @@ public class NoUniqueBeanDefinitionException extends NoSuchBeanDefinitionExcepti public NoUniqueBeanDefinitionException(Class<?> type, Collection<String> beanNamesFound) { this(type, beanNamesFound.size(), "expected single matching bean but found " + beanNamesFound.size() + ": " + StringUtils.collectionToCommaDelimitedString(beanNamesFound)); + this.beanNamesFound = beanNamesFound; } /** @@ -76,4 +79,14 @@ public class NoUniqueBeanDefinitionException extends NoSuchBeanDefinitionExcepti return this.numberOfBeansFound; } + /** + * Return the names of all beans found when only one matching bean was expected. + * Note that this may be {@code null} if not specified at construction time. + * @since 4.3 + * @see #getBeanType() + */ + public Collection<String> getBeanNamesFound() { + return this.beanNamesFound; + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java b/spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java new file mode 100644 index 00000000..ffa2683e --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/factory/ObjectProvider.java @@ -0,0 +1,61 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.beans.factory; + +import org.springframework.beans.BeansException; + +/** + * A variant of {@link ObjectFactory} designed specifically for injection points, + * allowing for programmatic optionality and lenient not-unique handling. + * + * @author Juergen Hoeller + * @since 4.3 + */ +public interface ObjectProvider<T> extends ObjectFactory<T> { + + /** + * Return an instance (possibly shared or independent) of the object + * managed by this factory. + * <p>Allows for specifying explicit construction arguments, along the + * lines of {@link BeanFactory#getBean(String, Object...)}. + * @param args arguments to use when creating a corresponding instance + * @return an instance of the bean + * @throws BeansException in case of creation errors + * @see #getObject() + */ + T getObject(Object... args) throws BeansException; + + /** + * Return an instance (possibly shared or independent) of the object + * managed by this factory. + * @return an instance of the bean, or {@code null} if not available + * @throws BeansException in case of creation errors + * @see #getObject() + */ + T getIfAvailable() throws BeansException; + + /** + * Return an instance (possibly shared or independent) of the object + * managed by this factory. + * @return an instance of the bean, or {@code null} if not available or + * not unique (i.e. multiple candidates found with none marked as primary) + * @throws BeansException in case of creation errors + * @see #getObject() + */ + T getIfUnique() throws BeansException; + +} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java b/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java index 9ec35a7e..0403abfc 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java @@ -31,6 +31,9 @@ import org.springframework.util.ClassUtils; @SuppressWarnings("serial") public class UnsatisfiedDependencyException extends BeanCreationException { + private InjectionPoint injectionPoint; + + /** * Create a new UnsatisfiedDependencyException. * @param resourceDescription description of the resource that the bean definition came from @@ -64,10 +67,42 @@ public class UnsatisfiedDependencyException extends BeanCreationException { * Create a new UnsatisfiedDependencyException. * @param resourceDescription description of the resource that the bean definition came from * @param beanName the name of the bean requested + * @param injectionPoint the injection point (field or method/constructor parameter) + * @param msg the detail message + * @since 4.3 + */ + public UnsatisfiedDependencyException( + String resourceDescription, String beanName, InjectionPoint injectionPoint, String msg) { + + super(resourceDescription, beanName, "Unsatisfied dependency expressed through " + injectionPoint + ": " + msg); + this.injectionPoint = injectionPoint; + } + + /** + * Create a new UnsatisfiedDependencyException. + * @param resourceDescription description of the resource that the bean definition came from + * @param beanName the name of the bean requested + * @param injectionPoint the injection point (field or method/constructor parameter) + * @param ex the bean creation exception that indicated the unsatisfied dependency + * @since 4.3 + */ + public UnsatisfiedDependencyException( + String resourceDescription, String beanName, InjectionPoint injectionPoint, BeansException ex) { + + this(resourceDescription, beanName, injectionPoint, (ex != null ? ex.getMessage() : "")); + initCause(ex); + } + + /** + * Create a new UnsatisfiedDependencyException. + * @param resourceDescription description of the resource that the bean definition came from + * @param beanName the name of the bean requested * @param ctorArgIndex the index of the constructor argument that couldn't be satisfied * @param ctorArgType the type of the constructor argument that couldn't be satisfied * @param msg the detail message + * @deprecated in favor of {@link #UnsatisfiedDependencyException(String, String, InjectionPoint, String)} */ + @Deprecated public UnsatisfiedDependencyException( String resourceDescription, String beanName, int ctorArgIndex, Class<?> ctorArgType, String msg) { @@ -84,7 +119,9 @@ public class UnsatisfiedDependencyException extends BeanCreationException { * @param ctorArgIndex the index of the constructor argument that couldn't be satisfied * @param ctorArgType the type of the constructor argument that couldn't be satisfied * @param ex the bean creation exception that indicated the unsatisfied dependency + * @deprecated in favor of {@link #UnsatisfiedDependencyException(String, String, InjectionPoint, BeansException)} */ + @Deprecated public UnsatisfiedDependencyException( String resourceDescription, String beanName, int ctorArgIndex, Class<?> ctorArgType, BeansException ex) { @@ -92,4 +129,13 @@ public class UnsatisfiedDependencyException extends BeanCreationException { initCause(ex); } + + /** + * Return the injection point (field or method/constructor parameter), if known. + * @since 4.3 + */ + public InjectionPoint getInjectionPoint() { + return this.injectionPoint; + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/access/SingletonBeanFactoryLocator.java b/spring-beans/src/main/java/org/springframework/beans/factory/access/SingletonBeanFactoryLocator.java index d7758bc1..45be7328 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/access/SingletonBeanFactoryLocator.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/access/SingletonBeanFactoryLocator.java @@ -88,7 +88,7 @@ import org.springframework.core.io.support.ResourcePatternUtils; * use object from a BeanFactory/ApplicationContext. One solutions is to make the * class created by the third party code be just a stub or proxy, which gets the * real object from a BeanFactory/ApplicationContext, and delegates to it. However, - * it is is not normally workable for the stub to create the BeanFactory on each + * it is not normally workable for the stub to create the BeanFactory on each * use, as depending on what is inside it, that can be an expensive operation. * Additionally, there is a fairly tight coupling between the stub and the name of * the definition resource for the BeanFactory/ApplicationContext. This is where @@ -291,7 +291,7 @@ public class SingletonBeanFactoryLocator implements BeanFactoryLocator { } /** - * Returns an instance which uses the the specified selector, as the name of the + * Returns an instance which uses the specified selector, as the name of the * definition file(s). In the case of a name with a Spring 'classpath*:' prefix, * or with no prefix, which is treated the same, the current thread context * ClassLoader's {@code getResources} method will be called with this value @@ -341,7 +341,7 @@ public class SingletonBeanFactoryLocator implements BeanFactoryLocator { /** - * Constructor which uses the the specified name as the resource name + * Constructor which uses the specified name as the resource name * of the definition file(s). * @param resourceLocation the Spring resource location to use * (either a URL or a "classpath:" / "classpath*:" pseudo URL) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Autowired.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Autowired.java index fc8b49bc..73779365 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Autowired.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/Autowired.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -64,7 +64,7 @@ import java.lang.annotation.Target; * @see Qualifier * @see Value */ -@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index c008a796..258f66bf 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -45,11 +45,12 @@ import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.InjectionPoint; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.UnsatisfiedDependencyException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; -import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.LookupOverride; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; @@ -107,6 +108,7 @@ import org.springframework.util.StringUtils; * * @author Juergen Hoeller * @author Mark Fisher + * @author Stephane Nicoll * @since 2.5 * @see #setAutowiredAnnotationType * @see Autowired @@ -270,6 +272,19 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean Constructor<?> defaultConstructor = null; for (Constructor<?> candidate : rawCandidates) { AnnotationAttributes ann = findAutowiredAnnotation(candidate); + if (ann == null) { + Class<?> userClass = ClassUtils.getUserClass(beanClass); + if (userClass != beanClass) { + try { + Constructor<?> superCtor = + userClass.getDeclaredConstructor(candidate.getParameterTypes()); + ann = findAutowiredAnnotation(superCtor); + } + catch (NoSuchMethodException ex) { + // Simply proceed, no equivalent superclass constructor found... + } + } + } if (ann != null) { if (requiredConstructor != null) { throw new BeanCreationException(beanName, @@ -312,6 +327,9 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean } candidateConstructors = candidates.toArray(new Constructor<?>[candidates.size()]); } + else if (rawCandidates.length == 1 && rawCandidates[0].getParameterTypes().length > 0) { + candidateConstructors = new Constructor<?>[] {rawCandidates[0]}; + } else { candidateConstructors = new Constructor<?>[0]; } @@ -330,6 +348,9 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean try { metadata.inject(bean, beanName, pvs); } + catch (BeanCreationException ex) { + throw ex; + } catch (Throwable ex) { throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex); } @@ -348,6 +369,9 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean try { metadata.inject(bean, null, null); } + catch (BeanCreationException ex) { + throw ex; + } catch (Throwable ex) { throw new BeanCreationException("Injection of autowired dependencies failed for class [" + clazz + "]", ex); } @@ -504,9 +528,6 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument; return this.beanFactory.resolveDependency(descriptor, beanName, null, null); } - else if (cachedArgument instanceof RuntimeBeanReference) { - return this.beanFactory.getBean(((RuntimeBeanReference) cachedArgument).getBeanName()); - } else { return cachedArgument; } @@ -532,45 +553,46 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean @Override protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable { Field field = (Field) this.member; - try { - Object value; - if (this.cached) { - value = resolvedCachedArgument(beanName, this.cachedFieldValue); - } - else { - DependencyDescriptor desc = new DependencyDescriptor(field, this.required); - desc.setContainingClass(bean.getClass()); - Set<String> autowiredBeanNames = new LinkedHashSet<String>(1); - TypeConverter typeConverter = beanFactory.getTypeConverter(); + Object value; + if (this.cached) { + value = resolvedCachedArgument(beanName, this.cachedFieldValue); + } + else { + DependencyDescriptor desc = new DependencyDescriptor(field, this.required); + desc.setContainingClass(bean.getClass()); + Set<String> autowiredBeanNames = new LinkedHashSet<String>(1); + TypeConverter typeConverter = beanFactory.getTypeConverter(); + try { value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); - synchronized (this) { - if (!this.cached) { - if (value != null || this.required) { - this.cachedFieldValue = desc; - registerDependentBeans(beanName, autowiredBeanNames); - if (autowiredBeanNames.size() == 1) { - String autowiredBeanName = autowiredBeanNames.iterator().next(); - if (beanFactory.containsBean(autowiredBeanName)) { - if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { - this.cachedFieldValue = new RuntimeBeanReference(autowiredBeanName); - } + } + catch (BeansException ex) { + throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex); + } + synchronized (this) { + if (!this.cached) { + if (value != null || this.required) { + this.cachedFieldValue = desc; + registerDependentBeans(beanName, autowiredBeanNames); + if (autowiredBeanNames.size() == 1) { + String autowiredBeanName = autowiredBeanNames.iterator().next(); + if (beanFactory.containsBean(autowiredBeanName)) { + if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { + this.cachedFieldValue = new ShortcutDependencyDescriptor( + desc, autowiredBeanName, field.getType()); } } } - else { - this.cachedFieldValue = null; - } - this.cached = true; } + else { + this.cachedFieldValue = null; + } + this.cached = true; } } - if (value != null) { - ReflectionUtils.makeAccessible(field); - field.set(bean, value); - } } - catch (Throwable ex) { - throw new BeanCreationException("Could not autowire field: " + field, ex); + if (value != null) { + ReflectionUtils.makeAccessible(field); + field.set(bean, value); } } } @@ -598,67 +620,70 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean return; } Method method = (Method) this.member; - try { - Object[] arguments; - if (this.cached) { - // Shortcut for avoiding synchronization... - arguments = resolveCachedArguments(beanName); - } - else { - Class<?>[] paramTypes = method.getParameterTypes(); - arguments = new Object[paramTypes.length]; - DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length]; - Set<String> autowiredBeanNames = new LinkedHashSet<String>(paramTypes.length); - TypeConverter typeConverter = beanFactory.getTypeConverter(); - for (int i = 0; i < arguments.length; i++) { - MethodParameter methodParam = new MethodParameter(method, i); - DependencyDescriptor desc = new DependencyDescriptor(methodParam, this.required); - desc.setContainingClass(bean.getClass()); - descriptors[i] = desc; - Object arg = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter); + Object[] arguments; + if (this.cached) { + // Shortcut for avoiding synchronization... + arguments = resolveCachedArguments(beanName); + } + else { + Class<?>[] paramTypes = method.getParameterTypes(); + arguments = new Object[paramTypes.length]; + DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length]; + Set<String> autowiredBeanNames = new LinkedHashSet<String>(paramTypes.length); + TypeConverter typeConverter = beanFactory.getTypeConverter(); + for (int i = 0; i < arguments.length; i++) { + MethodParameter methodParam = new MethodParameter(method, i); + DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required); + currDesc.setContainingClass(bean.getClass()); + descriptors[i] = currDesc; + try { + Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeanNames, typeConverter); if (arg == null && !this.required) { arguments = null; break; } arguments[i] = arg; } - synchronized (this) { - if (!this.cached) { - if (arguments != null) { - this.cachedMethodArguments = new Object[arguments.length]; - for (int i = 0; i < arguments.length; i++) { - this.cachedMethodArguments[i] = descriptors[i]; - } - registerDependentBeans(beanName, autowiredBeanNames); - if (autowiredBeanNames.size() == paramTypes.length) { - Iterator<String> it = autowiredBeanNames.iterator(); - for (int i = 0; i < paramTypes.length; i++) { - String autowiredBeanName = it.next(); - if (beanFactory.containsBean(autowiredBeanName)) { - if (beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) { - this.cachedMethodArguments[i] = new RuntimeBeanReference(autowiredBeanName); - } + catch (BeansException ex) { + throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex); + } + } + synchronized (this) { + if (!this.cached) { + if (arguments != null) { + this.cachedMethodArguments = new Object[paramTypes.length]; + for (int i = 0; i < arguments.length; i++) { + this.cachedMethodArguments[i] = descriptors[i]; + } + registerDependentBeans(beanName, autowiredBeanNames); + if (autowiredBeanNames.size() == paramTypes.length) { + Iterator<String> it = autowiredBeanNames.iterator(); + for (int i = 0; i < paramTypes.length; i++) { + String autowiredBeanName = it.next(); + if (beanFactory.containsBean(autowiredBeanName)) { + if (beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) { + this.cachedMethodArguments[i] = new ShortcutDependencyDescriptor( + descriptors[i], autowiredBeanName, paramTypes[i]); } } } } - else { - this.cachedMethodArguments = null; - } - this.cached = true; } + else { + this.cachedMethodArguments = null; + } + this.cached = true; } } - if (arguments != null) { + } + if (arguments != null) { + try { ReflectionUtils.makeAccessible(method); method.invoke(bean, arguments); } - } - catch (InvocationTargetException ex) { - throw ex.getTargetException(); - } - catch (Throwable ex) { - throw new BeanCreationException("Could not autowire method: " + method, ex); + catch (InvocationTargetException ex){ + throw ex.getTargetException(); + } } } @@ -674,4 +699,27 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean } } + + /** + * DependencyDescriptor variant with a pre-resolved target bean name. + */ + @SuppressWarnings("serial") + private static class ShortcutDependencyDescriptor extends DependencyDescriptor { + + private final String shortcutName; + + private final Class<?> requiredType; + + public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcutName, Class<?> requiredType) { + super(original); + this.shortcutName = shortcutName; + this.requiredType = requiredType; + } + + @Override + public Object resolveShortcut(BeanFactory beanFactory) { + return resolveCandidate(this.shortcutName, this.requiredType, beanFactory); + } + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java index eb691a14..65901004 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/BeanFactoryAnnotationUtils.java @@ -26,6 +26,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.AutowireCandidateQualifier; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.ObjectUtils; /** @@ -113,6 +114,7 @@ public class BeanFactoryAnnotationUtils { if (bf.containsBean(beanName)) { try { BeanDefinition bd = bf.getMergedBeanDefinition(beanName); + // Explicit qualifier metadata on bean definition? (typically in XML definition) if (bd instanceof AbstractBeanDefinition) { AbstractBeanDefinition abd = (AbstractBeanDefinition) bd; AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName()); @@ -121,15 +123,24 @@ public class BeanFactoryAnnotationUtils { return true; } } + // Corresponding qualifier on factory method? (typically in configuration class) if (bd instanceof RootBeanDefinition) { Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod(); if (factoryMethod != null) { - Qualifier targetAnnotation = factoryMethod.getAnnotation(Qualifier.class); - if (targetAnnotation != null && qualifier.equals(targetAnnotation.value())) { - return true; + Qualifier targetAnnotation = AnnotationUtils.getAnnotation(factoryMethod, Qualifier.class); + if (targetAnnotation != null) { + return qualifier.equals(targetAnnotation.value()); } } } + // Corresponding qualifier on bean implementation class? (for custom user types) + Class<?> beanType = bf.getType(beanName); + if (beanType != null) { + Qualifier targetAnnotation = AnnotationUtils.getAnnotation(beanType, Qualifier.class); + if (targetAnnotation != null) { + return qualifier.equals(targetAnnotation.value()); + } + } } catch (NoSuchBeanDefinitionException ex) { // Ignore - can't compare qualifiers for a manually registered singleton object diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java index 3c7c0577..37433239 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java @@ -136,7 +136,7 @@ public class InitDestroyAnnotationBeanPostProcessor throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException()); } catch (Throwable ex) { - throw new BeanCreationException(beanName, "Couldn't invoke init method", ex); + throw new BeanCreationException(beanName, "Failed to invoke init method", ex); } return bean; } @@ -162,10 +162,15 @@ public class InitDestroyAnnotationBeanPostProcessor } } catch (Throwable ex) { - logger.error("Couldn't invoke destroy method on bean with name '" + beanName + "'", ex); + logger.error("Failed to invoke destroy method on bean with name '" + beanName + "'", ex); } } + @Override + public boolean requiresDestruction(Object bean) { + return findLifecycleMetadata(bean.getClass()).hasDestroyMethods(); + } + private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) { if (this.lifecycleMetadataCache == null) { @@ -308,11 +313,11 @@ public class InitDestroyAnnotationBeanPostProcessor } public void invokeDestroyMethods(Object target, String beanName) throws Throwable { - Collection<LifecycleElement> destroyMethodsToIterate = + Collection<LifecycleElement> destroyMethodsToUse = (this.checkedDestroyMethods != null ? this.checkedDestroyMethods : this.destroyMethods); - if (!destroyMethodsToIterate.isEmpty()) { + if (!destroyMethodsToUse.isEmpty()) { boolean debug = logger.isDebugEnabled(); - for (LifecycleElement element : destroyMethodsToIterate) { + for (LifecycleElement element : destroyMethodsToUse) { if (debug) { logger.debug("Invoking destroy method on bean '" + beanName + "': " + element.getMethod()); } @@ -320,6 +325,12 @@ public class InitDestroyAnnotationBeanPostProcessor } } } + + public boolean hasDestroyMethods() { + Collection<LifecycleElement> destroyMethodsToUse = + (this.checkedDestroyMethods != null ? this.checkedDestroyMethods : this.destroyMethods); + return !destroyMethodsToUse.isEmpty(); + } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java index d8fcc731..0f6d1021 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2015 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. @@ -32,6 +32,8 @@ import org.springframework.beans.factory.support.AutowireCandidateResolver; import org.springframework.beans.factory.support.GenericTypeAwareAutowireCandidateResolver; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.MethodParameter; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -315,25 +317,20 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa * Determine a suggested value from any of the given candidate annotations. */ protected Object findValue(Annotation[] annotationsToSearch) { - for (Annotation annotation : annotationsToSearch) { - if (this.valueAnnotationType.isInstance(annotation)) { - return extractValue(annotation); - } - } - for (Annotation annotation : annotationsToSearch) { - Annotation metaAnn = annotation.annotationType().getAnnotation(this.valueAnnotationType); - if (metaAnn != null) { - return extractValue(metaAnn); - } + AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes( + AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType); + if (attr != null) { + return extractValue(attr); } return null; } /** * Extract the value attribute from the given annotation. + * @since 4.3 */ - protected Object extractValue(Annotation valueAnnotation) { - Object value = AnnotationUtils.getValue(valueAnnotation); + protected Object extractValue(AnnotationAttributes attr) { + Object value = attr.get(AnnotationUtils.VALUE); if (value == null) { throw new IllegalStateException("Value annotation must have a value attribute"); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java index bc8a42f7..85faab00 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -208,7 +208,7 @@ public abstract class AbstractFactoryBean<T> * <p>Invoked on initialization of this FactoryBean in case of * a singleton; else, on each {@link #getObject()} call. * @return the object returned by this factory - * @throws Exception if an exception occured during object creation + * @throws Exception if an exception occurred during object creation * @see #getObject() */ protected abstract T createInstance() throws Exception; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.java index ffa8849a..43602b48 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowireCapableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -114,9 +114,9 @@ public interface AutowireCapableBeanFactory extends BeanFactory { * <p>Performs full initialization of the bean, including all applicable * {@link BeanPostProcessor BeanPostProcessors}. * <p>Note: This is intended for creating a fresh instance, populating annotated - * fields and methods as well as applying all standard bean initialiation callbacks. + * fields and methods as well as applying all standard bean initialization callbacks. * It does <i>not</> imply traditional by-name or by-type autowiring of properties; - * use {@link #createBean(Class, int, boolean)} for that purposes. + * use {@link #createBean(Class, int, boolean)} for those purposes. * @param beanClass the class of the bean to create * @return the new bean instance * @throws BeansException if instantiation or wiring failed @@ -129,7 +129,7 @@ public interface AutowireCapableBeanFactory extends BeanFactory { * <p>Note: This is essentially intended for (re-)populating annotated fields and * methods, either for new instances or for deserialized instances. It does * <i>not</i> imply traditional by-name or by-type autowiring of properties; - * use {@link #autowireBeanProperties} for that purposes. + * use {@link #autowireBeanProperties} for those purposes. * @param existingBean the existing bean instance * @throws BeansException if wiring failed */ @@ -159,7 +159,7 @@ public interface AutowireCapableBeanFactory extends BeanFactory { * @param descriptor the descriptor for the dependency * @param beanName the name of the bean which declares the present dependency * @return the resolved object, or {@code null} if none found - * @throws BeansException in dependency resolution failed + * @throws BeansException if dependency resolution failed */ Object resolveDependency(DependencyDescriptor descriptor, String beanName) throws BeansException; @@ -321,7 +321,7 @@ public interface AutowireCapableBeanFactory extends BeanFactory { * @param typeConverter the TypeConverter to use for populating arrays and * collections * @return the resolved object, or {@code null} if none found - * @throws BeansException in dependency resolution failed + * @throws BeansException if dependency resolution failed */ Object resolveDependency(DependencyDescriptor descriptor, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java index d04eb15d..302deec5 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConfigurableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -210,6 +210,13 @@ public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, Single void addEmbeddedValueResolver(StringValueResolver valueResolver); /** + * Determine whether an embedded value resolver has been registered with this + * bean factory, to be applied through {@link #resolveEmbeddedValue(String)}. + * @since 4.3 + */ + boolean hasEmbeddedValueResolver(); + + /** * Resolve the given embedded value, e.g. an annotation attribute. * @param value the value to resolve * @return the resolved value (may be the original value as-is) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java index 9a57c7ae..6f7d2d90 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ConstructorArgumentValues.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -156,7 +156,7 @@ public class ConstructorArgumentValues { * @param requiredType the type to match (can be {@code null} to match * untyped values only) * @param requiredName the type to match (can be {@code null} to match - * unnamed values only) + * unnamed values only, or empty String to match any name) * @return the ValueHolder for the argument, or {@code null} if none set */ public ValueHolder getIndexedArgumentValue(int index, Class<?> requiredType, String requiredName) { @@ -165,7 +165,7 @@ public class ConstructorArgumentValues { if (valueHolder != null && (valueHolder.getType() == null || (requiredType != null && ClassUtils.matchesTypeName(requiredType, valueHolder.getType()))) && - (valueHolder.getName() == null || + (valueHolder.getName() == null || "".equals(requiredName) || (requiredName != null && requiredName.equals(valueHolder.getName())))) { return valueHolder; } @@ -268,7 +268,7 @@ public class ConstructorArgumentValues { * @param requiredType the type to match (can be {@code null} to find * an arbitrary next generic argument value) * @param requiredName the name to match (can be {@code null} to not - * match argument values by name) + * match argument values by name, or empty String to match any name) * @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} if none found @@ -278,7 +278,7 @@ public class ConstructorArgumentValues { if (usedValueHolders != null && usedValueHolders.contains(valueHolder)) { continue; } - if (valueHolder.getName() != null && + if (valueHolder.getName() != null && !"".equals(requiredName) && (requiredName == null || !valueHolder.getName().equals(requiredName))) { continue; } @@ -335,7 +335,7 @@ public class ConstructorArgumentValues { * @param requiredType the parameter type to match (can be {@code null} * to find an untyped argument value) * @param requiredName the parameter name to match (can be {@code null} - * to find an unnamed argument value) + * to find an unnamed argument value, or empty String to match any name) * @param usedValueHolders a Set of ValueHolder objects that have already * been used in the current resolution process and should therefore not * be returned again (allowing to return the next generic argument match diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java index 88642eb4..82c6883c 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java @@ -19,17 +19,20 @@ package org.springframework.beans.factory.config; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; -import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.Map; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.InjectionPoint; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.core.GenericCollectionTypeResolver; import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.ResolvableType; -import org.springframework.util.Assert; /** * Descriptor for a specific dependency that is about to be injected. @@ -40,15 +43,9 @@ import org.springframework.util.Assert; * @since 2.5 */ @SuppressWarnings("serial") -public class DependencyDescriptor implements Serializable { +public class DependencyDescriptor extends InjectionPoint implements Serializable { - private transient MethodParameter methodParameter; - - private transient Field field; - - private Class<?> declaringClass; - - private Class<?> containingClass; + private final Class<?> declaringClass; private String methodName; @@ -64,7 +61,7 @@ public class DependencyDescriptor implements Serializable { private int nestingLevel = 1; - private transient Annotation[] fieldAnnotations; + private Class<?> containingClass; /** @@ -85,10 +82,8 @@ public class DependencyDescriptor implements Serializable { * eagerly resolving potential target beans for type matching */ public DependencyDescriptor(MethodParameter methodParameter, boolean required, boolean eager) { - Assert.notNull(methodParameter, "MethodParameter must not be null"); - this.methodParameter = methodParameter; + super(methodParameter); this.declaringClass = methodParameter.getDeclaringClass(); - this.containingClass = methodParameter.getContainingClass(); if (this.methodParameter.getMethod() != null) { this.methodName = methodParameter.getMethod().getName(); this.parameterTypes = methodParameter.getMethod().getParameterTypes(); @@ -97,6 +92,7 @@ public class DependencyDescriptor implements Serializable { this.parameterTypes = methodParameter.getConstructor().getParameterTypes(); } this.parameterIndex = methodParameter.getParameterIndex(); + this.containingClass = methodParameter.getContainingClass(); this.required = required; this.eager = eager; } @@ -119,8 +115,7 @@ public class DependencyDescriptor implements Serializable { * eagerly resolving potential target beans for type matching */ public DependencyDescriptor(Field field, boolean required, boolean eager) { - Assert.notNull(field, "Field must not be null"); - this.field = field; + super(field); this.declaringClass = field.getDeclaringClass(); this.fieldName = field.getName(); this.required = required; @@ -132,52 +127,84 @@ public class DependencyDescriptor implements Serializable { * @param original the original descriptor to create a copy from */ public DependencyDescriptor(DependencyDescriptor original) { - this.methodParameter = (original.methodParameter != null ? new MethodParameter(original.methodParameter) : null); - this.field = original.field; + super(original); this.declaringClass = original.declaringClass; - this.containingClass = original.containingClass; this.methodName = original.methodName; this.parameterTypes = original.parameterTypes; this.parameterIndex = original.parameterIndex; this.fieldName = original.fieldName; + this.containingClass = original.containingClass; this.required = original.required; this.eager = original.eager; this.nestingLevel = original.nestingLevel; - this.fieldAnnotations = original.fieldAnnotations; } /** - * Return the wrapped MethodParameter, if any. - * <p>Note: Either MethodParameter or Field is available. - * @return the MethodParameter, or {@code null} if none + * Return whether this dependency is required. */ - public MethodParameter getMethodParameter() { - return this.methodParameter; + public boolean isRequired() { + return this.required; } /** - * Return the wrapped Field, if any. - * <p>Note: Either MethodParameter or Field is available. - * @return the Field, or {@code null} if none + * Return whether this dependency is 'eager' in the sense of + * eagerly resolving potential target beans for type matching. */ - public Field getField() { - return this.field; + public boolean isEager() { + return this.eager; } /** - * Return whether this dependency is required. + * Resolve the specified not-unique scenario: by default, + * throwing a {@link NoUniqueBeanDefinitionException}. + * <p>Subclasses may override this to select one of the instances or + * to opt out with no result at all through returning {@code null}. + * @param type the requested bean type + * @param matchingBeans a map of bean names and corresponding bean + * instances which have been pre-selected for the given type + * (qualifiers etc already applied) + * @return a bean instance to proceed with, or {@code null} for none + * @throws BeansException in case of the not-unique scenario being fatal + * @since 4.3 */ - public boolean isRequired() { - return this.required; + public Object resolveNotUnique(Class<?> type, Map<String, Object> matchingBeans) throws BeansException { + throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet()); } /** - * Return whether this dependency is 'eager' in the sense of - * eagerly resolving potential target beans for type matching. + * Resolve a shortcut for this dependency against the given factory, for example + * taking some pre-resolved information into account. + * <p>The resolution algorithm will first attempt to resolve a shortcut through this + * method before going into the regular type matching algorithm across all beans. + * Subclasses may override this method to improve resolution performance based on + * pre-cached information while still receiving {@link InjectionPoint} exposure etc. + * @param beanFactory the associated factory + * @return the shortcut result if any, or {@code null} if none + * @throws BeansException if the shortcut could not be obtained + * @since 4.3.1 */ - public boolean isEager() { - return this.eager; + public Object resolveShortcut(BeanFactory beanFactory) throws BeansException { + return null; + } + + /** + * Resolve the specified bean name, as a candidate result of the matching + * algorithm for this dependency, to a bean instance from the given factory. + * <p>The default implementation calls {@link BeanFactory#getBean(String)}. + * Subclasses may provide additional arguments or other customizations. + * @param beanName the bean name, as a candidate result for this dependency + * @param requiredType the expected type of the bean (as an assertion) + * @param beanFactory the associated factory + * @return the bean instance (never {@code null}) + * @throws BeansException if the bean could not be obtained + * @since 4.3.2 + * @see BeanFactory#getBean(String) + */ + public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) + throws BeansException { + + return beanFactory.getBean(beanName, requiredType); } @@ -272,6 +299,7 @@ public class DependencyDescriptor implements Serializable { Type[] args = ((ParameterizedType) type).getActualTypeArguments(); type = args[args.length - 1]; } + // TODO: Object.class if unresolvable } if (type instanceof Class) { return (Class<?>) type; @@ -323,19 +351,18 @@ public class DependencyDescriptor implements Serializable { GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter)); } - /** - * Obtain the annotations associated with the wrapped parameter/field, if any. - */ - public Annotation[] getAnnotations() { - if (this.field != null) { - if (this.fieldAnnotations == null) { - this.fieldAnnotations = this.field.getAnnotations(); - } - return this.fieldAnnotations; + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; } - else { - return this.methodParameter.getParameterAnnotations(); + if (!super.equals(other)) { + return false; } + DependencyDescriptor otherDesc = (DependencyDescriptor) other; + return (this.required == otherDesc.required && this.eager == otherDesc.eager && + this.nestingLevel == otherDesc.nestingLevel && this.containingClass == otherDesc.containingClass); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DestructionAwareBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DestructionAwareBeanPostProcessor.java index 76de1390..92316f13 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/DestructionAwareBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/DestructionAwareBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2015 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. @@ -43,4 +43,24 @@ public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor { */ void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException; + /** + * Determine whether the given bean instance requires destruction by this + * post-processor. + * <p><b>NOTE:</b> Even as a late addition, this method has been introduced on + * {@code DestructionAwareBeanPostProcessor} itself instead of on a SmartDABPP + * subinterface. This allows existing {@code DestructionAwareBeanPostProcessor} + * implementations to easily provide {@code requiresDestruction} logic while + * retaining compatibility with Spring <4.3, and it is also an easier onramp to + * declaring {@code requiresDestruction} as a Java 8 default method in Spring 5. + * <p>If an implementation of {@code DestructionAwareBeanPostProcessor} does + * not provide a concrete implementation of this method, Spring's invocation + * mechanism silently assumes a method returning {@code true} (the effective + * default before 4.3, and the to-be-default in the Java 8 method in Spring 5). + * @param bean the bean instance to check + * @return {@code true} if {@link #postProcessBeforeDestruction} is supposed to + * be called for this bean instance eventually, or {@code false} if not needed + * @since 4.3 + */ + boolean requiresDestruction(Object bean); + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/EmbeddedValueResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/EmbeddedValueResolver.java new file mode 100644 index 00000000..bd8c1666 --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/EmbeddedValueResolver.java @@ -0,0 +1,59 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.beans.factory.config; + +import org.springframework.util.StringValueResolver; + +/** + * {@link StringValueResolver} adapter for resolving placeholders and + * expressions against a {@link ConfigurableBeanFactory}. + * + * <p>Note that this adapter resolves expressions as well, in contrast + * to the {@link ConfigurableBeanFactory#resolveEmbeddedValue} method. + * The {@link BeanExpressionContext} used is for the plain bean factory, + * with no scope specified for any contextual objects to access. + * + * @author Juergen Hoeller + * @since 4.3 + * @see ConfigurableBeanFactory#resolveEmbeddedValue(String) + * @see ConfigurableBeanFactory#getBeanExpressionResolver() + * @see BeanExpressionContext + */ +public class EmbeddedValueResolver implements StringValueResolver { + + private final BeanExpressionContext exprContext; + + private final BeanExpressionResolver exprResolver; + + + public EmbeddedValueResolver(ConfigurableBeanFactory beanFactory) { + this.exprContext = new BeanExpressionContext(beanFactory, null); + this.exprResolver = beanFactory.getBeanExpressionResolver(); + } + + + @Override + public String resolveStringValue(String strVal) { + String value = this.exprContext.getBeanFactory().resolveEmbeddedValue(strVal); + if (this.exprResolver != null && value != null) { + Object evaluated = this.exprResolver.evaluate(value, this.exprContext); + value = (evaluated != null ? evaluated.toString() : null); + } + return value; + } + +} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java index f508e3ff..964ee204 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.java @@ -96,7 +96,7 @@ public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor { * dependency types - which the factory handles specifically - already filtered out) * @param bean the bean instance created, but whose properties have not yet been set * @param beanName the name of the bean - * @return the actual property values to apply to to the given bean + * @return the actual property values to apply to the given bean * (can be the passed-in PropertyValues instance), or {@code null} * to skip property population * @throws org.springframework.beans.BeansException in case of errors diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/PlaceholderConfigurerSupport.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/PlaceholderConfigurerSupport.java index a0152ed4..f7223a9a 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/PlaceholderConfigurerSupport.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/PlaceholderConfigurerSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -107,6 +107,8 @@ public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfi /** Defaults to {@value #DEFAULT_VALUE_SEPARATOR} */ protected String valueSeparator = DEFAULT_VALUE_SEPARATOR; + protected boolean trimValues = false; + protected String nullValue; protected boolean ignoreUnresolvablePlaceholders = false; @@ -143,6 +145,16 @@ public abstract class PlaceholderConfigurerSupport extends PropertyResourceConfi } /** + * Specify whether to trim resolved values before applying them, + * removing superfluous whitespace from the beginning and end. + * <p>Default is {@code false}. + * @since 4.3 + */ + public void setTrimValues(boolean trimValues) { + this.trimValues = trimValues; + } + + /** * Set a value that should be treated as {@code null} when resolved * as a placeholder value: e.g. "" (empty String) or "null". * <p>Note that this will only apply to full property values, diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java index 93a97a8d..adc415d0 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertiesFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -95,7 +95,7 @@ public class PropertiesFactoryBean extends PropertiesLoaderSupport * <p>Invoked on initialization of this FactoryBean in case of a * shared singleton; else, on each {@link #getObject()} call. * @return the object returned by this factory - * @throws IOException if an exception occured during properties loading + * @throws IOException if an exception occurred during properties loading * @see #mergeProperties() */ protected Properties createProperties() throws IOException { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java index f98ce40e..a0243a33 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -255,8 +255,11 @@ public class PropertyPlaceholderConfigurer extends PlaceholderConfigurerSupport @Override public String resolveStringValue(String strVal) throws BeansException { - String value = this.helper.replacePlaceholders(strVal, this.resolver); - return (value.equals(nullValue) ? null : value); + String resolved = this.helper.replacePlaceholders(strVal, this.resolver); + if (trimValues) { + resolved = resolved.trim(); + } + return (resolved.equals(nullValue) ? null : resolved); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java index 8777fa61..2911a123 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/Scope.java @@ -100,7 +100,7 @@ public interface Scope { * at the appropriate time. If such a callback is not supported by the * underlying runtime environment at all, the callback <i>must be * ignored and a corresponding warning should be logged</i>. - * <p>Note that 'destruction' refers to to automatic destruction of + * <p>Note that 'destruction' refers to automatic destruction of * the object as part of the scope's own lifecycle, not to the individual * scoped object having been explicitly removed by the application. * If a scoped object gets removed via this facade's {@link #remove(String)} diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java index e90712fc..bca9efeb 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java @@ -68,7 +68,7 @@ public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable * {@link org.springframework.context.ApplicationContext} implementations. * <p>If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a * {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}. - * <p>If the the passed-in bean factory also implements {@link EnvironmentCapable} its + * <p>If the passed-in bean factory also implements {@link EnvironmentCapable} its * environment will be used by this reader. Otherwise, the reader will initialize and * use a {@link StandardEnvironment}. All ApplicationContext implementations are * EnvironmentCapable, while normal BeanFactory implementations are not. diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java index 80e91528..62dfa38e 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java @@ -799,6 +799,11 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp } @Override + public boolean hasEmbeddedValueResolver() { + return !this.embeddedValueResolvers.isEmpty(); + } + + @Override public String resolveEmbeddedValue(String value) { String result = value; for (StringValueResolver resolver : this.embeddedValueResolvers) { @@ -1619,7 +1624,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp */ protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) { return (bean != null && - (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) || hasDestructionAwareBeanPostProcessors())); + (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) || (hasDestructionAwareBeanPostProcessors() && + DisposableBeanAdapter.hasApplicableProcessors(bean, getBeanPostProcessors())))); } /** diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java index 41653c84..cbb9d2f1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -115,7 +115,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt Class<?> subclass = createEnhancedSubclass(this.beanDefinition); Object instance; if (ctor == null) { - instance = BeanUtils.instantiate(subclass); + instance = BeanUtils.instantiateClass(subclass); } else { try { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index 50c56791..7c871d46 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -40,12 +40,14 @@ import org.springframework.beans.TypeConverter; import org.springframework.beans.TypeMismatchException; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.beans.factory.InjectionPoint; import org.springframework.beans.factory.UnsatisfiedDependencyException; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.core.GenericTypeResolver; import org.springframework.core.MethodParameter; +import org.springframework.core.NamedThreadLocal; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.util.ClassUtils; import org.springframework.util.MethodInvoker; @@ -68,6 +70,9 @@ import org.springframework.util.StringUtils; */ class ConstructorResolver { + private static final NamedThreadLocal<InjectionPoint> currentInjectionPoint = + new NamedThreadLocal<InjectionPoint>("Current injection point"); + private final AbstractAutowireCapableBeanFactory beanFactory; @@ -151,7 +156,7 @@ class ConstructorResolver { catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Resolution of declared constructors on bean Class [" + beanClass.getName() + - "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex); + "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex); } } AutowireUtils.sortConstructors(candidates); @@ -159,8 +164,7 @@ class ConstructorResolver { Set<Constructor<?>> ambiguousConstructors = null; LinkedList<UnsatisfiedDependencyException> causes = null; - for (int i = 0; i < candidates.length; i++) { - Constructor<?> candidate = candidates[i]; + for (Constructor<?> candidate : candidates) { Class<?>[] paramTypes = candidate.getParameterTypes(); if (constructorToUse != null && argsToUse.length > paramTypes.length) { @@ -182,8 +186,8 @@ class ConstructorResolver { paramNames = pnd.getParameterNames(candidate); } } - argsHolder = createArgumentArray( - beanName, mbd, resolvedValues, bw, paramTypes, paramNames, candidate, autowiring); + argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, + getUserDeclaredConstructor(candidate), autowiring); } catch (UnsatisfiedDependencyException ex) { if (this.beanFactory.logger.isTraceEnabled()) { @@ -268,7 +272,7 @@ class ConstructorResolver { mbd, beanName, this.beanFactory, constructorToUse, argsToUse); } - bw.setWrappedInstance(beanInstance); + bw.setBeanInstance(beanInstance); return bw; } catch (Throwable ex) { @@ -444,10 +448,9 @@ class ConstructorResolver { minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } - List<Exception> causes = null; + LinkedList<UnsatisfiedDependencyException> causes = null; - for (int i = 0; i < candidates.length; i++) { - Method candidate = candidates[i]; + for (Method candidate : candidates) { Class<?>[] paramTypes = candidate.getParameterTypes(); if (paramTypes.length >= minNrOfArgs) { @@ -469,22 +472,12 @@ class ConstructorResolver { this.beanFactory.logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex); } - if (i == candidates.length - 1 && argsHolderToUse == null) { - if (causes != null) { - for (Exception cause : causes) { - this.beanFactory.onSuppressedException(cause); - } - } - throw ex; - } - else { - // Swallow and try next overloaded factory method. - if (causes == null) { - causes = new LinkedList<Exception>(); - } - causes.add(ex); - continue; + // Swallow and try next overloaded factory method. + if (causes == null) { + causes = new LinkedList<UnsatisfiedDependencyException>(); } + causes.add(ex); + continue; } } @@ -525,6 +518,13 @@ class ConstructorResolver { } if (factoryMethodToUse == null) { + if (causes != null) { + UnsatisfiedDependencyException ex = causes.removeLast(); + for (Exception cause : causes) { + this.beanFactory.onSuppressedException(cause); + } + throw ex; + } List<String> argTypes = new ArrayList<String>(minNrOfArgs); if (explicitArgs != null) { for (Object arg : explicitArgs) { @@ -592,7 +592,7 @@ class ConstructorResolver { if (beanInstance == null) { return null; } - bw.setWrappedInstance(beanInstance); + bw.setBeanInstance(beanInstance); return bw; } catch (Throwable ex) { @@ -609,8 +609,8 @@ class ConstructorResolver { private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw, ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) { - TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ? - this.beanFactory.getCustomTypeConverter() : bw); + TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); + TypeConverter converter = (customConverter != null ? customConverter : bw); BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter); @@ -665,9 +665,8 @@ class ConstructorResolver { BeanWrapper bw, Class<?>[] paramTypes, String[] paramNames, Object methodOrCtor, boolean autowiring) throws UnsatisfiedDependencyException { - String methodType = (methodOrCtor instanceof Constructor ? "constructor" : "factory method"); - TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ? - this.beanFactory.getCustomTypeConverter() : bw); + TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); + TypeConverter converter = (customConverter != null ? customConverter : bw); ArgumentsHolder args = new ArgumentsHolder(paramTypes.length); Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = @@ -676,14 +675,14 @@ class ConstructorResolver { for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) { Class<?> paramType = paramTypes[paramIndex]; - String paramName = (paramNames != null ? paramNames[paramIndex] : null); + String paramName = (paramNames != null ? paramNames[paramIndex] : ""); // Try to find matching constructor argument value, either indexed or generic. ConstructorArgumentValues.ValueHolder valueHolder = 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) { + if (valueHolder == null && (!autowiring || paramTypes.length == resolvedValues.getArgumentCount())) { valueHolder = resolvedValues.getGenericArgumentValue(null, null, usedValueHolders); } if (valueHolder != null) { @@ -700,9 +699,9 @@ class ConstructorResolver { ConstructorArgumentValues.ValueHolder sourceHolder = (ConstructorArgumentValues.ValueHolder) valueHolder.getSource(); Object sourceValue = sourceHolder.getValue(); + MethodParameter methodParam = MethodParameter.forMethodOrConstructor(methodOrCtor, paramIndex); try { - convertedValue = converter.convertIfNecessary(originalValue, paramType, - MethodParameter.forMethodOrConstructor(methodOrCtor, paramIndex)); + convertedValue = converter.convertIfNecessary(originalValue, paramType, methodParam); // TODO re-enable once race condition has been found (SPR-7423) /* if (originalValue == sourceValue || sourceValue instanceof TypedStringValue) { @@ -718,8 +717,8 @@ class ConstructorResolver { } catch (TypeMismatchException ex) { throw new UnsatisfiedDependencyException( - mbd.getResourceDescription(), beanName, paramIndex, paramType, - "Could not convert " + methodType + " argument value of type [" + + mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), + "Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(valueHolder.getValue()) + "] to required type [" + paramType.getName() + "]: " + ex.getMessage()); } @@ -728,17 +727,18 @@ class ConstructorResolver { args.rawArguments[paramIndex] = originalValue; } else { + MethodParameter methodParam = MethodParameter.forMethodOrConstructor(methodOrCtor, paramIndex); // No explicit match found: we're either supposed to autowire or // have to fail creating an argument array for the given constructor. if (!autowiring) { throw new UnsatisfiedDependencyException( - mbd.getResourceDescription(), beanName, paramIndex, paramType, - "Ambiguous " + methodType + " argument types - " + - "did you specify the correct bean references as " + methodType + " arguments?"); + mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), + "Ambiguous argument values for parameter of type [" + paramType.getName() + + "] - did you specify the correct bean references as arguments?"); } try { - MethodParameter param = MethodParameter.forMethodOrConstructor(methodOrCtor, paramIndex); - Object autowiredArgument = resolveAutowiredArgument(param, beanName, autowiredBeanNames, converter); + Object autowiredArgument = + resolveAutowiredArgument(methodParam, beanName, autowiredBeanNames, converter); args.rawArguments[paramIndex] = autowiredArgument; args.arguments[paramIndex] = autowiredArgument; args.preparedArguments[paramIndex] = new AutowiredArgumentMarker(); @@ -746,7 +746,7 @@ class ConstructorResolver { } catch (BeansException ex) { throw new UnsatisfiedDependencyException( - mbd.getResourceDescription(), beanName, paramIndex, paramType, ex); + mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), ex); } } } @@ -755,7 +755,8 @@ class ConstructorResolver { this.beanFactory.registerDependentBean(autowiredBeanName, beanName); if (this.beanFactory.logger.isDebugEnabled()) { this.beanFactory.logger.debug("Autowiring by type from bean name '" + beanName + - "' via " + methodType + " to bean named '" + autowiredBeanName + "'"); + "' via " + (methodOrCtor instanceof Constructor ? "constructor" : "factory method") + + " to bean named '" + autowiredBeanName + "'"); } } @@ -768,12 +769,13 @@ class ConstructorResolver { private Object[] resolvePreparedArguments( String beanName, RootBeanDefinition mbd, BeanWrapper bw, Member methodOrCtor, Object[] argsToResolve) { - Class<?>[] paramTypes = (methodOrCtor instanceof Method ? - ((Method) methodOrCtor).getParameterTypes() : ((Constructor<?>) methodOrCtor).getParameterTypes()); - TypeConverter converter = (this.beanFactory.getCustomTypeConverter() != null ? - this.beanFactory.getCustomTypeConverter() : bw); + TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); + TypeConverter converter = (customConverter != null ? customConverter : bw); BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter); + Class<?>[] paramTypes = (methodOrCtor instanceof Method ? + ((Method) methodOrCtor).getParameterTypes() : ((Constructor<?>) methodOrCtor).getParameterTypes()); + Object[] resolvedArgs = new Object[argsToResolve.length]; for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) { Object argValue = argsToResolve[argIndex]; @@ -793,28 +795,61 @@ class ConstructorResolver { resolvedArgs[argIndex] = converter.convertIfNecessary(argValue, paramType, methodParam); } catch (TypeMismatchException ex) { - String methodType = (methodOrCtor instanceof Constructor ? "constructor" : "factory method"); throw new UnsatisfiedDependencyException( - mbd.getResourceDescription(), beanName, argIndex, paramType, - "Could not convert " + methodType + " argument value of type [" + - ObjectUtils.nullSafeClassName(argValue) + + mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), + "Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(argValue) + "] to required type [" + paramType.getName() + "]: " + ex.getMessage()); } } return resolvedArgs; } + protected Constructor<?> getUserDeclaredConstructor(Constructor<?> constructor) { + Class<?> declaringClass = constructor.getDeclaringClass(); + Class<?> userClass = ClassUtils.getUserClass(declaringClass); + if (userClass != declaringClass) { + try { + return userClass.getDeclaredConstructor(constructor.getParameterTypes()); + } + catch (NoSuchMethodException ex) { + // No equivalent constructor on user class (superclass)... + // Let's proceed with the given constructor as we usually would. + } + } + return constructor; + } + /** * Template method for resolving the specified argument which is supposed to be autowired. */ protected Object resolveAutowiredArgument( MethodParameter param, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) { + if (InjectionPoint.class.isAssignableFrom(param.getParameterType())) { + InjectionPoint injectionPoint = currentInjectionPoint.get(); + if (injectionPoint == null) { + throw new IllegalStateException("No current InjectionPoint available for " + param); + } + return injectionPoint; + } return this.beanFactory.resolveDependency( new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter); } + + static InjectionPoint setCurrentInjectionPoint(InjectionPoint injectionPoint) { + InjectionPoint old = currentInjectionPoint.get(); + if (injectionPoint != null) { + currentInjectionPoint.set(injectionPoint); + } + else { + currentInjectionPoint.remove(); + } + return old; + } + + /** * Private inner class for holding argument combinations. */ diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java index 7a32060e..bb4ef59b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -45,7 +45,6 @@ import java.util.concurrent.ConcurrentHashMap; import javax.inject.Provider; import org.springframework.beans.BeansException; -import org.springframework.beans.FatalBeanException; import org.springframework.beans.TypeConverter; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanCurrentlyInCreationException; @@ -53,11 +52,14 @@ import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.BeanNotOfRequiredTypeException; import org.springframework.beans.factory.CannotLoadBeanClassException; import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InjectionPoint; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.SmartFactoryBean; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.config.BeanDefinition; @@ -615,10 +617,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @Override public void registerResolvableDependency(Class<?> dependencyType, Object autowiredValue) { - Assert.notNull(dependencyType, "Type must not be null"); + Assert.notNull(dependencyType, "Dependency type must not be null"); if (autowiredValue != null) { - Assert.isTrue((autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue)), - "Value [" + autowiredValue + "] does not implement specified type [" + dependencyType.getName() + "]"); + if (!(autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue))) { + throw new IllegalArgumentException("Value [" + autowiredValue + + "] does not implement specified dependency type [" + dependencyType.getName() + "]"); + } this.resolvableDependencies.put(dependencyType, autowiredValue); } } @@ -999,14 +1003,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException { descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); - if (descriptor.getDependencyType().equals(javaUtilOptionalClass)) { + if (javaUtilOptionalClass == descriptor.getDependencyType()) { return new OptionalDependencyFactory().createOptionalDependency(descriptor, beanName); } - else if (ObjectFactory.class == descriptor.getDependencyType()) { - return new DependencyObjectFactory(descriptor, beanName); + else if (ObjectFactory.class == descriptor.getDependencyType() || + ObjectProvider.class == descriptor.getDependencyType()) { + return new DependencyObjectProvider(descriptor, beanName); } else if (javaxInjectProviderClass == descriptor.getDependencyType()) { - return new DependencyProviderFactory().createDependencyProvider(descriptor, beanName); + return new Jsr330ProviderFactory().createDependencyProvider(descriptor, beanName); } else { Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, beanName); @@ -1020,29 +1025,79 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto public Object doResolveDependency(DependencyDescriptor descriptor, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException { - Class<?> type = descriptor.getDependencyType(); - Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); - if (value != null) { - if (value instanceof String) { - String strVal = resolveEmbeddedValue((String) value); - BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); - value = evaluateBeanDefinitionString(strVal, bd); + InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); + try { + Object shortcut = descriptor.resolveShortcut(this); + if (shortcut != null) { + return shortcut; } - TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); - return (descriptor.getField() != null ? - converter.convertIfNecessary(value, type, descriptor.getField()) : - converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); + + Class<?> type = descriptor.getDependencyType(); + Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); + if (value != null) { + if (value instanceof String) { + String strVal = resolveEmbeddedValue((String) value); + BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); + value = evaluateBeanDefinitionString(strVal, bd); + } + TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); + return (descriptor.getField() != null ? + converter.convertIfNecessary(value, type, descriptor.getField()) : + converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); + } + + Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); + if (multipleBeans != null) { + return multipleBeans; + } + + Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); + if (matchingBeans.isEmpty()) { + if (descriptor.isRequired()) { + raiseNoMatchingBeanFound(type, descriptor.getResolvableType().toString(), descriptor); + } + return null; + } + if (matchingBeans.size() > 1) { + String primaryBeanName = determineAutowireCandidate(matchingBeans, descriptor); + if (primaryBeanName == null) { + if (descriptor.isRequired() || !indicatesMultipleBeans(type)) { + return descriptor.resolveNotUnique(type, matchingBeans); + } + else { + // In case of an optional Collection/Map, silently ignore a non-unique case: + // possibly it was meant to be an empty collection of multiple regular beans + // (before 4.3 in particular when we didn't even look for collection beans). + return null; + } + } + if (autowiredBeanNames != null) { + autowiredBeanNames.add(primaryBeanName); + } + return matchingBeans.get(primaryBeanName); + } + // We have exactly one match. + Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); + if (autowiredBeanNames != null) { + autowiredBeanNames.add(entry.getKey()); + } + return entry.getValue(); + } + finally { + ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); } + } + + private Object resolveMultipleBeans(DependencyDescriptor descriptor, String beanName, + Set<String> autowiredBeanNames, TypeConverter typeConverter) { + Class<?> type = descriptor.getDependencyType(); if (type.isArray()) { Class<?> componentType = type.getComponentType(); DependencyDescriptor targetDesc = new DependencyDescriptor(descriptor); targetDesc.increaseNestingLevel(); Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType, targetDesc); if (matchingBeans.isEmpty()) { - if (descriptor.isRequired()) { - raiseNoSuchBeanDefinitionException(componentType, "array of " + componentType.getName(), descriptor); - } return null; } if (autowiredBeanNames != null) { @@ -1058,18 +1113,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto else if (Collection.class.isAssignableFrom(type) && type.isInterface()) { Class<?> elementType = descriptor.getCollectionType(); if (elementType == null) { - if (descriptor.isRequired()) { - throw new FatalBeanException("No element type declared for collection [" + type.getName() + "]"); - } return null; } DependencyDescriptor targetDesc = new DependencyDescriptor(descriptor); targetDesc.increaseNestingLevel(); Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType, targetDesc); if (matchingBeans.isEmpty()) { - if (descriptor.isRequired()) { - raiseNoSuchBeanDefinitionException(elementType, "collection of " + elementType.getName(), descriptor); - } return null; } if (autowiredBeanNames != null) { @@ -1085,26 +1134,16 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto else if (Map.class.isAssignableFrom(type) && type.isInterface()) { Class<?> keyType = descriptor.getMapKeyType(); if (String.class != keyType) { - if (descriptor.isRequired()) { - throw new FatalBeanException("Key type [" + keyType + "] of map [" + type.getName() + - "] must be [java.lang.String]"); - } return null; } Class<?> valueType = descriptor.getMapValueType(); if (valueType == null) { - if (descriptor.isRequired()) { - throw new FatalBeanException("No value type declared for map [" + type.getName() + "]"); - } return null; } DependencyDescriptor targetDesc = new DependencyDescriptor(descriptor); targetDesc.increaseNestingLevel(); Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType, targetDesc); if (matchingBeans.isEmpty()) { - if (descriptor.isRequired()) { - raiseNoSuchBeanDefinitionException(valueType, "map with value type " + valueType.getName(), descriptor); - } return null; } if (autowiredBeanNames != null) { @@ -1113,32 +1152,15 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto return matchingBeans; } else { - Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); - if (matchingBeans.isEmpty()) { - if (descriptor.isRequired()) { - raiseNoSuchBeanDefinitionException(type, "", descriptor); - } - return null; - } - if (matchingBeans.size() > 1) { - String primaryBeanName = determineAutowireCandidate(matchingBeans, descriptor); - if (primaryBeanName == null) { - throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet()); - } - if (autowiredBeanNames != null) { - autowiredBeanNames.add(primaryBeanName); - } - return matchingBeans.get(primaryBeanName); - } - // We have exactly one match. - Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); - if (autowiredBeanNames != null) { - autowiredBeanNames.add(entry.getKey()); - } - return entry.getValue(); + return null; } } + private boolean indicatesMultipleBeans(Class<?> type) { + return (type.isArray() || (type.isInterface() && + (Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type)))); + } + private Comparator<Object> adaptDependencyComparator(Map<String, Object> matchingBeans) { Comparator<Object> comparator = getDependencyComparator(); if (comparator instanceof OrderComparator) { @@ -1189,14 +1211,23 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } for (String candidateName : candidateNames) { if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, descriptor)) { - result.put(candidateName, getBean(candidateName)); + result.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this)); } } - if (result.isEmpty()) { + if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) { + // Consider fallback matches if the first pass failed to find anything... DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch(); for (String candidateName : candidateNames) { - if (!candidateName.equals(beanName) && isAutowireCandidate(candidateName, fallbackDescriptor)) { - result.put(candidateName, getBean(candidateName)); + if (!isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) { + result.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this)); + } + } + if (result.isEmpty()) { + // Consider self references before as a final pass + for (String candidateName : candidateNames) { + if (isSelfReference(beanName, candidateName) && isAutowireCandidate(candidateName, fallbackDescriptor)) { + result.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this)); + } } } } @@ -1362,17 +1393,43 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto } /** - * Raise a NoSuchBeanDefinitionException for an unresolvable dependency. + * Raise a NoSuchBeanDefinitionException or BeanNotOfRequiredTypeException + * for an unresolvable dependency. */ - private void raiseNoSuchBeanDefinitionException( - Class<?> type, String dependencyDescription, DependencyDescriptor descriptor) - throws NoSuchBeanDefinitionException { + private void raiseNoMatchingBeanFound( + Class<?> type, String dependencyDescription, DependencyDescriptor descriptor) throws BeansException { + + checkBeanNotOfRequiredType(type, descriptor); throw new NoSuchBeanDefinitionException(type, dependencyDescription, "expected at least 1 bean which qualifies as autowire candidate for this dependency. " + "Dependency annotations: " + ObjectUtils.nullSafeToString(descriptor.getAnnotations())); } + /** + * Raise a BeanNotOfRequiredTypeException for an unresolvable dependency, if applicable, + * i.e. if the target type of the bean would match but an exposed proxy doesn't. + */ + private void checkBeanNotOfRequiredType(Class<?> type, DependencyDescriptor descriptor) { + for (String beanName : this.beanDefinitionNames) { + RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); + Class<?> targetType = mbd.getTargetType(); + if (targetType != null && type.isAssignableFrom(targetType) && + isAutowireCandidate(beanName, mbd, descriptor, getAutowireCandidateResolver())) { + // Probably a poxy interfering with target type match -> throw meaningful exception. + Object beanInstance = getSingleton(beanName, false); + Class<?> beanType = (beanInstance != null ? beanInstance.getClass() : predictBeanType(beanName, mbd)); + if (type != beanType) { + throw new BeanNotOfRequiredTypeException(beanName, type, beanType); + } + } + } + + if (getParentBeanFactory() instanceof DefaultListableBeanFactory) { + ((DefaultListableBeanFactory) getParentBeanFactory()).checkBeanNotOfRequiredType(type, descriptor); + } + } + @Override public String toString() { @@ -1424,16 +1481,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto private Object readResolve() { Reference<?> ref = serializableFactories.get(this.id); - if (ref == null) { - throw new IllegalStateException( - "Cannot deserialize BeanFactory with id " + this.id + ": no factory registered for this id"); - } - Object result = ref.get(); - if (result == null) { - throw new IllegalStateException( - "Cannot deserialize BeanFactory with id " + this.id + ": factory has been garbage-collected"); + if (ref != null) { + Object result = ref.get(); + if (result != null) { + return result; + } } - return result; + // Lenient fallback: dummy factory in case of original factory not found... + return new StaticListableBeanFactory(Collections.<String, Object> emptyMap()); } } @@ -1444,12 +1499,17 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto @UsesJava8 private class OptionalDependencyFactory { - public Object createOptionalDependency(DependencyDescriptor descriptor, String beanName) { + public Object createOptionalDependency(DependencyDescriptor descriptor, String beanName, final Object... args) { DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) { @Override public boolean isRequired() { return false; } + @Override + public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) { + return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, requiredType, args) : + super.resolveCandidate(beanName, requiredType, beanFactory)); + } }; descriptorToUse.increaseNestingLevel(); return Optional.ofNullable(doResolveDependency(descriptorToUse, beanName, null, null)); @@ -1458,9 +1518,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto /** - * Serializable ObjectFactory for lazy resolution of a dependency. + * Serializable ObjectFactory/ObjectProvider for lazy resolution of a dependency. */ - private class DependencyObjectFactory implements ObjectFactory<Object>, Serializable { + private class DependencyObjectProvider implements ObjectProvider<Object>, Serializable { private final DependencyDescriptor descriptor; @@ -1468,7 +1528,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto private final String beanName; - public DependencyObjectFactory(DependencyDescriptor descriptor, String beanName) { + public DependencyObjectProvider(DependencyDescriptor descriptor, String beanName) { this.descriptor = new DependencyDescriptor(descriptor); this.descriptor.increaseNestingLevel(); this.optional = this.descriptor.getDependencyType().equals(javaUtilOptionalClass); @@ -1484,15 +1544,67 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto return doResolveDependency(this.descriptor, this.beanName, null, null); } } + + @Override + public Object getObject(final Object... args) throws BeansException { + if (this.optional) { + return new OptionalDependencyFactory().createOptionalDependency(this.descriptor, this.beanName, args); + } + else { + DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) { + @Override + public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) { + return ((AbstractBeanFactory) beanFactory).getBean(beanName, requiredType, args); + } + }; + return doResolveDependency(descriptorToUse, this.beanName, null, null); + } + } + + @Override + public Object getIfAvailable() throws BeansException { + if (this.optional) { + return new OptionalDependencyFactory().createOptionalDependency(this.descriptor, this.beanName); + } + else { + DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) { + @Override + public boolean isRequired() { + return false; + } + }; + return doResolveDependency(descriptorToUse, this.beanName, null, null); + } + } + + @Override + public Object getIfUnique() throws BeansException { + DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) { + @Override + public boolean isRequired() { + return false; + } + @Override + public Object resolveNotUnique(Class<?> type, Map<String, Object> matchingBeans) { + return null; + } + }; + if (this.optional) { + return new OptionalDependencyFactory().createOptionalDependency(descriptorToUse, this.beanName); + } + else { + return doResolveDependency(descriptorToUse, this.beanName, null, null); + } + } } /** * Serializable ObjectFactory for lazy resolution of a dependency. */ - private class DependencyProvider extends DependencyObjectFactory implements Provider<Object> { + private class Jsr330DependencyProvider extends DependencyObjectProvider implements Provider<Object> { - public DependencyProvider(DependencyDescriptor descriptor, String beanName) { + public Jsr330DependencyProvider(DependencyDescriptor descriptor, String beanName) { super(descriptor, beanName); } @@ -1506,10 +1618,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto /** * Separate inner class for avoiding a hard dependency on the {@code javax.inject} API. */ - private class DependencyProviderFactory { + private class Jsr330ProviderFactory { public Object createDependencyProvider(DependencyDescriptor descriptor, String beanName) { - return new DependencyProvider(descriptor, beanName); + return new Jsr330DependencyProvider(descriptor, beanName); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java index 9042bad5..d3d21347 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -449,10 +449,10 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements } private boolean isDependent(String beanName, String dependentBeanName, Set<String> alreadySeen) { - String canonicalName = canonicalName(beanName); if (alreadySeen != null && alreadySeen.contains(beanName)) { return false; } + String canonicalName = canonicalName(beanName); Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName); if (dependentBeans == null) { return false; diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java index d3a6c361..b79403d6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java @@ -139,7 +139,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { } } } - this.beanPostProcessors = filterPostProcessors(postProcessors); + this.beanPostProcessors = filterPostProcessors(postProcessors, bean); } /** @@ -155,7 +155,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { this.invokeDisposableBean = (this.bean instanceof DisposableBean); this.nonPublicAccessAllowed = true; this.acc = acc; - this.beanPostProcessors = filterPostProcessors(postProcessors); + this.beanPostProcessors = filterPostProcessors(postProcessors, bean); } /** @@ -214,16 +214,26 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { /** * Search for all DestructionAwareBeanPostProcessors in the List. - * @param postProcessors the List to search + * @param processors the List to search * @return the filtered List of DestructionAwareBeanPostProcessors */ - private List<DestructionAwareBeanPostProcessor> filterPostProcessors(List<BeanPostProcessor> postProcessors) { + private List<DestructionAwareBeanPostProcessor> filterPostProcessors(List<BeanPostProcessor> processors, Object bean) { List<DestructionAwareBeanPostProcessor> filteredPostProcessors = null; - if (!CollectionUtils.isEmpty(postProcessors)) { - filteredPostProcessors = new ArrayList<DestructionAwareBeanPostProcessor>(postProcessors.size()); - for (BeanPostProcessor postProcessor : postProcessors) { - if (postProcessor instanceof DestructionAwareBeanPostProcessor) { - filteredPostProcessors.add((DestructionAwareBeanPostProcessor) postProcessor); + if (!CollectionUtils.isEmpty(processors)) { + filteredPostProcessors = new ArrayList<DestructionAwareBeanPostProcessor>(processors.size()); + for (BeanPostProcessor processor : processors) { + if (processor instanceof DestructionAwareBeanPostProcessor) { + DestructionAwareBeanPostProcessor dabpp = (DestructionAwareBeanPostProcessor) processor; + try { + if (dabpp.requiresDestruction(bean)) { + filteredPostProcessors.add(dabpp); + } + } + catch (AbstractMethodError err) { + // A pre-4.3 third-party DestructionAwareBeanPostProcessor... + // As of 5.0, we can let requiresDestruction be a Java 8 default method which returns true. + filteredPostProcessors.add(dabpp); + } } } } @@ -401,9 +411,36 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { } String destroyMethodName = beanDefinition.getDestroyMethodName(); if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName)) { - return ClassUtils.hasMethod(bean.getClass(), CLOSE_METHOD_NAME); + return (ClassUtils.hasMethod(bean.getClass(), CLOSE_METHOD_NAME) || + ClassUtils.hasMethod(bean.getClass(), SHUTDOWN_METHOD_NAME)); } return StringUtils.hasLength(destroyMethodName); } + /** + * Check whether the given bean has destruction-aware post-processors applying to it. + * @param bean the bean instance + * @param postProcessors the post-processor candidates + */ + public static boolean hasApplicableProcessors(Object bean, List<BeanPostProcessor> postProcessors) { + if (!CollectionUtils.isEmpty(postProcessors)) { + for (BeanPostProcessor processor : postProcessors) { + if (processor instanceof DestructionAwareBeanPostProcessor) { + DestructionAwareBeanPostProcessor dabpp = (DestructionAwareBeanPostProcessor) processor; + try { + if (dabpp.requiresDestruction(bean)) { + return true; + } + } + catch (AbstractMethodError err) { + // A pre-4.3 third-party DestructionAwareBeanPostProcessor... + // As of 5.0, we can let requiresDestruction be a Java 8 default method which returns true. + return true; + } + } + } + } + return false; + } + } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java index d2ac971c..91ad5d8e 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -125,7 +125,7 @@ public class RootBeanDefinition extends AbstractBeanDefinition { setBeanClass(beanClass); setAutowireMode(autowireMode); if (dependencyCheck && getResolvedAutowireMode() != AUTOWIRE_CONSTRUCTOR) { - setDependencyCheck(RootBeanDefinition.DEPENDENCY_CHECK_OBJECTS); + setDependencyCheck(DEPENDENCY_CHECK_OBJECTS); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java index f878c57e..421805cb 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/SimpleInstantiationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 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. @@ -81,7 +81,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy { } bd.resolvedConstructorOrFactoryMethod = constructorToUse; } - catch (Exception ex) { + catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } @@ -171,12 +171,12 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy { } } catch (IllegalArgumentException ex) { - throw new BeanInstantiationException(factoryMethod.getReturnType(), + throw new BeanInstantiationException(factoryMethod, "Illegal arguments to factory method '" + factoryMethod.getName() + "'; " + "args: " + StringUtils.arrayToCommaDelimitedString(args), ex); } catch (IllegalAccessException ex) { - throw new BeanInstantiationException(factoryMethod.getReturnType(), + throw new BeanInstantiationException(factoryMethod, "Cannot access factory method '" + factoryMethod.getName() + "'; is it public?", ex); } catch (InvocationTargetException ex) { @@ -186,7 +186,7 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy { msg = "Circular reference involving containing bean '" + bd.getFactoryBeanName() + "' - consider " + "declaring the factory method as static for independence from its containing instance. " + msg; } - throw new BeanInstantiationException(factoryMethod.getReturnType(), msg, ex.getTargetException()); + throw new BeanInstantiationException(factoryMethod, msg, ex.getTargetException()); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java index 6e064c5d..32866846 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/StaticListableBeanFactory.java @@ -34,6 +34,7 @@ import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.SmartFactoryBean; import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -58,7 +59,31 @@ import org.springframework.util.StringUtils; public class StaticListableBeanFactory implements ListableBeanFactory { /** Map from bean name to bean instance */ - private final Map<String, Object> beans = new LinkedHashMap<String, Object>(); + private final Map<String, Object> beans; + + + /** + * Create a regular {@code StaticListableBeanFactory}, to be populated + * with singleton bean instances through {@link #addBean} calls. + */ + public StaticListableBeanFactory() { + this.beans = new LinkedHashMap<String, Object>(); + } + + /** + * Create a {@code StaticListableBeanFactory} wrapping the given {@code Map}. + * <p>Note that the given {@code Map} may be pre-populated with beans; + * or new, still allowing for beans to be registered via {@link #addBean}; + * or {@link java.util.Collections#emptyMap()} for a dummy factory which + * enforces operating against an empty set of beans. + * @param beans a {@code Map} for holding this factory's beans, with the + * bean name String as key and the corresponding singleton object as value + * @since 4.3 + */ + public StaticListableBeanFactory(Map<String, Object> beans) { + Assert.notNull(beans, "Beans Map must not be null"); + this.beans = beans; + } /** diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java index 494bcd39..6e2f6ad9 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/xml/DefaultBeanDefinitionDocumentReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -129,6 +129,10 @@ public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocume String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { + if (logger.isInfoEnabled()) { + logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + + "] not matching: " + getReaderContext().getResource()); + } return; } } diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomCollectionEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomCollectionEditor.java index 607a99af..4c63ac79 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomCollectionEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomCollectionEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -154,9 +154,9 @@ public class CustomCollectionEditor extends PropertyEditorSupport { try { return collectionType.newInstance(); } - catch (Exception ex) { + catch (Throwable ex) { throw new IllegalArgumentException( - "Could not instantiate collection class [" + collectionType.getName() + "]: " + ex.getMessage()); + "Could not instantiate collection class: " + collectionType.getName(), ex); } } else if (List.class == collectionType) { diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomMapEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomMapEditor.java index 85865e54..05b5d872 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomMapEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/CustomMapEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -132,9 +132,9 @@ public class CustomMapEditor extends PropertyEditorSupport { try { return mapType.newInstance(); } - catch (Exception ex) { + catch (Throwable ex) { throw new IllegalArgumentException( - "Could not instantiate map class [" + mapType.getName() + "]: " + ex.getMessage()); + "Could not instantiate map class: " + mapType.getName(), ex); } } else if (SortedMap.class == mapType) { diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/FileEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/FileEditor.java index a57b69ea..676fd0c6 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/FileEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/FileEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -59,16 +59,14 @@ public class FileEditor extends PropertyEditorSupport { /** - * Create a new FileEditor, - * using the default ResourceEditor underneath. + * Create a new FileEditor, using a default ResourceEditor underneath. */ public FileEditor() { this.resourceEditor = new ResourceEditor(); } /** - * Create a new FileEditor, - * using the given ResourceEditor underneath. + * Create a new FileEditor, using the given ResourceEditor underneath. * @param resourceEditor the ResourceEditor to use */ public FileEditor(ResourceEditor resourceEditor) { @@ -105,7 +103,7 @@ public class FileEditor extends PropertyEditorSupport { } catch (IOException ex) { throw new IllegalArgumentException( - "Could not retrieve File for " + resource + ": " + ex.getMessage()); + "Could not retrieve file for " + resource + ": " + ex.getMessage()); } } else { diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/InputStreamEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/InputStreamEditor.java index b4b014b7..dc982393 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/InputStreamEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/InputStreamEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -47,16 +47,14 @@ public class InputStreamEditor extends PropertyEditorSupport { /** - * Create a new InputStreamEditor, - * using the default ResourceEditor underneath. + * Create a new InputStreamEditor, using the default ResourceEditor underneath. */ public InputStreamEditor() { this.resourceEditor = new ResourceEditor(); } /** - * Create a new InputStreamEditor, - * using the given ResourceEditor underneath. + * Create a new InputStreamEditor, using the given ResourceEditor underneath. * @param resourceEditor the ResourceEditor to use */ public InputStreamEditor(ResourceEditor resourceEditor) { diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/PathEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/PathEditor.java new file mode 100644 index 00000000..4767b198 --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/PathEditor.java @@ -0,0 +1,116 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.beans.propertyeditors; + +import java.beans.PropertyEditorSupport; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceEditor; +import org.springframework.core.io.ResourceLoader; +import org.springframework.lang.UsesJava7; +import org.springframework.util.Assert; + +/** + * Editor for {@code java.nio.file.Path}, to directly populate a Path + * property instead of using a String property as bridge. + * + * <p>Based on {@link Paths#get(URI)}'s resolution algorithm, checking + * registered NIO file system providers, including the default file system + * for "file:..." paths. Also supports Spring-style URL notation: any fully + * qualified standard URL and Spring's special "classpath:" pseudo-URL, + * as well as Spring's context-specific relative file paths. + * + * <p>Note that, in contrast to {@link FileEditor}, relative paths are only + * supported by Spring's resource abstraction here. Direct {@code Paths.get} + * resolution in a file system always has to go through the corresponding + * file system provider's scheme, i.e. "file" for the default file system. + * + * @author Juergen Hoeller + * @since 4.3.2 + * @see java.nio.file.Path + * @see Paths#get(URI) + * @see ResourceEditor + * @see org.springframework.core.io.ResourceLoader + * @see FileEditor + * @see URLEditor + */ +@UsesJava7 +public class PathEditor extends PropertyEditorSupport { + + private final ResourceEditor resourceEditor; + + + /** + * Create a new PathEditor, using the default ResourceEditor underneath. + */ + public PathEditor() { + this.resourceEditor = new ResourceEditor(); + } + + /** + * Create a new PathEditor, using the given ResourceEditor underneath. + * @param resourceEditor the ResourceEditor to use + */ + public PathEditor(ResourceEditor resourceEditor) { + Assert.notNull(resourceEditor, "ResourceEditor must not be null"); + this.resourceEditor = resourceEditor; + } + + + @Override + public void setAsText(String text) throws IllegalArgumentException { + if (!text.startsWith("/") && !text.startsWith(ResourceLoader.CLASSPATH_URL_PREFIX)) { + try { + URI uri = new URI(text); + if (uri.getScheme() != null) { + // Let's try NIO file system providers via Paths.get(URI) + setValue(Paths.get(uri).normalize()); + return; + } + } + catch (URISyntaxException ex) { + // Not a valid URI: Let's try as Spring resource location. + } + catch (FileSystemNotFoundException ex) { + // URI scheme not registered for NIO: + // Let's try URL protocol handlers via Spring's resource mechanism. + } + } + + this.resourceEditor.setAsText(text); + Resource resource = (Resource) this.resourceEditor.getValue(); + try { + setValue(resource != null ? resource.getFile().toPath() : null); + } + catch (IOException ex) { + throw new IllegalArgumentException("Failed to retrieve file for " + resource, ex); + } + } + + @Override + public String getAsText() { + Path value = (Path) getValue(); + return (value != null ? value.toString() : ""); + } + +} diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ReaderEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ReaderEditor.java index 826760ee..894d9770 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ReaderEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/ReaderEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -47,16 +47,14 @@ public class ReaderEditor extends PropertyEditorSupport { /** - * Create a new ReaderEditor, - * using the default ResourceEditor underneath. + * Create a new ReaderEditor, using the default ResourceEditor underneath. */ public ReaderEditor() { this.resourceEditor = new ResourceEditor(); } /** - * Create a new ReaderEditor, - * using the given ResourceEditor underneath. + * Create a new ReaderEditor, using the given ResourceEditor underneath. * @param resourceEditor the ResourceEditor to use */ public ReaderEditor(ResourceEditor resourceEditor) { diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/StringArrayPropertyEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/StringArrayPropertyEditor.java index c8875b38..85adf515 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/StringArrayPropertyEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/StringArrayPropertyEditor.java @@ -84,7 +84,7 @@ public class StringArrayPropertyEditor extends PropertyEditorSupport { * @param emptyArrayAsNull {@code true} if an empty String array * is to be transformed into {@code null} * @param trimValues {@code true} if the values in the parsed arrays - * are to be be trimmed of whitespace (default is true). + * are to be trimmed of whitespace (default is true). */ public StringArrayPropertyEditor(String separator, boolean emptyArrayAsNull, boolean trimValues) { this(separator, null, emptyArrayAsNull, trimValues); @@ -112,7 +112,7 @@ public class StringArrayPropertyEditor extends PropertyEditorSupport { * @param emptyArrayAsNull {@code true} if an empty String array * is to be transformed into {@code null} * @param trimValues {@code true} if the values in the parsed arrays - * are to be be trimmed of whitespace (default is true). + * are to be trimmed of whitespace (default is true). */ public StringArrayPropertyEditor(String separator, String charsToDelete, boolean emptyArrayAsNull, boolean trimValues) { this.separator = separator; diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URIEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URIEditor.java index f5ac6d6a..7dc173a0 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URIEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URIEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -60,8 +60,7 @@ public class URIEditor extends PropertyEditorSupport { * standard URIs (not trying to resolve them into physical resources). */ public URIEditor() { - this.classLoader = null; - this.encode = true; + this(true); } /** @@ -74,7 +73,6 @@ public class URIEditor extends PropertyEditorSupport { this.encode = encode; } - /** * Create a new URIEditor, using the given ClassLoader to resolve * "classpath:" locations into physical resource URLs. @@ -82,8 +80,7 @@ public class URIEditor extends PropertyEditorSupport { * (may be {@code null} to indicate the default ClassLoader) */ public URIEditor(ClassLoader classLoader) { - this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); - this.encode = true; + this(classLoader, true); } /** diff --git a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URLEditor.java b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URLEditor.java index c5f9755d..85ef824d 100644 --- a/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URLEditor.java +++ b/spring-beans/src/main/java/org/springframework/beans/propertyeditors/URLEditor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -50,7 +50,7 @@ public class URLEditor extends PropertyEditorSupport { /** - * Create a new URLEditor, using the default ResourceEditor underneath. + * Create a new URLEditor, using a default ResourceEditor underneath. */ public URLEditor() { this.resourceEditor = new ResourceEditor(); diff --git a/spring-beans/src/main/java/org/springframework/beans/support/ResourceEditorRegistrar.java b/spring-beans/src/main/java/org/springframework/beans/support/ResourceEditorRegistrar.java index 06d2ee5d..8cffcc78 100644 --- a/spring-beans/src/main/java/org/springframework/beans/support/ResourceEditorRegistrar.java +++ b/spring-beans/src/main/java/org/springframework/beans/support/ResourceEditorRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -33,6 +33,7 @@ import org.springframework.beans.propertyeditors.ClassEditor; import org.springframework.beans.propertyeditors.FileEditor; import org.springframework.beans.propertyeditors.InputSourceEditor; import org.springframework.beans.propertyeditors.InputStreamEditor; +import org.springframework.beans.propertyeditors.PathEditor; import org.springframework.beans.propertyeditors.ReaderEditor; import org.springframework.beans.propertyeditors.URIEditor; import org.springframework.beans.propertyeditors.URLEditor; @@ -43,6 +44,7 @@ import org.springframework.core.io.ResourceEditor; import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.ResourceArrayPropertyEditor; import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.util.ClassUtils; /** * PropertyEditorRegistrar implementation that populates a given @@ -58,6 +60,19 @@ import org.springframework.core.io.support.ResourcePatternResolver; */ public class ResourceEditorRegistrar implements PropertyEditorRegistrar { + private static Class<?> pathClass; + + static { + try { + pathClass = ClassUtils.forName("java.nio.file.Path", ResourceEditorRegistrar.class.getClassLoader()); + } + catch (ClassNotFoundException ex) { + // Java 7 Path class not available + pathClass = null; + } + } + + private final PropertyResolver propertyResolver; private final ResourceLoader resourceLoader; @@ -103,6 +118,9 @@ public class ResourceEditorRegistrar implements PropertyEditorRegistrar { doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor)); doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor)); doRegisterEditor(registry, File.class, new FileEditor(baseEditor)); + if (pathClass != null) { + doRegisterEditor(registry, pathClass, new PathEditor(baseEditor)); + } doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor)); doRegisterEditor(registry, URL.class, new URLEditor(baseEditor)); diff --git a/spring-beans/src/main/resources/META-INF/spring.schemas b/spring-beans/src/main/resources/META-INF/spring.schemas index 3f5be7aa..ffd70f14 100644 --- a/spring-beans/src/main/resources/META-INF/spring.schemas +++ b/spring-beans/src/main/resources/META-INF/spring.schemas @@ -6,7 +6,8 @@ http\://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springfram http\://www.springframework.org/schema/beans/spring-beans-4.0.xsd=org/springframework/beans/factory/xml/spring-beans-4.0.xsd http\://www.springframework.org/schema/beans/spring-beans-4.1.xsd=org/springframework/beans/factory/xml/spring-beans-4.1.xsd http\://www.springframework.org/schema/beans/spring-beans-4.2.xsd=org/springframework/beans/factory/xml/spring-beans-4.2.xsd -http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-4.2.xsd +http\://www.springframework.org/schema/beans/spring-beans-4.3.xsd=org/springframework/beans/factory/xml/spring-beans-4.3.xsd +http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-4.3.xsd 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 @@ -15,7 +16,8 @@ http\://www.springframework.org/schema/tool/spring-tool-3.2.xsd=org/springframew http\://www.springframework.org/schema/tool/spring-tool-4.0.xsd=org/springframework/beans/factory/xml/spring-tool-4.0.xsd http\://www.springframework.org/schema/tool/spring-tool-4.1.xsd=org/springframework/beans/factory/xml/spring-tool-4.1.xsd http\://www.springframework.org/schema/tool/spring-tool-4.2.xsd=org/springframework/beans/factory/xml/spring-tool-4.2.xsd -http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-4.2.xsd +http\://www.springframework.org/schema/tool/spring-tool-4.3.xsd=org/springframework/beans/factory/xml/spring-tool-4.3.xsd +http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-4.3.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-3.0.xsd=org/springframework/beans/factory/xml/spring-util-3.0.xsd @@ -24,4 +26,5 @@ http\://www.springframework.org/schema/util/spring-util-3.2.xsd=org/springframew http\://www.springframework.org/schema/util/spring-util-4.0.xsd=org/springframework/beans/factory/xml/spring-util-4.0.xsd http\://www.springframework.org/schema/util/spring-util-4.1.xsd=org/springframework/beans/factory/xml/spring-util-4.1.xsd http\://www.springframework.org/schema/util/spring-util-4.2.xsd=org/springframework/beans/factory/xml/spring-util-4.2.xsd -http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-4.2.xsd +http\://www.springframework.org/schema/util/spring-util-4.3.xsd=org/springframework/beans/factory/xml/spring-util-4.3.xsd +http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-4.3.xsd diff --git a/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-4.3.xsd b/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-4.3.xsd new file mode 100644 index 00000000..8532e96f --- /dev/null +++ b/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-4.3.xsd @@ -0,0 +1,1201 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> + +<xsd:schema xmlns="http://www.springframework.org/schema/beans" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + targetNamespace="http://www.springframework.org/schema/beans"> + + <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/> + + <xsd:annotation> + <xsd:documentation><![CDATA[ + Spring XML Beans Schema, version 4.3 + Authors: Juergen Hoeller, Rob Harrop, Mark Fisher, Chris Beams + + This defines a simple and consistent way of creating a namespace + of JavaBeans objects, managed by a Spring BeanFactory, read by + XmlBeanDefinitionReader (with DefaultBeanDefinitionDocumentReader). + + This document type is used by most Spring functionality, including + web application contexts, which are based on bean factories. + + Each "bean" element in this document defines a JavaBean. + Typically the bean class is specified, along with JavaBean properties + and/or constructor arguments. + + A bean instance can be a "singleton" (shared instance) or a "prototype" + (independent instance). Further scopes can be provided by extended + bean factories, for example in a web environment. + + References among beans are supported, that is, setting a JavaBean property + or a constructor argument to refer to another bean in the same factory + (or an ancestor factory). + + As alternative to bean references, "inner bean definitions" can be used. + Such inner beans do not have an independent lifecycle; they are typically + anonymous nested objects that share the scope of their containing bean. + + There is also support for lists, sets, maps, and java.util.Properties + as bean property types or constructor argument types. + ]]></xsd:documentation> + </xsd:annotation> + + <!-- base types --> + <xsd:complexType name="identifiedType" abstract="true"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The unique identifier for a bean. The scope of the identifier + is the enclosing bean factory. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:attribute name="id" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The unique identifier for a bean. A bean id may not be used more than once + within the same <beans> element. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + + <!-- Top-level <beans> tag --> + <xsd:element name="beans"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Container for <bean> and other elements, typically the root element in the document. + Allows the definition of default values for all nested bean definitions. May itself + be nested for the purpose of defining a subset of beans with certain default values or + to be registered only when certain profile(s) are active. Any such nested <beans> element + must be declared as the last element in the document. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:sequence> + <xsd:element ref="description" minOccurs="0"/> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element ref="import"/> + <xsd:element ref="alias"/> + <xsd:element ref="bean"/> + <xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/> + </xsd:choice> + <xsd:element ref="beans" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + <xsd:attribute name="profile" use="optional" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The set of profiles for which this <beans> element should be parsed. Multiple profiles + can be separated by spaces, commas, or semi-colons. + + If one or more of the specified profiles are active at time of parsing, the <beans> + element will be parsed, and all of its <bean> elements registered, <import> + elements followed, etc. If none of the specified profiles are active at time of + parsing, then the entire element and its contents will be ignored. + + If a profile is prefixed with the NOT operator '!', e.g. + + <beans profile="p1,!p2"> + + indicates that the <beans> element should be parsed if profile "p1" is active or + if profile "p2" is not active. + + Profiles are activated in one of two ways: + Programmatic: + ConfigurableEnvironment#setActiveProfiles(String...) + ConfigurableEnvironment#setDefaultProfiles(String...) + + Properties (typically through -D system properties, environment variables, or + servlet context init params): + spring.profiles.active=p1,p2 + spring.profiles.default=p1,p2 + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="default-lazy-init" default="default" type="defaultable-boolean"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The default 'lazy-init' value; see the documentation for the + 'lazy-init' attribute of the 'bean' element. The default is "default", + indicating inheritance from outer 'beans' sections in case of nesting, + otherwise falling back to "false". + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="default-merge" default="default" type="defaultable-boolean"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The default 'merge' value; see the documentation for the 'merge' + attribute of the various collection elements. The default is "default", + indicating inheritance from outer 'beans' sections in case of nesting, + otherwise falling back to "false". + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="default-autowire" default="default"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The default 'autowire' value; see the documentation for the + 'autowire' attribute of the 'bean' element. The default is "default", + indicating inheritance from outer 'beans' sections in case of nesting, + otherwise falling back to "no" (i.e. no externally driven autowiring). + ]]></xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:NMTOKEN"> + <xsd:enumeration value="default"/> + <xsd:enumeration value="no"/> + <xsd:enumeration value="byName"/> + <xsd:enumeration value="byType"/> + <xsd:enumeration value="constructor"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="default-autowire-candidates" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + A default bean name pattern for identifying autowire candidates: + e.g. "*Service", "data*", "*Service*", "data*Service". + Also accepts a comma-separated list of patterns: e.g. "*Service,*Dao". + See the documentation for the 'autowire-candidate' attribute of the + 'bean' element for the semantic details of autowire candidate beans. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="default-init-method" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The default 'init-method' value; see the documentation for the + 'init-method' attribute of the 'bean' element. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="default-destroy-method" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The default 'destroy-method' value; see the documentation for the + 'destroy-method' attribute of the 'bean' element. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:anyAttribute namespace="##other" processContents="lax"/> + </xsd:complexType> + </xsd:element> + + <xsd:element name="description"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Contains informative text describing the purpose of the enclosing element. + Used primarily for user documentation of XML bean definition documents. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType mixed="true"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"/> + </xsd:complexType> + </xsd:element> + + <xsd:element name="import"> + <xsd:annotation> + <xsd:documentation source="java:org.springframework.core.io.Resource"><![CDATA[ + Specifies an XML bean definition resource to import. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:complexContent> + <xsd:restriction base="xsd:anyType"> + <xsd:attribute name="resource" type="xsd:string" use="required"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The relative resource location of the XML (bean definition) file to import, + for example "myImport.xml" or "includes/myImport.xml" or "../myImport.xml". + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:restriction> + </xsd:complexContent> + </xsd:complexType> + </xsd:element> + + <xsd:element name="alias"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Defines an alias for a bean (which can reside in a different definition + resource). + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:complexContent> + <xsd:restriction base="xsd:anyType"> + <xsd:attribute name="name" type="xsd:string" use="required"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The name of the bean to define an alias for. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="alias" type="xsd:string" use="required"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The alias name to define for the bean. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:restriction> + </xsd:complexContent> + </xsd:complexType> + </xsd:element> + + <xsd:group name="beanElements"> + <xsd:sequence> + <xsd:element ref="description" minOccurs="0"/> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element ref="meta"/> + <xsd:element ref="constructor-arg"/> + <xsd:element ref="property"/> + <xsd:element ref="qualifier"/> + <xsd:element ref="lookup-method"/> + <xsd:element ref="replaced-method"/> + <xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/> + </xsd:choice> + </xsd:sequence> + </xsd:group> + + <xsd:attributeGroup name="beanAttributes"> + <xsd:attribute name="name" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Can be used to create one or more aliases illegal in an (XML) id. + Multiple aliases can be separated by any number of spaces, commas, + or semi-colons (or indeed any mixture of the three). + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="class" type="xsd:string"> + <xsd:annotation> + <xsd:documentation source="java:java.lang.Class"><![CDATA[ + The fully qualified name of the bean's class, except if it serves only + as a parent definition for child bean definitions. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="parent" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The name of the parent bean definition. + + Will use the bean class of the parent if none is specified, but can + also override it. In the latter case, the child bean class must be + compatible with the parent, i.e. accept the parent's property values + and constructor argument values, if any. + + A child bean definition will inherit constructor argument values, + property values and method overrides from the parent, with the option + to add new values. If init method, destroy method, factory bean and/or + factory method are specified, they will override the corresponding + parent settings. + + The remaining settings will always be taken from the child definition: + depends on, autowire mode, scope, lazy init. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="scope" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The scope of this 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). + + By default, a bean will be a singleton, unless the bean has a parent + bean definition in which case it will inherit the parent's scope. + + Singletons are most commonly used, and are ideal for multi-threaded + service objects. Further scopes, such as "request" or "session", might + be supported by extended bean factories (e.g. in a web environment). + + Inner bean definitions inherit the scope of their containing bean + definition, unless explicitly specified: The inner bean will be a + singleton if the containing bean is a singleton, and a prototype if + the containing bean is a prototype, etc. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="abstract" type="xsd:boolean"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Is this bean "abstract", that is, not meant to be instantiated itself + but rather just serving as parent for concrete child bean definitions? + The default is "false". Specify "true" to tell the bean factory to not + try to instantiate that particular bean in any case. + + Note: This attribute will not be inherited by child bean definitions. + Hence, it needs to be specified per abstract bean definition. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="lazy-init" default="default" type="defaultable-boolean"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Indicates whether this bean is to be lazily initialized. If "false", + it will be instantiated on startup by bean factories that perform eager + initialization of singletons. The effective default is "false". + + Note: This attribute will not be inherited by child bean definitions. + Hence, it needs to be specified per concrete bean definition. It can be + shared through the 'default-lazy-init' attribute at the 'beans' level + and potentially inherited from outer 'beans' defaults in case of nested + 'beans' sections (e.g. with different profiles). + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="autowire" default="default"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Controls whether bean properties are "autowired". + This is an automagical process in which bean references don't need + to be coded explicitly in the XML bean definition file, but rather the + Spring container works out dependencies. The effective default is "no". + + There are 4 modes: + + 1. "no" + The traditional Spring default. No automagical wiring. Bean references + must be defined in the XML file via the <ref/> element (or "ref" + attribute). We recommend this in most cases as it makes documentation + more explicit. + + Note that this default mode also allows for annotation-driven autowiring, + if activated. "no" refers to externally driven autowiring only, not + affecting any autowiring demands that the bean class itself expresses. + + 2. "byName" + Autowiring by property name. If a bean of class Cat exposes a "dog" + property, Spring will try to set this to the value of the bean "dog" + in the current container. If there is no matching bean by name, nothing + special happens. + + 3. "byType" + Autowiring if there is exactly one bean of the property type in the + container. If there is more than one, a fatal error is raised, and + you cannot use byType autowiring for that bean. If there is none, + nothing special happens. + + 4. "constructor" + Analogous to "byType" for constructor arguments. If there is not exactly + one bean of the constructor argument type in the bean factory, a fatal + error is raised. + + Note that explicit dependencies, i.e. "property" and "constructor-arg" + elements, always override autowiring. + + Note: This attribute will not be inherited by child bean definitions. + Hence, it needs to be specified per concrete bean definition. It can be + shared through the 'default-autowire' attribute at the 'beans' level + and potentially inherited from outer 'beans' defaults in case of nested + 'beans' sections (e.g. with different profiles). + ]]></xsd:documentation> + </xsd:annotation> + <xsd:simpleType> + <xsd:restriction base="xsd:NMTOKEN"> + <xsd:enumeration value="default"/> + <xsd:enumeration value="no"/> + <xsd:enumeration value="byName"/> + <xsd:enumeration value="byType"/> + <xsd:enumeration value="constructor"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + <xsd:attribute name="depends-on" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The names of the beans that this bean depends on being initialized. + The bean factory will guarantee that these beans get initialized + before this bean. + + Note that dependencies are normally expressed through bean properties + or constructor arguments. This property should just be necessary for + other kinds of dependencies like statics (*ugh*) or database preparation + on startup. + + Note: This attribute will not be inherited by child bean definitions. + Hence, it needs to be specified per concrete bean definition. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="autowire-candidate" default="default" type="defaultable-boolean"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Indicates whether or not this bean should be considered when looking + for matching candidates to satisfy another bean's autowiring requirements. + Note that this does not affect explicit references by name, which will get + resolved even if the specified bean is not marked as an autowire candidate. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="primary" type="xsd:boolean"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Specifies that this bean should be given preference when multiple + candidates are qualified to autowire a single-valued dependency. + If exactly one 'primary' bean exists among the candidates, it + will be the autowired value. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="init-method" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The name of the custom initialization method to invoke after setting + bean properties. The method must have no arguments, but may throw any + exception. + + This is an alternative to implementing Spring's InitializingBean + interface or marking a method with the PostConstruct annotation. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="destroy-method" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The name of the custom destroy method to invoke on bean factory shutdown. + The method must have no arguments, but may throw any exception. + + This is an alternative to implementing Spring's DisposableBean + interface or the standard Java Closeable/AutoCloseable interface, + or marking a method with the PreDestroy annotation. + + Note: Only invoked on beans whose lifecycle is under the full + control of the factory - which is always the case for singletons, + but not guaranteed for any other scope. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="factory-method" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The name of a factory method to use to create this object. Use + constructor-arg elements to specify arguments to the factory method, + if it takes arguments. Autowiring does not apply to factory methods. + + If the "class" attribute is present, the factory method will be a static + method on the class specified by the "class" attribute on this bean + definition. Often this will be the same class as that of the constructed + object - for example, when the factory method is used as an alternative + to a constructor. However, it may be on a different class. In that case, + the created object will *not* be of the class specified in the "class" + attribute. This is analogous to FactoryBean behavior. + + If the "factory-bean" attribute is present, the "class" attribute is not + used, and the factory method will be an instance method on the object + returned from a getBean call with the specified bean name. The factory + bean may be defined as a singleton or a prototype. + + The factory method can have any number of arguments. Autowiring is not + supported. Use indexed constructor-arg elements in conjunction with the + factory-method attribute. + + Setter Injection can be used in conjunction with a factory method. + Method Injection cannot, as the factory method returns an instance, + which will be used when the container creates the bean. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="factory-bean" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Alternative to class attribute for factory-method usage. + If this is specified, no class attribute should be used. + This must be set to the name of a bean in the current or + ancestor factories that contains the relevant factory method. + This allows the factory itself to be configured using Dependency + Injection, and an instance (rather than static) method to be used. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:anyAttribute namespace="##other" processContents="lax"/> + </xsd:attributeGroup> + + <xsd:element name="meta" type="metaType"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Arbitrary metadata attached to a bean definition. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:element> + + <xsd:complexType name="metaType"> + <xsd:attribute name="key" type="xsd:string" use="required"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The key name of the metadata attribute being defined. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="value" type="xsd:string" use="required"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The value of the metadata attribute being defined (as a simple String). + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + + <xsd:element name="bean"> + <xsd:annotation> + <xsd:documentation source="java:org.springframework.beans.factory.config.BeanDefinition"><![CDATA[ + Defines a single (usually named) bean. + + A bean definition may contain nested tags for constructor arguments, + property values, lookup methods, and replaced methods. Mixing constructor + injection and setter injection on the same bean is explicitly supported. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:complexContent> + <xsd:extension base="identifiedType"> + <xsd:group ref="beanElements"/> + <xsd:attributeGroup ref="beanAttributes"/> + </xsd:extension> + </xsd:complexContent> + </xsd:complexType> + </xsd:element> + + <xsd:element name="constructor-arg"> + <xsd:annotation> + <xsd:documentation source="java:org.springframework.beans.factory.config.ConstructorArgumentValues"> + <![CDATA[ + Bean definitions can specify zero or more constructor arguments. + This is an alternative to "autowire constructor". + Arguments correspond to either a specific index of the constructor + argument list or are supposed to be matched generically by type. + + Note: A single generic argument value will just be used once, rather + than potentially matched multiple times (as of Spring 1.1). + + constructor-arg elements are also used in conjunction with the + factory-method element to construct beans using static or instance + factory methods. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:sequence> + <xsd:element ref="description" minOccurs="0"/> + <xsd:choice minOccurs="0" maxOccurs="1"> + <xsd:element ref="bean"/> + <xsd:element ref="ref"/> + <xsd:element ref="idref"/> + <xsd:element ref="value"/> + <xsd:element ref="null"/> + <xsd:element ref="array"/> + <xsd:element ref="list"/> + <xsd:element ref="set"/> + <xsd:element ref="map"/> + <xsd:element ref="props"/> + <xsd:any namespace="##other" processContents="strict"/> + </xsd:choice> + </xsd:sequence> + <xsd:attribute name="index" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The exact index 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. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="type" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The exact type of the constructor argument. Only needed to avoid + ambiguities, e.g. in case of 2 single argument constructors + that can both be converted from a String. + ]]></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[ + A short-cut alternative to a nested "<ref bean='...'/>" element. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="value" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + A short-cut alternative to a nested "<value>...<value/>" element. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + </xsd:element> + + <xsd:element name="property" type="propertyType"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Bean definitions can have zero or more properties. + Property elements correspond to JavaBean setter methods exposed + by the bean classes. Spring supports primitives, references to other + beans in the same or related factories, lists, maps and properties. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:element> + + <xsd:element name="qualifier"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Bean definitions can provide qualifiers to match against annotations + on a field or parameter for fine-grained autowire candidate resolution. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:sequence> + <xsd:element ref="attribute" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + <xsd:attribute name="type" type="xsd:string" default="org.springframework.beans.factory.annotation.Qualifier"/> + <xsd:attribute name="value" type="xsd:string"/> + </xsd:complexType> + </xsd:element> + + <xsd:element name="attribute" type="metaType"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + A qualifier element may contain attribute child elements as key-value + pairs. These will be available for matching against attributes of a + qualifier annotation on an autowired field or parameter if present. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:element> + + <xsd:element name="lookup-method"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + A lookup method causes the IoC container to override the given method + and return the bean with the name given in the bean attribute. This is + a form of Method Injection. It is particularly useful as an alternative + to implementing the BeanFactoryAware interface, in order to be able to + make getBean() calls for non-singleton instances at runtime. In this + case, Method Injection is a less invasive alternative. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:complexContent> + <xsd:restriction base="xsd:anyType"> + <xsd:attribute name="name" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The name of the lookup method. This method may have arguments which + will be passed on to the target constructor or factory method. Note + that for backwards compatibility reasons, in a scenario with overloaded + non-abstract methods of the given name, only the no-arg variant of a + method will be turned into a container-driven lookup method. + Consider using the @Lookup annotation for more specific demarcation. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="bean" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The name of the bean in the current or ancestor factories that + the lookup method should resolve to. Usually this bean will be a + prototype, in which case the lookup method will return a distinct + instance on every invocation. If not specified, the lookup method's + return type will be used for a type-based lookup. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:restriction> + </xsd:complexContent> + </xsd:complexType> + </xsd:element> + + <xsd:element name="replaced-method"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Similar to the lookup method mechanism, the replaced-method element + is used to control IoC container method overriding: Method Injection. + This mechanism allows the overriding of a method with arbitrary code. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:sequence> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element ref="arg-type"/> + </xsd:choice> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The name of the method whose implementation must be replaced by the + IoC container. If this method is not overloaded, there is no need + to use arg-type subelements. If this method is overloaded, arg-type + subelements must be used for all override definitions for the method. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="replacer" type="xsd:string"> + <xsd:annotation> + <xsd:documentation source="java:org.springframework.beans.factory.support.MethodReplacer"><![CDATA[ + Bean name of an implementation of the MethodReplacer interface in the + current or ancestor factories. This may be a singleton or prototype + bean. If it is a prototype, a new instance will be used for each + method replacement. Singleton usage is the norm. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + </xsd:element> + + <xsd:element name="arg-type"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Identifies an argument for a replaced method in the event of + method overloading. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType mixed="true"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"/> + <xsd:attribute name="match" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Specification of the type of an overloaded method argument as a String. + For convenience, this may be a substring of the FQN. E.g. all the + following would match "java.lang.String": + - java.lang.String + - String + - Str + + As the number of arguments will be checked also, this convenience + can often be used to save typing. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + </xsd:element> + + <xsd:element name="ref"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Defines a reference to another bean in this factory or an external + factory (parent or included factory). + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:complexContent> + <xsd:restriction base="xsd:anyType"> + <xsd:attribute name="bean" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The name of the referenced bean. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="parent" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The name of the referenced bean in a parent factory. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:restriction> + </xsd:complexContent> + </xsd:complexType> + </xsd:element> + + <xsd:element name="idref"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The id of another bean in this factory or an external factory + (parent or included factory). + While a regular 'value' element could instead be used for the + same effect, using idref indicates that the Spring container + should check that the value actually corresponds to a bean id. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:complexContent> + <xsd:restriction base="xsd:anyType"> + <xsd:attribute name="bean" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The name of the referenced bean. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:restriction> + </xsd:complexContent> + </xsd:complexType> + </xsd:element> + + <xsd:element name="value"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Contains a string representation of a property value. + The property may be a string, or may be converted to the required + type using the JavaBeans PropertyEditor machinery. This makes it + possible for application developers to write custom PropertyEditor + implementations that can convert strings to arbitrary target objects. + + Note that this is recommended for simple objects only. Configure + more complex objects by populating JavaBean properties with + references to other beans. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType mixed="true"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"/> + <xsd:attribute name="type" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The exact type that the value should be converted to. Only needed + if the type of the target property or constructor argument is + too generic: for example, in case of a collection element. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + </xsd:element> + + <xsd:element name="null"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Denotes a Java null value. Necessary because an empty "value" tag + will resolve to an empty String, which will not be resolved to a + null value unless a special PropertyEditor does so. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType mixed="true"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"/> + </xsd:complexType> + </xsd:element> + + <!-- Collection Elements --> + <xsd:group name="collectionElements"> + <xsd:sequence> + <xsd:element ref="description" minOccurs="0"/> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element ref="bean"/> + <xsd:element ref="ref"/> + <xsd:element ref="idref"/> + <xsd:element ref="value"/> + <xsd:element ref="null"/> + <xsd:element ref="array"/> + <xsd:element ref="list"/> + <xsd:element ref="set"/> + <xsd:element ref="map"/> + <xsd:element ref="props"/> + <xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/> + </xsd:choice> + </xsd:sequence> + </xsd:group> + + <xsd:element name="array"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + An array can contain multiple inner bean, ref, collection, or value elements. + This configuration element will always result in an array, even when being + defined e.g. as a value for a map with value type Object. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:complexContent> + <xsd:extension base="listOrSetType"> + <xsd:attribute name="merge" default="default" type="defaultable-boolean"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Enables/disables merging for collections when using parent/child beans. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:extension> + </xsd:complexContent> + </xsd:complexType> + </xsd:element> + + <xsd:element name="list"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + A list can contain multiple inner bean, ref, collection, or value elements. + A list can also map to an array type; the necessary conversion is performed + automatically. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:complexContent> + <xsd:extension base="listOrSetType"> + <xsd:attribute name="merge" default="default" type="defaultable-boolean"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Enables/disables merging for collections when using parent/child beans. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:extension> + </xsd:complexContent> + </xsd:complexType> + </xsd:element> + + <xsd:element name="set"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + A set can contain multiple inner bean, ref, collection, or value elements. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:complexContent> + <xsd:extension base="listOrSetType"> + <xsd:attribute name="merge" default="default" type="defaultable-boolean"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Enables/disables merging for collections when using parent/child beans. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:extension> + </xsd:complexContent> + </xsd:complexType> + </xsd:element> + + <xsd:element name="map"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + A mapping from a key to an object. Maps may be empty. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:complexContent> + <xsd:extension base="mapType"> + <xsd:attribute name="merge" default="default" type="defaultable-boolean"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Enables/disables merging for collections when using parent/child beans. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:extension> + </xsd:complexContent> + </xsd:complexType> + </xsd:element> + + <xsd:element name="entry" type="entryType"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + A map entry can be an inner bean, ref, value, or collection. + The key of the entry is given by the "key" attribute or child element. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:element> + + <xsd:element name="props"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Props elements differ from map elements in that values must be strings. + Props may be empty. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:complexContent> + <xsd:extension base="propsType"> + <xsd:attribute name="merge" default="default" type="defaultable-boolean"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Enables/disables merging for collections when using parent/child beans. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:extension> + </xsd:complexContent> + </xsd:complexType> + </xsd:element> + + <xsd:element name="key"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + A key element can contain an inner bean, ref, value, or collection. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:group ref="collectionElements"/> + </xsd:complexType> + </xsd:element> + + <xsd:element name="prop"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The string value of the property. Note that whitespace is trimmed + off to avoid unwanted whitespace caused by typical XML formatting. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:complexType mixed="true"> + <xsd:choice minOccurs="0" maxOccurs="unbounded"/> + <xsd:attribute name="key" type="xsd:string" use="required"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The key of the property entry. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + </xsd:element> + + <xsd:complexType name="propertyType"> + <xsd:sequence> + <xsd:element ref="description" minOccurs="0"/> + <xsd:choice minOccurs="0" maxOccurs="1"> + <xsd:element ref="meta"/> + <xsd:element ref="bean"/> + <xsd:element ref="ref"/> + <xsd:element ref="idref"/> + <xsd:element ref="value"/> + <xsd:element ref="null"/> + <xsd:element ref="array"/> + <xsd:element ref="list"/> + <xsd:element ref="set"/> + <xsd:element ref="map"/> + <xsd:element ref="props"/> + <xsd:any namespace="##other" processContents="strict"/> + </xsd:choice> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The name of the property, following JavaBean naming conventions. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="ref" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + A short-cut alternative to a nested "<ref bean='...'/>". + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="value" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + A short-cut alternative to a nested "<value>...</value>" element. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + + <!-- Collection Types --> + + <!-- base type for collections that have (possibly) typed nested values --> + <xsd:complexType name="collectionType"> + <xsd:attribute name="value-type" type="xsd:string"> + <xsd:annotation> + <xsd:documentation source="java:java.lang.Class"><![CDATA[ + The default Java type for nested values. Must be a fully qualified + class name. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + + <!-- 'list' and 'set' collection type --> + <xsd:complexType name="listOrSetType"> + <xsd:complexContent> + <xsd:extension base="collectionType"> + <xsd:group ref="collectionElements"/> + </xsd:extension> + </xsd:complexContent> + </xsd:complexType> + + <!-- 'map' element type --> + <xsd:complexType name="mapType"> + <xsd:complexContent> + <xsd:extension base="collectionType"> + <xsd:sequence> + <xsd:element ref="description" minOccurs="0"/> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element ref="entry"/> + </xsd:choice> + </xsd:sequence> + <xsd:attribute name="key-type" type="xsd:string"> + <xsd:annotation> + <xsd:documentation source="java:java.lang.Class"><![CDATA[ + The default Java type for nested entry keys. Must be a fully qualified + class name. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:extension> + </xsd:complexContent> + </xsd:complexType> + + <!-- 'entry' element type --> + <xsd:complexType name="entryType"> + <xsd:sequence> + <xsd:element ref="key" minOccurs="0"/> + <xsd:group ref="collectionElements"/> + </xsd:sequence> + <xsd:attribute name="key" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Each map element must specify its key as attribute or as child element. + A key attribute is always a String value. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="key-ref" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + A short-cut alternative to a to a "key" element with a nested + "<ref bean='...'/>". + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="value" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + A short-cut alternative to a nested "<value>...</value>" + element. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="value-ref" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + A short-cut alternative to a nested "<ref bean='...'/>". + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="value-type" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + A short-cut alternative to a 'type' attribute on a nested + "<value type='...' >...</value>" element. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + + <!-- 'props' collection type --> + <xsd:complexType name="propsType"> + <xsd:complexContent> + <xsd:extension base="collectionType"> + <xsd:sequence> + <xsd:choice minOccurs="0" maxOccurs="unbounded"> + <xsd:element ref="prop"/> + </xsd:choice> + </xsd:sequence> + </xsd:extension> + </xsd:complexContent> + </xsd:complexType> + + <!-- simple internal types --> + <xsd:simpleType name="defaultable-boolean"> + <xsd:restriction base="xsd:NMTOKEN"> + <xsd:enumeration value="default"/> + <xsd:enumeration value="true"/> + <xsd:enumeration value="false"/> + </xsd:restriction> + </xsd:simpleType> + +</xsd:schema> diff --git a/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-tool-4.3.xsd b/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-tool-4.3.xsd new file mode 100644 index 00000000..9d84906a --- /dev/null +++ b/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-tool-4.3.xsd @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> + +<xsd:schema xmlns="http://www.springframework.org/schema/tool" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + targetNamespace="http://www.springframework.org/schema/tool" + elementFormDefault="qualified"> + + <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/> + + <xsd:annotation> + <xsd:documentation><![CDATA[ + Defines the tool support annotations for Spring's configuration namespaces. + Used in other namespace XSD files; not intended for direct use in config files. + ]]></xsd:documentation> + </xsd:annotation> + + <xsd:element name="annotation"> + <xsd:complexType> + <xsd:sequence minOccurs="0"> + <xsd:element name="expected-type" type="typedParameterType" minOccurs="0" maxOccurs="1"/> + <xsd:element name="assignable-to" type="assignableToType" minOccurs="0" maxOccurs="1"/> + <xsd:element name="exports" type="exportsType" minOccurs="0" maxOccurs="unbounded"/> + <xsd:element name="registers-scope" type="registersScopeType" minOccurs="0" maxOccurs="unbounded"/> + <xsd:element name="expected-method" type="expectedMethodType" minOccurs="0" maxOccurs="unbounded"/> + </xsd:sequence> + <xsd:attribute name="kind" default="direct"> + <xsd:simpleType> + <xsd:restriction base="xsd:string"> + <xsd:enumeration value="ref"/> + <xsd:enumeration value="direct"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + </xsd:complexType> + </xsd:element> + + <xsd:complexType name="typedParameterType"> + <xsd:attribute name="type" type="xsd:string" use="required"/> + </xsd:complexType> + + <xsd:complexType name="assignableToType"> + <xsd:attribute name="type" type="xsd:string"/> + <xsd:attribute name="restriction" default="both"> + <xsd:simpleType> + <xsd:restriction base="xsd:NMTOKEN"> + <xsd:enumeration value="both"/> + <xsd:enumeration value="interface-only"/> + <xsd:enumeration value="class-only"/> + </xsd:restriction> + </xsd:simpleType> + </xsd:attribute> + </xsd:complexType> + + <xsd:complexType name="expectedMethodType"> + <xsd:attribute name="type" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Defines an XPath query that can be executed against the node annotated with this + type to determine the class for which the this method is valid + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="type-ref" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Defines an XPath query that can be executed against the node annotated with this + type to determine a referenced bean (by id or alias) for which the given method is valid + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="expression" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Defines an AspectJ method execution pointcut expressions that matches valid methods + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + + <xsd:complexType name="exportsType"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Indicates that an annotated type exports an application visible component. + ]]></xsd:documentation> + </xsd:annotation> + <xsd:attribute name="type" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The type of the exported component. May be null if the type is not known until runtime. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="identifier" type="xsd:string" default="@id"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Defines an XPath query that can be executed against the node annotated with this + type to determine the identifier of any exported component. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + + <xsd:complexType name="registersScopeType"> + <xsd:attribute name="name" type="xsd:string" use="required"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Defines the name of a custom bean scope that the annotated type registers, e.g. "conversation". + Such a scope will be available in addition to the standard "singleton" and "prototype" scopes + (plus "request", "session" and "globalSession" in a web application environment). + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + </xsd:complexType> + +</xsd:schema> diff --git a/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-util-4.3.xsd b/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-util-4.3.xsd new file mode 100644 index 00000000..a14a2c51 --- /dev/null +++ b/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-util-4.3.xsd @@ -0,0 +1,221 @@ +<?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" schemaLocation="http://www.springframework.org/schema/beans/spring-beans-4.3.xsd"/> + <xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="http://www.springframework.org/schema/tool/spring-tool-4.3.xsd"/> + + <xsd:element name="constant"> + <xsd:annotation> + <xsd:documentation> + Reference a public, static field on a type and expose its value as + a bean. For example <code><util:constant static-field="java.lang.Integer.MAX_VALUE"/></code>. + </xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:attribute name="id" type="xsd:string"/> + <xsd:attribute name="static-field" type="xsd:string" use="required"/> + </xsd:complexType> + </xsd:element> + + <xsd:element name="property-path"> + <xsd:annotation> + <xsd:documentation> + Reference a property on a bean (or as a nested value) and expose its values as + a bean. For example <util:property-path path="order.customer.name"/>. + </xsd:documentation> + </xsd:annotation> + <xsd:complexType> + <xsd:attribute name="id" type="xsd:string"/> + <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:string"/> + <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:string"/> + <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:string"/> + <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:string"/> + <xsd:attribute name="location" type="xsd:string"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + The location of the properties file, as a Spring resource location: a URL, + a "classpath:" pseudo URL, or a relative file path. Multiple locations may be + specified, separated by commas. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="ignore-resource-not-found" type="xsd:boolean" default="false"> + <xsd:annotation> + <xsd:documentation><![CDATA[ + Specifies if failure to find the property resource location should be ignored. + Default is "false", meaning that if there is no file in the location specified + an exception will be raised at runtime. + ]]></xsd:documentation> + </xsd:annotation> + </xsd:attribute> + <xsd:attribute name="local-override" type="xsd:boolean" default="false"> + <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> diff --git a/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java b/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java index 14118896..d801c377 100644 --- a/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/AbstractPropertyAccessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -1763,6 +1763,14 @@ public abstract class AbstractPropertyAccessorTests { assertEquals("val1", Spr10115Bean.prop1); } + @Test + public void cornerSpr13837() { + Spr13837Bean target = new Spr13837Bean(); + AbstractPropertyAccessor accessor = createAccessor(target); + accessor.setPropertyValue("something", 42); + assertEquals(Integer.valueOf(42), target.something); + } + private Person createPerson(String name, String city, String country) { return new Person(name, new Address(city, country)); @@ -2201,6 +2209,30 @@ public abstract class AbstractPropertyAccessorTests { } } + interface Spr13837 { + + Integer getSomething(); + + <T extends Spr13837> T setSomething(Integer something); + + } + + static class Spr13837Bean implements Spr13837 { + + protected Integer something; + + @Override + public Integer getSomething() { + return this.something; + } + + @Override + public Spr13837Bean setSomething(final Integer something) { + this.something = something; + return this; + } + } + @SuppressWarnings("serial") public static class ReadOnlyMap<K, V> extends HashMap<K, V> { diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java index a99bc92d..89b2922e 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java @@ -62,6 +62,14 @@ public class BeanWrapperTests extends AbstractPropertyAccessorTests { } @Test + public void aliasedSetterThroughDefaultMethod() { + GetterBean target = new GetterBean(); + BeanWrapper accessor = createAccessor(target); + accessor.setPropertyValue("aliasedName", "tom"); + assertTrue("Set name to tom", target.getAliasedName().equals("tom")); + } + + @Test public void setValidAndInvalidPropertyValuesShouldContainExceptionDetails() { TestBean target = new TestBean(); String newName = "tony"; @@ -209,7 +217,24 @@ public class BeanWrapperTests extends AbstractPropertyAccessorTests { @SuppressWarnings("unused") - private static class GetterBean { + private interface AliasedProperty { + + default void setAliasedName(String name) { + setName(name); + } + + default String getAliasedName() { + return getName(); + } + + void setName(String name); + + String getName(); + } + + + @SuppressWarnings("unused") + private static class GetterBean implements AliasedProperty { private String name; diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index cbf2f37b..00e41497 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -31,6 +31,7 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.concurrent.Callable; @@ -2711,7 +2712,7 @@ public class DefaultListableBeanFactoryTests { } @Test - public void resolveEmbeddedValue() throws Exception { + public void resolveEmbeddedValue() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); StringValueResolver r1 = mock(StringValueResolver.class); StringValueResolver r2 = mock(StringValueResolver.class); @@ -2730,6 +2731,25 @@ public class DefaultListableBeanFactoryTests { verify(r3, never()).resolveStringValue(isNull(String.class)); } + @Test + public void populatedJavaUtilOptionalBean() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + RootBeanDefinition bd = new RootBeanDefinition(Optional.class); + bd.setFactoryMethodName("of"); + bd.getConstructorArgumentValues().addGenericArgumentValue("CONTENT"); + bf.registerBeanDefinition("optionalBean", bd); + assertEquals(Optional.of("CONTENT"), bf.getBean(Optional.class)); + } + + @Test + public void emptyJavaUtilOptionalBean() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + RootBeanDefinition bd = new RootBeanDefinition(Optional.class); + bd.setFactoryMethodName("empty"); + bf.registerBeanDefinition("optionalBean", bd); + assertSame(Optional.empty(), bf.getBean(Optional.class)); + } + /** * Test that by-type bean lookup caching is working effectively by searching for a * bean of type B 10K times within a container having 1K additional beans of type A. diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/Spr5475Tests.java b/spring-beans/src/test/java/org/springframework/beans/factory/Spr5475Tests.java index f61119bc..32fa6847 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/Spr5475Tests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/Spr5475Tests.java @@ -1,3 +1,19 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.beans.factory; import org.junit.Test; @@ -49,8 +65,7 @@ public class Spr5475Tests { cav.addIndexedArgumentValue(1, "bogusArg2".getBytes()); def.setConstructorArgumentValues(cav); - assertExceptionMessageForMisconfiguredFactoryMethod( - def, + assertExceptionMessageForMisconfiguredFactoryMethod(def, "Error creating bean with name 'foo': No matching factory method found: factory method 'noArgFactory(CharSequence,byte[])'. " + "Check that a method with the specified name and arguments exists and that it is static."); } @@ -62,7 +77,8 @@ public class Spr5475Tests { try { factory.preInstantiateSingletons(); fail("should have failed with BeanCreationException due to incorrectly invoked factory method"); - } catch (BeanCreationException ex) { + } + catch (BeanCreationException ex) { assertThat(ex.getMessage(), equalTo(expectedMessage)); } } @@ -72,15 +88,17 @@ public class Spr5475Tests { // calling a factory method that accepts arguments without any arguments emits an exception unlike cases // where a no-arg factory method is called with arguments. Adding this test just to document the difference assertExceptionMessageForMisconfiguredFactoryMethod( - rootBeanDefinition(Foo.class) - .setFactoryMethod("singleArgFactory").getBeanDefinition(), + rootBeanDefinition(Foo.class). + setFactoryMethod("singleArgFactory").getBeanDefinition(), "Error creating bean with name 'foo': " + - "Unsatisfied dependency expressed through constructor argument with index 0 of type [java.lang.String]: " + - "Ambiguous factory method argument types - did you specify the correct bean references as factory method arguments?"); + "Unsatisfied dependency expressed through method 'singleArgFactory' parameter 0: " + + "Ambiguous argument values for parameter of type [java.lang.String] - " + + "did you specify the correct bean references as arguments?"); } static class Foo { + static Foo noArgFactory() { return new Foo(); } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java index 3983807c..2a4afd0a 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -24,8 +24,15 @@ import java.lang.annotation.Target; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Properties; +import java.util.Set; import java.util.concurrent.Callable; import org.junit.Ignore; @@ -35,7 +42,10 @@ import org.mockito.Mockito; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.beans.factory.ObjectFactory; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.UnsatisfiedDependencyException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; @@ -74,6 +84,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { bf.registerBeanDefinition("testBean", new GenericBeanDefinition()); try { bf.getBean("testBean"); + fail("Should have thrown BeanCreationException"); } catch (BeanCreationException ex) { assertTrue(ex.getRootCause() instanceof IllegalStateException); @@ -626,6 +637,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { } catch (UnsatisfiedDependencyException ex) { // expected + assertSame(ConstructorWithoutFallbackBean.class, ex.getInjectionPoint().getMethodParameter().getDeclaringClass()); } } @@ -741,10 +753,10 @@ public class AutowiredAnnotationBeanPostProcessorTests { RootBeanDefinition bd = new RootBeanDefinition(MapConstructorInjectionBean.class); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); - TestBean tb1 = new TestBean(); - TestBean tb2 = new TestBean(); + TestBean tb1 = new TestBean("tb1"); + TestBean tb2 = new TestBean("tb2"); bf.registerSingleton("testBean1", tb1); - bf.registerSingleton("testBean2", tb1); + bf.registerSingleton("testBean2", tb2); MapConstructorInjectionBean bean = (MapConstructorInjectionBean) bf.getBean("annotatedBean"); assertEquals(2, bean.getTestBeanMap().size()); @@ -770,10 +782,10 @@ public class AutowiredAnnotationBeanPostProcessorTests { RootBeanDefinition bd = new RootBeanDefinition(MapFieldInjectionBean.class); bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); bf.registerBeanDefinition("annotatedBean", bd); - TestBean tb1 = new TestBean(); - TestBean tb2 = new TestBean(); + TestBean tb1 = new TestBean("tb1"); + TestBean tb2 = new TestBean("tb2"); bf.registerSingleton("testBean1", tb1); - bf.registerSingleton("testBean2", tb1); + bf.registerSingleton("testBean2", tb2); MapFieldInjectionBean bean = (MapFieldInjectionBean) bf.getBean("annotatedBean"); assertEquals(2, bean.getTestBeanMap().size()); @@ -829,8 +841,9 @@ public class AutowiredAnnotationBeanPostProcessorTests { bf.getBean("annotatedBean"); fail("should have failed, more than one bean of type"); } - catch (BeanCreationException e) { + catch (UnsatisfiedDependencyException ex) { // expected + assertSame(MapMethodInjectionBean.class, ex.getInjectionPoint().getMethodParameter().getDeclaringClass()); } bf.destroySingletons(); } @@ -871,6 +884,106 @@ public class AutowiredAnnotationBeanPostProcessorTests { } @Test + public void testConstructorInjectionWithTypedMapAsBean() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + RootBeanDefinition bd = new RootBeanDefinition(MapConstructorInjectionBean.class); + bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bf.registerBeanDefinition("annotatedBean", bd); + MyTestBeanMap tbm = new MyTestBeanMap(); + tbm.put("testBean1", new TestBean("tb1")); + tbm.put("testBean2", new TestBean("tb2")); + bf.registerSingleton("testBeans", tbm); + bf.registerSingleton("otherMap", new Properties()); + + MapConstructorInjectionBean bean = (MapConstructorInjectionBean) bf.getBean("annotatedBean"); + assertSame(tbm, bean.getTestBeanMap()); + bean = (MapConstructorInjectionBean) bf.getBean("annotatedBean"); + assertSame(tbm, bean.getTestBeanMap()); + } + + @Test + public void testConstructorInjectionWithPlainMapAsBean() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + RootBeanDefinition bd = new RootBeanDefinition(MapConstructorInjectionBean.class); + bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bf.registerBeanDefinition("annotatedBean", bd); + RootBeanDefinition tbm = new RootBeanDefinition(CollectionFactoryMethods.class); + tbm.setUniqueFactoryMethodName("testBeanMap"); + bf.registerBeanDefinition("myTestBeanMap", tbm); + bf.registerSingleton("otherMap", new HashMap<Object, Object>()); + + MapConstructorInjectionBean bean = (MapConstructorInjectionBean) bf.getBean("annotatedBean"); + assertSame(bf.getBean("myTestBeanMap"), bean.getTestBeanMap()); + bean = (MapConstructorInjectionBean) bf.getBean("annotatedBean"); + assertSame(bf.getBean("myTestBeanMap"), bean.getTestBeanMap()); + } + + @Test + public void testConstructorInjectionWithTypedSetAsBean() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + RootBeanDefinition bd = new RootBeanDefinition(SetConstructorInjectionBean.class); + bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bf.registerBeanDefinition("annotatedBean", bd); + MyTestBeanSet tbs = new MyTestBeanSet(); + tbs.add(new TestBean("tb1")); + tbs.add(new TestBean("tb2")); + bf.registerSingleton("testBeans", tbs); + bf.registerSingleton("otherSet", new HashSet<Object>()); + + SetConstructorInjectionBean bean = (SetConstructorInjectionBean) bf.getBean("annotatedBean"); + assertSame(tbs, bean.getTestBeanSet()); + bean = (SetConstructorInjectionBean) bf.getBean("annotatedBean"); + assertSame(tbs, bean.getTestBeanSet()); + } + + @Test + public void testConstructorInjectionWithPlainSetAsBean() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + RootBeanDefinition bd = new RootBeanDefinition(SetConstructorInjectionBean.class); + bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bf.registerBeanDefinition("annotatedBean", bd); + RootBeanDefinition tbs = new RootBeanDefinition(CollectionFactoryMethods.class); + tbs.setUniqueFactoryMethodName("testBeanSet"); + bf.registerBeanDefinition("myTestBeanSet", tbs); + bf.registerSingleton("otherSet", new HashSet<Object>()); + + SetConstructorInjectionBean bean = (SetConstructorInjectionBean) bf.getBean("annotatedBean"); + assertSame(bf.getBean("myTestBeanSet"), bean.getTestBeanSet()); + bean = (SetConstructorInjectionBean) bf.getBean("annotatedBean"); + assertSame(bf.getBean("myTestBeanSet"), bean.getTestBeanSet()); + } + + @Test + public void testSelfReference() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + RootBeanDefinition bd = new RootBeanDefinition(SelfInjectionBean.class); + bf.registerBeanDefinition("annotatedBean", bd); + + SelfInjectionBean bean = (SelfInjectionBean) bf.getBean("annotatedBean"); + assertSame(bean, bean.selfReference); + } + + @Test public void testObjectFactoryInjection() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); @@ -938,6 +1051,110 @@ public class AutowiredAnnotationBeanPostProcessorTests { } @Test + public void testSmartObjectFactoryInjectionWithPrototype() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SmartObjectFactoryInjectionBean.class)); + RootBeanDefinition tbd = new RootBeanDefinition(TestBean.class); + tbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE); + bf.registerBeanDefinition("testBean", tbd); + + SmartObjectFactoryInjectionBean bean = (SmartObjectFactoryInjectionBean) bf.getBean("annotatedBean"); + assertEquals(bf.getBean("testBean"), bean.getTestBean()); + assertEquals(bf.getBean("testBean", "myName"), bean.getTestBean("myName")); + assertEquals(bf.getBean("testBean"), bean.getOptionalTestBean()); + assertEquals(bf.getBean("testBean"), bean.getUniqueTestBean()); + bf.destroySingletons(); + } + + @Test + public void testSmartObjectFactoryInjectionWithSingletonTarget() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SmartObjectFactoryInjectionBean.class)); + bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class)); + + SmartObjectFactoryInjectionBean bean = (SmartObjectFactoryInjectionBean) bf.getBean("annotatedBean"); + assertSame(bf.getBean("testBean"), bean.getTestBean()); + assertSame(bf.getBean("testBean"), bean.getOptionalTestBean()); + assertSame(bf.getBean("testBean"), bean.getUniqueTestBean()); + bf.destroySingletons(); + } + + @Test + public void testSmartObjectFactoryInjectionWithTargetNotAvailable() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SmartObjectFactoryInjectionBean.class)); + + SmartObjectFactoryInjectionBean bean = (SmartObjectFactoryInjectionBean) bf.getBean("annotatedBean"); + try { + bean.getTestBean(); + fail("Should have thrown NoSuchBeanDefinitionException"); + } + catch (NoSuchBeanDefinitionException ex) { + // expected + } + assertNull(bean.getOptionalTestBean()); + assertNull(bean.getUniqueTestBean()); + bf.destroySingletons(); + } + + @Test + public void testSmartObjectFactoryInjectionWithTargetNotUnique() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SmartObjectFactoryInjectionBean.class)); + bf.registerBeanDefinition("testBean1", new RootBeanDefinition(TestBean.class)); + bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class)); + + SmartObjectFactoryInjectionBean bean = (SmartObjectFactoryInjectionBean) bf.getBean("annotatedBean"); + try { + bean.getTestBean(); + fail("Should have thrown NoUniqueBeanDefinitionException"); + } + catch (NoUniqueBeanDefinitionException ex) { + // expected + } + try { + bean.getOptionalTestBean(); + fail("Should have thrown NoUniqueBeanDefinitionException"); + } + catch (NoUniqueBeanDefinitionException ex) { + // expected + } + assertNull(bean.getUniqueTestBean()); + bf.destroySingletons(); + } + + @Test + public void testSmartObjectFactoryInjectionWithTargetPrimary() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SmartObjectFactoryInjectionBean.class)); + RootBeanDefinition tb1 = new RootBeanDefinition(TestBean.class); + tb1.setPrimary(true); + bf.registerBeanDefinition("testBean1", tb1); + bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class)); + + SmartObjectFactoryInjectionBean bean = (SmartObjectFactoryInjectionBean) bf.getBean("annotatedBean"); + assertSame(bf.getBean("testBean1"), bean.getTestBean()); + assertSame(bf.getBean("testBean1"), bean.getOptionalTestBean()); + assertSame(bf.getBean("testBean1"), bean.getUniqueTestBean()); + bf.destroySingletons(); + } + + @Test public void testCustomAnnotationRequiredFieldResourceInjection() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); @@ -951,7 +1168,8 @@ public class AutowiredAnnotationBeanPostProcessorTests { TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); - CustomAnnotationRequiredFieldResourceInjectionBean bean = (CustomAnnotationRequiredFieldResourceInjectionBean) bf.getBean("customBean"); + CustomAnnotationRequiredFieldResourceInjectionBean bean = + (CustomAnnotationRequiredFieldResourceInjectionBean) bf.getBean("customBean"); assertSame(tb, bean.getTestBean()); bf.destroySingletons(); } @@ -970,10 +1188,12 @@ public class AutowiredAnnotationBeanPostProcessorTests { try { bf.getBean("customBean"); - fail("expected BeanCreationException; no dependency available for required field"); + fail("Should have thrown UnsatisfiedDependencyException"); } - catch (BeanCreationException e) { + catch (UnsatisfiedDependencyException ex) { // expected + assertSame(CustomAnnotationRequiredFieldResourceInjectionBean.class, + ex.getInjectionPoint().getField().getDeclaringClass()); } bf.destroySingletons(); } @@ -996,10 +1216,13 @@ public class AutowiredAnnotationBeanPostProcessorTests { try { bf.getBean("customBean"); - fail("expected BeanCreationException; multiple beans of dependency type available"); + fail("Should have thrown UnsatisfiedDependencyException"); } - catch (BeanCreationException e) { + catch (UnsatisfiedDependencyException ex) { // expected + ex.printStackTrace(); + assertSame(CustomAnnotationRequiredFieldResourceInjectionBean.class, + ex.getInjectionPoint().getField().getDeclaringClass()); } bf.destroySingletons(); } @@ -1018,7 +1241,8 @@ public class AutowiredAnnotationBeanPostProcessorTests { TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); - CustomAnnotationRequiredMethodResourceInjectionBean bean = (CustomAnnotationRequiredMethodResourceInjectionBean) bf.getBean("customBean"); + CustomAnnotationRequiredMethodResourceInjectionBean bean = + (CustomAnnotationRequiredMethodResourceInjectionBean) bf.getBean("customBean"); assertSame(tb, bean.getTestBean()); bf.destroySingletons(); } @@ -1037,10 +1261,12 @@ public class AutowiredAnnotationBeanPostProcessorTests { try { bf.getBean("customBean"); - fail("expected BeanCreationException; no dependency available for required method"); + fail("Should have thrown UnsatisfiedDependencyException"); } - catch (BeanCreationException e) { + catch (UnsatisfiedDependencyException ex) { // expected + assertSame(CustomAnnotationRequiredMethodResourceInjectionBean.class, + ex.getInjectionPoint().getMethodParameter().getDeclaringClass()); } bf.destroySingletons(); } @@ -1063,10 +1289,12 @@ public class AutowiredAnnotationBeanPostProcessorTests { try { bf.getBean("customBean"); - fail("expected BeanCreationException; multiple beans of dependency type available"); + fail("Should have thrown UnsatisfiedDependencyException"); } - catch (BeanCreationException e) { + catch (UnsatisfiedDependencyException ex) { // expected + assertSame(CustomAnnotationRequiredMethodResourceInjectionBean.class, + ex.getInjectionPoint().getMethodParameter().getDeclaringClass()); } bf.destroySingletons(); } @@ -1085,7 +1313,8 @@ public class AutowiredAnnotationBeanPostProcessorTests { TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); - CustomAnnotationOptionalFieldResourceInjectionBean bean = (CustomAnnotationOptionalFieldResourceInjectionBean) bf.getBean("customBean"); + CustomAnnotationOptionalFieldResourceInjectionBean bean = + (CustomAnnotationOptionalFieldResourceInjectionBean) bf.getBean("customBean"); assertSame(tb, bean.getTestBean3()); assertNull(bean.getTestBean()); assertNull(bean.getTestBean2()); @@ -1104,7 +1333,8 @@ public class AutowiredAnnotationBeanPostProcessorTests { bf.registerBeanDefinition("customBean", new RootBeanDefinition( CustomAnnotationOptionalFieldResourceInjectionBean.class)); - CustomAnnotationOptionalFieldResourceInjectionBean bean = (CustomAnnotationOptionalFieldResourceInjectionBean) bf.getBean("customBean"); + CustomAnnotationOptionalFieldResourceInjectionBean bean = + (CustomAnnotationOptionalFieldResourceInjectionBean) bf.getBean("customBean"); assertNull(bean.getTestBean3()); assertNull(bean.getTestBean()); assertNull(bean.getTestBean2()); @@ -1129,10 +1359,12 @@ public class AutowiredAnnotationBeanPostProcessorTests { try { bf.getBean("customBean"); - fail("expected BeanCreationException; multiple beans of dependency type available"); + fail("Should have thrown UnsatisfiedDependencyException"); } - catch (BeanCreationException e) { + catch (UnsatisfiedDependencyException ex) { // expected + assertSame(CustomAnnotationOptionalFieldResourceInjectionBean.class, + ex.getInjectionPoint().getField().getDeclaringClass()); } bf.destroySingletons(); } @@ -1151,7 +1383,8 @@ public class AutowiredAnnotationBeanPostProcessorTests { TestBean tb = new TestBean(); bf.registerSingleton("testBean", tb); - CustomAnnotationOptionalMethodResourceInjectionBean bean = (CustomAnnotationOptionalMethodResourceInjectionBean) bf.getBean("customBean"); + CustomAnnotationOptionalMethodResourceInjectionBean bean = + (CustomAnnotationOptionalMethodResourceInjectionBean) bf.getBean("customBean"); assertSame(tb, bean.getTestBean3()); assertNull(bean.getTestBean()); assertNull(bean.getTestBean2()); @@ -1170,7 +1403,8 @@ public class AutowiredAnnotationBeanPostProcessorTests { bf.registerBeanDefinition("customBean", new RootBeanDefinition( CustomAnnotationOptionalMethodResourceInjectionBean.class)); - CustomAnnotationOptionalMethodResourceInjectionBean bean = (CustomAnnotationOptionalMethodResourceInjectionBean) bf.getBean("customBean"); + CustomAnnotationOptionalMethodResourceInjectionBean bean = + (CustomAnnotationOptionalMethodResourceInjectionBean) bf.getBean("customBean"); assertNull(bean.getTestBean3()); assertNull(bean.getTestBean()); assertNull(bean.getTestBean2()); @@ -1195,10 +1429,12 @@ public class AutowiredAnnotationBeanPostProcessorTests { try { bf.getBean("customBean"); - fail("expected BeanCreationException; multiple beans of dependency type available"); + fail("Should have thrown UnsatisfiedDependencyException"); } - catch (BeanCreationException e) { + catch (UnsatisfiedDependencyException ex) { // expected + assertSame(CustomAnnotationOptionalMethodResourceInjectionBean.class, + ex.getInjectionPoint().getMethodParameter().getDeclaringClass()); } bf.destroySingletons(); } @@ -1351,8 +1587,8 @@ public class AutowiredAnnotationBeanPostProcessorTests { bf.registerBeanDefinition("integerRepo", rbd); RepositoryFieldInjectionBeanWithQualifiers bean = (RepositoryFieldInjectionBeanWithQualifiers) bf.getBean("annotatedBean"); - Repository sr = bf.getBean("stringRepo", Repository.class); - Repository ir = bf.getBean("integerRepo", Repository.class); + Repository<?> sr = bf.getBean("stringRepo", Repository.class); + Repository<?> ir = bf.getBean("integerRepo", Repository.class); assertSame(sr, bean.stringRepository); assertSame(ir, bean.integerRepository); assertSame(1, bean.stringRepositoryArray.length); @@ -1383,7 +1619,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { bf.registerSingleton("repo", new StringRepository()); RepositoryFieldInjectionBeanWithSimpleMatch bean = (RepositoryFieldInjectionBeanWithSimpleMatch) bf.getBean("annotatedBean"); - Repository repo = bf.getBean("repo", Repository.class); + Repository<?> repo = bf.getBean("repo", Repository.class); assertSame(repo, bean.repository); assertSame(repo, bean.stringRepository); assertSame(1, bean.repositoryArray.length); @@ -1415,7 +1651,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { bf.registerBeanDefinition("repoFactoryBean", new RootBeanDefinition(RepositoryFactoryBean.class)); RepositoryFactoryBeanInjectionBean bean = (RepositoryFactoryBeanInjectionBean) bf.getBean("annotatedBean"); - RepositoryFactoryBean repoFactoryBean = bf.getBean("&repoFactoryBean", RepositoryFactoryBean.class); + RepositoryFactoryBean<?> repoFactoryBean = bf.getBean("&repoFactoryBean", RepositoryFactoryBean.class); assertSame(repoFactoryBean, bean.repositoryFactoryBean); } @@ -1432,7 +1668,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { bf.registerSingleton("repoFactoryBean", new RepositoryFactoryBean<>()); RepositoryFactoryBeanInjectionBean bean = (RepositoryFactoryBeanInjectionBean) bf.getBean("annotatedBean"); - RepositoryFactoryBean repoFactoryBean = bf.getBean("&repoFactoryBean", RepositoryFactoryBean.class); + RepositoryFactoryBean<?> repoFactoryBean = bf.getBean("&repoFactoryBean", RepositoryFactoryBean.class); assertSame(repoFactoryBean, bean.repositoryFactoryBean); } @@ -1456,7 +1692,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { bf.registerBeanDefinition("repo", rbd); RepositoryFieldInjectionBeanWithSimpleMatch bean = (RepositoryFieldInjectionBeanWithSimpleMatch) bf.getBean("annotatedBean"); - Repository repo = bf.getBean("repo", Repository.class); + Repository<?> repo = bf.getBean("repo", Repository.class); assertSame(repo, bean.repository); assertSame(repo, bean.stringRepository); assertSame(1, bean.repositoryArray.length); @@ -1492,7 +1728,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { bf.registerBeanDefinition("repo", rbd); RepositoryFieldInjectionBeanWithSimpleMatch bean = (RepositoryFieldInjectionBeanWithSimpleMatch) bf.getBean("annotatedBean"); - Repository repo = bf.getBean("repo", Repository.class); + Repository<?> repo = bf.getBean("repo", Repository.class); assertSame(repo, bean.repository); assertSame(repo, bean.stringRepository); assertSame(1, bean.repositoryArray.length); @@ -1606,6 +1842,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { } @Test + @SuppressWarnings("rawtypes") public void testGenericsBasedConstructorInjectionWithNonTypedTarget() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); @@ -1666,6 +1903,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { } @Test + @SuppressWarnings("rawtypes") public void testGenericsBasedConstructorInjectionWithMixedTargets() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); @@ -1730,6 +1968,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { } @Test + @SuppressWarnings("rawtypes") public void testGenericsBasedInjectionIntoMatchingTypeVariable() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); @@ -1747,6 +1986,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { } @Test + @SuppressWarnings("rawtypes") public void testGenericsBasedInjectionIntoUnresolvedTypeVariable() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); @@ -1764,6 +2004,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { } @Test + @SuppressWarnings("rawtypes") public void testGenericsBasedInjectionIntoTypeVariableSelectingBestMatch() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); @@ -1787,6 +2028,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { @Test @Ignore // SPR-11521 + @SuppressWarnings("rawtypes") public void testGenericsBasedInjectionIntoTypeVariableSelectingBestMatchAgainstFactoryMethodSignature() { DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver()); @@ -1833,6 +2075,18 @@ public class AutowiredAnnotationBeanPostProcessorTests { assertNotNull(bf.getBean(FooBar.class)); } + @Test + public void testSingleConstructorWithProvidedArgument() { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); + bpp.setBeanFactory(bf); + bf.addBeanPostProcessor(bpp); + RootBeanDefinition bd = new RootBeanDefinition(ProvidedArgumentBean.class); + bd.getConstructorArgumentValues().addGenericArgumentValue(Collections.singletonList("value")); + bf.registerBeanDefinition("beanWithArgs", bd); + assertNotNull(bf.getBean(ProvidedArgumentBean.class)); + } + public static class ResourceInjectionBean { @@ -2237,6 +2491,16 @@ public class AutowiredAnnotationBeanPostProcessorTests { } + @SuppressWarnings("serial") + public static class MyTestBeanMap extends LinkedHashMap<String, TestBean> { + } + + + @SuppressWarnings("serial") + public static class MyTestBeanSet extends LinkedHashSet<TestBean> { + } + + public static class MapConstructorInjectionBean { private Map<String, TestBean> testBeanMap; @@ -2252,6 +2516,28 @@ public class AutowiredAnnotationBeanPostProcessorTests { } + public static class SetConstructorInjectionBean { + + private Set<TestBean> testBeanSet; + + @Autowired + public SetConstructorInjectionBean(Set<TestBean> testBeanSet) { + this.testBeanSet = testBeanSet; + } + + public Set<TestBean> getTestBeanSet() { + return this.testBeanSet; + } + } + + + public static class SelfInjectionBean { + + @Autowired + public SelfInjectionBean selfReference; + } + + public static class MapFieldInjectionBean { @Autowired @@ -2309,6 +2595,29 @@ public class AutowiredAnnotationBeanPostProcessorTests { } + public static class SmartObjectFactoryInjectionBean { + + @Autowired + private ObjectProvider<TestBean> testBeanFactory; + + public TestBean getTestBean() { + return this.testBeanFactory.getObject(); + } + + public TestBean getTestBean(String name) { + return this.testBeanFactory.getObject(name); + } + + public TestBean getOptionalTestBean() { + return this.testBeanFactory.getIfAvailable(); + } + + public TestBean getUniqueTestBean() { + return this.testBeanFactory.getIfUnique(); + } + } + + public static class CustomAnnotationRequiredFieldResourceInjectionBean { @MyAutowired(optional = false) @@ -2363,7 +2672,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { @Target({ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) - public static @interface MyAutowired { + public @interface MyAutowired { boolean optional() default false; } @@ -2438,9 +2747,11 @@ public class AutowiredAnnotationBeanPostProcessorTests { public static class GenericRepository<T> implements Repository<T> { } + @SuppressWarnings("rawtypes") public static class GenericRepositorySubclass extends GenericRepository { } + @SuppressWarnings("rawtypes") public static class SimpleRepository implements Repository { } @@ -2534,16 +2845,16 @@ public class AutowiredAnnotationBeanPostProcessorTests { public Repository<?> stringRepository; @Autowired @Qualifier("integerRepo") - public Repository integerRepository; + public Repository<?> integerRepository; @Autowired @Qualifier("stringRepo") public Repository<?>[] stringRepositoryArray; @Autowired @Qualifier("integerRepo") - public Repository[] integerRepositoryArray; + public Repository<?>[] integerRepositoryArray; @Autowired @Qualifier("stringRepo") - public List<Repository> stringRepositoryList; + public List<Repository<?>> stringRepositoryList; @Autowired @Qualifier("integerRepo") public List<Repository<?>> integerRepositoryList; @@ -2552,7 +2863,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { public Map<String, Repository<?>> stringRepositoryMap; @Autowired @Qualifier("integerRepo") - public Map<String, Repository> integerRepositoryMap; + public Map<String, Repository<?>> integerRepositoryMap; } @@ -2565,13 +2876,13 @@ public class AutowiredAnnotationBeanPostProcessorTests { public Repository<String> stringRepository; @Autowired - public Repository[] repositoryArray; + public Repository<?>[] repositoryArray; @Autowired public Repository<String>[] stringRepositoryArray; @Autowired - public List<Repository> repositoryList; + public List<Repository<?>> repositoryList; @Autowired public List<Repository<String>> stringRepositoryList; @@ -2793,6 +3104,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { return new GenericInterface1Impl<String>(); } + @SuppressWarnings("rawtypes") public static GenericInterface1 createPlain() { return new GenericInterface1Impl(); } @@ -2827,6 +3139,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { } + @SuppressWarnings("rawtypes") public static class PlainGenericInterface2Impl implements GenericInterface2 { @Override @@ -2836,26 +3149,32 @@ public class AutowiredAnnotationBeanPostProcessorTests { } + @SuppressWarnings("rawtypes") public interface StockMovement<P extends StockMovementInstruction> { } + @SuppressWarnings("rawtypes") public interface StockMovementInstruction<C extends StockMovement> { } + @SuppressWarnings("rawtypes") public interface StockMovementDao<S extends StockMovement> { } + @SuppressWarnings("rawtypes") public static class StockMovementImpl<P extends StockMovementInstruction> implements StockMovement<P> { } + @SuppressWarnings("rawtypes") public static class StockMovementInstructionImpl<C extends StockMovement> implements StockMovementInstruction<C> { } + @SuppressWarnings("rawtypes") public static class StockMovementDaoImpl<E extends StockMovement> implements StockMovementDao<E> { } @@ -2863,6 +3182,7 @@ public class AutowiredAnnotationBeanPostProcessorTests { public static class StockServiceImpl { @Autowired + @SuppressWarnings("rawtypes") private StockMovementDao<StockMovement> stockMovementDao; } @@ -2926,4 +3246,30 @@ public class AutowiredAnnotationBeanPostProcessorTests { } } + + public static class ProvidedArgumentBean { + + public ProvidedArgumentBean(String[] args) { + } + } + + + public static class CollectionFactoryMethods { + + public static Map<String, TestBean> testBeanMap() { + Map<String, TestBean> tbm = new LinkedHashMap<String, TestBean>(); + tbm.put("testBean1", new TestBean("tb1")); + tbm.put("testBean2", new TestBean("tb2")); + return tbm; + } + + public static Set<TestBean> testBeanSet() { + Set<TestBean> tbs = new LinkedHashSet<TestBean>(); + tbs.add(new TestBean("tb1")); + tbs.add(new TestBean("tb2")); + return tbs; + } + + } + } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurerTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurerTests.java index 38a42d1d..903b34f1 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurerTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/PropertyPlaceholderConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -39,11 +39,10 @@ import static org.springframework.beans.factory.support.BeanDefinitionReaderUtil /** * Unit tests for {@link PropertyPlaceholderConfigurer}. * - * @see PropertySourcesPlaceholderConfigurerTests - * @see PropertyResourceConfigurerTests * @author Chris Beams */ public class PropertyPlaceholderConfigurerTests { + private static final String P1 = "p1"; private static final String P1_LOCAL_PROPS_VAL = "p1LocalPropsVal"; private static final String P1_SYSTEM_PROPS_VAL = "p1SystemPropsVal"; @@ -55,11 +54,12 @@ public class PropertyPlaceholderConfigurerTests { private AbstractBeanDefinition p1BeanDef; + @Before public void setUp() { p1BeanDef = rootBeanDefinition(TestBean.class) - .addPropertyValue("name", "${"+P1+"}") - .getBeanDefinition(); + .addPropertyValue("name", "${" + P1 + "}") + .getBeanDefinition(); bf = new DefaultListableBeanFactory(); @@ -97,9 +97,9 @@ public class PropertyPlaceholderConfigurerTests { public void resolveFromSystemProperties() { getModifiableSystemEnvironment().put("otherKey", "systemValue"); p1BeanDef = rootBeanDefinition(TestBean.class) - .addPropertyValue("name", "${"+P1+"}") - .addPropertyValue("sex", "${otherKey}") - .getBeanDefinition(); + .addPropertyValue("name", "${" + P1 + "}") + .addPropertyValue("sex", "${otherKey}") + .getBeanDefinition(); registerWithGeneratedName(p1BeanDef, bf); ppc.postProcessBeanFactory(bf); TestBean bean = bf.getBean(TestBean.class); @@ -167,9 +167,9 @@ public class PropertyPlaceholderConfigurerTests { String P2_SYSTEM_ENV_VAL = "p2SystemEnvVal"; AbstractBeanDefinition p2BeanDef = rootBeanDefinition(TestBean.class) - .addPropertyValue("name", "${"+P1+"}") - .addPropertyValue("country", "${"+P2+"}") - .getBeanDefinition(); + .addPropertyValue("name", "${" + P1 + "}") + .addPropertyValue("country", "${" + P2 + "}") + .getBeanDefinition(); bf.registerBeanDefinition("p1Bean", p1BeanDef); bf.registerBeanDefinition("p2Bean", p2BeanDef); @@ -238,6 +238,33 @@ public class PropertyPlaceholderConfigurerTests { getModifiableSystemEnvironment().remove("my.name"); } + @Test + public void trimValuesIsOffByDefault() { + PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer(); + getModifiableSystemEnvironment().put("my.name", " myValue "); + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.registerBeanDefinition("testBean", rootBeanDefinition(TestBean.class) + .addPropertyValue("name", "${my.name}") + .getBeanDefinition()); + ppc.postProcessBeanFactory(bf); + assertThat(bf.getBean(TestBean.class).getName(), equalTo(" myValue ")); + getModifiableSystemEnvironment().remove("my.name"); + } + + @Test + public void trimValuesIsApplied() { + PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer(); + ppc.setTrimValues(true); + getModifiableSystemEnvironment().put("my.name", " myValue "); + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.registerBeanDefinition("testBean", rootBeanDefinition(TestBean.class) + .addPropertyValue("name", "${my.name}") + .getBeanDefinition()); + ppc.postProcessBeanFactory(bf); + assertThat(bf.getBean(TestBean.class).getName(), equalTo("myValue")); + getModifiableSystemEnvironment().remove("my.name"); + } + @SuppressWarnings("unchecked") private static Map<String, String> getModifiableSystemEnvironment() { @@ -253,7 +280,8 @@ public class PropertyPlaceholderConfigurerTests { if (obj != null && obj.getClass().getName().equals("java.lang.ProcessEnvironment$StringEnvironment")) { return (Map<String, String>) obj; } - } catch (Exception ex) { + } + catch (Exception ex) { throw new RuntimeException(ex); } } @@ -263,8 +291,9 @@ public class PropertyPlaceholderConfigurerTests { Class<?> processEnvironmentClass; try { processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment"); - } catch (Exception e) { - throw new RuntimeException(e); + } + catch (Exception ex) { + throw new RuntimeException(ex); } try { @@ -272,10 +301,12 @@ public class PropertyPlaceholderConfigurerTests { theCaseInsensitiveEnvironmentField.setAccessible(true); Object obj = theCaseInsensitiveEnvironmentField.get(null); return (Map<String, String>) obj; - } catch (NoSuchFieldException e) { + } + catch (NoSuchFieldException ex) { // do nothing - } catch (Exception e) { - throw new RuntimeException(e); + } + catch (Exception ex) { + throw new RuntimeException(ex); } try { @@ -283,10 +314,12 @@ public class PropertyPlaceholderConfigurerTests { theEnvironmentField.setAccessible(true); Object obj = theEnvironmentField.get(null); return (Map<String, String>) obj; - } catch (NoSuchFieldException e) { + } + catch (NoSuchFieldException ex) { // do nothing - } catch (Exception e) { - throw new RuntimeException(e); + } + catch (Exception ex) { + throw new RuntimeException(ex); } throw new IllegalStateException(); diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBeanTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBeanTests.java index 062f7757..e360a669 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBeanTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBeanTests.java @@ -81,19 +81,22 @@ public final class ServiceLocatorFactoryBeanTests { TestServiceLocator factory = (TestServiceLocator) bf.getBean("factory"); factory.getTestService(); fail("Must fail on more than one matching type"); - } catch (NoSuchBeanDefinitionException ex) { /* expected */ } + } + catch (NoSuchBeanDefinitionException ex) { /* expected */ } try { TestServiceLocator2 factory = (TestServiceLocator2) bf.getBean("factory2"); factory.getTestService(null); fail("Must fail on more than one matching type"); - } catch (NoSuchBeanDefinitionException ex) { /* expected */ } + } + catch (NoSuchBeanDefinitionException ex) { /* expected */ } try { TestService2Locator factory = (TestService2Locator) bf.getBean("factory3"); factory.getTestService(); fail("Must fail on no matching types"); - } catch (NoSuchBeanDefinitionException ex) { /* expected */ } + } + catch (NoSuchBeanDefinitionException ex) { /* expected */ } } @Test @@ -138,7 +141,8 @@ public final class ServiceLocatorFactoryBeanTests { TestService2Locator factory3 = (TestService2Locator) bf.getBean("factory3"); factory3.getTestService(); fail("Must fail on no matching type"); - } catch (CustomServiceLocatorException3 ex) { /* expected */ } + } + catch (CustomServiceLocatorException3 ex) { /* expected */ } } @Test @@ -160,7 +164,8 @@ public final class ServiceLocatorFactoryBeanTests { try { factory.getTestService("bogusTestService"); fail("Illegal operation allowed"); - } catch (NoSuchBeanDefinitionException ex) { /* expected */ } + } + catch (NoSuchBeanDefinitionException ex) { /* expected */ } } @Ignore @Test // worked when using an ApplicationContext (see commented), fails when using BeanFactory @@ -275,7 +280,8 @@ public final class ServiceLocatorFactoryBeanTests { try { ServiceLocatorFactoryBean factory = new ServiceLocatorFactoryBean(); factory.setBeanFactory(beanFactory); - } catch (FatalBeanException ex) { + } + catch (FatalBeanException ex) { // expected } } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/support/security/CallbacksSecurityTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/support/security/CallbacksSecurityTests.java index 42bb2334..73cf1ffc 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/support/security/CallbacksSecurityTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/support/security/CallbacksSecurityTests.java @@ -324,7 +324,8 @@ public class CallbacksSecurityTests { try { acc.checkPermission(new PropertyPermission("*", "read")); fail("Acc should not have any permissions"); - } catch (SecurityException se) { + } + catch (SecurityException se) { // expected } @@ -342,7 +343,8 @@ public class CallbacksSecurityTests { } }, acc); fail("expected security exception"); - } catch (Exception ex) { + } + catch (Exception ex) { } final Class<ConstructorBean> cl = ConstructorBean.class; @@ -356,7 +358,8 @@ public class CallbacksSecurityTests { } }, acc); fail("expected security exception"); - } catch (Exception ex) { + } + catch (Exception ex) { } } @@ -365,7 +368,8 @@ public class CallbacksSecurityTests { try { beanFactory.getBean("spring-init"); fail("expected security exception"); - } catch (BeanCreationException ex) { + } + catch (BeanCreationException ex) { assertTrue(ex.getCause() instanceof SecurityException); } } @@ -375,7 +379,8 @@ public class CallbacksSecurityTests { try { beanFactory.getBean("custom-init"); fail("expected security exception"); - } catch (BeanCreationException ex) { + } + catch (BeanCreationException ex) { assertTrue(ex.getCause() instanceof SecurityException); } } @@ -399,7 +404,8 @@ public class CallbacksSecurityTests { try { beanFactory.getBean("spring-factory"); fail("expected security exception"); - } catch (BeanCreationException ex) { + } + catch (BeanCreationException ex) { assertTrue(ex.getCause() instanceof SecurityException); } @@ -416,7 +422,8 @@ public class CallbacksSecurityTests { try { beanFactory.getBean("custom-static-factory-method"); fail("expected security exception"); - } catch (BeanCreationException ex) { + } + catch (BeanCreationException ex) { assertTrue(ex.getMostSpecificCause() instanceof SecurityException); } } @@ -426,7 +433,8 @@ public class CallbacksSecurityTests { try { beanFactory.getBean("custom-factory-method"); fail("expected security exception"); - } catch (BeanCreationException ex) { + } + catch (BeanCreationException ex) { assertTrue(ex.getMostSpecificCause() instanceof SecurityException); } } @@ -436,7 +444,8 @@ public class CallbacksSecurityTests { try { beanFactory.getBean("privileged-static-factory-method"); fail("expected security exception"); - } catch (BeanCreationException ex) { + } + catch (BeanCreationException ex) { assertTrue(ex.getMostSpecificCause() instanceof SecurityException); } } @@ -446,7 +455,8 @@ public class CallbacksSecurityTests { try { beanFactory.getBean("constructor"); fail("expected security exception"); - } catch (BeanCreationException ex) { + } + catch (BeanCreationException ex) { // expected assertTrue(ex.getMostSpecificCause() instanceof SecurityException); } @@ -472,7 +482,8 @@ public class CallbacksSecurityTests { try { beanFactory.getBean("property-injection"); fail("expected security exception"); - } catch (BeanCreationException ex) { + } + catch (BeanCreationException ex) { assertTrue(ex.getMessage().contains("security")); } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/DuplicateBeanIdTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/DuplicateBeanIdTests.java index 04c31efe..a42359b9 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/DuplicateBeanIdTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/DuplicateBeanIdTests.java @@ -48,7 +48,8 @@ public class DuplicateBeanIdTests { try { reader.loadBeanDefinitions(new ClassPathResource("DuplicateBeanIdTests-sameLevel-context.xml", this.getClass())); fail("expected parsing exception due to duplicate ids in same nesting level"); - } catch (Exception ex) { + } + catch (Exception ex) { // expected } } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlBeanCollectionTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlBeanCollectionTests.java index 53c951eb..c7ac6910 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlBeanCollectionTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/xml/XmlBeanCollectionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 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. @@ -29,6 +29,7 @@ import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; +import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.BeanCreationException; @@ -52,14 +53,16 @@ import static org.junit.Assert.*; */ public class XmlBeanCollectionTests { - private final DefaultListableBeanFactory beanFactory; + private final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); - public XmlBeanCollectionTests() { - this.beanFactory = new DefaultListableBeanFactory(); + + @Before + public void loadBeans() { new XmlBeanDefinitionReader(this.beanFactory).loadBeanDefinitions( new ClassPathResource("collections.xml", getClass())); } + @Test public void testCollectionFactoryDefaults() throws Exception { ListFactoryBean listFactory = new ListFactoryBean(); @@ -328,6 +331,15 @@ public class XmlBeanCollectionTests { } @Test + public void testIntegerArray() throws Exception { + HasMap hasMap = (HasMap) this.beanFactory.getBean("integerArray"); + assertTrue(hasMap.getIntegerArray().length == 3); + assertTrue(hasMap.getIntegerArray()[0].intValue() == 0); + assertTrue(hasMap.getIntegerArray()[1].intValue() == 1); + assertTrue(hasMap.getIntegerArray()[2].intValue() == 2); + } + + @Test public void testClassArray() throws Exception { HasMap hasMap = (HasMap) this.beanFactory.getBean("classArray"); assertTrue(hasMap.getClassArray().length == 2); @@ -336,12 +348,11 @@ public class XmlBeanCollectionTests { } @Test - public void testIntegerArray() throws Exception { - HasMap hasMap = (HasMap) this.beanFactory.getBean("integerArray"); - assertTrue(hasMap.getIntegerArray().length == 3); - assertTrue(hasMap.getIntegerArray()[0].intValue() == 0); - assertTrue(hasMap.getIntegerArray()[1].intValue() == 1); - assertTrue(hasMap.getIntegerArray()[2].intValue() == 2); + public void testClassList() throws Exception { + HasMap hasMap = (HasMap) this.beanFactory.getBean("classList"); + assertTrue(hasMap.getClassList().size()== 2); + assertTrue(hasMap.getClassList().get(0).equals(String.class)); + assertTrue(hasMap.getClassList().get(1).equals(Exception.class)); } @Test @@ -447,4 +458,5 @@ public class XmlBeanCollectionTests { return obj; } } + } diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/CustomEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/CustomEditorTests.java index 5f233a66..806b6f3f 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/CustomEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/CustomEditorTests.java @@ -485,7 +485,8 @@ public class CustomEditorTests { CustomNumberEditor editor = new CustomNumberEditor(Short.class, true); editor.setAsText(String.valueOf(Short.MAX_VALUE + 1)); fail(Short.MAX_VALUE + 1 + " is greater than max value"); - } catch (NumberFormatException ex) { + } + catch (NumberFormatException ex) { // expected } } diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/FileEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/FileEditorTests.java index 17f6d984..ae6a5c68 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/FileEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/FileEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2016 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. @@ -29,20 +29,20 @@ import static org.junit.Assert.*; * @author Thomas Risberg * @author Chris Beams */ -public final class FileEditorTests { +public class FileEditorTests { @Test public void testClasspathFileName() throws Exception { PropertyEditor fileEditor = new FileEditor(); - fileEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) + "/" - + ClassUtils.getShortName(getClass()) + ".class"); + fileEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) + "/" + + ClassUtils.getShortName(getClass()) + ".class"); Object value = fileEditor.getValue(); assertTrue(value instanceof File); File file = (File) value; assertTrue(file.exists()); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testWithNonExistentResource() throws Exception { PropertyEditor propertyEditor = new FileEditor(); propertyEditor.setAsText("classpath:no_way_this_file_is_found.doc"); @@ -71,8 +71,8 @@ public final class FileEditorTests { @Test public void testUnqualifiedFileNameFound() throws Exception { PropertyEditor fileEditor = new FileEditor(); - String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" + ClassUtils.getShortName(getClass()) - + ".class"; + String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" + + ClassUtils.getShortName(getClass()) + ".class"; fileEditor.setAsText(fileName); Object value = fileEditor.getValue(); assertTrue(value instanceof File); @@ -88,8 +88,8 @@ public final class FileEditorTests { @Test public void testUnqualifiedFileNameNotFound() throws Exception { PropertyEditor fileEditor = new FileEditor(); - String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" + ClassUtils.getShortName(getClass()) - + ".clazz"; + String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" + + ClassUtils.getShortName(getClass()) + ".clazz"; fileEditor.setAsText(fileName); Object value = fileEditor.getValue(); assertTrue(value instanceof File); @@ -101,4 +101,5 @@ public final class FileEditorTests { } assertTrue(absolutePath.endsWith(fileName)); } + } diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/InputStreamEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/InputStreamEditorTests.java index 5073e5d5..6e90e92a 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/InputStreamEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/InputStreamEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -60,9 +60,8 @@ public class InputStreamEditorTests { @Test(expected = IllegalArgumentException.class) public void testWhenResourceDoesNotExist() throws Exception { - String resource = "classpath:bingo!"; InputStreamEditor editor = new InputStreamEditor(); - editor.setAsText(resource); + editor.setAsText("classpath:bingo!"); } @Test diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/PathEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/PathEditorTests.java new file mode 100644 index 00000000..440a3e38 --- /dev/null +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/PathEditorTests.java @@ -0,0 +1,80 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.beans.propertyeditors; + +import java.beans.PropertyEditor; +import java.io.File; +import java.nio.file.Path; + +import org.junit.Test; + +import org.springframework.util.ClassUtils; + +import static org.junit.Assert.*; + +/** + * @author Juergen Hoeller + * @since 4.3.2 + */ +public class PathEditorTests { + + @Test + public void testClasspathPathName() throws Exception { + PropertyEditor pathEditor = new PathEditor(); + pathEditor.setAsText("classpath:" + ClassUtils.classPackageAsResourcePath(getClass()) + "/" + + ClassUtils.getShortName(getClass()) + ".class"); + Object value = pathEditor.getValue(); + assertTrue(value instanceof Path); + Path path = (Path) value; + assertTrue(path.toFile().exists()); + } + + @Test(expected = IllegalArgumentException.class) + public void testWithNonExistentResource() throws Exception { + PropertyEditor propertyEditor = new PathEditor(); + propertyEditor.setAsText("classpath:/no_way_this_file_is_found.doc"); + } + + @Test + public void testWithNonExistentPath() throws Exception { + PropertyEditor pathEditor = new PathEditor(); + pathEditor.setAsText("file:/no_way_this_file_is_found.doc"); + Object value = pathEditor.getValue(); + assertTrue(value instanceof Path); + Path path = (Path) value; + assertTrue(!path.toFile().exists()); + } + + @Test + public void testUnqualifiedPathNameFound() throws Exception { + PropertyEditor pathEditor = new PathEditor(); + String fileName = ClassUtils.classPackageAsResourcePath(getClass()) + "/" + + ClassUtils.getShortName(getClass()) + ".class"; + pathEditor.setAsText(fileName); + Object value = pathEditor.getValue(); + assertTrue(value instanceof Path); + Path path = (Path) value; + File file = path.toFile(); + assertTrue(file.exists()); + String absolutePath = file.getAbsolutePath(); + if (File.separatorChar == '\\') { + absolutePath = absolutePath.replace('\\', '/'); + } + assertTrue(absolutePath.endsWith(fileName)); + } + +} diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java index 2729922a..d4e19b8b 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/ReaderEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 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. @@ -34,7 +34,7 @@ public class ReaderEditorTests { @Test(expected = IllegalArgumentException.class) public void testCtorWithNullResourceEditor() throws Exception { - new InputStreamEditor(null); + new ReaderEditor(null); } @Test diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URIEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URIEditorTests.java index a347cfa8..4eb6245d 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URIEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URIEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2016 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. @@ -31,15 +31,6 @@ import static org.junit.Assert.*; */ public class URIEditorTests { - private void doTestURI(String uriSpec) { - PropertyEditor uriEditor = new URIEditor(); - uriEditor.setAsText(uriSpec); - Object value = uriEditor.getValue(); - assertTrue(value instanceof URI); - URI uri = (URI) value; - assertEquals(uriSpec, uri.toString()); - } - @Test public void standardURI() throws Exception { doTestURI("mailto:juergen.hoeller@interface21.com"); @@ -141,4 +132,14 @@ public class URIEditorTests { assertEquals("http://example.com/spaces%20and%20%E2%82%AC", uri.toASCIIString()); } + + private void doTestURI(String uriSpec) { + PropertyEditor uriEditor = new URIEditor(); + uriEditor.setAsText(uriSpec); + Object value = uriEditor.getValue(); + assertTrue(value instanceof URI); + URI uri = (URI) value; + assertEquals(uriSpec, uri.toString()); + } + } diff --git a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URLEditorTests.java b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URLEditorTests.java index 36738e1b..3259ec47 100644 --- a/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URLEditorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/propertyeditors/URLEditorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 the original author or authors. + * Copyright 2002-2016 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. @@ -29,7 +29,12 @@ import static org.junit.Assert.*; * @author Rick Evans * @author Chris Beams */ -public final class URLEditorTests { +public class URLEditorTests { + + @Test(expected = IllegalArgumentException.class) + public void testCtorWithNullResourceEditor() throws Exception { + new URLEditor(null); + } @Test public void testStandardURI() throws Exception { @@ -63,7 +68,7 @@ public final class URLEditorTests { assertTrue(!url.getProtocol().startsWith("classpath")); } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testWithNonExistentResource() throws Exception { PropertyEditor urlEditor = new URLEditor(); urlEditor.setAsText("gonna:/freak/in/the/morning/freak/in/the.evening"); @@ -83,9 +88,4 @@ public final class URLEditorTests { assertEquals("", urlEditor.getAsText()); } - @Test(expected=IllegalArgumentException.class) - public void testCtorWithNullResourceEditor() throws Exception { - new URLEditor(null); - } - } diff --git a/spring-beans/src/test/java/org/springframework/tests/sample/beans/HasMap.java b/spring-beans/src/test/java/org/springframework/tests/sample/beans/HasMap.java index 4639050b..51c5415b 100644 --- a/spring-beans/src/test/java/org/springframework/tests/sample/beans/HasMap.java +++ b/spring-beans/src/test/java/org/springframework/tests/sample/beans/HasMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 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. @@ -17,6 +17,7 @@ package org.springframework.tests.sample.beans; import java.util.IdentityHashMap; +import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; @@ -38,9 +39,11 @@ public class HasMap { private Object[] objectArray; + private Integer[] intArray; + private Class<?>[] classArray; - private Integer[] intArray; + private List<Class<?>> classList; private IdentityHashMap identityMap; @@ -81,6 +84,14 @@ public class HasMap { this.objectArray = objectArray; } + public Integer[] getIntegerArray() { + return intArray; + } + + public void setIntegerArray(Integer[] is) { + intArray = is; + } + public Class<?>[] getClassArray() { return classArray; } @@ -89,12 +100,12 @@ public class HasMap { this.classArray = classArray; } - public Integer[] getIntegerArray() { - return intArray; + public List<Class<?>> getClassList() { + return classList; } - public void setIntegerArray(Integer[] is) { - intArray = is; + public void setClassList(List<Class<?>> classList) { + this.classList = classList; } public IdentityHashMap getIdentityMap() { diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collections.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collections.xml index 73294dfb..9047d26b 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collections.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/collections.xml @@ -288,15 +288,6 @@ </property> </bean> - <bean id="classArray" class="org.springframework.tests.sample.beans.HasMap"> - <property name="classArray"> - <list> - <value>java.lang.String</value> - <value>java.lang.Exception</value> - </list> - </property> - </bean> - <bean id="integerArray" class="org.springframework.tests.sample.beans.HasMap"> <property name="integerArray"> <list> @@ -307,6 +298,14 @@ </property> </bean> + <bean id="classArray" class="org.springframework.tests.sample.beans.HasMap"> + <property name="classArray" value="java.lang.String,java.lang.Exception"/> + </bean> + + <bean id="classList" class="org.springframework.tests.sample.beans.HasMap"> + <property name="classList" value="java.lang.String,java.lang.Exception"/> + </bean> + <bean id="listFactory" class="org.springframework.beans.factory.config.ListFactoryBean"> <property name="sourceList"> <list> diff --git a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/import.xml b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/import.xml index 96446f02..278e5dff 100644 --- a/spring-beans/src/test/resources/org/springframework/beans/factory/xml/import.xml +++ b/spring-beans/src/test/resources/org/springframework/beans/factory/xml/import.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> +<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans> |