From 3db924064792608a9fdb4a670e0aeaa2da178e61 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Sat, 8 Jan 2011 09:27:16 +0100 Subject: [PATCH] Fixed premature initialization of AuditorAware implementations. Set bean scope of AuditingEntityListener to prototype to ensure DI for each instance. Before that AuditorAware implementations referencing a Repository would cause premature initialization of those as the AuditingEntityListener gets created and thus wired when the EntityManagerFactory gets created. At that point in time we unfortunately don't have no DAOs yet. Added @DirtiesContext to AuditingEntitListenerIntegrationTest because otherwise the @Configurable backing prototype bean definition leaks into other test cases. --- .../AuditingBeanFactoryPostProcessor.java | 9 ++++++++ .../support/AuditingEntityListener.java | 2 ++ .../config/AuditingBeanDefinitionParser.java | 22 ++++++++++++++++++- .../jpa/domain/sample/AuditorAwareStub.java | 11 ++++++++++ .../support/AuditingEntityListenerTests.java | 2 ++ .../AuditingEntityListenerUnitTests.java | 3 +-- .../support/AuditingNamespaceUnitTests.java | 9 ++++---- .../auditing/auditing-entity-listener.xml | 12 ++++++---- .../auditing/auditing-namespace-context.xml | 8 ++++++- 9 files changed, 66 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java b/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java index aa4fef467..7db2c39cb 100644 --- a/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java +++ b/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java @@ -19,10 +19,12 @@ import java.util.Arrays; import java.util.List; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.data.domain.AuditorAware; import org.springframework.util.StringUtils; @@ -77,6 +79,13 @@ public class AuditingBeanFactoryPostProcessor implements BEAN_CONFIGURER_ASPECT_BEAN_NAME)); } } + + for (String beanName : BeanFactoryUtils + .beanNamesForTypeIncludingAncestors(beanFactory, + AuditorAware.class, true, false)) { + BeanDefinition definition = beanFactory.getBeanDefinition(beanName); + definition.setLazyInit(true); + } } diff --git a/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java b/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java index 71b8744ff..f9465b1c7 100644 --- a/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java +++ b/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java @@ -25,6 +25,7 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.data.domain.Auditable; import org.springframework.data.domain.AuditorAware; +import org.springframework.util.Assert; /** @@ -70,6 +71,7 @@ public class AuditingEntityListener implements InitializingBean { */ public void setAuditorAware(final AuditorAware auditorAware) { + Assert.notNull(auditorAware); this.auditorAware = auditorAware; } diff --git a/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java b/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java index 96c0df7af..e08f4cd8b 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParser.java @@ -17,6 +17,8 @@ package org.springframework.data.jpa.repository.config; import static org.springframework.beans.factory.support.BeanDefinitionBuilder.*; +import org.springframework.aop.framework.ProxyFactoryBean; +import org.springframework.aop.target.LazyInitTargetSource; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; @@ -55,11 +57,13 @@ public class AuditingBeanDefinitionParser implements BeanDefinitionParser { BeanDefinitionBuilder builder = rootBeanDefinition(AUDITING_ENTITY_LISTENER_CLASS_NAME); + builder.setScope("prototype"); String auditorAwareRef = element.getAttribute("auditor-aware-ref"); if (StringUtils.hasText(auditorAwareRef)) { - builder.addPropertyReference("auditorAware", auditorAwareRef); + builder.addPropertyValue("auditorAware", + createLazyInitTargetSourceBeanDefinition(auditorAwareRef)); } registerInfrastructureBeanWithId(builder.getRawBeanDefinition(), @@ -74,6 +78,22 @@ public class AuditingBeanDefinitionParser implements BeanDefinitionParser { } + private BeanDefinition createLazyInitTargetSourceBeanDefinition( + String auditorAwareRef) { + + BeanDefinitionBuilder targetSourceBuilder = + rootBeanDefinition(LazyInitTargetSource.class); + targetSourceBuilder.addPropertyValue("targetBeanName", auditorAwareRef); + + BeanDefinitionBuilder builder = + rootBeanDefinition(ProxyFactoryBean.class); + builder.addPropertyValue("targetSource", + targetSourceBuilder.getBeanDefinition()); + + return builder.getBeanDefinition(); + } + + private void registerInfrastructureBeanWithId(AbstractBeanDefinition def, String id, ParserContext context, Element element) { diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java b/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java index 3c95ae0b8..2cb017e9c 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/AuditorAwareStub.java @@ -16,6 +16,8 @@ package org.springframework.data.jpa.domain.sample; import org.springframework.data.domain.AuditorAware; +import org.springframework.data.jpa.repository.sample.AuditableUserRepository; +import org.springframework.util.Assert; /** @@ -26,9 +28,18 @@ import org.springframework.data.domain.AuditorAware; */ public class AuditorAwareStub implements AuditorAware { + @SuppressWarnings("unused") + private final AuditableUserRepository repository; private AuditableUser auditor; + public AuditorAwareStub(AuditableUserRepository repository) { + + Assert.notNull(repository); + this.repository = repository; + } + + public void setAuditor(AuditableUser auditor) { this.auditor = auditor; diff --git a/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java b/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java index 06f5b8352..8294e1e25 100644 --- a/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java +++ b/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java @@ -27,6 +27,7 @@ import org.springframework.data.jpa.domain.sample.AuditableRole; import org.springframework.data.jpa.domain.sample.AuditableUser; import org.springframework.data.jpa.domain.sample.AuditorAwareStub; import org.springframework.data.jpa.repository.sample.AuditableUserRepository; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; @@ -40,6 +41,7 @@ import org.springframework.transaction.annotation.Transactional; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:auditing/auditing-entity-listener.xml") @Transactional +@DirtiesContext public class AuditingEntityListenerTests { @Autowired diff --git a/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerUnitTests.java b/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerUnitTests.java index 9bb8476b6..ea2230135 100644 --- a/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerUnitTests.java @@ -44,7 +44,7 @@ public class AuditingEntityListenerUnitTests { listener = new AuditingEntityListener(); // Explicitly null the AuditorAware as it might have been DI'ed if test // is run in a test suite with integration tests - listener.setAuditorAware(null); + // listener.setAuditorAware(null); user = new AuditableUser(); @@ -65,7 +65,6 @@ public class AuditingEntityListenerUnitTests { assertNotNull(user.getCreatedDate()); assertNotNull(user.getLastModifiedDate()); - System.out.println(user.getCreatedBy()); assertNull(user.getCreatedBy()); assertNull(user.getLastModifiedBy()); } diff --git a/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java b/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java index 719e7e91a..7c72802e9 100644 --- a/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/domain/support/AuditingNamespaceUnitTests.java @@ -15,11 +15,12 @@ */ package org.springframework.data.jpa.domain.support; +import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import org.junit.Test; +import org.springframework.beans.PropertyValue; import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.RuntimeBeanReference; /** @@ -49,8 +50,8 @@ public class AuditingNamespaceUnitTests extends BeanDefinition definition = beanFactory.getBeanDefinition(AuditingEntityListener.class .getName()); - assertEquals( - definition.getPropertyValues().getPropertyValue("auditorAware") - .getValue(), new RuntimeBeanReference("auditorAware")); + PropertyValue propertyValue = + definition.getPropertyValues().getPropertyValue("auditorAware"); + assertThat(propertyValue, is(notNullValue())); } } diff --git a/src/test/resources/auditing/auditing-entity-listener.xml b/src/test/resources/auditing/auditing-entity-listener.xml index 79b099de2..592befe3c 100644 --- a/src/test/resources/auditing/auditing-entity-listener.xml +++ b/src/test/resources/auditing/auditing-entity-listener.xml @@ -3,14 +3,18 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:context="http://www.springframework.org/schema/context" - xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.0.xsd + http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> + - + + + + diff --git a/src/test/resources/auditing/auditing-namespace-context.xml b/src/test/resources/auditing/auditing-namespace-context.xml index 6ab9f4775..b367175cc 100644 --- a/src/test/resources/auditing/auditing-namespace-context.xml +++ b/src/test/resources/auditing/auditing-namespace-context.xml @@ -9,6 +9,12 @@ - + + + + + + +