21
21
import java .lang .invoke .MethodType ;
22
22
import java .lang .reflect .Constructor ;
23
23
import java .lang .reflect .Method ;
24
- import java .util . Arrays ;
24
+ import java .lang . reflect . Modifier ;
25
25
import java .util .Map ;
26
- import java .util .Optional ;
27
26
28
27
import org .aopalliance .intercept .MethodInterceptor ;
29
28
import org .aopalliance .intercept .MethodInvocation ;
30
29
import org .springframework .aop .ProxyMethodInvocation ;
30
+ import org .springframework .data .util .Lazy ;
31
31
import org .springframework .lang .Nullable ;
32
32
import org .springframework .util .ConcurrentReferenceHashMap ;
33
33
import org .springframework .util .ConcurrentReferenceHashMap .ReferenceType ;
@@ -85,13 +85,56 @@ private MethodHandle getMethodHandle(Method method) throws Exception {
85
85
*/
86
86
enum MethodHandleLookup {
87
87
88
+ /**
89
+ * Encapsulated {@link MethodHandle} lookup working on Java 9.
90
+ */
91
+ ENCAPSULATED {
92
+
93
+ private final @ Nullable Method privateLookupIn = ReflectionUtils .findMethod (MethodHandles .class ,
94
+ "privateLookupIn" , Class .class , Lookup .class );
95
+
96
+ /*
97
+ * (non-Javadoc)
98
+ * @see org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.MethodHandleLookup#lookup(java.lang.reflect.Method)
99
+ */
100
+ @ Override
101
+ MethodHandle lookup (Method method ) throws ReflectiveOperationException {
102
+
103
+ if (privateLookupIn == null ) {
104
+ throw new IllegalStateException ("Could not obtain MethodHandles.privateLookupIn!" );
105
+ }
106
+
107
+ return doLookup (method , getLookup (method .getDeclaringClass (), privateLookupIn ));
108
+ }
109
+
110
+ /*
111
+ * (non-Javadoc)
112
+ * @see org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.MethodHandleLookup#isAvailable()
113
+ */
114
+ @ Override
115
+ boolean isAvailable () {
116
+ return privateLookupIn != null ;
117
+ }
118
+
119
+ private Lookup getLookup (Class <?> declaringClass , Method privateLookupIn ) {
120
+
121
+ Lookup lookup = MethodHandles .lookup ();
122
+
123
+ try {
124
+ return (Lookup ) privateLookupIn .invoke (MethodHandles .class , declaringClass , lookup );
125
+ } catch (ReflectiveOperationException e ) {
126
+ return lookup ;
127
+ }
128
+ }
129
+ },
130
+
88
131
/**
89
132
* Open (via reflection construction of {@link MethodHandles.Lookup}) method handle lookup. Works with Java 8 and
90
133
* with Java 9 permitting illegal access.
91
134
*/
92
135
OPEN {
93
136
94
- private final Optional <Constructor <Lookup >> constructor = getLookupConstructor ( );
137
+ private final Lazy <Constructor <Lookup >> constructor = Lazy . of ( MethodHandleLookup :: getLookupConstructor );
95
138
96
139
/*
97
140
* (non-Javadoc)
@@ -100,8 +143,11 @@ enum MethodHandleLookup {
100
143
@ Override
101
144
MethodHandle lookup (Method method ) throws ReflectiveOperationException {
102
145
103
- Constructor <Lookup > constructor = this .constructor
104
- .orElseThrow (() -> new IllegalStateException ("Could not obtain MethodHandles.lookup constructor" ));
146
+ if (!isAvailable ()) {
147
+ throw new IllegalStateException ("Could not obtain MethodHandles.lookup constructor!" );
148
+ }
149
+
150
+ Constructor <Lookup > constructor = this .constructor .get ();
105
151
106
152
return constructor .newInstance (method .getDeclaringClass ()).unreflectSpecial (method , method .getDeclaringClass ());
107
153
}
@@ -112,29 +158,24 @@ MethodHandle lookup(Method method) throws ReflectiveOperationException {
112
158
*/
113
159
@ Override
114
160
boolean isAvailable () {
115
- return constructor .isPresent () ;
161
+ return constructor .orElse ( null ) != null ;
116
162
}
117
163
},
118
164
119
165
/**
120
- * Encapsulated {@link MethodHandle} lookup working on Java 9.
166
+ * Fallback {@link MethodHandle} lookup using {@link MethodHandles#lookup() public lookup}.
167
+ *
168
+ * @since 2.1
121
169
*/
122
- ENCAPSULATED {
123
-
124
- private final @ Nullable Method privateLookupIn = ReflectionUtils .findMethod (MethodHandles .class ,
125
- "privateLookupIn" , Class .class , Lookup .class );
170
+ FALLBACK {
126
171
127
172
/*
128
173
* (non-Javadoc)
129
174
* @see org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.MethodHandleLookup#lookup(java.lang.reflect.Method)
130
175
*/
131
176
@ Override
132
177
MethodHandle lookup (Method method ) throws ReflectiveOperationException {
133
-
134
- MethodType methodType = MethodType .methodType (method .getReturnType (), method .getParameterTypes ());
135
-
136
- return getLookup (method .getDeclaringClass (), privateLookupIn ).findSpecial (method .getDeclaringClass (),
137
- method .getName (), methodType , method .getDeclaringClass ());
178
+ return doLookup (method , MethodHandles .lookup ());
138
179
}
139
180
140
181
/*
@@ -145,22 +186,19 @@ MethodHandle lookup(Method method) throws ReflectiveOperationException {
145
186
boolean isAvailable () {
146
187
return true ;
147
188
}
189
+ };
148
190
149
- private Lookup getLookup (Class <?> declaringClass , @ Nullable Method privateLookupIn ) {
191
+ private static MethodHandle doLookup (Method method , Lookup lookup )
192
+ throws NoSuchMethodException , IllegalAccessException {
150
193
151
- if (privateLookupIn == null ) {
152
- return MethodHandles .lookup ();
153
- }
154
-
155
- Lookup lookup = MethodHandles .lookup ();
194
+ MethodType methodType = MethodType .methodType (method .getReturnType (), method .getParameterTypes ());
156
195
157
- try {
158
- return (Lookup ) privateLookupIn .invoke (MethodHandles .class , declaringClass , lookup );
159
- } catch (ReflectiveOperationException e ) {
160
- return lookup ;
161
- }
196
+ if (Modifier .isStatic (method .getModifiers ())) {
197
+ return lookup .findStatic (method .getDeclaringClass (), method .getName (), methodType );
162
198
}
163
- };
199
+
200
+ return lookup .findSpecial (method .getDeclaringClass (), method .getName (), methodType , method .getDeclaringClass ());
201
+ }
164
202
165
203
/**
166
204
* Lookup a {@link MethodHandle} given {@link Method} to look up.
@@ -184,26 +222,30 @@ private Lookup getLookup(Class<?> declaringClass, @Nullable Method privateLookup
184
222
*/
185
223
public static MethodHandleLookup getMethodHandleLookup () {
186
224
187
- return Arrays .stream (MethodHandleLookup .values ()) //
188
- .filter (it -> it .isAvailable ()) //
189
- .findFirst () //
190
- .orElseThrow (() -> new IllegalStateException ("No MethodHandleLookup available!" ));
225
+ for (MethodHandleLookup it : MethodHandleLookup .values ()) {
226
+
227
+ if (it .isAvailable ()) {
228
+ return it ;
229
+ }
230
+ }
231
+
232
+ throw new IllegalStateException ("No MethodHandleLookup available!" );
191
233
}
192
234
193
- private static Optional <Constructor <Lookup >> getLookupConstructor () {
235
+ @ Nullable
236
+ private static Constructor <Lookup > getLookupConstructor () {
194
237
195
238
try {
196
239
197
240
Constructor <Lookup > constructor = Lookup .class .getDeclaredConstructor (Class .class );
198
241
ReflectionUtils .makeAccessible (constructor );
199
242
200
- return Optional .of (constructor );
201
-
243
+ return constructor ;
202
244
} catch (Exception ex ) {
203
245
204
246
// this is the signal that we are on Java 9 (encapsulated) and can't use the accessible constructor approach.
205
247
if (ex .getClass ().getName ().equals ("java.lang.reflect.InaccessibleObjectException" )) {
206
- return Optional . empty () ;
248
+ return null ;
207
249
}
208
250
209
251
throw new IllegalStateException (ex );
0 commit comments