Spring Boot中CRUDRepository注入工具类后调用时为空问题求助
兄弟,我一眼就看到问题所在了——**你在ActionTrackerInterceptor里手动new MyActionLogUtil()了!**这是Spring依赖注入里最典型的错误之一。
核心问题
Spring的依赖注入只对它自己容器中管理的Bean生效。你给MyActionLogUtilImpl加了@Component,Spring确实会创建一个实例并正确注入actionLogRepository,但你在拦截器里手动new出来的那个MyActionLogUtil实例,完全是脱离Spring管控的“野生对象”,Spring根本没机会给它注入任何依赖,所以actionLogRepository自然就是null。
另外,你的ActionTrackerInterceptor作为Hibernate拦截器,如果不是Spring管理的Bean,也没法直接注入其他组件,这也是需要解决的点。
解决方案(推荐优雅版)
让拦截器成为Spring管理的Bean,并通过构造注入获取MyActionLogUtil:
改造拦截器,让它被Spring管理
给ActionTrackerInterceptor加上@Component注解,并用构造注入获取工具类实例:@Component public class ActionTrackerInterceptor extends EmptyInterceptor { private final MyActionLogUtil actionLogUtil; // 构造注入(Spring推荐的注入方式,避免空指针风险) public ActionTrackerInterceptor(MyActionLogUtil actionLogUtil) { this.actionLogUtil = actionLogUtil; } @Override public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { Action act = (Action) entity; ActionLog actionLog = new ActionLog(...some data inserted...); // 直接用Spring注入的工具类,不要再new! actionLogUtil.logIt("DELETED", actionLog); System.out.println("Record " + entity + " is deleted."); } }配置Hibernate使用这个Spring管理的拦截器
在application.properties或application.yml中添加配置,告诉Hibernate用Spring容器里的拦截器:spring.jpa.properties.hibernate.ejb.interceptor=com.yourpackage.ActionTrackerInterceptor(把
com.yourpackage替换成你拦截器所在的实际包路径)
备选方案(如果拦截器无法被Spring直接管理)
如果因为某些限制,拦截器必须手动创建,可以通过Spring上下文获取容器中的Bean:
创建Spring上下文持有类
@Component public class SpringContextHolder implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext context) throws BeansException { applicationContext = context; } public static <T> T getBean(Class<T> beanClass) { return applicationContext.getBean(beanClass); } }在拦截器中获取工具类实例
public class ActionTrackerInterceptor extends EmptyInterceptor { @Override public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { Action act = (Action) entity; ActionLog actionLog = new ActionLog(...some data inserted...); // 从Spring上下文获取Bean,而不是new MyActionLogUtil util = SpringContextHolder.getBean(MyActionLogUtil.class); util.logIt("DELETED", actionLog); System.out.println("Record " + entity + " is deleted."); } }这种方式不如第一种优雅,仅作为特殊场景的备选。
额外注意事项
- 你的工具类接口名字有拼写错误:
ActionyLogUtil应该是ActionLogUtil吧?修正拼写避免不必要的错误。 - 在
logIt方法中,你注释掉了actionLog = new ActionLog(...),实际运行时要确保actionLog不为null,否则调用save会抛出空指针异常。 - 事务方面:
deleteActionRecord方法的@Transactional会覆盖拦截器中的操作,logIt里的save会继承当前事务。如果需要日志操作独立于删除事务,可以给logIt方法加上@Transactional(propagation = Propagation.REQUIRES_NEW)。
内容的提问来源于stack exchange,提问作者Matexon

