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.
This commit is contained in:
Oliver Gierke
2011-01-08 09:27:16 +01:00
parent b6767afadc
commit 3db9240647
9 changed files with 66 additions and 12 deletions

View File

@@ -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);
}
}

View File

@@ -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<T> implements InitializingBean {
*/
public void setAuditorAware(final AuditorAware<T> auditorAware) {
Assert.notNull(auditorAware);
this.auditorAware = auditorAware;
}

View File

@@ -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) {

View File

@@ -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<AuditableUser> {
@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;

View File

@@ -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

View File

@@ -44,7 +44,7 @@ public class AuditingEntityListenerUnitTests {
listener = new AuditingEntityListener<AuditableUser>();
// 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());
}

View File

@@ -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()));
}
}

View File

@@ -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">
<import resource="../infrastructure.xml" />
<context:spring-configured />
<jpa:auditing auditor-aware-ref="auditorAware" />
<bean id="auditorAware" class="org.springframework.data.jpa.domain.sample.AuditorAwareStub" />
<bean id="auditorAware" class="org.springframework.data.jpa.domain.sample.AuditorAwareStub">
<constructor-arg ref="auditableUserRepository" />
</bean>
<jpa:repositories base-package="org.springframework.data.jpa.repository.sample">
<jpa:repository id="auditableUserRepository" />

View File

@@ -9,6 +9,12 @@
<jpa:auditing auditor-aware-ref="auditorAware" />
<bean id="auditorAware" class="org.springframework.data.jpa.domain.sample.AuditorAwareStub" />
<bean id="auditorAware" class="org.springframework.data.jpa.domain.sample.AuditorAwareStub">
<constructor-arg>
<bean class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.springframework.data.jpa.repository.sample.AuditableUserRepository" />
</bean>
</constructor-arg>
</bean>
</beans>