Introduce interface cache for EntityManager and Query types

We now reuse interfaces for EntityManager and Query classes that
are proxied through ExtendedEntityManagerCreator and SharedEntityManagerCreator.

These caches prevent excessive object allocations through
ClassUtils.getAllInterfacesForClass(…) and
ClassUtils.getAllInterfacesForClassAsSet(…).
This commit is contained in:
Mark Paluch 2019-07-23 15:50:55 +02:00 committed by Juergen Hoeller
parent 96ea3a8924
commit 1890e04df1
2 changed files with 27 additions and 6 deletions

View File

@ -43,6 +43,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
/**
* Delegate for creating a variety of {@link javax.persistence.EntityManager}
@ -65,6 +66,7 @@ import org.springframework.util.CollectionUtils;
*
* @author Juergen Hoeller
* @author Rod Johnson
* @author Mark Paluch
* @since 2.0
* @see javax.persistence.EntityManagerFactory#createEntityManager()
* @see javax.persistence.PersistenceContextType#EXTENDED
@ -73,6 +75,8 @@ import org.springframework.util.CollectionUtils;
*/
public abstract class ExtendedEntityManagerCreator {
private static final Map<Class<?>, Class[]> CACHED_ENTITY_MANAGER_INTERFACES = new ConcurrentReferenceHashMap<>();
/**
* Create an application-managed extended EntityManager proxy.
* @param rawEntityManager the raw EntityManager to decorate
@ -222,17 +226,29 @@ public abstract class ExtendedEntityManagerCreator {
boolean containerManaged, boolean synchronizedWithTransaction) {
Assert.notNull(rawEm, "EntityManager must not be null");
Set<Class<?>> ifcs = new LinkedHashSet<>();
Class[] interfaces;
if (emIfc != null) {
ifcs.add(emIfc);
interfaces = CACHED_ENTITY_MANAGER_INTERFACES.computeIfAbsent(emIfc, key -> {
Set<Class<?>> ifcs = new LinkedHashSet<>();
ifcs.add(key);
ifcs.add(EntityManagerProxy.class);
return ClassUtils.toClassArray(ifcs);
});
}
else {
ifcs.addAll(ClassUtils.getAllInterfacesForClassAsSet(rawEm.getClass(), cl));
interfaces = CACHED_ENTITY_MANAGER_INTERFACES.computeIfAbsent(rawEm.getClass(), key -> {
Set<Class<?>> ifcs = new LinkedHashSet<>();
ifcs.addAll(ClassUtils
.getAllInterfacesForClassAsSet(key, cl));
ifcs.add(EntityManagerProxy.class);
return ClassUtils.toClassArray(ifcs);
});
}
ifcs.add(EntityManagerProxy.class);
return (EntityManager) Proxy.newProxyInstance(
(cl != null ? cl : ExtendedEntityManagerCreator.class.getClassLoader()),
ClassUtils.toClassArray(ifcs),
interfaces,
new ExtendedEntityManagerInvocationHandler(
rawEm, exceptionTranslator, jta, containerManaged, synchronizedWithTransaction));
}

View File

@ -41,6 +41,7 @@ import org.springframework.lang.Nullable;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
/**
* Delegate for creating a shareable JPA {@link javax.persistence.EntityManager}
@ -59,6 +60,7 @@ import org.springframework.util.CollectionUtils;
* @author Juergen Hoeller
* @author Rod Johnson
* @author Oliver Gierke
* @author Mark Paluch
* @since 2.0
* @see javax.persistence.PersistenceContext
* @see javax.persistence.PersistenceContextType#TRANSACTION
@ -73,6 +75,8 @@ public abstract class SharedEntityManagerCreator {
private static final Set<String> queryTerminatingMethods = new HashSet<>(8);
private static final Map<Class<?>, Class[]> CACHED_QUERY_INTERFACES = new ConcurrentReferenceHashMap<>();
static {
transactionRequiringMethods.add("joinTransaction");
transactionRequiringMethods.add("flush");
@ -310,7 +314,8 @@ public abstract class SharedEntityManagerCreator {
if (result instanceof Query) {
Query query = (Query) result;
if (isNewEm) {
Class<?>[] ifcs = ClassUtils.getAllInterfacesForClass(query.getClass(), this.proxyClassLoader);
Class<?>[] ifcs = CACHED_QUERY_INTERFACES.computeIfAbsent(query.getClass(), key ->
ClassUtils.getAllInterfacesForClass(key, this.proxyClassLoader));
result = Proxy.newProxyInstance(this.proxyClassLoader, ifcs,
new DeferredQueryInvocationHandler(query, target));
isNewEm = false;