Skip to content

Commit b6dd8a9

Browse files
committed
Consistent UnsatisfiedDependencyException exposure with injection point metadata
Issue: SPR-13968
1 parent 4c96447 commit b6dd8a9

File tree

10 files changed

+428
-188
lines changed

10 files changed

+428
-188
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/BeanCreationException.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -63,7 +63,7 @@ public BeanCreationException(String msg, Throwable cause) {
6363
* @param msg the detail message
6464
*/
6565
public BeanCreationException(String beanName, String msg) {
66-
super("Error creating bean with name '" + beanName + "': " + msg);
66+
super("Error creating bean" + (beanName != null ? " with name '" + beanName + "'" : "") + ": " + msg);
6767
this.beanName = beanName;
6868
}
6969

@@ -86,7 +86,7 @@ public BeanCreationException(String beanName, String msg, Throwable cause) {
8686
* @param msg the detail message
8787
*/
8888
public BeanCreationException(String resourceDescription, String beanName, String msg) {
89-
super("Error creating bean with name '" + beanName + "'" +
89+
super("Error creating bean" + (beanName != null ? " with name '" + beanName + "'" : "") +
9090
(resourceDescription != null ? " defined in " + resourceDescription : "") + ": " + msg);
9191
this.resourceDescription = resourceDescription;
9292
this.beanName = beanName;

spring-beans/src/main/java/org/springframework/beans/factory/BeanInitializationException.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,8 +23,8 @@
2323
* factory-aware initialization code fails. BeansExceptions thrown by
2424
* bean factory methods themselves should simply be propagated as-is.
2525
*
26-
* <p>Note that non-factory-aware initialization methods like afterPropertiesSet()
27-
* or a custom "init-method" can throw any exception.
26+
* <p>Note that {@code afterPropertiesSet()} or a custom "init-method"
27+
* can throw any exception.
2828
*
2929
* @author Juergen Hoeller
3030
* @since 13.11.2003
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*
2+
* Copyright 2002-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.beans.factory;
18+
19+
import java.lang.annotation.Annotation;
20+
import java.lang.reflect.AnnotatedElement;
21+
import java.lang.reflect.Field;
22+
import java.lang.reflect.Member;
23+
24+
import org.springframework.core.MethodParameter;
25+
import org.springframework.util.Assert;
26+
27+
/**
28+
* A simple descriptor for an injection point, pointing to a method/constructor
29+
* parameter or a field. Exposed by {@link UnsatisfiedDependencyException}.
30+
*
31+
* @author Juergen Hoeller
32+
* @since 4.3
33+
* @see UnsatisfiedDependencyException#getInjectionPoint()
34+
* @see org.springframework.beans.factory.config.DependencyDescriptor
35+
*/
36+
public class InjectionPoint {
37+
38+
protected MethodParameter methodParameter;
39+
40+
protected Field field;
41+
42+
private volatile Annotation[] fieldAnnotations;
43+
44+
45+
/**
46+
* Create an injection point descriptor for a method or constructor parameter.
47+
* @param methodParameter the MethodParameter to wrap
48+
*/
49+
public InjectionPoint(MethodParameter methodParameter) {
50+
Assert.notNull(methodParameter, "MethodParameter must not be null");
51+
this.methodParameter = methodParameter;
52+
}
53+
54+
/**
55+
* Create an injection point descriptor for a field.
56+
* @param field the field to wrap
57+
*/
58+
public InjectionPoint(Field field) {
59+
Assert.notNull(field, "Field must not be null");
60+
this.field = field;
61+
}
62+
63+
/**
64+
* Copy constructor.
65+
* @param original the original descriptor to create a copy from
66+
*/
67+
protected InjectionPoint(InjectionPoint original) {
68+
this.methodParameter = (original.methodParameter != null ?
69+
new MethodParameter(original.methodParameter) : null);
70+
this.field = original.field;
71+
this.fieldAnnotations = original.fieldAnnotations;
72+
}
73+
74+
/**
75+
* Just available for serialization purposes in subclasses.
76+
*/
77+
protected InjectionPoint() {
78+
}
79+
80+
81+
/**
82+
* Return the wrapped MethodParameter, if any.
83+
* <p>Note: Either MethodParameter or Field is available.
84+
* @return the MethodParameter, or {@code null} if none
85+
*/
86+
public MethodParameter getMethodParameter() {
87+
return this.methodParameter;
88+
}
89+
90+
/**
91+
* Return the wrapped Field, if any.
92+
* <p>Note: Either MethodParameter or Field is available.
93+
* @return the Field, or {@code null} if none
94+
*/
95+
public Field getField() {
96+
return this.field;
97+
}
98+
99+
/**
100+
* Obtain the annotations associated with the wrapped field or method/constructor parameter.
101+
*/
102+
public Annotation[] getAnnotations() {
103+
if (this.field != null) {
104+
if (this.fieldAnnotations == null) {
105+
this.fieldAnnotations = this.field.getAnnotations();
106+
}
107+
return this.fieldAnnotations;
108+
}
109+
else {
110+
return this.methodParameter.getParameterAnnotations();
111+
}
112+
}
113+
114+
/**
115+
* Return the type declared by the underlying field or method/constructor parameter,
116+
* indicating the injection type.
117+
*/
118+
public Class<?> getDeclaredType() {
119+
return (this.field != null ? this.field.getType() : this.methodParameter.getParameterType());
120+
}
121+
122+
/**
123+
* Returns the wrapped member, containing the injection point.
124+
* @return the Field / Method / Constructor as Member
125+
*/
126+
public Member getMember() {
127+
return (this.field != null ? this.field : this.methodParameter.getMember());
128+
}
129+
130+
/**
131+
* Return the wrapped annotated element.
132+
* <p>Note: In case of a method/constructor parameter, this exposes
133+
* the annotations declared on the method or constructor itself
134+
* (i.e. at the method/constructor level, not at the parameter level).
135+
* Use {@link #getAnnotations()} to obtain parameter-level annotations in
136+
* such a scenario, transparently with corresponding field annotations.
137+
* @return the Field / Method / Constructor as AnnotatedElement
138+
*/
139+
public AnnotatedElement getAnnotatedElement() {
140+
return (this.field != null ? this.field : this.methodParameter.getAnnotatedElement());
141+
}
142+
143+
144+
@Override
145+
public boolean equals(Object other) {
146+
if (this == other) {
147+
return true;
148+
}
149+
if (getClass() != other.getClass()) {
150+
return false;
151+
}
152+
InjectionPoint otherPoint = (InjectionPoint) other;
153+
return (this.field != null ? this.field.equals(otherPoint.field) :
154+
this.methodParameter.equals(otherPoint.methodParameter));
155+
}
156+
157+
@Override
158+
public int hashCode() {
159+
return (this.field != null ? this.field.hashCode() : this.methodParameter.hashCode());
160+
}
161+
162+
@Override
163+
public String toString() {
164+
return (this.field != null ? "field '" + this.field.getName() + "'" : this.methodParameter.toString());
165+
}
166+
167+
}

spring-beans/src/main/java/org/springframework/beans/factory/UnsatisfiedDependencyException.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
@SuppressWarnings("serial")
3232
public class UnsatisfiedDependencyException extends BeanCreationException {
3333

34+
private InjectionPoint injectionPoint;
35+
36+
3437
/**
3538
* Create a new UnsatisfiedDependencyException.
3639
* @param resourceDescription description of the resource that the bean definition came from
@@ -60,14 +63,46 @@ public UnsatisfiedDependencyException(
6063
initCause(ex);
6164
}
6265

66+
/**
67+
* Create a new UnsatisfiedDependencyException.
68+
* @param resourceDescription description of the resource that the bean definition came from
69+
* @param beanName the name of the bean requested
70+
* @param injectionPoint the injection point (field or method/constructor parameter)
71+
* @param msg the detail message
72+
* @since 4.3
73+
*/
74+
public UnsatisfiedDependencyException(
75+
String resourceDescription, String beanName, InjectionPoint injectionPoint, String msg) {
76+
77+
super(resourceDescription, beanName, "Unsatisfied dependency expressed through " + injectionPoint + ": " + msg);
78+
this.injectionPoint = injectionPoint;
79+
}
80+
81+
/**
82+
* Create a new UnsatisfiedDependencyException.
83+
* @param resourceDescription description of the resource that the bean definition came from
84+
* @param beanName the name of the bean requested
85+
* @param injectionPoint the injection point (field or method/constructor parameter)
86+
* @param ex the bean creation exception that indicated the unsatisfied dependency
87+
* @since 4.3
88+
*/
89+
public UnsatisfiedDependencyException(
90+
String resourceDescription, String beanName, InjectionPoint injectionPoint, BeansException ex) {
91+
92+
this(resourceDescription, beanName, injectionPoint, (ex != null ? ex.getMessage() : ""));
93+
initCause(ex);
94+
}
95+
6396
/**
6497
* Create a new UnsatisfiedDependencyException.
6598
* @param resourceDescription description of the resource that the bean definition came from
6699
* @param beanName the name of the bean requested
67100
* @param ctorArgIndex the index of the constructor argument that couldn't be satisfied
68101
* @param ctorArgType the type of the constructor argument that couldn't be satisfied
69102
* @param msg the detail message
103+
* @deprecated in favor of {@link #UnsatisfiedDependencyException(String, String, InjectionPoint, String)}
70104
*/
105+
@Deprecated
71106
public UnsatisfiedDependencyException(
72107
String resourceDescription, String beanName, int ctorArgIndex, Class<?> ctorArgType, String msg) {
73108

@@ -84,12 +119,23 @@ public UnsatisfiedDependencyException(
84119
* @param ctorArgIndex the index of the constructor argument that couldn't be satisfied
85120
* @param ctorArgType the type of the constructor argument that couldn't be satisfied
86121
* @param ex the bean creation exception that indicated the unsatisfied dependency
122+
* @deprecated in favor of {@link #UnsatisfiedDependencyException(String, String, InjectionPoint, BeansException)}
87123
*/
124+
@Deprecated
88125
public UnsatisfiedDependencyException(
89126
String resourceDescription, String beanName, int ctorArgIndex, Class<?> ctorArgType, BeansException ex) {
90127

91128
this(resourceDescription, beanName, ctorArgIndex, ctorArgType, (ex != null ? ex.getMessage() : ""));
92129
initCause(ex);
93130
}
94131

132+
133+
/**
134+
* Return the injection point (field or method/constructor parameter), if known.
135+
* @since 4.3
136+
*/
137+
public InjectionPoint getInjectionPoint() {
138+
return this.injectionPoint;
139+
}
140+
95141
}

0 commit comments

Comments
 (0)