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 f0c2fa1dae..dc60afb462 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;
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 c52d0ae766..5c5ed79cfa 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.
*
- *
Note that non-factory-aware initialization methods like afterPropertiesSet()
- * or a custom "init-method" can throw any exception.
+ *
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/InjectionPoint.java b/spring-beans/src/main/java/org/springframework/beans/factory/InjectionPoint.java
new file mode 100644
index 0000000000..0a3d55a931
--- /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.
+ *
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.
+ *
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.
+ *
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/UnsatisfiedDependencyException.java b/spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java
index 9ec35a7e63..0403abfc7a 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
@@ -60,6 +63,36 @@ public class UnsatisfiedDependencyException extends BeanCreationException {
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 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
@@ -67,7 +100,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 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/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java
index 90427f18ce..10a0dc76fc 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-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,9 @@ 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;
@@ -347,6 +349,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);
}
@@ -365,6 +370,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);
}
@@ -549,45 +557,45 @@ 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 autowiredBeanNames = new LinkedHashSet(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 autowiredBeanNames = new LinkedHashSet(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 RuntimeBeanReference(autowiredBeanName);
}
}
}
- 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);
}
}
}
@@ -615,67 +623,69 @@ 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 autowiredBeanNames = new LinkedHashSet(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 autowiredBeanNames = new LinkedHashSet(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 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 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);
}
}
}
}
- 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();
+ }
}
}
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 cadc893a1e..e5410e859c 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,7 +19,6 @@ 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;
@@ -27,13 +26,13 @@ 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.
@@ -44,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;
@@ -68,7 +61,7 @@ public class DependencyDescriptor implements Serializable {
private int nestingLevel = 1;
- private transient Annotation[] fieldAnnotations;
+ private Class> containingClass;
/**
@@ -89,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();
@@ -101,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;
}
@@ -123,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;
@@ -136,39 +127,19 @@ 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.
- * 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.
- *
Note: Either MethodParameter or Field is available.
- * @return the Field, or {@code null} if none
- */
- public Field getField() {
- return this.field;
- }
-
/**
* Return whether this dependency is required.
*/
@@ -358,19 +329,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/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java
index a0285be6a3..a3ae158217 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-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.
@@ -40,6 +40,7 @@ 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;
@@ -151,7 +152,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 +160,7 @@ class ConstructorResolver {
Set> ambiguousConstructors = null;
LinkedList 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) {
@@ -665,7 +665,6 @@ 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);
@@ -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 + "'");
}
}
@@ -793,11 +794,9 @@ 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());
}
}
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 f61119bc40..32fa684760 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 928737e6ea..633791c303 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
@@ -83,6 +83,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);
@@ -635,6 +636,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
}
catch (UnsatisfiedDependencyException ex) {
// expected
+ assertSame(ConstructorWithoutFallbackBean.class, ex.getInjectionPoint().getMethodParameter().getDeclaringClass());
}
}
@@ -838,8 +840,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
bf.getBean("annotatedBean");
fail("should have failed, more than one bean of type");
}
- catch (BeanCreationException ex) {
+ catch (UnsatisfiedDependencyException ex) {
// expected
+ assertSame(MapMethodInjectionBean.class, ex.getInjectionPoint().getMethodParameter().getDeclaringClass());
}
bf.destroySingletons();
}
@@ -1164,7 +1167,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();
}
@@ -1183,10 +1187,12 @@ public class AutowiredAnnotationBeanPostProcessorTests {
try {
bf.getBean("customBean");
- fail("expected BeanCreationException; no dependency available for required field");
+ fail("Should have thrown UnsatisfiedDependencyException");
}
- catch (BeanCreationException ex) {
+ catch (UnsatisfiedDependencyException ex) {
// expected
+ assertSame(CustomAnnotationRequiredFieldResourceInjectionBean.class,
+ ex.getInjectionPoint().getField().getDeclaringClass());
}
bf.destroySingletons();
}
@@ -1209,10 +1215,13 @@ public class AutowiredAnnotationBeanPostProcessorTests {
try {
bf.getBean("customBean");
- fail("expected BeanCreationException; multiple beans of dependency type available");
+ fail("Should have thrown UnsatisfiedDependencyException");
}
- catch (BeanCreationException ex) {
+ catch (UnsatisfiedDependencyException ex) {
// expected
+ ex.printStackTrace();
+ assertSame(CustomAnnotationRequiredFieldResourceInjectionBean.class,
+ ex.getInjectionPoint().getField().getDeclaringClass());
}
bf.destroySingletons();
}
@@ -1231,7 +1240,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();
}
@@ -1250,10 +1260,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();
}
@@ -1276,10 +1288,12 @@ public class AutowiredAnnotationBeanPostProcessorTests {
try {
bf.getBean("customBean");
- fail("expected BeanCreationException; multiple beans of dependency type available");
+ fail("Should have thrown UnsatisfiedDependencyException");
}
- catch (BeanCreationException ex) {
+ catch (UnsatisfiedDependencyException ex) {
// expected
+ assertSame(CustomAnnotationRequiredMethodResourceInjectionBean.class,
+ ex.getInjectionPoint().getMethodParameter().getDeclaringClass());
}
bf.destroySingletons();
}
@@ -1298,7 +1312,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());
@@ -1317,7 +1332,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());
@@ -1342,10 +1358,12 @@ public class AutowiredAnnotationBeanPostProcessorTests {
try {
bf.getBean("customBean");
- fail("expected BeanCreationException; multiple beans of dependency type available");
+ fail("Should have thrown UnsatisfiedDependencyException");
}
- catch (BeanCreationException ex) {
+ catch (UnsatisfiedDependencyException ex) {
// expected
+ assertSame(CustomAnnotationOptionalFieldResourceInjectionBean.class,
+ ex.getInjectionPoint().getField().getDeclaringClass());
}
bf.destroySingletons();
}
@@ -1364,7 +1382,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());
@@ -1383,7 +1402,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());
@@ -1408,10 +1428,12 @@ public class AutowiredAnnotationBeanPostProcessorTests {
try {
bf.getBean("customBean");
- fail("expected BeanCreationException; multiple beans of dependency type available");
+ fail("Should have thrown UnsatisfiedDependencyException");
}
- catch (BeanCreationException ex) {
+ catch (UnsatisfiedDependencyException ex) {
// expected
+ assertSame(CustomAnnotationOptionalMethodResourceInjectionBean.class,
+ ex.getInjectionPoint().getMethodParameter().getDeclaringClass());
}
bf.destroySingletons();
}
@@ -2644,7 +2666,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
- public static @interface MyAutowired {
+ public @interface MyAutowired {
boolean optional() default false;
}
diff --git a/spring-core/src/main/java/org/springframework/core/MethodParameter.java b/spring-core/src/main/java/org/springframework/core/MethodParameter.java
index f3dbbfb14b..2338866dc2 100644
--- a/spring-core/src/main/java/org/springframework/core/MethodParameter.java
+++ b/spring-core/src/main/java/org/springframework/core/MethodParameter.java
@@ -180,7 +180,14 @@ public class MethodParameter {
}
/**
- * Returns the wrapped member.
+ * Return the class that declares the underlying Method or Constructor.
+ */
+ public Class> getDeclaringClass() {
+ return getMember().getDeclaringClass();
+ }
+
+ /**
+ * Return the wrapped member.
* @return the Method or Constructor as Member
*/
public Member getMember() {
@@ -196,7 +203,9 @@ public class MethodParameter {
}
/**
- * Returns the wrapped annotated element.
+ * Return the wrapped annotated element.
+ * Note: This method exposes the annotations declared on the method/constructor
+ * itself (i.e. at the method/constructor level, not at the parameter level).
* @return the Method or Constructor as AnnotatedElement
*/
public AnnotatedElement getAnnotatedElement() {
@@ -211,13 +220,6 @@ public class MethodParameter {
}
}
- /**
- * Return the class that declares the underlying Method or Constructor.
- */
- public Class> getDeclaringClass() {
- return getMember().getDeclaringClass();
- }
-
/**
* Return the index of the method/constructor parameter.
* @return the parameter index (-1 in case of the return type)
@@ -577,6 +579,12 @@ public class MethodParameter {
return (getMember().hashCode() * 31 + this.parameterIndex);
}
+ @Override
+ public String toString() {
+ return (this.method != null ? "method '" + this.method.getName() + "'" : "constructor") +
+ " parameter " + this.parameterIndex;
+ }
+
@Override
public MethodParameter clone() {
return new MethodParameter(this);