Spring Boot 啟動(dòng)失敗:循環(huán)依賴排查到懶加載配置的過(guò)程解析
Spring Boot 啟動(dòng)失?。貉h(huán)依賴排查到懶加載配置的坑
摘要
作為一名在Spring Boot生態(tài)中摸爬滾打多年的開(kāi)發(fā)者,我深知循環(huán)依賴問(wèn)題是每個(gè)Java工程師都會(huì)遇到的經(jīng)典難題。最近在一個(gè)微服務(wù)項(xiàng)目中,我遭遇了一個(gè)看似簡(jiǎn)單卻極其隱蔽的啟動(dòng)失敗問(wèn)題:應(yīng)用在本地開(kāi)發(fā)環(huán)境運(yùn)行正常,但在生產(chǎn)環(huán)境部署時(shí)卻頻繁出現(xiàn)循環(huán)依賴異常。經(jīng)過(guò)深入排查,我發(fā)現(xiàn)這個(gè)問(wèn)題的根源竟然隱藏在Spring Boot 2.6版本后的懶加載配置變更中。
這次排查過(guò)程讓我重新審視了Spring容器的依賴注入機(jī)制,從最基礎(chǔ)的Bean生命周期到高級(jí)的循環(huán)依賴檢測(cè)算法,從傳統(tǒng)的setter注入到現(xiàn)代的構(gòu)造器注入最佳實(shí)踐。我發(fā)現(xiàn)許多開(kāi)發(fā)者對(duì)循環(huán)依賴的理解還停留在表面,往往只知道使用@Lazy注解來(lái)"解決"問(wèn)題,卻不明白背后的原理和潛在風(fēng)險(xiǎn)。更令人擔(dān)憂的是,隨著Spring Boot版本的不斷演進(jìn),一些默認(rèn)配置的變化可能會(huì)讓原本運(yùn)行良好的代碼突然出現(xiàn)問(wèn)題。
在這篇文章中,我將從一個(gè)真實(shí)的生產(chǎn)環(huán)境故障案例出發(fā),帶你深入了解Spring Boot循環(huán)依賴的檢測(cè)機(jī)制、排查方法和解決方案。我們將探討從Spring 5.1到Spring Boot 2.6版本中循環(huán)依賴處理邏輯的重要變化,分析不同注入方式對(duì)循環(huán)依賴的影響,并提供一套完整的最佳實(shí)踐指南。通過(guò)系統(tǒng)性的分析和實(shí)戰(zhàn)演練,你將掌握如何在復(fù)雜的企業(yè)級(jí)應(yīng)用中優(yōu)雅地處理循環(huán)依賴問(wèn)題,避免因?yàn)榕渲貌划?dāng)而導(dǎo)致的生產(chǎn)事故。
1. 循環(huán)依賴問(wèn)題概述
1.1 什么是循環(huán)依賴
循環(huán)依賴是指兩個(gè)或多個(gè)Bean之間存在相互依賴的關(guān)系,形成一個(gè)閉環(huán)。在Spring容器初始化過(guò)程中,如果檢測(cè)到循環(huán)依賴且無(wú)法通過(guò)三級(jí)緩存機(jī)制解決,就會(huì)拋出BeanCurrentlyInCreationException異常。

圖1:循環(huán)依賴關(guān)系圖 - 展示Bean間的相互依賴關(guān)系
1.2 Spring的三級(jí)緩存機(jī)制
Spring通過(guò)三級(jí)緩存來(lái)解決循環(huán)依賴問(wèn)題:
// Spring容器中的三級(jí)緩存
public class DefaultSingletonBeanRegistry {
// 一級(jí)緩存:完整的Bean實(shí)例
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 二級(jí)緩存:早期Bean實(shí)例(未完成屬性注入)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
// 三級(jí)緩存:Bean工廠對(duì)象
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/**
* 獲取單例Bean的核心方法
* 依次從三級(jí)緩存中查找Bean實(shí)例
*/
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 從一級(jí)緩存獲取完整Bean
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 從二級(jí)緩存獲取早期Bean
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// 從三級(jí)緩存獲取Bean工廠
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
// 將早期Bean放入二級(jí)緩存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
}這段代碼展示了Spring三級(jí)緩存的核心邏輯:首先嘗試從一級(jí)緩存獲取完整的Bean實(shí)例,如果獲取不到且Bean正在創(chuàng)建中,則依次從二級(jí)和三級(jí)緩存中查找。
2. 循環(huán)依賴檢測(cè)機(jī)制演進(jìn)
2.1 Spring Boot 2.6版本的重大變化
Spring Boot 2.6版本引入了一個(gè)重要變化:默認(rèn)禁用了循環(huán)依賴。這個(gè)變化通過(guò)spring.main.allow-circular-references配置項(xiàng)控制。

