From 0ff80930a38dc4f3e94b24b694e94cb25d30bb32 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 26 Jul 2017 17:10:44 +0200 Subject: [PATCH] =?UTF-8?q?DATACMNS-1080=20-=20Inject=20generated=20proper?= =?UTF-8?q?ty=20accessor=20classes=20via=20ReflectUtils.defineClass(?= =?UTF-8?q?=E2=80=A6).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now use CGLibs ReflectUtils.defineClass(…) to inject classes into the entity's class loader instead of our own code. ReflectUtils itself discovers whether the class loader is encapsulated and falls back to Unsafe for class definition. We no longer require our own code so the evil class was removed. Original pull request: #234. --- ...lassGeneratingPropertyAccessorFactory.java | 64 ++++--------------- 1 file changed, 13 insertions(+), 51 deletions(-) diff --git a/src/main/java/org/springframework/data/mapping/model/ClassGeneratingPropertyAccessorFactory.java b/src/main/java/org/springframework/data/mapping/model/ClassGeneratingPropertyAccessorFactory.java index 00323bb0c..658142a1a 100644 --- a/src/main/java/org/springframework/data/mapping/model/ClassGeneratingPropertyAccessorFactory.java +++ b/src/main/java/org/springframework/data/mapping/model/ClassGeneratingPropertyAccessorFactory.java @@ -19,7 +19,6 @@ import static org.springframework.asm.Opcodes.*; import lombok.NonNull; import lombok.RequiredArgsConstructor; -import lombok.experimental.UtilityClass; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -43,6 +42,7 @@ import org.springframework.asm.Label; import org.springframework.asm.MethodVisitor; import org.springframework.asm.Opcodes; import org.springframework.asm.Type; +import org.springframework.cglib.core.ReflectUtils; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mapping.PersistentPropertyAccessor; @@ -52,6 +52,7 @@ import org.springframework.data.util.Optionals; import org.springframework.data.util.TypeInformation; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; /** * A factory that can generate byte code to speed-up dynamic property access. Uses the {@link PersistentEntity}'s @@ -118,17 +119,17 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert Assert.notNull(entity, "PersistentEntity must not be null!"); - return isClassLoaderDefineClassAccessible(entity) && isTypeInjectable(entity) && hasUniquePropertyHashCodes(entity); + return isClassLoaderDefineClassAvailable(entity) && isTypeInjectable(entity) && hasUniquePropertyHashCodes(entity); } - private static boolean isClassLoaderDefineClassAccessible(PersistentEntity entity) { + private static boolean isClassLoaderDefineClassAvailable(PersistentEntity entity) { try { - Evil.getClassLoaderMethod(entity); - } catch (Exception o_O) { + return ReflectionUtils.findMethod(entity.getType().getClassLoader().getClass(), "defineClass", String.class, + byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class) != null; + } catch (Exception e) { return false; } - return true; } private static boolean isTypeInjectable(PersistentEntity entity) { @@ -295,8 +296,13 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert String className = generateClassName(entity); byte[] bytecode = generateBytecode(className.replace('.', '/'), entity); + Class type = entity.getType(); - return Evil.defineClass(className, bytecode, 0, bytecode.length, entity); + try { + return ReflectUtils.defineClass(className, bytecode, type.getClassLoader(), type.getProtectionDomain()); + } catch (Exception e) { + throw new IllegalStateException(e); + } } /** @@ -1365,48 +1371,4 @@ public class ClassGeneratingPropertyAccessorFactory implements PersistentPropert return hash < o.hash ? -1 : hash == o.hash ? 0 : 1; } } - - /** - * Yep, the name tells the truth. This little guy registers a class in the class loader of the - * {@link PersistentEntity} to allow protected and package-default access as protected/package-default members must be - * accessed from a class in the same class loader. - * - * @author Mark Paluch - * @author Oliver Gierke - */ - @UtilityClass - private static class Evil { - - /** - * Define a Class in the {@link ClassLoader} of the {@link PersistentEntity} type. - */ - Class defineClass(String name, byte[] bytes, int offset, int len, PersistentEntity persistentEntity) { - - ClassLoader classLoader = persistentEntity.getType().getClassLoader(); - - try { - - Method defineClass = getClassLoaderMethod(persistentEntity); - - return (Class) defineClass.invoke(classLoader, name, bytes, offset, len, - persistentEntity.getClass().getProtectionDomain()); - - } catch (ReflectiveOperationException e) { - throw new IllegalStateException(e); - } - } - - static Method getClassLoaderMethod(PersistentEntity entity) { - - ClassLoader classLoader = entity.getType().getClassLoader(); - Class classLoaderClass = classLoader.getClass(); - - Method defineClass = org.springframework.data.util.ReflectionUtils.findRequiredMethod(classLoaderClass, - "defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE, ProtectionDomain.class); - - defineClass.setAccessible(true); - - return defineClass; - } - } }