Spring Boot 3+自注入实现代理与事务:循环依赖问题求解
问题背景
要在服务类中创建自引用,通过Spring代理触发@Transactional事务行为,同时满足java:S6809规范(类内部直接调用带@Async/@Transactional的方法会绕过代理,导致注解失效)。
编写的代码如下:
@RequiredArgsConstructor public class Service { @Lazy private final Service self; public void foo() { self.bar(); } @Transactional public void bar() { // do some transactional stuff } }
启动时触发循环依赖错误:
The dependencies of some of the beans in the application context form a cycle:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
已尝试@Lazy注解及Lombok配置lombok.copyableAnnotations += org.springframework.context.annotation.Lazy,但问题未解决,询问是否只能通过开启循环引用配置解决。
无需开启循环引用的解决方法
1. 实现ApplicationContextAware获取代理实例
让服务类实现ApplicationContextAware接口,在初始化完成后从上下文获取自身代理对象,完全规避构造注入的循环依赖:
@Service public class Service implements ApplicationContextAware { private Service self; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.self = applicationContext.getBean(Service.class); } public void foo() { self.bar(); } @Transactional public void bar() { // do some transactional stuff } }
2. 改用@Autowired+@Lazy字段注入
放弃构造注入,改用字段注入并结合@Lazy,确保类被Spring管理(添加@Service注解):
@Service public class Service { @Autowired @Lazy private Service self; public void foo() { self.bar(); } @Transactional public void bar() { // do some transactional stuff } }
构造注入时@Lazy可能因Lombok注解传递问题失效,字段注入的延迟加载能有效打破循环依赖。
3. 拆分业务到独立服务
若业务允许,将带事务的方法拆分到独立子服务,直接注入调用,从根源避免自引用需求:
@Service public class Service { private final SubService subService; public Service(SubService subService) { this.subService = subService; } public void foo() { subService.bar(); } } @Service public class SubService { @Transactional public void bar() { // do some transactional stuff } }
构造注入+@Lazy失效的原因
Lombok的@RequiredArgsConstructor默认不会将字段上的@Lazy复制到构造器参数,即便配置了lombok.copyableAnnotations也可能因版本或配置生效问题失效。可手动编写构造器验证:
@Service public class Service { private final Service self; public Service(@Lazy Service self) { this.self = self; } // 方法省略 }
若手动添加参数级@Lazy解决问题,说明是Lombok配置未生效;若仍失败,建议改用前两种方案。
内容的提问来源于stack exchange,提问作者dorian.naa

