如何创建扩展 AuditingEntityListener 的自定义审计实体侦听器

Posted

技术标签:

【中文标题】如何创建扩展 AuditingEntityListener 的自定义审计实体侦听器【英文标题】:How to create custom auditing entity listener that extends AuditingEntityListener 【发布时间】:2021-09-24 08:45:30 【问题描述】:

我已经通过 Sping Data JPA 实现了审计,但现在我希望能够控制更新并使用自定义逻辑创建时间戳。所以我想写一个自定义的审计实体监听器来扩展 Auditingentitylistener。

我有以下通用实体类,我在其中注册了自定义审计实体侦听器:

@MappedSuperclass
@Audited
@EntityListeners(CustomAuditingEntityListener.class)
public class AuditableEntity extends BaseEntity 

    @CreatedDate
    @Column(name = "created", nullable = false)
    private Date created;

    @CreatedBy
    @JsonIgnore
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "created_by_id", foreignKey = @ForeignKey(name = "fk_entity_created_by_id"))
    private Account createdBy;

    @LastModifiedDate
    @Column(name = "last_updated", nullable = false)
    private Date lastUpdated;

    @LastModifiedBy
    @JsonIgnore
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "last_updated_by_id", foreignKey = @ForeignKey(name = "fk_entity_last_updated_by_id"))
    private Account lastUpdatedBy;

    protected AuditableEntity() 
    

    ...


CustomAuditingEntityListener 类定义为

