Skip to content

Commit 3ad796e

Browse files
committed
Keep getEffectiveAnnotatedParameter for user code compiled with javac 8
1 parent ac5dc69 commit 3ad796e

File tree

1 file changed

+42
-4
lines changed

1 file changed

+42
-4
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/annotation/ParameterResolutionDelegate.java

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.lang.annotation.Annotation;
2020
import java.lang.reflect.AnnotatedElement;
21+
import java.lang.reflect.Constructor;
2122
import java.lang.reflect.Executable;
2223
import java.lang.reflect.Parameter;
2324

@@ -29,6 +30,7 @@
2930
import org.springframework.core.annotation.SynthesizingMethodParameter;
3031
import org.springframework.lang.Nullable;
3132
import org.springframework.util.Assert;
33+
import org.springframework.util.ClassUtils;
3234

3335
/**
3436
* Public delegate for resolving autowirable parameters on externally managed
@@ -79,9 +81,10 @@ private ParameterResolutionDelegate() {
7981
*/
8082
public static boolean isAutowirable(Parameter parameter, int parameterIndex) {
8183
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));
8588
}
8689

8790
/**
@@ -122,7 +125,8 @@ public static Object resolveDependency(
122125
Assert.notNull(containingClass, "Containing class must not be null");
123126
Assert.notNull(beanFactory, "AutowireCapableBeanFactory must not be null");
124127

125-
Autowired autowired = AnnotatedElementUtils.findMergedAnnotation(parameter, Autowired.class);
128+
AnnotatedElement annotatedParameter = getEffectiveAnnotatedParameter(parameter, parameterIndex);
129+
Autowired autowired = AnnotatedElementUtils.findMergedAnnotation(annotatedParameter, Autowired.class);
126130
boolean required = (autowired == null || autowired.required());
127131

128132
MethodParameter methodParameter = SynthesizingMethodParameter.forExecutable(
@@ -132,4 +136,38 @@ public static Object resolveDependency(
132136
return beanFactory.resolveDependency(descriptor, null);
133137
}
134138

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 &lt; 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+
135173
}

0 commit comments

Comments
 (0)