From 1890e04df18c152e61da62b5442008f8064ab127 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 23 Jul 2019 15:50:55 +0200 Subject: [PATCH] Introduce interface cache for EntityManager and Query types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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(…). --- .../orm/jpa/ExtendedEntityManagerCreator.java | 26 +++++++++++++++---- .../orm/jpa/SharedEntityManagerCreator.java | 7 ++++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java b/spring-orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java index 5e7a5259b1..714d691e91 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/ExtendedEntityManagerCreator.java @@ -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[]> 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> ifcs = new LinkedHashSet<>(); + Class[] interfaces; + if (emIfc != null) { - ifcs.add(emIfc); + interfaces = CACHED_ENTITY_MANAGER_INTERFACES.computeIfAbsent(emIfc, key -> { + Set> 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> 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)); } diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java b/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java index 70b6b06da9..162733e3dc 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java @@ -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 queryTerminatingMethods = new HashSet<>(8); + private static final Map, 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;