Browse Source

Reuse generated entity instantiators from `ClassGeneratingEntityInstantiator` instances.

We now reuse generated ObjectInstantiator classes instead of attempting to redefine classes with the same name. Redefinition leads to LinkageError and thus to the use of reflection-based instantiators and an increased allocation rate due to the failed attempt of class redeclaration.

Closes: #2446.
2.4.x
Mark Paluch 4 years ago
parent
commit
bd280f8dfd
No known key found for this signature in database
GPG Key ID: 4406B84C1661DCD1
  1. 23
      src/main/java/org/springframework/data/mapping/model/ClassGeneratingEntityInstantiator.java
  2. 26
      src/test/java/org/springframework/data/mapping/model/ClassGeneratingEntityInstantiatorUnitTests.java

23
src/main/java/org/springframework/data/mapping/model/ClassGeneratingEntityInstantiator.java

@ -124,6 +124,13 @@ class ClassGeneratingEntityInstantiator implements EntityInstantiator { @@ -124,6 +124,13 @@ class ClassGeneratingEntityInstantiator implements EntityInstantiator {
try {
return doCreateEntityInstantiator(entity);
} catch (Throwable ex) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(
String.format("Cannot create entity instantiator for %s. Falling back to ReflectionEntityInstantiator.",
entity.getName()),
ex);
}
return ReflectionEntityInstantiator.INSTANCE;
}
}
@ -336,12 +343,22 @@ class ClassGeneratingEntityInstantiator implements EntityInstantiator { @@ -336,12 +343,22 @@ class ClassGeneratingEntityInstantiator implements EntityInstantiator {
@Nullable PreferredConstructor<?, ?> constructor) {
String className = generateClassName(entity);
byte[] bytecode = generateBytecode(className, entity, constructor);
Class<?> type = entity.getType();
ClassLoader classLoader = type.getClassLoader();
if (ClassUtils.isPresent(className, classLoader)) {
try {
return ClassUtils.forName(className, classLoader);
} catch (Exception o_O) {
throw new IllegalStateException(o_O);
}
}
byte[] bytecode = generateBytecode(className, entity, constructor);
try {
return ReflectUtils.defineClass(className, bytecode, type.getClassLoader(), type.getProtectionDomain(), type);
return ReflectUtils.defineClass(className, bytecode, classLoader, type.getProtectionDomain(), type);
} catch (Exception e) {
throw new IllegalStateException(e);
}

26
src/test/java/org/springframework/data/mapping/model/ClassGeneratingEntityInstantiatorUnitTests.java

@ -23,6 +23,7 @@ import static org.springframework.data.util.ClassTypeInformation.from; @@ -23,6 +23,7 @@ import static org.springframework.data.util.ClassTypeInformation.from;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;
import org.junit.jupiter.api.Test;
@ -39,6 +40,8 @@ import org.springframework.data.mapping.PreferredConstructor; @@ -39,6 +40,8 @@ import org.springframework.data.mapping.PreferredConstructor;
import org.springframework.data.mapping.PreferredConstructor.Parameter;
import org.springframework.data.mapping.model.ClassGeneratingEntityInstantiator.ObjectInstantiator;
import org.springframework.data.mapping.model.ClassGeneratingEntityInstantiatorUnitTests.Outer.Inner;
import org.springframework.data.util.TypeInformation;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.ReflectionUtils;
/**
@ -210,6 +213,9 @@ class ClassGeneratingEntityInstantiatorUnitTests<P extends PersistentProperty<P> @@ -210,6 +213,9 @@ class ClassGeneratingEntityInstantiatorUnitTests<P extends PersistentProperty<P>
assertThat(instance).isInstanceOf(ObjCtorNoArgs.class);
assertThat(((ObjCtorNoArgs) instance).ctorInvoked).isTrue();
});
ClassGeneratingEntityInstantiator classGeneratingEntityInstantiator = new ClassGeneratingEntityInstantiator();
classGeneratingEntityInstantiator.createInstance(entity, provider);
}
@Test // DATACMNS-578, DATACMNS-1126
@ -360,6 +366,26 @@ class ClassGeneratingEntityInstantiatorUnitTests<P extends PersistentProperty<P> @@ -360,6 +366,26 @@ class ClassGeneratingEntityInstantiatorUnitTests<P extends PersistentProperty<P>
assertThat(this.instance.shouldUseReflectionEntityInstantiator(entity)).isTrue();
}
@Test // GH-2446
void shouldReuseGeneratedClasses() {
prepareMocks(ProtectedInnerClass.class);
this.instance.createInstance(entity, provider);
ClassGeneratingEntityInstantiator instantiator = new ClassGeneratingEntityInstantiator();
instantiator.createInstance(entity, provider);
Map<TypeInformation<?>, EntityInstantiator> first = (Map<TypeInformation<?>, EntityInstantiator>) ReflectionTestUtils
.getField(this.instance, "entityInstantiators");
Map<TypeInformation<?>, EntityInstantiator> second = (Map<TypeInformation<?>, EntityInstantiator>) ReflectionTestUtils
.getField(instantiator, "entityInstantiators");
assertThat(first.get(null)).isNotNull().isNotInstanceOf(Enum.class);
assertThat(second.get(null)).isNotNull().isNotInstanceOf(Enum.class);
}
@Test // DATACMNS-1422
void shouldUseReflectionIfFrameworkTypesNotVisible() throws Exception {

Loading…
Cancel
Save