|
20 | 20 | import java.lang.invoke.MethodHandles; |
21 | 21 | import java.lang.invoke.MethodHandles.Lookup; |
22 | 22 | import java.lang.invoke.MethodType; |
23 | | -import java.lang.reflect.Constructor; |
24 | 23 | import java.lang.reflect.Method; |
| 24 | +import java.lang.reflect.Modifier; |
25 | 25 | import java.util.Map; |
26 | | -import java.util.function.Supplier; |
27 | 26 |
|
28 | 27 | import org.aopalliance.intercept.MethodInterceptor; |
29 | 28 | import org.aopalliance.intercept.MethodInvocation; |
|
32 | 31 | import org.springframework.aop.ProxyMethodInvocation; |
33 | 32 | import org.springframework.util.ConcurrentReferenceHashMap; |
34 | 33 | import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType; |
35 | | -import org.springframework.util.ReflectionUtils; |
36 | 34 |
|
37 | 35 | /** |
38 | | - * Method interceptor to invoke default methods on the gateway proxy. |
| 36 | + * Method interceptor to invoke default methods from the interfaces on the proxy. |
39 | 37 | * <p> |
40 | 38 | * The copy of {@code DefaultMethodInvokingMethodInterceptor} from Spring Data Commons. |
41 | 39 | * |
|
46 | 44 | * |
47 | 45 | * @since 5.3 |
48 | 46 | */ |
49 | | -class DefaultMethodInvokingMethodInterceptor implements MethodInterceptor { |
| 47 | +public class DefaultMethodInvokingMethodInterceptor implements MethodInterceptor { |
50 | 48 |
|
51 | | - private final MethodHandleLookup methodHandleLookup = MethodHandleLookup.getMethodHandleLookup(); |
| 49 | + private static final Lookup LOOKUP = MethodHandles.lookup(); |
52 | 50 |
|
53 | 51 | private final Map<Method, MethodHandle> methodHandleCache = |
54 | 52 | new ConcurrentReferenceHashMap<>(10, ReferenceType.WEAK); |
55 | 53 |
|
56 | 54 | @Override |
57 | | - public @Nullable Object invoke(MethodInvocation invocation) throws Throwable { // NOSONAR |
| 55 | + public @Nullable Object invoke(MethodInvocation invocation) throws Throwable { |
58 | 56 | Method method = invocation.getMethod(); |
59 | 57 | if (!method.isDefault()) { |
60 | 58 | return invocation.proceed(); |
61 | 59 | } |
62 | | - @Nullable Object[] arguments = invocation.getArguments(); |
63 | 60 | Object proxy = ((ProxyMethodInvocation) invocation).getProxy(); |
| 61 | + @Nullable Object[] arguments = invocation.getArguments(); |
64 | 62 | return getMethodHandle(method) |
65 | 63 | .bindTo(proxy) |
66 | 64 | .invokeWithArguments(arguments); |
67 | 65 | } |
68 | 66 |
|
69 | | - private MethodHandle getMethodHandle(Method method) { |
70 | | - return this.methodHandleCache.computeIfAbsent(method, |
71 | | - (key) -> { |
72 | | - try { |
73 | | - return this.methodHandleLookup.lookup(key); |
74 | | - } |
75 | | - catch (ReflectiveOperationException ex) { |
76 | | - throw new IllegalStateException(ex); |
77 | | - } |
78 | | - }); |
| 67 | + private MethodHandle getMethodHandle(Method method) throws Exception { |
| 68 | + return this.methodHandleCache.computeIfAbsent(method, DefaultMethodInvokingMethodInterceptor::lookup); |
79 | 69 | } |
80 | 70 |
|
81 | | - enum MethodHandleLookup { |
82 | | - |
83 | | - /** |
84 | | - * Encapsulated {@link MethodHandle} lookup working on Java 9. |
85 | | - */ |
86 | | - ENCAPSULATED { |
87 | | - |
88 | | - @Nullable |
89 | | - private final transient Method privateLookupIn = |
90 | | - ReflectionUtils.findMethod(MethodHandles.class, "privateLookupIn", Class.class, Lookup.class); |
91 | | - |
92 | | - @Override |
93 | | - MethodHandle lookup(Method method) throws ReflectiveOperationException { |
94 | | - if (this.privateLookupIn == null) { |
95 | | - throw new IllegalStateException("Could not obtain MethodHandles.privateLookupIn!"); |
96 | | - } |
97 | | - return doLookup(method, getLookup(method.getDeclaringClass(), this.privateLookupIn)); |
98 | | - } |
99 | | - |
100 | | - @Override |
101 | | - boolean isAvailable() { |
102 | | - return this.privateLookupIn != null; |
103 | | - } |
104 | | - |
105 | | - private Lookup getLookup(Class<?> declaringClass, Method privateLookupIn) { |
106 | | - Lookup lookup = MethodHandles.lookup(); |
107 | | - try { |
108 | | - return (Lookup) privateLookupIn.invoke(MethodHandles.class, declaringClass, lookup); |
109 | | - } |
110 | | - catch (ReflectiveOperationException e) { |
111 | | - return lookup; |
112 | | - } |
113 | | - } |
114 | | - |
115 | | - }, |
116 | | - |
117 | | - /** |
118 | | - * Open (via reflection construction of {@link Lookup}) method handle lookup. Works with Java 8 and |
119 | | - * with Java 9 permitting illegal access. |
120 | | - */ |
121 | | - OPEN { |
122 | | - |
123 | | - private volatile boolean constructorResolved; |
124 | | - |
125 | | - private transient @Nullable Constructor<Lookup> constructor; |
126 | | - |
127 | | - private final Supplier<@Nullable Constructor<Lookup>> constructorSupplier = |
128 | | - () -> { |
129 | | - if (!this.constructorResolved) { |
130 | | - Constructor<Lookup> ctor = null; |
131 | | - try { |
132 | | - ctor = Lookup.class.getDeclaredConstructor(Class.class); |
133 | | - ReflectionUtils.makeAccessible(ctor); |
134 | | - } |
135 | | - catch (Exception ex) { |
136 | | - // this is the signal that we are on Java 9 (encapsulated) and can't use the accessible |
137 | | - // constructor approach. |
138 | | - if (!ex.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) { |
139 | | - throw new IllegalStateException(ex); |
140 | | - } |
141 | | - } |
142 | | - this.constructor = ctor; |
143 | | - this.constructorResolved = true; |
144 | | - } |
145 | | - return this.constructor; |
146 | | - }; |
147 | | - |
148 | | - @Override |
149 | | - MethodHandle lookup(Method method) throws ReflectiveOperationException { |
150 | | - Constructor<Lookup> lookupConstructor = this.constructorSupplier.get(); |
151 | | - if (lookupConstructor != null) { |
152 | | - return lookupConstructor.newInstance(method.getDeclaringClass()) |
153 | | - .unreflectSpecial(method, method.getDeclaringClass()); |
154 | | - } |
155 | | - else { |
156 | | - throw new IllegalStateException("Could not obtain MethodHandles.lookup constructor!"); |
157 | | - } |
158 | | - } |
159 | | - |
160 | | - @Override |
161 | | - boolean isAvailable() { |
162 | | - return this.constructorSupplier.get() != null; |
163 | | - } |
164 | | - |
165 | | - }, |
166 | | - |
167 | | - /** |
168 | | - * Fallback {@link MethodHandle} lookup using {@link MethodHandles#lookup() public lookup}. |
169 | | - */ |
170 | | - FALLBACK { |
171 | | - @Override |
172 | | - MethodHandle lookup(Method method) throws ReflectiveOperationException { |
173 | | - return doLookup(method, MethodHandles.lookup()); |
174 | | - } |
175 | | - |
176 | | - @Override |
177 | | - boolean isAvailable() { |
178 | | - return true; |
179 | | - } |
180 | | - |
181 | | - }; |
182 | | - |
183 | | - private static MethodHandle doLookup(Method method, Lookup lookup) throws ReflectiveOperationException { |
| 71 | + /** |
| 72 | + * Lookup a {@link MethodHandle} given {@link Method} to look up. |
| 73 | + * @param method must not be {@literal null}. |
| 74 | + * @return the method handle. |
| 75 | + */ |
| 76 | + private static MethodHandle lookup(Method method) { |
| 77 | + try { |
| 78 | + Class<?> declaringClass = method.getDeclaringClass(); |
| 79 | + Lookup lookup = MethodHandles.privateLookupIn(declaringClass, LOOKUP); |
184 | 80 | MethodType methodType = MethodType.methodType(method.getReturnType(), method.getParameterTypes()); |
185 | | - return lookup.findSpecial(method.getDeclaringClass(), method.getName(), |
186 | | - methodType, method.getDeclaringClass()); |
187 | | - } |
188 | | - |
189 | | - abstract MethodHandle lookup(Method method) throws ReflectiveOperationException; |
190 | 81 |
|
191 | | - /** |
192 | | - * @return {@literal true} if the lookup is available. |
193 | | - */ |
194 | | - abstract boolean isAvailable(); |
195 | | - |
196 | | - /** |
197 | | - * Obtain the first available {@link MethodHandleLookup}. |
198 | | - * @return the {@link MethodHandleLookup} |
199 | | - * @throws IllegalStateException if no {@link MethodHandleLookup} is available. |
200 | | - */ |
201 | | - static MethodHandleLookup getMethodHandleLookup() { |
202 | | - for (MethodHandleLookup it : MethodHandleLookup.values()) { |
203 | | - if (it.isAvailable()) { |
204 | | - return it; |
205 | | - } |
206 | | - } |
207 | | - throw new IllegalStateException("No MethodHandleLookup available!"); |
| 82 | + return Modifier.isStatic(method.getModifiers()) |
| 83 | + ? lookup.findStatic(declaringClass, method.getName(), methodType) |
| 84 | + : lookup.findSpecial(declaringClass, method.getName(), methodType, declaringClass); |
| 85 | + } |
| 86 | + catch (Exception ex) { |
| 87 | + throw new IllegalStateException(ex); |
208 | 88 | } |
209 | | - |
210 | 89 | } |
211 | 90 |
|
212 | 91 | } |
0 commit comments