圖2:Spring循環(huán)依賴處理演進(jìn)時(shí)間線 - 展示關(guān)鍵版本變化
2.2 配置項(xiàng)詳解
# application.yml
spring:
main:
# 是否允許循環(huán)依賴(Spring Boot 2.6+默認(rèn)為false)
allow-circular-references: true
# 懶加載配置
lazy-initialization: false
# 更細(xì)粒度的配置
management:
endpoints:
web:
exposure:
include: beans,health,info這個(gè)配置變化的影響范圍很廣,許多原本運(yùn)行正常的應(yīng)用在升級(jí)到Spring Boot 2.6后可能會(huì)遇到啟動(dòng)失敗的問(wèn)題。
3. 循環(huán)依賴類型分析
3.1 不同注入方式的循環(huán)依賴表現(xiàn)
注入方式 | 循環(huán)依賴支持 | 檢測(cè)時(shí)機(jī) | 解決難度 | 推薦程度 |
構(gòu)造器注入 | ? 不支持 | 實(shí)例化時(shí) | 困難 | ????? |
Setter注入 | ? 支持 | 屬性注入時(shí) | 簡(jiǎn)單 | ??? |
字段注入 | ? 支持 | 屬性注入時(shí) | 簡(jiǎn)單 | ?? |
方法注入 | ? 支持 | 方法調(diào)用時(shí) | 中等 | ???? |
3.2 構(gòu)造器注入循環(huán)依賴示例
@Service
public class UserService {
private final OrderService orderService;
// 構(gòu)造器注入 - 會(huì)導(dǎo)致循環(huán)依賴異常
public UserService(OrderService orderService) {
this.orderService = orderService;
}
public void processUser(Long userId) {
// 業(yè)務(wù)邏輯
orderService.getOrdersByUser(userId);
}
}
@Service
public class OrderService {
private final UserService userService;
// 構(gòu)造器注入 - 形成循環(huán)依賴
public OrderService(UserService userService) {
this.userService = userService;
}
public List<Order> getOrdersByUser(Long userId) {
// 業(yè)務(wù)邏輯
userService.processUser(userId);
return Collections.emptyList();
}
}上述代碼在Spring Boot 2.6+版本中會(huì)拋出循環(huán)依賴異常,因?yàn)闃?gòu)造器注入無(wú)法通過(guò)三級(jí)緩存機(jī)制解決。
3.3 Setter注入解決方案
@Service
public class UserService {
private OrderService orderService;
// 使用Setter注入避免循環(huán)依賴
@Autowired
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
public void processUser(Long userId) {
if (orderService != null) {
orderService.getOrdersByUser(userId);
}
}
}
@Service
public class OrderService {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public List<Order> getOrdersByUser(Long userId) {
if (userService != null) {
// 注意:這里可能導(dǎo)致無(wú)限遞歸
// userService.processUser(userId);
}
return Collections.emptyList();
}
}這種方式可以解決循環(huán)依賴問(wèn)題,但需要注意空指針檢查和潛在的無(wú)限遞歸風(fēng)險(xiǎn)。
4. 懶加載配置的陷阱
4.1 懶加載與循環(huán)依賴的關(guān)系

