如何让Hibernate Cache自定义RegionFactory获取Spring注入的Bean?
自定义SpringRedisRegionFactory继承RegionFactoryTemplate,尝试通过SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)注入RedisConnectionFactory,运行时抛出错误:
Current WebApplicationContext is not available for processing of SpringRedisRegionFactory: Make sure this class gets constructed in a Spring web application after the Spring WebApplicationContext has been initialized. Proceeding without injection.
此前在EntityListeners中使用相同注入方法可正常工作,但在RegionFactory中无效。
核心原因
RegionFactory的初始化时机早于Spring WebApplicationContext的就绪时间,或者该类未纳入Spring Bean生命周期管理,导致SpringBeanAutowiringSupport无法获取有效上下文完成注入。
解决方案
方案一:将RegionFactory纳入Spring Bean管理
让Spring直接管理SpringRedisRegionFactory实例,自动完成依赖注入:
- 给类添加
@Component注解,使其被Spring扫描并托管:
@Component public class SpringRedisRegionFactory extends RegionFactoryTemplate { @Autowired private transient RedisConnectionFactory redisConnectionFactory; // 原有方法实现... }
- 配置Hibernate使用该Spring托管的RegionFactory(以Spring Boot为例):
# application.properties spring.jpa.properties.hibernate.cache.region.factory_class=com.yourpackage.SpringRedisRegionFactory
方案二:手动通过ApplicationContext工具类完成注入
如果无法将RegionFactory纳入Spring Bean管理,可通过静态持有上下文的工具类手动注入:
- 创建ApplicationContext持有类:
@Component public class ApplicationContextHolder implements ApplicationContextAware { private static ApplicationContext context; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { context = applicationContext; } public static ApplicationContext getContext() { return context; } }
- 修改
registerNewStorageAccess方法,手动触发注入:
private DomainDataStorageAccess registerNewStorageAccess(final String regionName) { if (storageAccesses == null) { throw new IllegalStateException("prepareForUse was not executed yet"); } ApplicationContext context = ApplicationContextHolder.getContext(); if (context != null) { context.getAutowireCapableBeanFactory().autowireBean(this); } else { throw new IllegalStateException("ApplicationContext not initialized"); } final var storageAccess = new SpringRedisStorageAccess(regionName, redisConnectionFactory); storageAccesses.add(storageAccess); return storageAccess; }
方案三:利用RegionFactory的prepareForUse方法延迟注入
RegionFactoryTemplate的prepareForUse方法执行时机晚于初始化阶段,此时Spring上下文已就绪,可将注入逻辑移至该方法:
@Override public void prepareForUse(DomainDataRegionBuildingContext buildingContext) { super.prepareForUse(buildingContext); // 在此处执行注入,确保上下文已初始化 SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); } private DomainDataStorageAccess registerNewStorageAccess(final String regionName) { if (storageAccesses == null) { throw new IllegalStateException("prepareForUse was not executed yet"); } // 无需再手动调用注入,prepareForUse已完成注入 final var storageAccess = new SpringRedisStorageAccess(regionName, redisConnectionFactory); storageAccesses.add(storageAccess); return storageAccess; }
内容的提问来源于stack exchange,提问作者Archimedes Trajano

