DATAGRAPH-1061 - Register AuditingEventListener in post processor.

When using SpringBoot it will register all event listener in the
context it can find in the SessionFactory. This will also include
the Neo4jAuditionEventListener that would like to register itself.
This leads to a dependency cycle in the bean creation.
To get around this problem the registration will still be done in
SpringBoot but the self-registration will be removed from the
listener. If the listener is in the context, it will be registered
with a previous de-registration in a new BeanPostProcessor.

(cherry picked from commit caf0f5c)
This commit is contained in:
Gerrit Meier
2018-01-26 13:15:51 +01:00
parent 0182dbf3c7
commit f0fe0f00cd
4 changed files with 54 additions and 11 deletions

View File

@@ -21,10 +21,9 @@ import org.springframework.beans.factory.ObjectFactory;
import org.springframework.data.auditing.AuditingHandler;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.neo4j.repository.support.Neo4jAuditingBeanFactoryPostProcessor;
import org.springframework.util.Assert;
import java.util.Optional;
/**
* @author Frantisek Hartman
*/
@@ -34,8 +33,9 @@ public class Neo4jAuditingEventListener extends EventListenerAdapter implements
/**
* Creates a new {@link Neo4jAuditingEventListener} using the given {@link MappingContext} and {@link AuditingHandler}
* provided by the given {@link ObjectFactory}.
*
* provided by the given {@link ObjectFactory}. This constructor does an additional registration to the
* {@link SessionFactory}. Therefore the {@link SessionFactory} must already be instantiated.
*
* @param auditingHandlerFactory must not be {@literal null}.
*/
public Neo4jAuditingEventListener(ObjectFactory<IsNewAwareAuditingHandler> auditingHandlerFactory,
@@ -47,6 +47,17 @@ public class Neo4jAuditingEventListener extends EventListenerAdapter implements
sessionFactory.register(this);
}
/**
* Constructor used for creating an instance in the {@link Neo4jAuditingRegistrar} to get registered in the session
* "manually". The registration is done within the {@link Neo4jAuditingBeanFactoryPostProcessor}.
*
* @param auditingHandlerFactory {@link AuditingHandler} to hook into the {@code preSave} phase for auditing.
*/
public Neo4jAuditingEventListener(ObjectFactory<IsNewAwareAuditingHandler> auditingHandlerFactory) {
Assert.notNull(auditingHandlerFactory, "IsNewAwareAuditingHandler must not be null!");
this.auditingHandlerFactory = auditingHandlerFactory;
}
@Override
public void onPreSave(Event event) {
Object object = event.getObject();

View File

@@ -13,19 +13,18 @@
package org.springframework.data.neo4j.repository.config;
import java.lang.annotation.Annotation;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
import org.springframework.data.auditing.config.AuditingConfiguration;
import org.springframework.data.config.ParsingUtils;
import org.springframework.data.neo4j.annotation.EnableNeo4jAuditing;
import org.springframework.util.Assert;
import java.lang.annotation.Annotation;
/**
* @author Frantisek Hartman
*/
@@ -65,8 +64,7 @@ public class Neo4jAuditingRegistrar extends AuditingBeanDefinitionRegistrarSuppo
BeanDefinitionBuilder listenerBeanDefinitionBuilder = BeanDefinitionBuilder
.rootBeanDefinition(Neo4jAuditingEventListener.class);
listenerBeanDefinitionBuilder
.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry))
.addConstructorArgReference("sessionFactory");
.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), registry));
registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
Neo4jAuditingEventListener.class.getName(), registry);

View File

@@ -20,8 +20,6 @@ import java.util.Optional;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.RelationshipEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
@@ -29,6 +27,7 @@ import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.neo4j.repository.support.Neo4jAuditingBeanFactoryPostProcessor;
import org.springframework.data.neo4j.repository.support.Neo4jRepositoryFactoryBean;
import org.springframework.data.neo4j.repository.support.SessionBeanDefinitionRegistrarPostProcessor;
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
@@ -53,6 +52,7 @@ public class Neo4jRepositoryConfigurationExtension extends RepositoryConfigurati
private static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
private static final String NEO4J_MAPPING_CONTEXT_BEAN_NAME = "neo4jMappingContext";
private static final String NEO4J_ENTITY_INSTANTIATOR_CONFIGURATION_BEAN_NAME = "neo4jOgmEntityInstantiatorConfigurationBean";
private static final String NEO4J_AUDITING_POST_PROCESSOR_NAME = "neo4jAuditionBeanFactoryPostProcessor";
private static final String ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE = "enableDefaultTransactions";
private static final String SESSION_BEAN_DEFINITION_REGISTRAR_POST_PROCESSOR_BEAN_NAME = "sessionBeanDefinitionRegistrarPostProcessor";
private static final boolean HAS_ENTITY_INSTANTIATOR_FEATURE = ClassUtils.isPresent("org.neo4j.ogm.session.EntityInstantiator",
@@ -164,6 +164,9 @@ public class Neo4jRepositoryConfigurationExtension extends RepositoryConfigurati
registerIfNotAlreadyRegistered(new RootBeanDefinition(Neo4jMappingContextFactoryBean.class), registry,
NEO4J_MAPPING_CONTEXT_BEAN_NAME, source);
registerIfNotAlreadyRegistered(new RootBeanDefinition(Neo4jAuditingBeanFactoryPostProcessor.class), registry,
NEO4J_AUDITING_POST_PROCESSOR_NAME, source);
if (HAS_ENTITY_INSTANTIATOR_FEATURE) {
registerIfNotAlreadyRegistered(new RootBeanDefinition(Neo4jOgmEntityInstantiatorConfigurationBean.class),
registry, NEO4J_ENTITY_INSTANTIATOR_CONFIGURATION_BEAN_NAME, source);

View File

@@ -0,0 +1,31 @@
package org.springframework.data.neo4j.repository.support;
import org.neo4j.ogm.session.SessionFactory;
import org.neo4j.ogm.session.event.EventListener;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.data.neo4j.repository.config.Neo4jAuditingEventListener;
public class Neo4jAuditingBeanFactoryPostProcessor implements BeanPostProcessor {
private final SessionFactory sessionFactory;
@Autowired
public Neo4jAuditingBeanFactoryPostProcessor(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Neo4jAuditingEventListener) {
EventListener auditingEventListener = (EventListener) bean;
// we need to de-register first to be sure we just register once.
// Background: SpringBoot auto configuration does register all event listener in the context.
sessionFactory.deregister(auditingEventListener);
sessionFactory.register(auditingEventListener);
}
return bean;
}
}