圖3:懶加載與循環(huán)依賴檢測(cè)時(shí)序圖 - 展示不同配置下的行為差異
4.2 懶加載配置示例
@Configuration
@EnableConfigurationProperties
public class LazyLoadingConfig {
/**
* 全局懶加載配置
* 注意:這可能會(huì)掩蓋循環(huán)依賴問(wèn)題
*/
@Bean
@Lazy
public UserService userService() {
return new UserService();
}
/**
* 選擇性懶加載
* 推薦:只對(duì)特定Bean使用懶加載
*/
@Bean
@Lazy
@ConditionalOnProperty(name = "app.lazy-loading.enabled", havingValue = "true")
public ExpensiveService expensiveService() {
return new ExpensiveService();
}
}
// 在Bean級(jí)別使用懶加載
@Service
@Lazy // 這個(gè)注解會(huì)延遲Bean的創(chuàng)建
public class ReportService {
@Autowired
@Lazy // 延遲注入依賴
private DataService dataService;
public void generateReport() {
// 只有在實(shí)際調(diào)用時(shí)才會(huì)創(chuàng)建dataService
dataService.fetchData();
}
}懶加載雖然可以避免啟動(dòng)時(shí)的循環(huán)依賴檢測(cè),但可能會(huì)將問(wèn)題延遲到運(yùn)行時(shí),增加排查難度。
5. 實(shí)戰(zhàn)排查方法
5.1 循環(huán)依賴檢測(cè)工具
@Component
public class CircularDependencyDetector implements BeanPostProcessor {
private static final Logger logger = LoggerFactory.getLogger(CircularDependencyDetector.class);
private final Set<String> beansInCreation = ConcurrentHashMap.newKeySet();
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
beansInCreation.add(beanName);
logger.debug("開(kāi)始創(chuàng)建Bean: {}", beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
beansInCreation.remove(beanName);
logger.debug("完成創(chuàng)建Bean: {}", beanName);
// 檢測(cè)潛在的循環(huán)依賴
if (hasCircularDependency(bean)) {
logger.warn("檢測(cè)到潛在循環(huán)依賴: {}", beanName);
}
return bean;
}
/**
* 檢測(cè)Bean是否存在循環(huán)依賴
*/
private boolean hasCircularDependency(Object bean) {
Class<?> beanClass = bean.getClass();
Field[] fields = beanClass.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Autowired.class)) {
String fieldTypeName = field.getType().getSimpleName();
if (beansInCreation.contains(fieldTypeName.toLowerCase())) {
return true;
}
}
}
return false;
}
}這個(gè)檢測(cè)器可以幫助我們?cè)陂_(kāi)發(fā)階段及早發(fā)現(xiàn)潛在的循環(huán)依賴問(wèn)題。
5.2 啟動(dòng)日志分析
@Configuration
@Slf4j
public class BeanCreationMonitor {
@EventListener
public void handleContextRefresh(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
String[] beanNames = context.getBeanDefinitionNames();
log.info("=== Bean創(chuàng)建統(tǒng)計(jì) ===");
log.info("總Bean數(shù)量: {}", beanNames.length);
// 分析Bean依賴關(guān)系
analyzeBeanDependencies(context, beanNames);
}
private void analyzeBeanDependencies(ApplicationContext context, String[] beanNames) {
Map<String, Set<String>> dependencyGraph = new HashMap<>();
for (String beanName : beanNames) {
try {
BeanDefinition beanDefinition = ((ConfigurableApplicationContext) context)
.getBeanFactory().getBeanDefinition(beanName);
if (beanDefinition instanceof AbstractBeanDefinition) {
String[] dependsOn = ((AbstractBeanDefinition) beanDefinition).getDependsOn();
if (dependsOn != null) {
dependencyGraph.put(beanName, Set.of(dependsOn));
}
}
} catch (Exception e) {
log.debug("無(wú)法獲取Bean定義: {}", beanName);
}
}
// 檢測(cè)循環(huán)依賴
detectCircularDependencies(dependencyGraph);
}
private void detectCircularDependencies(Map<String, Set<String>> graph) {
Set<String> visited = new HashSet<>();
Set<String> recursionStack = new HashSet<>();
for (String node : graph.keySet()) {
if (hasCycle(graph, node, visited, recursionStack)) {
log.warn("檢測(cè)到循環(huán)依賴路徑包含: {}", node);
}
}
}
private boolean hasCycle(Map<String, Set<String>> graph, String node,
Set<String> visited, Set<String> recursionStack) {
if (recursionStack.contains(node)) {
return true;
}
if (visited.contains(node)) {
return false;
}
visited.add(node);
recursionStack.add(node);
Set<String> neighbors = graph.get(node);
if (neighbors != null) {
for (String neighbor : neighbors) {
if (hasCycle(graph, neighbor, visited, recursionStack)) {
return true;
}
}
}
recursionStack.remove(node);
return false;
}
}這個(gè)監(jiān)控器可以在應(yīng)用啟動(dòng)時(shí)分析Bean的依賴關(guān)系,幫助識(shí)別潛在的循環(huán)依賴問(wèn)題。
6. 解決方案最佳實(shí)踐
6.1 架構(gòu)重構(gòu)方案

