Spring管理的Hibernate拦截器@Autowired字段空指针异常问题
问题根因
- 核心矛盾是:你自定义的
MvaSupportInterceptor默认是Hibernate框架直接实例化的,不属于Spring IoC容器的管理范围- 用
@Autowired注入时,Spring只会给自身容器管理的Bean注入依赖,Hibernate自己new出来的拦截器对象里的CommentService自然是null,触发NPE - 改用构造器注入后,你主动给拦截器加了带
CommentService参数的构造方法,Java默认不会再生成无参构造,而Hibernate实例化拦截器时必须调用无参构造,直接启动报错
- 用
可行解决方案
方案1(推荐):把拦截器交给Spring管理,配置Hibernate使用Spring容器中的拦截器实例
- 给
MvaSupportInterceptor类加@Component注解,用构造器注入需要的CommentService
@Component public class MvaSupportInterceptor extends EmptyInterceptor { private final CommentService commentService; // Spring 4.3+ 单例Bean无需额外加@Autowired注解 public MvaSupportInterceptor(CommentService commentService) { this.commentService = commentService; } // 其余拦截器逻辑保持不变 }
- 调整JPA配置,把Spring管理的拦截器实例注入到Hibernate配置中:
如果是手动配置LocalContainerEntityManagerFactoryBean,参考代码如下:
@Configuration public class JpaConfig { private final MvaSupportInterceptor mvaSupportInterceptor; public JpaConfig(MvaSupportInterceptor mvaSupportInterceptor) { this.mvaSupportInterceptor = mvaSupportInterceptor; } @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) { LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean(); em.setDataSource(dataSource); // 包扫描、JPA厂商适配器等其余配置保持不变 Map<String, Object> jpaProperties = new HashMap<>(); // 直接传入Spring容器中的拦截器实例 jpaProperties.put("hibernate.session_factory.interceptor", mvaSupportInterceptor); em.setJpaPropertyMap(jpaProperties); return em; } }
如果是用Spring Boot自动配置,也可以直接在配置文件中指定拦截器全类名:
spring: jpa: properties: hibernate: session_factory: interceptor: com.xxx.xxx.MvaSupportInterceptor # 替换为你拦截器的全限定类名
方案2(应急使用):通过Spring上下文工具类手动获取Bean
如果不想改动Hibernate配置,可以实现一个Spring上下文持有工具类,在拦截器逻辑执行时手动从容器取依赖:
@Component public class SpringContextHolder implements ApplicationContextAware { private static ApplicationContext context; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { context = applicationContext; } public static <T> T getBean(Class<T> beanClazz) { return context.getBean(beanClazz); } }
拦截器中使用方式:
public class MvaSupportInterceptor extends EmptyInterceptor { @Override public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { // *不要把Bean声明为类成员变量*,直接在方法执行时从上下文获取 CommentService commentService = SpringContextHolder.getBean(CommentService.class); // 其余业务逻辑保持不变 return super.onSave(entity, id, state, propertyNames, types); } }
内容的提问来源于stack exchange,提问作者Garfield
相关产品推荐
相关产品推荐

