Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save mp911de/c1d92154479fe7c8248b15565f156aa8 to your computer and use it in GitHub Desktop.

Select an option

Save mp911de/c1d92154479fe7c8248b15565f156aa8 to your computer and use it in GitHub Desktop.
/*
* Copyright 2015-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.projection;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Optional;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.ProxyMethodInvocation;
import org.springframework.util.ReflectionUtils;
/**
* Method interceptor to invoke default methods on the repository proxy.
*
* @author Oliver Gierke
* @author Jens Schauder
* @author Mark Paluch
*/
public class DefaultMethodInvokingMethodInterceptor implements MethodInterceptor {
private final MethodHandleLookup methodHandleLookup = MethodHandleLookup.getMethodHandleLookup();
/*
* (non-Javadoc)
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
*/
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
if (!method.isDefault()) {
return invocation.proceed();
}
Object[] arguments = invocation.getArguments();
Object proxy = ((ProxyMethodInvocation) invocation).getProxy();
return methodHandleLookup.lookup(method).bindTo(proxy).invokeWithArguments(arguments);
}
/**
* Strategies for MethodHandle lookup.
*/
enum MethodHandleLookup {
PreJava9 {
private final Optional<Constructor<Lookup>> constructor = getLookupConstructor();
@Override
MethodHandle lookup(Method method) throws ReflectiveOperationException {
Constructor<Lookup> constructor = this.constructor
.orElseThrow(() -> new IllegalStateException("Could not obtain MethodHandles.lookup constructor"));
return constructor.newInstance(method.getDeclaringClass()).unreflectSpecial(method, method.getDeclaringClass());
}
@Override
boolean isAvailable() {
return constructor.isPresent();
}
},
PostJava9 {
@Override
MethodHandle lookup(Method method) throws ReflectiveOperationException {
MethodType methodType = MethodType.methodType(method.getReturnType(), method.getParameterTypes());
return MethodHandles.lookup().findSpecial(method.getDeclaringClass(), method.getName(), methodType,
method.getDeclaringClass());
}
@Override
boolean isAvailable() {
return true;
}
};
/**
* Lookup a {@link MethodHandle} given {@link Method} to look up.
*
* @param method must not be {@literal null}.
* @return the method handle.
* @throws ReflectiveOperationException
*/
abstract MethodHandle lookup(Method method) throws ReflectiveOperationException;
/**
* @return {@literal true} if the lookup is available.
*/
abstract boolean isAvailable();
/**
* Obtain the first available {@link MethodHandleLookup}.
*
* @return the {@link MethodHandleLookup}
* @throws IllegalStateException if no {@link MethodHandleLookup} is available.
*/
public static MethodHandleLookup getMethodHandleLookup() {
for (MethodHandleLookup lookup : MethodHandleLookup.values()) {
if (lookup.isAvailable()) {
return lookup;
}
}
throw new IllegalStateException("No MethodHandleLookup available!");
}
private static Optional<Constructor<Lookup>> getLookupConstructor() {
try {
Constructor<Lookup> constructor = Lookup.class.getDeclaredConstructor(Class.class);
ReflectionUtils.makeAccessible(constructor);
return Optional.of(constructor);
} catch (Exception ex) {
// this is the signal that we are on Java 9 and can't use the accessible constructor approach.
if (ex.getClass().getName().equals("java.lang.reflect.InaccessibleObjectException")) {
return Optional.empty();
}
throw new IllegalStateException(ex);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment