From b3324a0e7da73ddff625e9f3fa5c64aefa1c73c6 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Thu, 5 Nov 2015 18:53:32 +0100 Subject: [PATCH] DATAJPA-871 - CrudMethodMetadataPostProcessor now uses the bean ClassLoader. The thread-local proxy for the CrudMethodMetadata is now created using the bean ClassLoader, which the repository factory gets set from the container. Renamed the property for the post processor and its lookup method. --- .../CrudMethodMetadataPostProcessor.java | 20 +++++++++++++++---- .../support/JpaRepositoryFactory.java | 18 +++++++++++++---- .../JpaRepositoryFactoryUnitTests.java | 17 ++++++++++++++++ 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java index 3eb164d56..a0cdd48a7 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java @@ -30,6 +30,7 @@ import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.interceptor.ExposeInvocationInterceptor; import org.springframework.aop.target.AbstractLazyCreationTargetSource; +import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.Lock; @@ -38,6 +39,7 @@ import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.support.RepositoryProxyPostProcessor; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; /** * {@link RepositoryProxyPostProcessor} that sets up interceptors to read metadata information from the invoked method. @@ -47,9 +49,19 @@ import org.springframework.util.Assert; * @author Oliver Gierke * @author Thomas Darimont */ -enum CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor { +class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, BeanClassLoaderAware { - INSTANCE; + private ClassLoader classLoader = ClassUtils.getDefaultClassLoader(); + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader) + */ + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader == null ? ClassUtils.getDefaultClassLoader() : classLoader; + + } /* * (non-Javadoc) @@ -64,14 +76,14 @@ enum CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor { * Returns a {@link CrudMethodMetadata} proxy that will lookup the actual target object by obtaining a thread bound * instance from the {@link TransactionSynchronizationManager} later. */ - public CrudMethodMetadata getLockMetadataProvider() { + public CrudMethodMetadata getCrudMethodMetadata() { ProxyFactory factory = new ProxyFactory(); factory.addInterface(CrudMethodMetadata.class); factory.setTargetSource(new ThreadBoundTargetSource()); - return (CrudMethodMetadata) factory.getProxy(); + return (CrudMethodMetadata) factory.getProxy(this.classLoader); } /** diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index 15209a7a4..01eb14543 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -43,7 +43,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { private final EntityManager entityManager; private final QueryExtractor extractor; - private final CrudMethodMetadataPostProcessor lockModePostProcessor; + private final CrudMethodMetadataPostProcessor crudMethodMetadataPostProcessor; /** * Creates a new {@link JpaRepositoryFactory}. @@ -56,9 +56,19 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { this.entityManager = entityManager; this.extractor = PersistenceProvider.fromEntityManager(entityManager); - this.lockModePostProcessor = CrudMethodMetadataPostProcessor.INSTANCE; + this.crudMethodMetadataPostProcessor = new CrudMethodMetadataPostProcessor(); - addRepositoryProxyPostProcessor(lockModePostProcessor); + addRepositoryProxyPostProcessor(crudMethodMetadataPostProcessor); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#setBeanClassLoader(java.lang.ClassLoader) + */ + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + super.setBeanClassLoader(classLoader); + this.crudMethodMetadataPostProcessor.setBeanClassLoader(classLoader); } /* @@ -69,7 +79,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { protected Object getTargetRepository(RepositoryInformation information) { SimpleJpaRepository repository = getTargetRepository(information, entityManager); - repository.setRepositoryMethodMetadata(lockModePostProcessor.getLockMetadataProvider()); + repository.setRepositoryMethodMetadata(crudMethodMetadataPostProcessor.getCrudMethodMetadata()); return repository; } diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java index 444658cdb..bb76e1b58 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryUnitTests.java @@ -31,6 +31,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.aop.framework.Advised; +import org.springframework.core.OverridingClassLoader; import org.springframework.data.jpa.domain.sample.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.custom.CustomGenericJpaRepositoryFactory; @@ -38,7 +39,9 @@ import org.springframework.data.jpa.repository.custom.UserCustomExtendedReposito import org.springframework.data.querydsl.QueryDslPredicateExecutor; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; import org.springframework.data.repository.query.QueryLookupStrategy.Key; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ClassUtils; /** * Unit test for {@code JpaRepositoryFactory}. @@ -164,6 +167,20 @@ public class JpaRepositoryFactoryUnitTests { assertEquals(CustomJpaRepository.class, ((Advised) repository).getTargetClass()); } + /** + * @see DATAJPA-819 + */ + @Test + public void crudMethodMetadataPostProcessorUsesBeanClassLoader() { + + ClassLoader classLoader = new OverridingClassLoader(ClassUtils.getDefaultClassLoader()); + + factory.setBeanClassLoader(classLoader); + + Object processor = ReflectionTestUtils.getField(factory, "crudMethodMetadataPostProcessor"); + assertThat(ReflectionTestUtils.getField(processor, "classLoader"), is((Object) classLoader)); + } + private interface SimpleSampleRepository extends JpaRepository { @Transactional