18
18
19
19
import java .lang .annotation .Annotation ;
20
20
import java .lang .reflect .AnnotatedElement ;
21
+ import java .lang .reflect .Constructor ;
21
22
import java .lang .reflect .Executable ;
22
23
import java .lang .reflect .Parameter ;
23
24
29
30
import org .springframework .core .annotation .SynthesizingMethodParameter ;
30
31
import org .springframework .lang .Nullable ;
31
32
import org .springframework .util .Assert ;
33
+ import org .springframework .util .ClassUtils ;
32
34
33
35
/**
34
36
* Public delegate for resolving autowirable parameters on externally managed
@@ -79,9 +81,10 @@ private ParameterResolutionDelegate() {
79
81
*/
80
82
public static boolean isAutowirable (Parameter parameter , int parameterIndex ) {
81
83
Assert .notNull (parameter , "Parameter must not be null" );
82
- return (AnnotatedElementUtils .hasAnnotation (parameter , Autowired .class ) ||
83
- AnnotatedElementUtils .hasAnnotation (parameter , Qualifier .class ) ||
84
- AnnotatedElementUtils .hasAnnotation (parameter , Value .class ));
84
+ AnnotatedElement annotatedParameter = getEffectiveAnnotatedParameter (parameter , parameterIndex );
85
+ return (AnnotatedElementUtils .hasAnnotation (annotatedParameter , Autowired .class ) ||
86
+ AnnotatedElementUtils .hasAnnotation (annotatedParameter , Qualifier .class ) ||
87
+ AnnotatedElementUtils .hasAnnotation (annotatedParameter , Value .class ));
85
88
}
86
89
87
90
/**
@@ -122,7 +125,8 @@ public static Object resolveDependency(
122
125
Assert .notNull (containingClass , "Containing class must not be null" );
123
126
Assert .notNull (beanFactory , "AutowireCapableBeanFactory must not be null" );
124
127
125
- Autowired autowired = AnnotatedElementUtils .findMergedAnnotation (parameter , Autowired .class );
128
+ AnnotatedElement annotatedParameter = getEffectiveAnnotatedParameter (parameter , parameterIndex );
129
+ Autowired autowired = AnnotatedElementUtils .findMergedAnnotation (annotatedParameter , Autowired .class );
126
130
boolean required = (autowired == null || autowired .required ());
127
131
128
132
MethodParameter methodParameter = SynthesizingMethodParameter .forExecutable (
@@ -132,4 +136,38 @@ public static Object resolveDependency(
132
136
return beanFactory .resolveDependency (descriptor , null );
133
137
}
134
138
139
+ /**
140
+ * Due to a bug in {@code javac} on JDK versions prior to JDK 9, looking up
141
+ * annotations directly on a {@link Parameter} will fail for inner class
142
+ * constructors.
143
+ * <p>Note: Since Spring 6 may still encounter user code compiled with
144
+ * {@code javac 8}, this workaround is kept in place for the time being.
145
+ * <h4>Bug in javac in JDK < 9</h4>
146
+ * <p>The parameter annotations array in the compiled byte code excludes an entry
147
+ * for the implicit <em>enclosing instance</em> parameter for an inner class
148
+ * constructor.
149
+ * <h4>Workaround</h4>
150
+ * <p>This method provides a workaround for this off-by-one error by allowing the
151
+ * caller to access annotations on the preceding {@link Parameter} object (i.e.,
152
+ * {@code index - 1}). If the supplied {@code index} is zero, this method returns
153
+ * an empty {@code AnnotatedElement}.
154
+ * <h4>WARNING</h4>
155
+ * <p>The {@code AnnotatedElement} returned by this method should never be cast and
156
+ * treated as a {@code Parameter} since the metadata (e.g., {@link Parameter#getName()},
157
+ * {@link Parameter#getType()}, etc.) will not match those for the declared parameter
158
+ * at the given index in an inner class constructor.
159
+ * @return the supplied {@code parameter} or the <em>effective</em> {@code Parameter}
160
+ * if the aforementioned bug is in effect
161
+ */
162
+ private static AnnotatedElement getEffectiveAnnotatedParameter (Parameter parameter , int index ) {
163
+ Executable executable = parameter .getDeclaringExecutable ();
164
+ if (executable instanceof Constructor && ClassUtils .isInnerClass (executable .getDeclaringClass ()) &&
165
+ executable .getParameterAnnotations ().length == executable .getParameterCount () - 1 ) {
166
+ // Bug in javac in JDK <9: annotation array excludes enclosing instance parameter
167
+ // for inner classes, so access it with the actual parameter index lowered by 1
168
+ return (index == 0 ? EMPTY_ANNOTATED_ELEMENT : executable .getParameters ()[index - 1 ]);
169
+ }
170
+ return parameter ;
171
+ }
172
+
135
173
}
0 commit comments