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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user