圖4:循環(huán)依賴解決方案占比圖 - 展示不同方案的使用頻率
6.2 事件驅(qū)動(dòng)解耦
// 定義業(yè)務(wù)事件
@Data
@AllArgsConstructor
public class UserCreatedEvent {
private Long userId;
private String username;
private LocalDateTime createdAt;
}
@Service
public class UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void createUser(CreateUserRequest request) {
// 創(chuàng)建用戶邏輯
User user = new User();
user.setUsername(request.getUsername());
user.setCreatedAt(LocalDateTime.now());
// 保存用戶
// userRepository.save(user);
// 發(fā)布事件而不是直接調(diào)用其他服務(wù)
eventPublisher.publishEvent(new UserCreatedEvent(
user.getId(),
user.getUsername(),
user.getCreatedAt()
));
}
}
@Service
public class OrderService {
// 通過(guò)事件監(jiān)聽(tīng)處理業(yè)務(wù)邏輯,避免直接依賴UserService
@EventListener
@Async
public void handleUserCreated(UserCreatedEvent event) {
// 為新用戶創(chuàng)建默認(rèn)訂單配置
createDefaultOrderSettings(event.getUserId());
}
private void createDefaultOrderSettings(Long userId) {
// 創(chuàng)建默認(rèn)訂單設(shè)置的邏輯
log.info("為用戶 {} 創(chuàng)建默認(rèn)訂單設(shè)置", userId);
}
}事件驅(qū)動(dòng)模式可以有效解耦服務(wù)間的直接依賴關(guān)系,是解決循環(huán)依賴的優(yōu)雅方案。
6.3 接口抽象解耦
// 定義接口抽象
public interface UserOperations {
void processUser(Long userId);
UserInfo getUserInfo(Long userId);
}
public interface OrderOperations {
List<Order> getOrdersByUser(Long userId);
void createOrder(CreateOrderRequest request);
}
// 實(shí)現(xiàn)類只依賴接口
@Service
public class UserServiceImpl implements UserOperations {
@Autowired
private OrderOperations orderOperations; // 依賴接口而非具體實(shí)現(xiàn)
@Override
public void processUser(Long userId) {
// 處理用戶邏輯
List<Order> orders = orderOperations.getOrdersByUser(userId);
// 進(jìn)一步處理
}
@Override
public UserInfo getUserInfo(Long userId) {
return new UserInfo(userId, "用戶" + userId);
}
}
@Service
public class OrderServiceImpl implements OrderOperations {
@Autowired
private UserOperations userOperations; // 依賴接口而非具體實(shí)現(xiàn)
@Override
public List<Order> getOrdersByUser(Long userId) {
// 獲取用戶信息
UserInfo userInfo = userOperations.getUserInfo(userId);
// 返回訂單列表
return Collections.emptyList();
}
@Override
public void createOrder(CreateOrderRequest request) {
// 創(chuàng)建訂單邏輯
}
}通過(guò)接口抽象,我們可以在保持業(yè)務(wù)邏輯完整性的同時(shí),降低類之間的耦合度。
7. 性能影響分析
7.1 不同解決方案的性能對(duì)比

