DATACMNS-1080 - Inject generated property accessor classes via ReflectUtils.defineClass(…).

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.
This commit is contained in:
Mark Paluch
2017-07-26 17:10:44 +02:00
committed by Oliver Gierke
parent 85fb08a797
commit 0ff80930a3

View File

@@ -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;
}
}
}