Skip to content

Annotation lookup on parameter in inner class constructor fails when using javac from JDK versions prior to 9 [SPR-16652] #21193

@spring-projects-issues

Description

@spring-projects-issues

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:

Referenced from: commits 9244090, 5f6b042, 4b9e3a9, 53d0139

Backported to: 4.3.15

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)status: backportedAn issue that has been backported to maintenance branchestype: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions