-
Notifications
You must be signed in to change notification settings - Fork 38.6k
Description
Sam Brannen opened SPR-16652 and commented
Overview
An issue in JUnit Jupiter has brought it to my attention that there is a bug in javac
in JDK versions prior to JDK 9. The bug was fixed in JDK 9 (see linked JDK bug issues below).
JDK Bugs
For example, if an inner class is compiled using javac
on JDK 8 and its constructor has parameters that are annotated, then a lookup for such annotations via java.lang.reflect.Parameter
might fail with an exception similar to the following (if the constructor declares only one argument).
Caused by: java.lang.ArrayIndexOutOfBoundsException: 1
at java.lang.reflect.Parameter.getDeclaredAnnotations(Parameter.java:305)
at java.lang.reflect.Parameter.declaredAnnotations(Parameter.java:342)
at java.lang.reflect.Parameter.getAnnotation(Parameter.java:287)
at java.lang.reflect.AnnotatedElement.isAnnotationPresent(AnnotatedElement.java:258)
If the constructor declares multiple arguments, looking up an annotation on a parameter will actually search for the annotation on the parameter to its left in the constructor signature.
The following is a quote from one of the JDK bugs explaining the root cause.
Root cause is javac generates RuntimeVisibleParameterAnnotations without an annotation slot for the synthetic/mandated parameters.
Workaround
For JUnit Jupiter I have come up with the following workaround for finding the AnnotatedElement
on which annotations should be searched.
private AnnotatedElement getAnnotatedElement() {
Executable executable = getDeclaringExecutable();
// Take into account a bug in javac in JDK 8:
if (executable instanceof Constructor //
&& isInnerClass(executable.getDeclaringClass()) //
&& (executable.getParameters().length == executable.getParameterAnnotations().length + 1)) {
return executable.getParameters()[this.index - 1];
}
return this.parameter;
}
Note that the above code snippet comes from the implementation of ParameterContext
in JUnit Jupiter which is somewhat analogous to Spring's MethodParameter
.
Also, please keep in mind that the Parameter
returned by the above method could literally be the Parameter
for the preceding constructor parameter... except that it sees the annotations from the subsequent parameter. Thus, it should only be used to look up annotations: it should not be used to retrieve the parameter's name, type, etc.
Deliverables
- Revise annotation lookup methods in
MethodParameter
so that annotations are properly discovered on parameters in constructors for inner classes compiled using JDK < 9.
Affects: 4.3.14, 5.0.4
Issue Links:
- Cannot autowire individual constructor argument in @Nested test class [SPR-16653] #21194 Cannot autowire individual constructor argument in
@Nested
test class ("is depended on by") - Generic constructor argument (e.g. ObjectProvider) fails to resolve for inner class [SPR-16734] #21275 Generic constructor argument (e.g. ObjectProvider) fails to resolve for inner class
Referenced from: commits 9244090, 5f6b042, 4b9e3a9, 53d0139
Backported to: 4.3.15