1
1
/*
2
- * Copyright 2002-2016 the original author or authors.
2
+ * Copyright 2002-2018 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
18
18
19
19
import java .lang .annotation .Annotation ;
20
20
import java .lang .reflect .AnnotatedElement ;
21
+ import java .lang .reflect .Constructor ;
22
+ import java .lang .reflect .Executable ;
21
23
import java .lang .reflect .Parameter ;
22
24
import java .util .Optional ;
23
25
32
34
import org .springframework .core .annotation .AnnotatedElementUtils ;
33
35
import org .springframework .core .annotation .SynthesizingMethodParameter ;
34
36
import org .springframework .lang .Nullable ;
37
+ import org .springframework .util .ClassUtils ;
35
38
36
39
/**
37
40
* Collection of utilities related to autowiring of individual method parameters.
43
46
*/
44
47
abstract class ParameterAutowireUtils {
45
48
49
+ private static final AnnotatedElement EMPTY_ANNOTATED_ELEMENT = new AnnotatedElement () {
50
+
51
+ @ Override
52
+ public <T extends Annotation > T getAnnotation (Class <T > annotationClass ) {
53
+ return null ;
54
+ }
55
+
56
+ @ Override
57
+ public Annotation [] getAnnotations () {
58
+ return new Annotation [0 ];
59
+ }
60
+
61
+ @ Override
62
+ public Annotation [] getDeclaredAnnotations () {
63
+ return new Annotation [0 ];
64
+ }
65
+ };
66
+
67
+
68
+
46
69
private ParameterAutowireUtils () {
47
70
/* no-op */
48
71
}
@@ -54,13 +77,18 @@ private ParameterAutowireUtils() {
54
77
* {@link ApplicationContext} (or a sub-type thereof) or is annotated or
55
78
* meta-annotated with {@link Autowired @Autowired},
56
79
* {@link Qualifier @Qualifier}, or {@link Value @Value}.
80
+ * @param parameter the parameter whose dependency should be autowired
81
+ * @param parameterIndex the index of the parameter
57
82
* @see #resolveDependency(Parameter, Class, ApplicationContext)
58
83
*/
59
- static boolean isAutowirable (Parameter parameter ) {
60
- return ApplicationContext .class .isAssignableFrom (parameter .getType ())
61
- || AnnotatedElementUtils .hasAnnotation (parameter , Autowired .class )
62
- || AnnotatedElementUtils .hasAnnotation (parameter , Qualifier .class )
63
- || AnnotatedElementUtils .hasAnnotation (parameter , Value .class );
84
+ static boolean isAutowirable (Parameter parameter , int parameterIndex ) {
85
+ if (ApplicationContext .class .isAssignableFrom (parameter .getType ())) {
86
+ return true ;
87
+ }
88
+ AnnotatedElement annotatedParameter = getEffectiveAnnotatedParameter (parameter , parameterIndex );
89
+ return AnnotatedElementUtils .hasAnnotation (annotatedParameter , Autowired .class )
90
+ || AnnotatedElementUtils .hasAnnotation (annotatedParameter , Qualifier .class )
91
+ || AnnotatedElementUtils .hasAnnotation (annotatedParameter , Value .class );
64
92
}
65
93
66
94
/**
@@ -77,6 +105,7 @@ static boolean isAutowirable(Parameter parameter) {
77
105
* <p>If an explicit <em>qualifier</em> is not declared, the name of the parameter
78
106
* will be used as the qualifier for resolving ambiguities.
79
107
* @param parameter the parameter whose dependency should be resolved
108
+ * @param parameterIndex the index of the parameter
80
109
* @param containingClass the concrete class that contains the parameter; this may
81
110
* differ from the class that declares the parameter in that it may be a subclass
82
111
* thereof, potentially substituting type variables
@@ -90,8 +119,9 @@ static boolean isAutowirable(Parameter parameter) {
90
119
* @see AutowireCapableBeanFactory#resolveDependency(DependencyDescriptor, String)
91
120
*/
92
121
@ Nullable
93
- static Object resolveDependency (Parameter parameter , Class <?> containingClass , ApplicationContext applicationContext ) {
94
- boolean required = findMergedAnnotation (parameter , Autowired .class ).map (Autowired ::required ).orElse (true );
122
+ static Object resolveDependency (Parameter parameter , int parameterIndex , Class <?> containingClass , ApplicationContext applicationContext ) {
123
+ AnnotatedElement annotatedParameter = getEffectiveAnnotatedParameter (parameter , parameterIndex );
124
+ boolean required = findMergedAnnotation (annotatedParameter , Autowired .class ).map (Autowired ::required ).orElse (true );
95
125
MethodParameter methodParameter = SynthesizingMethodParameter .forParameter (parameter );
96
126
DependencyDescriptor descriptor = new DependencyDescriptor (methodParameter , required );
97
127
descriptor .setContainingClass (containingClass );
@@ -102,4 +132,44 @@ private static <A extends Annotation> Optional<A> findMergedAnnotation(Annotated
102
132
return Optional .ofNullable (AnnotatedElementUtils .findMergedAnnotation (element , annotationType ));
103
133
}
104
134
135
+ /**
136
+ * Due to a bug in {@code javac} on JDK versions prior to JDK 9, looking up
137
+ * annotations directly on a {@link Parameter} will fail for inner class
138
+ * constructors.
139
+ *
140
+ * <h4>Bug in javac in JDK < 9</h4>
141
+ * <p>The parameter annotations array in the compiled byte code excludes an entry
142
+ * for the implicit <em>enclosing instance</em> parameter for an inner class
143
+ * constructor.
144
+ *
145
+ * <h4>Workaround</h4>
146
+ * <p>This method provides a workaround for this off-by-one error by allowing the
147
+ * caller to access annotations on the preceding {@link Parameter} object (i.e.,
148
+ * {@code index - 1}). If the supplied {@code index} is zero, this method returns
149
+ * an empty {@code AnnotatedElement}.
150
+ *
151
+ * <h4>WARNING</h4>
152
+ * <p>The {@code AnnotatedElement} returned by this method should never be cast and
153
+ * treated as a {@code Parameter} since the metadata (e.g., {@link Parameter#getName()},
154
+ * {@link Parameter#getType()}, etc.) will not match those for the declared parameter
155
+ * at the given index in an inner class constructor.
156
+ *
157
+ * @return the supplied {@code parameter} or the <em>effective</em> {@code Parameter}
158
+ * if the aforementioned bug is in effect
159
+ */
160
+ private static AnnotatedElement getEffectiveAnnotatedParameter (Parameter parameter , int index ) {
161
+ Executable executable = parameter .getDeclaringExecutable ();
162
+
163
+ if (executable instanceof Constructor &&
164
+ ClassUtils .isInnerClass (executable .getDeclaringClass ()) &&
165
+ executable .getParameterAnnotations ().length == executable .getParameterCount () - 1 ) {
166
+
167
+ // Bug in javac in JDK <9: annotation array excludes enclosing instance parameter
168
+ // for inner classes, so access it with the actual parameter index lowered by 1
169
+ return (index == 0 ) ? EMPTY_ANNOTATED_ELEMENT : executable .getParameters ()[index - 1 ];
170
+ }
171
+
172
+ return parameter ;
173
+ }
174
+
105
175
}
0 commit comments