55
66import java .lang .invoke .MethodHandles ;
77import java .lang .reflect .Field ;
8+ import java .lang .reflect .InvocationTargetException ;
89import java .lang .reflect .Method ;
910import java .lang .reflect .Proxy ;
11+ import java .util .HashMap ;
12+ import java .util .Map ;
1013
1114/**
1215 * Utilities for reflections
1518 */
1619public class ReflectionUtils {
1720
18- private static final MethodHandles .Lookup lookup ;
19-
20- static {
21- try {
22- Field field = MethodHandles .Lookup .class .getDeclaredField ("IMPL_LOOKUP" );
23- field .setAccessible (true );
24-
25- lookup = (MethodHandles .Lookup ) field .get (null );
26- } catch (NoSuchFieldException | IllegalAccessException e ) {
27- throw new InvalidConfigException ("Could not get MethodHandles.Lookup" , e );
28- }
29- }
21+ private static final Map <Class <?>, MethodHandles .Lookup > lookups = new HashMap <>();
3022
3123 /**
3224 * Allows to get default value of method from interface
@@ -36,7 +28,7 @@ public class ReflectionUtils {
3628 public static Object getDefaultValue (Method method ) {
3729 try {
3830 Class <?> clazz = method .getDeclaringClass ();
39- return lookup
31+ return getLookup ( clazz )
4032 .in (clazz )
4133 .unreflectSpecial (method , clazz )
4234 .bindTo (createHelperProxy (method .getDeclaringClass ()))
@@ -46,6 +38,46 @@ public static Object getDefaultValue(Method method) {
4638 }
4739 }
4840
41+ /**
42+ * Creates private lookup for given class
43+ * For Java 8 it gets value of MethodHandles.Lookup.IMPL_LOOKUP, for newer versions invokes MethodHandles.privateLookupIn()
44+ * Reference: https://github.com/OpenFeign/feign/commit/3494a76f160d6622129d59a6c79358dbccf6e6d6
45+ * @param clazz for which you want to create lookup
46+ * @return instance of lookup
47+ */
48+ private static MethodHandles .Lookup createLookup (Class <?> clazz ) {
49+ boolean oldJava = isOldJava ();
50+
51+ try {
52+ if (oldJava ) {
53+ Field field = MethodHandles .Lookup .class .getDeclaredField ("IMPL_LOOKUP" );
54+ field .setAccessible (true );
55+
56+ return (MethodHandles .Lookup ) field .get (null );
57+ }
58+
59+ Object privateLookupIn = MethodHandles .class .getMethod ("privateLookupIn" , Class .class , MethodHandles .Lookup .class )
60+ .invoke (null , clazz , MethodHandles .lookup ());
61+
62+ return (MethodHandles .Lookup ) privateLookupIn ;
63+ } catch (IllegalAccessException | NoSuchFieldException | NoSuchMethodException | InvocationTargetException e ) {
64+ throw new InvalidConfigException ("Could not get MethodHandles.Lookup for " + clazz .getName () + " (legacy way: " + oldJava + ")" , e );
65+ }
66+ }
67+
68+ /**
69+ * Gets lookup for given class from cache or create new one if it doesn't exist
70+ * @param clazz for which you want to get lookup
71+ * @return instance of lookup for given class
72+ */
73+ private static MethodHandles .Lookup getLookup (Class <?> clazz ) {
74+ if (!lookups .containsKey (clazz )) {
75+ lookups .put (clazz , createLookup (clazz ));
76+ }
77+
78+ return lookups .get (clazz );
79+ }
80+
4981 /**
5082 * Creates instance of proxy
5183 * @param clazz class which you want to get instance of
@@ -56,6 +88,15 @@ private static Object createHelperProxy(Class<?> clazz) {
5688 (Object object , Method method , Object [] args ) -> null );
5789 }
5890
91+ /**
92+ * Check Java version
93+ * @return true for Java 8, false for Java 9 or newer
94+ */
95+ private static boolean isOldJava () {
96+ String javaVersion = System .getProperty ("java.version" );
97+ return javaVersion .startsWith ("1.8" ) || javaVersion .startsWith ("8" );
98+ }
99+
59100 /**
60101 * Check bukkit versions
61102 * @return false for Minecraft 1.12 or older, true for 1.13 or newer
0 commit comments