Spring Boot 2.7中EntityListener无法注入Repository问题求助
解决Spring Boot 2.7实体监听器注入Repository的空指针与循环依赖问题
问题根源
JPA的@EntityListener实例由JPA的EntityManager创建管理,不属于Spring容器Bean范畴。直接用@Autowired或构造注入Spring Repository时,Spring无法完成依赖注入,导致空指针;构造注入触发的循环依赖,本质是User实体关联UserListener、UserRepository依赖User实体形成的闭环,但即使开启允许循环依赖也无效——因为监听器根本不在Spring容器的管理链条里。
解决方案
方案一:手动从Spring上下文获取Bean(最直接)
无需大幅修改现有代码,通过工具类手动获取Spring Bean:
- 编写Spring上下文工具类:
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext context) { applicationContext = context; } public static <T> T getBean(Class<T> beanClass) { return applicationContext.getBean(beanClass); } }
- 修改UserListener,移除Spring注入注解,改用工具类获取Repository:
public class UserListener { @PostPersist @PostUpdate public void saveUserHistorical(User user) { // 从Spring上下文获取Repository实例 UserHistoricalRepository historicalRepo = SpringContextUtil.getBean(UserHistoricalRepository.class); UserHistorical userHistorical = new UserHistorical(); userHistorical.setId(user.getId()); userHistorical.setName(user.getName()); userHistorical.setEmail(user.getEmail()); userHistorical.setDateCreation(new Date()); historicalRepo.save(userHistorical); } }
注意:此时UserListener不需要加@Component,它由JPA负责实例化。
方案二:使用Spring Data JPA事件监听(更规范)
改用Spring容器管理的事件监听器,避开JPA监听器的管理限制:
- 主类启用JPA审计支持:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; @SpringBootApplication @EnableJpaAuditing public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
- 编写Spring管理的审计监听器:
import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; @Component public class UserAuditListener { private final UserHistoricalRepository userHistoricalRepository; // 构造注入正常生效,因为这是Spring管理的Bean public UserAuditListener(UserHistoricalRepository userHistoricalRepository) { this.userHistoricalRepository = userHistoricalRepository; } // 监听User实体的保存/更新事件 @EventListener public void handleUserSaveEvent(User user) { UserHistorical userHistorical = new UserHistorical(); userHistorical.setId(user.getId()); userHistorical.setName(user.getName()); userHistorical.setEmail(user.getEmail()); userHistorical.setDateCreation(new Date()); userHistoricalRepository.save(userHistorical); } }
同时移除User实体上的@EntityListeners(UserListener.class)注解。
方案三:@Configurable + AspectJ织入(不推荐,配置复杂)
如果一定要保留@EntityListener,可以通过AspectJ织入让Spring接管监听器的依赖注入:
- 添加AspectJ依赖到pom.xml:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency>
- 给UserListener添加
@Configurable注解:
@Configurable public class UserListener { @Autowired private UserHistoricalRepository userHistoricalRepository; @PostPersist @PostUpdate public void saveUserHistorical(User user) { UserHistorical userHistorical = new UserHistorical(); userHistorical.setId(user.getId()); userHistorical.setName(user.getName()); userHistorical.setEmail(user.getEmail()); userHistorical.setDateCreation(new Date()); userHistoricalRepository.save(userHistorical); } }
- 配置文件中添加织入参数:
spring.aop.proxy-target-class=true spring.main.allow-circular-references=true
此方案需要额外处理AspectJ织入,部署环节可能有额外要求,优先级低于前两种方案。
额外修正点
UserHistoricalRepository的泛型写错了,应该是JpaRepository<UserHistorical, Long>,而非JpaRepository<User, Long>;- 代码中存在拼写错误:
new userHistorical()应改为new UserHistorical(),personaHistorico应改为userHistorical,这些错误会导致编译或运行异常。
内容的提问来源于stack exchange,提问作者Darklawz
相关产品推荐
相关产品推荐