@Configurable
public class CustomAuditingEntityListener extends AuditingEntityListener 

    public CustomAuditingEntityListener() 
        super();
    

    @Override
    @PrePersist
    public void touchForCreate(Object target) 
        if (//custom logic) 
            super.touchForCreate(target);
        
    

    @Override
    @PreUpdate
    public void touchForUpdate(Object target) 
        if (//custom logic) 
            super.touchForUpdate(target);
        
    


自定义审计实体监听器被正确调用,但是当 super.touchForCreate(target)super.touchForUpdate(target) 被调用时,时间戳未设置,因为 AuditingEntityListener 类中的处理程序为空。

这是 AuditingEntityListener 类的代码:

package org.springframework.data.jpa.domain.support;

@Configurable
public class AuditingEntityListener implements ConfigurableObject 
    private ObjectFactory<AuditingHandler> handler;

    public AuditingEntityListener() 
        JoinPoint var2 = Factory.makeJP(ajc$tjp_1, this, this);
        JoinPoint var1 = Factory.makeJP(ajc$tjp_0, this, this);
        if (this != null && this.getClass().isAnnotationPresent(Configurable.class) && AnnotationBeanConfigurerAspect.ajc$if$bb0((Configurable)this.getClass().getAnnotation(Configurable.class))) 
            AnnotationBeanConfigurerAspect.aspectOf().ajc$before$org_springframework_beans_factory_aspectj_AbstractDependencyInjectionAspect$1$e854fa65(this);
        

        if ((this == null || !this.getClass().isAnnotationPresent(Configurable.class) || !AnnotationBeanConfigurerAspect.ajc$if$bb0((Configurable)this.getClass().getAnnotation(Configurable.class))) && this != null && this.getClass().isAnnotationPresent(Configurable.class) && AbstractDependencyInjectionAspect.ajc$if$6f1(var1)) 
            AnnotationBeanConfigurerAspect.aspectOf().ajc$afterReturning$org_springframework_beans_factory_aspectj_AbstractDependencyInjectionAspect$2$1ea6722c(this);
        

        if (!AnnotationBeanConfigurerAspect.ajc$if$bb0((Configurable)this.getClass().getAnnotation(Configurable.class)) && AbstractDependencyInjectionAspect.ajc$if$6f1(var2)) 
            AnnotationBeanConfigurerAspect.aspectOf().ajc$afterReturning$org_springframework_beans_factory_aspectj_AbstractDependencyInjectionAspect$2$1ea6722c(this);
        

    

    public void setAuditingHandler(ObjectFactory<AuditingHandler> auditingHandler) 
        Assert.notNull(auditingHandler, "AuditingHandler must not be null!");
        this.handler = auditingHandler;
    

    @PrePersist
    public void touchForCreate(Object target) 
        if (this.handler != null) 
            ((AuditingHandler)this.handler.getObject()).markCreated(target);
        

    

    @PreUpdate
    public void touchForUpdate(Object target) 
        if (this.handler != null) 
            ((AuditingHandler)this.handler.getObject()).markModified(target);
        

    

    static 
        ajc$preClinit();
    

谁能解释我如何确保 AuditingHandler 不为 null 并且设置为与默认 AuditingEntityListener 类一样?

【问题讨论】:

AuditingEntityListener 以一种奇怪的方式工作。与@EntityListeners 中声明的类无关,AuditingEntityListener 被实例化,setAuditingHandler() 被调用。当调用CustomAuditingEntityListener.touchForCreate() 时,它的handlernull,因为它是一个不同的对象。 【参考方案1】:

在 Spring 5.1 中,有一种相当简单的方法可以在自定义实体侦听器中自动装配 Spring 组件,因此可以使用 AuditingHandler

1. 将客户实体侦听器声明为 Spring 组件。使用构造函数注入。

@Component
public class CustomAuditingEntityListener 

    private ObjectFactory<AuditingHandler> handler; 
    
    public CustomAuditingEntityListener(ObjectFactory<AuditingHandler> auditingHandler) 
        this.handler = auditingHandler;
    

    @Override
    @PrePersist
    public void touchForCreate(Object target) 
        if (//custom logic) 
            AuditingHandler object = handler.getObject();
            if (object != null) 
                object.markCreated(target);
            

        
    

    @Override
    @PreUpdate
    public void touchForUpdate(Object target) 
        if (//custom logic) 
            AuditingHandler object = handler.getObject();
            if (object != null) 
                object.markModified(target);
            
        
    


2. 通过设置适当的配置属性告诉 Hibernate 使用 Spring 作为 bean 提供者:

@Configuration
public class JpaConfig extends JpaBaseConfiguration 
    
    @Autowired
    private ConfigurableListableBeanFactory beanFactory;

    protected JpaConfig(DataSource dataSource, JpaProperties properties,
            ObjectProvider<JtaTransactionManager> jtaTransactionManager) 
        super(dataSource, properties, jtaTransactionManager);
    

    @Override
    protected AbstractJpaVendorAdapter createJpaVendorAdapter() 
        return new HibernateJpaVendorAdapter();
    

    @Override
    protected Map<String, Object> getVendorProperties() 
        Map<String, Object> properties = new HashMap<>();

        // This is the important line
        properties.put(org.hibernate.cfg.AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory));

        return properties;
    

--Edit-- 2 的变体。 或者,SpringBeanContainer 可以通过这种方式进行配置,这样做的好处是不会丢失 Spring Boot 填充的 Hibernate 属性:

@Configuration
public class JpaConfig 

    @Bean(name = "entityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
            EntityManagerFactoryBuilder builder, ConfigurableListableBeanFactory beanFactory) 

        return builder.dataSource(dataSource) //
                .packages(BaseEntity.class) //
                .persistenceUnit("myunit") //
                .properties(Map.of(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory))) //
                .build();
    

【讨论】:

以上是关于如何创建扩展 AuditingEntityListener 的自定义审计实体侦听器的主要内容,如果未能解决你的问题,请参考以下文章

如何创建通知 chrome 扩展?

如何创建 URL slug 扩展?

Swift - TableView 如何创建可扩展部分

如何检测数组是不是是从迭代器(或数组扩展)创建的

如何在 docker 中为 postgresql 创建 postgis 扩展?

如何从 ExoPlayer Cast 扩展创建 CastPlayer?