1. 报错信息如下
dart
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'webConfig' defined in file [E:\code_repository\target\classes\com\kym\kymconfig\config\WebConfig.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userDetailsServiceImpl': Unsatisfied dependency expressed through field 'dictService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dictServiceImpl': Unsatisfied dependency expressed through field 'aService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'AServiceImpl': Unsatisfied dependency expressed through field 'orderService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderServiceImpl': Unsatisfied dependency expressed through field 'order'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.kym.kymconfig.sdk.ISdkOrder' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
2. 代码如下
java
@Configuration
@Order(1)
public class WebConfig {
private UserDetailsService userDetailsService;
@Bean
public Map<String, Object> initMap() {
return new HashMap<>();
}
public WebConfig(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
}
@Order(2)
@Configuration
public class SdkConfig {
@Autowired
private DictService dictService;
@Bean
public ISdkOrder init() {
try {
int dict = dictService.getDict();
return new SdkOrder(dict);
} catch (Exception e) {
return null;
}
}
}
public interface ISdkOrder {
void track(@NonNull int eventRecord);
}
public interface AService {
int doS();
}
public interface DictService {
int getDict();
}
public interface OrderService {
void track(int num);
}
public interface UserDetailsService {
int loadUserByUsername(String username);
}
public class SdkOrder implements ISdkOrder {
private final int worker;
public SdkOrder(final int sdkProducer) {
worker = sdkProducer;
}
@Override
public void track(@NonNull int eventRecord) {
System.out.println(eventRecord);
}
}
@Service
public class AServiceImpl implements AService {
@Autowired
private OrderService orderService;
@Override
public int doS() {
orderService.track(8);
return 0;
}
}
@Service
public class DictServiceImpl implements DictService {
@Autowired
private AService aService;
@Override
public int getDict() {
aService.doS();
return 5;
}
}
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
@Lazy
private ISdkOrder order;
@Override
public void track(int num) {
order.track(num);
}
}
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private DictService dictService;
@Override
public int loadUserByUsername(String username) {
dictService.getDict();
return 1;
}
}
3. 依赖链分析
-
完整循环路径
textWebConfig → UserDetailsServiceImpl → DictServiceImpl → AServiceImpl → OrderServiceImpl → IOrder(需SdkConfig初始化)
最终
SdkConfig
又依赖DictService
形成闭环。 -
关键问题点
OrderServiceImpl
需要注入ISdkOrder
,但ISdkOrder
的初始化依赖DictService
DictService
又通过AService
间接依赖OrderService
,形成死锁
4. 解决方案
方案:打破循环依赖
java
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
@Lazy
private ISdkOrder order;
@Override
public void track(int num) {
order.track(num);
}
}
在自动注入ISdkOrder
时添加@Lazy
注解。
这样解决了循环依赖问题,原因在于 @Lazy
注解改变了 Spring 的依赖注入行为。具体原理如下:
-
延迟加载机制
@Lazy
使 Spring 不会在容器启动时立即注入真实的ISdkOrder
Bean,而是先注入一个代理对象。只有当第一次实际调用order.track()
方法时,才会触发真实 Bean 的初始化。 -
打破初始化死锁
原始循环依赖链:
textOrderServiceImpl → ISdkOrder → DictService → AService → OrderServiceImpl
添加
@Lazy
后,ISdkOrder
的初始化被推迟,使得其他 Bean 能先完成初始化。 -
代理对象特性
Spring 会生成一个 CGLIB 代理对象暂时代替
ISdkOrder
,这个代理对象不依赖DictService
等下游组件,因此容器可以顺利完成初始化阶段。 -
运行时解析
当实际调用
track()
方法时,代理对象会检查真实 Bean 是否已初始化。此时其他组件都已就绪,可以安全创建真实ISdkOrder
实例。
这种方案适用于需要保留原有代码结构的场景,但要注意:
- 代理对象会增加少量性能开销
- 首次调用可能出现短暂延迟
- 需确保被
@Lazy
标注的字段不会在@PostConstruct
方法中使用