圖5:循環(huán)依賴解決方案性能對(duì)比圖 - 展示不同方案的性能表現(xiàn)
7.2 內(nèi)存使用分析
@Component
@Slf4j
public class MemoryUsageAnalyzer {
private final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
@EventListener
public void analyzeMemoryUsage(ContextRefreshedEvent event) {
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
log.info("=== 內(nèi)存使用分析 ===");
log.info("堆內(nèi)存使用: {} MB / {} MB",
heapUsage.getUsed() / 1024 / 1024,
heapUsage.getMax() / 1024 / 1024);
log.info("非堆內(nèi)存使用: {} MB / {} MB",
nonHeapUsage.getUsed() / 1024 / 1024,
nonHeapUsage.getMax() / 1024 / 1024);
// 分析Bean實(shí)例占用的內(nèi)存
analyzeBeanMemoryUsage(event.getApplicationContext());
}
private void analyzeBeanMemoryUsage(ApplicationContext context) {
String[] beanNames = context.getBeanDefinitionNames();
Map<String, Long> beanSizes = new HashMap<>();
for (String beanName : beanNames) {
try {
Object bean = context.getBean(beanName);
long size = calculateObjectSize(bean);
beanSizes.put(beanName, size);
} catch (Exception e) {
log.debug("無(wú)法計(jì)算Bean大小: {}", beanName);
}
}
// 輸出占用內(nèi)存最多的前10個(gè)Bean
beanSizes.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
.limit(10)
.forEach(entry -> log.info("Bean: {} - 大小: {} bytes",
entry.getKey(), entry.getValue()));
}
private long calculateObjectSize(Object obj) {
// 簡(jiǎn)化的對(duì)象大小計(jì)算
// 實(shí)際項(xiàng)目中可以使用更精確的工具如JOL
return obj.toString().length() * 2; // 粗略估算
}
}這個(gè)分析器可以幫助我們了解不同循環(huán)依賴解決方案對(duì)內(nèi)存使用的影響。
8. 監(jiān)控與告警
8.1 循環(huán)依賴監(jiān)控指標(biāo)
@Component
public class CircularDependencyMetrics {
private final MeterRegistry meterRegistry;
private final Counter circularDependencyCounter;
private final Timer beanCreationTimer;
public CircularDependencyMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.circularDependencyCounter = Counter.builder("circular.dependency.detected")
.description("檢測(cè)到的循環(huán)依賴數(shù)量")
.register(meterRegistry);
this.beanCreationTimer = Timer.builder("bean.creation.time")
.description("Bean創(chuàng)建耗時(shí)")
.register(meterRegistry);
}
public void recordCircularDependency(String beanName) {
circularDependencyCounter.increment(
Tags.of("bean.name", beanName)
);
}
public void recordBeanCreationTime(String beanName, Duration duration) {
beanCreationTimer.record(duration,
Tags.of("bean.name", beanName)
);
}
@EventListener
public void handleApplicationReady(ApplicationReadyEvent event) {
// 應(yīng)用啟動(dòng)完成后,輸出循環(huán)依賴統(tǒng)計(jì)
double totalCircularDependencies = circularDependencyCounter.count();
if (totalCircularDependencies > 0) {
log.warn("應(yīng)用啟動(dòng)過(guò)程中檢測(cè)到 {} 個(gè)循環(huán)依賴", totalCircularDependencies);
}
}
}通過(guò)監(jiān)控指標(biāo),我們可以及時(shí)發(fā)現(xiàn)和處理循環(huán)依賴問(wèn)題。
8.2 告警配置
# Prometheus告警規(guī)則
groups:
- name: spring-boot-circular-dependency
rules:
- alert: CircularDependencyDetected
expr: increase(circular_dependency_detected_total[5m]) > 0
for: 0m
labels:
severity: warning
annotations:
summary: "檢測(cè)到循環(huán)依賴"
description: "應(yīng)用 {{ $labels.application }} 在過(guò)去5分鐘內(nèi)檢測(cè)到循環(huán)依賴"
- alert: BeanCreationTimeHigh
expr: histogram_quantile(0.95, rate(bean_creation_time_seconds_bucket[5m])) > 10
for: 2m
labels:
severity: critical
annotations:
summary: "Bean創(chuàng)建耗時(shí)過(guò)長(zhǎng)"
description: "Bean創(chuàng)建95%分位數(shù)耗時(shí)超過(guò)10秒"最佳實(shí)踐提醒
循環(huán)依賴不僅僅是技術(shù)問(wèn)題,更是架構(gòu)設(shè)計(jì)問(wèn)題。優(yōu)秀的架構(gòu)設(shè)計(jì)應(yīng)該避免循環(huán)依賴的產(chǎn)生,而不是依賴框架的機(jī)制來(lái)解決。在設(shè)計(jì)階段就應(yīng)該考慮模塊間的依賴關(guān)系,遵循單一職責(zé)原則和依賴倒置原則。
9. 故障案例復(fù)盤
9.1 生產(chǎn)環(huán)境故障分析
在一次生產(chǎn)環(huán)境部署中,我們遇到了一個(gè)典型的循環(huán)依賴問(wèn)題。應(yīng)用在本地和測(cè)試環(huán)境運(yùn)行正常,但在生產(chǎn)環(huán)境啟動(dòng)時(shí)卻拋出了循環(huán)依賴異常。

