From 3ad796e91374f5b0475bdbe93f444c975cb30cab Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 29 Sep 2021 09:56:29 +0200 Subject: [PATCH] Keep getEffectiveAnnotatedParameter for user code compiled with javac 8 --- .../ParameterResolutionDelegate.java | 46 +++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/ParameterResolutionDelegate.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/ParameterResolutionDelegate.java index 5f5d1a9d82f..f8f7b0dce6f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/ParameterResolutionDelegate.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/ParameterResolutionDelegate.java @@ -18,6 +18,7 @@ package org.springframework.beans.factory.annotation; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Parameter; @@ -29,6 +30,7 @@ import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; /** * Public delegate for resolving autowirable parameters on externally managed @@ -79,9 +81,10 @@ public final class ParameterResolutionDelegate { */ public static boolean isAutowirable(Parameter parameter, int parameterIndex) { Assert.notNull(parameter, "Parameter must not be null"); - return (AnnotatedElementUtils.hasAnnotation(parameter, Autowired.class) || - AnnotatedElementUtils.hasAnnotation(parameter, Qualifier.class) || - AnnotatedElementUtils.hasAnnotation(parameter, Value.class)); + AnnotatedElement annotatedParameter = getEffectiveAnnotatedParameter(parameter, parameterIndex); + return (AnnotatedElementUtils.hasAnnotation(annotatedParameter, Autowired.class) || + AnnotatedElementUtils.hasAnnotation(annotatedParameter, Qualifier.class) || + AnnotatedElementUtils.hasAnnotation(annotatedParameter, Value.class)); } /** @@ -122,7 +125,8 @@ public final class ParameterResolutionDelegate { Assert.notNull(containingClass, "Containing class must not be null"); Assert.notNull(beanFactory, "AutowireCapableBeanFactory must not be null"); - Autowired autowired = AnnotatedElementUtils.findMergedAnnotation(parameter, Autowired.class); + AnnotatedElement annotatedParameter = getEffectiveAnnotatedParameter(parameter, parameterIndex); + Autowired autowired = AnnotatedElementUtils.findMergedAnnotation(annotatedParameter, Autowired.class); boolean required = (autowired == null || autowired.required()); MethodParameter methodParameter = SynthesizingMethodParameter.forExecutable( @@ -132,4 +136,38 @@ public final class ParameterResolutionDelegate { return beanFactory.resolveDependency(descriptor, null); } + /** + * Due to a bug in {@code javac} on JDK versions prior to JDK 9, looking up + * annotations directly on a {@link Parameter} will fail for inner class + * constructors. + *

Note: Since Spring 6 may still encounter user code compiled with + * {@code javac 8}, this workaround is kept in place for the time being. + *

Bug in javac in JDK < 9

+ *

The parameter annotations array in the compiled byte code excludes an entry + * for the implicit enclosing instance parameter for an inner class + * constructor. + *

Workaround

+ *

This method provides a workaround for this off-by-one error by allowing the + * caller to access annotations on the preceding {@link Parameter} object (i.e., + * {@code index - 1}). If the supplied {@code index} is zero, this method returns + * an empty {@code AnnotatedElement}. + *

WARNING

+ *

The {@code AnnotatedElement} returned by this method should never be cast and + * treated as a {@code Parameter} since the metadata (e.g., {@link Parameter#getName()}, + * {@link Parameter#getType()}, etc.) will not match those for the declared parameter + * at the given index in an inner class constructor. + * @return the supplied {@code parameter} or the effective {@code Parameter} + * if the aforementioned bug is in effect + */ + private static AnnotatedElement getEffectiveAnnotatedParameter(Parameter parameter, int index) { + Executable executable = parameter.getDeclaringExecutable(); + if (executable instanceof Constructor && ClassUtils.isInnerClass(executable.getDeclaringClass()) && + executable.getParameterAnnotations().length == executable.getParameterCount() - 1) { + // Bug in javac in JDK <9: annotation array excludes enclosing instance parameter + // for inner classes, so access it with the actual parameter index lowered by 1 + return (index == 0 ? EMPTY_ANNOTATED_ELEMENT : executable.getParameters()[index - 1]); + } + return parameter; + } + }