From f173a6f5dad6f3bf60f06f2b8986325a4f751b03 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 350c55b99..dbcaf1958 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; @@ -39,6 +40,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. @@ -48,9 +50,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) @@ -65,14 +77,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 96cb0b531..2f2c3930d 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 @@ -42,7 +42,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}. @@ -55,9 +55,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); } /* @@ -68,7 +78,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { protected Object getTargetRepository(RepositoryMetadata metadata) { SimpleJpaRepository repository = getTargetRepository(metadata, 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 c470443d7..fe92b61f7 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}. @@ -152,6 +155,20 @@ public class JpaRepositoryFactoryUnitTests { } } + /** + * @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