圖6:循環(huán)依賴問(wèn)題影響矩陣 - 展示不同場(chǎng)景下的影響程度
9.2 根因分析
經(jīng)過(guò)深入分析,我們發(fā)現(xiàn)問(wèn)題的根源在于:
- 環(huán)境配置差異:生產(chǎn)環(huán)境使用了Spring Boot 2.6版本,而本地環(huán)境使用的是2.4版本
- 懶加載配置不一致:生產(chǎn)環(huán)境禁用了懶加載,導(dǎo)致啟動(dòng)時(shí)立即檢測(cè)循環(huán)依賴
- Bean創(chuàng)建順序變化:不同環(huán)境下Bean的創(chuàng)建順序可能不同
// 問(wèn)題代碼示例
@Service
public class PaymentService {
@Autowired
private OrderService orderService; // 構(gòu)造器注入導(dǎo)致循環(huán)依賴
public void processPayment(Long orderId) {
Order order = orderService.getOrder(orderId);
// 支付處理邏輯
}
}
@Service
public class OrderService {
@Autowired
private PaymentService paymentService; // 形成循環(huán)依賴
public Order getOrder(Long orderId) {
// 獲取訂單邏輯
return new Order();
}
}9.3 解決方案實(shí)施
我們采用了多層次的解決方案:
// 解決方案1:重構(gòu)為事件驅(qū)動(dòng)架構(gòu)
@Service
public class PaymentService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void processPayment(PaymentRequest request) {
// 處理支付邏輯
Payment payment = createPayment(request);
// 發(fā)布支付完成事件
eventPublisher.publishEvent(new PaymentCompletedEvent(
payment.getId(),
payment.getOrderId(),
payment.getAmount()
));
}
private Payment createPayment(PaymentRequest request) {
// 創(chuàng)建支付記錄
return new Payment();
}
}
@Service
public class OrderService {
// 通過(guò)事件監(jiān)聽(tīng)處理訂單狀態(tài)更新
@EventListener
@Async
public void handlePaymentCompleted(PaymentCompletedEvent event) {
updateOrderStatus(event.getOrderId(), OrderStatus.PAID);
}
public Order getOrder(Long orderId) {
// 獲取訂單邏輯
return new Order();
}
private void updateOrderStatus(Long orderId, OrderStatus status) {
// 更新訂單狀態(tài)
log.info("訂單 {} 狀態(tài)更新為: {}", orderId, status);
}
}10. 最佳實(shí)踐總結(jié)
10.1 設(shè)計(jì)原則
- 依賴倒置原則:依賴抽象而非具體實(shí)現(xiàn)
- 單一職責(zé)原則:每個(gè)類只負(fù)責(zé)一個(gè)職責(zé)
- 開(kāi)閉原則:對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉
10.2 代碼規(guī)范
// 推薦:使用構(gòu)造器注入 + 接口依賴
@Service
public class RecommendedUserService {
private final UserRepository userRepository;
private final NotificationService notificationService;
// 構(gòu)造器注入,依賴明確
public RecommendedUserService(UserRepository userRepository,
NotificationService notificationService) {
this.userRepository = userRepository;
this.notificationService = notificationService;
}
public void createUser(CreateUserRequest request) {
User user = new User(request.getUsername(), request.getEmail());
userRepository.save(user);
// 異步發(fā)送通知,避免強(qiáng)依賴
notificationService.sendWelcomeNotification(user.getId());
}
}
// 不推薦:字段注入 + 循環(huán)依賴
@Service
public class NotRecommendedUserService {
@Autowired
private OrderService orderService; // 可能導(dǎo)致循環(huán)依賴
@Autowired
private PaymentService paymentService; // 可能導(dǎo)致循環(huán)依賴
// 業(yè)務(wù)邏輯與依賴管理混合
public void createUser(CreateUserRequest request) {
// 復(fù)雜的業(yè)務(wù)邏輯
}
}10.3 配置管理
# 推薦的配置方式
spring:
main:
# 明確禁用循環(huán)依賴,強(qiáng)制良好的架構(gòu)設(shè)計(jì)
allow-circular-references: false
# 根據(jù)需要配置懶加載
lazy-initialization: false
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
# 環(huán)境特定配置
---
spring:
config:
activate:
on-profile: prod
main:
# 生產(chǎn)環(huán)境嚴(yán)格模式
allow-circular-references: false
lazy-initialization: false
---
spring:
config:
activate:
on-profile: dev
main:
# 開(kāi)發(fā)環(huán)境可以適當(dāng)放寬
allow-circular-references: true
lazy-initialization: true11. 工具與資源
11.1 開(kāi)發(fā)工具推薦
工具名稱 | 功能描述 | 使用場(chǎng)景 | 推薦指數(shù) |
Spring Boot Actuator | 應(yīng)用監(jiān)控和管理 | 生產(chǎn)環(huán)境監(jiān)控 | ????? |
JProfiler | Java性能分析 | 性能調(diào)優(yōu) | ???? |
VisualVM | JVM監(jiān)控工具 | 內(nèi)存分析 | ???? |
SonarQube | 代碼質(zhì)量檢測(cè) | 代碼審查 | ????? |
Micrometer | 指標(biāo)收集 | 監(jiān)控告警 | ???? |
11.2 檢測(cè)腳本
#!/bin/bash
# 循環(huán)依賴檢測(cè)腳本
echo "=== Spring Boot 循環(huán)依賴檢測(cè) ==="
# 檢查Spring Boot版本
echo "檢查Spring Boot版本..."
grep -r "spring-boot-starter" pom.xml | head -5
# 檢查循環(huán)依賴配置
echo "檢查循環(huán)依賴配置..."
find . -name "*.yml" -o -name "*.yaml" -o -name "*.properties" | \
xargs grep -l "allow-circular-references\|lazy-initialization"
# 掃描可能的循環(huán)依賴
echo "掃描潛在循環(huán)依賴..."
find . -name "*.java" -exec grep -l "@Autowired\|@Inject" {} \; | \
while read file; do
echo "分析文件: $file"
# 簡(jiǎn)單的循環(huán)依賴檢測(cè)邏輯
done
echo "檢測(cè)完成!"總結(jié)
通過(guò)這次深入的循環(huán)依賴問(wèn)題排查和解決過(guò)程,我深刻體會(huì)到了架構(gòu)設(shè)計(jì)的重要性。循環(huán)依賴不僅僅是一個(gè)技術(shù)問(wèn)題,更是架構(gòu)設(shè)計(jì)和代碼質(zhì)量的體現(xiàn)。在我多年的Spring Boot開(kāi)發(fā)經(jīng)驗(yàn)中,我發(fā)現(xiàn)最好的解決方案往往不是依賴框架的機(jī)制來(lái)"修復(fù)"循環(huán)依賴,而是從根本上避免循環(huán)依賴的產(chǎn)生。
Spring Boot 2.6版本默認(rèn)禁用循環(huán)依賴的決定是明智的,它迫使開(kāi)發(fā)者重新審視自己的架構(gòu)設(shè)計(jì),采用更加清晰和可維護(hù)的依賴關(guān)系。雖然這可能會(huì)在短期內(nèi)增加一些重構(gòu)工作,但從長(zhǎng)遠(yuǎn)來(lái)看,這將大大提高代碼的質(zhì)量和可維護(hù)性。
在實(shí)際項(xiàng)目中,我建議采用以下策略來(lái)處理循環(huán)依賴問(wèn)題:首先,在設(shè)計(jì)階段就要考慮模塊間的依賴關(guān)系,遵循SOLID原則;其次,優(yōu)先使用事件驅(qū)動(dòng)架構(gòu)來(lái)解耦服務(wù)間的直接依賴;最后,建立完善的監(jiān)控和告警機(jī)制,及時(shí)發(fā)現(xiàn)和處理潛在的循環(huán)依賴問(wèn)題。
記住,優(yōu)秀的架構(gòu)設(shè)計(jì)應(yīng)該像一首和諧的交響樂(lè),每個(gè)組件都有自己的職責(zé),相互協(xié)作而不相互依賴。只有這樣,我們才能構(gòu)建出真正健壯、可擴(kuò)展的企業(yè)級(jí)應(yīng)用。在技術(shù)的道路上,讓我們始終保持對(duì)代碼質(zhì)量的追求,在每一次重構(gòu)中都能聽(tīng)到架構(gòu)優(yōu)化的美妙音符。
參考鏈接
到此這篇關(guān)于Spring Boot 啟動(dòng)失敗:循環(huán)依賴排查到懶加載配置的過(guò)程解析的文章就介紹到這了,更多相關(guān)Spring Boot 循環(huán)依賴內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 如何解決SpringBoot2.6及之后版本取消了循環(huán)依賴的支持問(wèn)題
- SpringBoot構(gòu)造器注入循環(huán)依賴及解決方案
- SpringBoot中@Autowired注入service時(shí)出現(xiàn)循環(huán)依賴問(wèn)題的解決方法
- SpringBoot3.x循環(huán)依賴問(wèn)題解決方案
- springboot配置允許循環(huán)依賴問(wèn)題
- SpringBoot啟動(dòng)報(bào)錯(cuò)屬性循環(huán)依賴報(bào)錯(cuò)問(wèn)題的解決
- SpringBoot JPA懶加載失效的解決方案(親測(cè)有效)
- 在springboot中實(shí)現(xiàn)個(gè)別bean懶加載的操作
相關(guān)文章
SpringCloud配置中心Config過(guò)程解析
這篇文章主要介紹了SpringCloud配置中心Config過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
java通過(guò)控制鼠標(biāo)實(shí)現(xiàn)屏幕廣播的方法
這篇文章主要介紹了java通過(guò)控制鼠標(biāo)實(shí)現(xiàn)屏幕廣播的方法,針對(duì)前面一篇Java屏幕共享功能進(jìn)行了改進(jìn),實(shí)現(xiàn)了鼠標(biāo)控制功能,具有一定的實(shí)用價(jià)值,需要的朋友可以參考下2014-12-12
Java Collections.EMPTY_LIST與Collections.emptyList()的區(qū)別
這篇文章主要介紹了Java Collections.EMPTY_LIST與Collections.emptyList()的區(qū)別,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
Maven最佳實(shí)踐之一個(gè)好的parent依賴基礎(chǔ)
今天小編就為大家分享一篇關(guān)于Maven最佳實(shí)踐之一個(gè)好的parent依賴基礎(chǔ),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12
如何利用java控制鼠標(biāo)操作一些重復(fù)的事情
這篇文章主要給大家介紹了關(guān)于如何利用java控制鼠標(biāo)操作一些重復(fù)的事情,主要利用的是Robot類,Robot可以模擬鼠標(biāo)和鍵盤的輸入,相當(dāng)于Java版的按鍵精靈,需要的朋友可以參考下2021-12-12
Java面試題沖刺第十九天--數(shù)據(jù)庫(kù)(4)
這篇文章主要為大家分享了最有價(jià)值的三道關(guān)于數(shù)據(jù)庫(kù)的面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下2021-08-08
MyBatis-Plus條件構(gòu)造器Wrapper應(yīng)用實(shí)例
QueryWrapper是用于查詢的Wrapper條件構(gòu)造器,可以通過(guò)它來(lái)構(gòu)建SELECT語(yǔ)句中的WHERE條件,這篇文章主要介紹了MyBatis-Plus數(shù)據(jù)表操作條件構(gòu)造器Wrapper,需要的朋友可以參考下2023-09-09

