Spring Boot升级后EmptyInterceptor的onSave/onFlushDirty在测试中失效
我们有一个基于Spring Boot 2.4.5的应用,使用自定义EmptyInterceptor,该拦截器的onSave和onFlushDirty方法在应用及测试中均能正常执行。将Spring Boot升级至2.7.4(同步升级Hibernate、JUnit等依赖)后,应用仍正常运行,但测试环境中拦截器的onSave和onFlushDirty方法不再执行,不过其他拦截器方法可正常运行,说明拦截器已正确注册。我们查阅了Spring、Spring JPA及Hibernate的发布说明,未找到相关变更点,想了解升级过程中是否遗漏了与拦截器工作方式相关的配置或变更?
技术细节
自定义拦截器代码
@Component public class AuditedEntityInterceptor extends EmptyInterceptor implements BeanFactoryAware
拦截器配置代码
@Configuration @Slf4j public class HibernateInterceptorConfig implements HibernatePropertiesCustomizer { @Autowired private AuditedEntityInterceptor auditedEntityInterceptor; //... @Override public void customize(Map<String, Object> hibernateProperties) { hibernateProperties.put("hibernate.session_factory.interceptor", auditedEntityInterceptor); //... } }
集成测试结构
// 每个集成测试类结构如下: @Transactional public class SomethingSomethingServiceIT extends AppSharedIT @SpringBootTest(classes = { AppSharedItConfig.class }) public abstract class AppSharedIT extends AppSupportHsqlIT @SpringBootTest(classes = AppSupportItConfig.class) @Sql(scripts = { // ... 用于初始化参考数据的SQL脚本 }) @Transactional @ActiveProfiles(profiles = { "test" }) public abstract class AppHsqlIT implements BeanFactoryAware @Configuration @EnableAutoConfiguration(exclude = { QuartzAutoConfiguration.class }) @Import({ AppSupportConfig.class }) @ComponentScan(basePackages = { //... }) public class AppSupportItConfig @Configuration @EnableTransactionManagement @EntityScan({ //... }) @EnableJpaRepositories({ //... }) @ComponentScan( basePackages = { //... }) @Import(value = { //... HibernateInterceptorConfig.class }) public class AppSupportConfig
核心依赖版本变更
junit-jupiter: 5.7.1 -> 5.8.2 spring-boot: 2.4.5 -> 2.7.4 hibernate-core: 5.4.30.Final -> 5.6.11.Final
分析与解决方案
1. Hibernate 5.6中SessionFactory拦截器注册逻辑变更
Hibernate 5.6对hibernate.session_factory.interceptor属性的生效逻辑做了调整,测试环境的事务上下文差异可能导致拦截器未绑定到当前Session。
解决办法:
改用EntityManagerFactoryBuilderCustomizer替代HibernatePropertiesCustomizer配置拦截器,确保属性被正确应用:
@Configuration public class HibernateInterceptorConfig { private final AuditedEntityInterceptor auditedEntityInterceptor; public HibernateInterceptorConfig(AuditedEntityInterceptor auditedEntityInterceptor) { this.auditedEntityInterceptor = auditedEntityInterceptor; } @Bean public EntityManagerFactoryBuilderCustomizer entityManagerFactoryBuilderCustomizer() { return builder -> builder.applySetting("hibernate.session_factory.interceptor", auditedEntityInterceptor); } }
2. 测试环境事务与Session绑定冲突
Spring Boot 2.7调整了测试事务的管理逻辑,多层@Transactional注解可能导致Session创建时机偏移,拦截器未关联到测试用Session。
解决办法:
- 移除测试类层级中重复的
@Transactional注解,避免事务上下文冲突; - 在测试方法结束前调用
entityManager.flush(),强制触发脏数据检查,确保onFlushDirty执行; - 给测试方法添加
@Transactional(propagation = Propagation.REQUIRES_NEW),强制创建新事务与Session。
3. Hibernate 5.6中脏数据检测逻辑优化
Hibernate 5.6对onSave和onFlushDirty的触发条件做了严格限制,仅当实体属性真正发生变更时才会触发,测试中可能因数据初始化或对象引用问题未标记为脏数据。
解决办法:
- 检查测试代码中实体修改逻辑,确保属性值确实被修改(避免复用同一对象实例未触发状态变更);
- 考虑迁移到JPA标准的
EntityListener实现审计逻辑,更适配新版本的持久化规范。
4. Spring Boot 2.7中配置类加载顺序问题
Spring Boot 2.7调整了配置类的优先级,测试环境下HibernateInterceptorConfig可能未被正确加载,导致拦截器配置未生效。
解决办法:
- 确保
HibernateInterceptorConfig在测试配置类中被显式导入,且无其他配置覆盖该属性; - 直接通过
LocalContainerEntityManagerFactoryBean手动设置拦截器:
@Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder, DataSource dataSource, AuditedEntityInterceptor interceptor) { Map<String, Object> properties = new HashMap<>(); properties.put("hibernate.session_factory.interceptor", interceptor); return builder.dataSource(dataSource) .packages("your.entity.package") .properties(properties) .build(); }
内容的提问来源于stack exchange,提问作者Chris Williams

