Consistent UnsatisfiedDependencyException exposure with injection point metadata

Issue: SPR-13968
This commit is contained in:
Juergen Hoeller 2016-02-25 21:36:49 +01:00
parent 4c964473b1
commit b6dd8a9233
10 changed files with 428 additions and 188 deletions

View File

@ -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;

View File

@ -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

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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,7 +557,6 @@ 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);
@ -559,7 +566,12 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
synchronized (this) {
if (!this.cached) {
if (value != null || this.required) {
@ -586,10 +598,6 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
field.set(bean, value);
}
}
catch (Throwable ex) {
throw new BeanCreationException("Could not autowire field: " + field, ex);
}
}
}
@ -615,7 +623,6 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
return;
}
Method method = (Method) this.member;
try {
Object[] arguments;
if (this.cached) {
// Shortcut for avoiding synchronization...
@ -629,20 +636,25 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
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);
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;
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
}
}
synchronized (this) {
if (!this.cached) {
if (arguments != null) {
this.cachedMethodArguments = new Object[arguments.length];
this.cachedMethodArguments = new Object[paramTypes.length];
for (int i = 0; i < arguments.length; i++) {
this.cachedMethodArguments[i] = descriptors[i];
}
@ -667,15 +679,13 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
}
}
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);
}
}

View File

@ -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.
* <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;
}
/**
* 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();
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
return this.fieldAnnotations;
}
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);
}

View File

@ -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;
@ -159,8 +160,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) {
@ -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());
}
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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.
* <p>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);