Spring如何解決循環依賴:深入理解三級緩存機制

Spring如何解決循環依賴:深入理解三級緩存機制

引言

在我們之前的文章中,我們探討了什么是循環依賴以及它帶來的問題。作為Java生態系統中最重要的框架之一,Spring Framework在處理循環依賴方面有著獨特而精妙的解決方案。今天,讓我們深入了解Spring是如何通過三級緩存機制巧妙地解決循環依賴問題的。

Spring中的循環依賴場景

典型的循環依賴示例

@Component
public class ServiceA {@Autowiredprivate ServiceB serviceB;public void doSomething() {System.out.println("ServiceA doing something");serviceB.doSomething();}
}@Component
public class ServiceB {@Autowiredprivate ServiceC serviceC;public void doSomething() {System.out.println("ServiceB doing something");serviceC.doSomething();}
}@Component
public class ServiceC {@Autowiredprivate ServiceA serviceA;  // 形成循環依賴:A -> B -> C -> Apublic void doSomething() {System.out.println("ServiceC doing something");serviceA.doSomething();}
}

構造器循環依賴(Spring無法解決)

@Component
public class ServiceA {private final ServiceB serviceB;// 構造器注入形成的循環依賴,Spring無法解決public ServiceA(ServiceB serviceB) {this.serviceB = serviceB;}
}@Component
public class ServiceB {private final ServiceA serviceA;public ServiceB(ServiceA serviceA) {this.serviceA = serviceA;}
}

Spring Bean的生命周期

要理解Spring如何解決循環依賴,首先需要了解Bean的創建過程:

public class SpringBeanLifecycle {/*** Spring Bean的完整生命周期*/public Object createBean(String beanName, BeanDefinition beanDefinition) {// 1. 實例化 - 創建對象實例(調用構造器)Object bean = instantiateBean(beanName, beanDefinition);// 2. 屬性填充 - 依賴注入populateBean(bean, beanName, beanDefinition);// 3. 初始化 - 調用初始化方法initializeBean(bean, beanName);return bean;}private Object instantiateBean(String beanName, BeanDefinition bd) {// 通過反射創建實例Constructor<?> constructor = bd.getBeanClass().getDeclaredConstructor();return constructor.newInstance();}private void populateBean(Object bean, String beanName, BeanDefinition bd) {// 處理@Autowired、@Resource等注解// 這里會觸發依賴Bean的創建Field[] fields = bean.getClass().getDeclaredFields();for (Field field : fields) {if (field.isAnnotationPresent(Autowired.class)) {Object dependency = getBean(field.getType());field.setAccessible(true);field.set(bean, dependency);}}}private void initializeBean(Object bean, String beanName) {// 調用初始化回調方法if (bean instanceof InitializingBean) {((InitializingBean) bean).afterPropertiesSet();}}
}

Spring的三級緩存機制

Spring通過三級緩存來解決單例Bean的循環依賴問題:

public class DefaultSingletonBeanRegistry {/** 一級緩存:完成初始化的單例Bean */private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);/** 二級緩存:完成實例化但未完成初始化的Bean */private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);/** 三級緩存:單例Bean的工廠 */private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);/** 正在創建的Bean名稱集合 */private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));/*** 獲取單例Bean的核心方法*/protected Object getSingleton(String beanName) {return getSingleton(beanName, true);}protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 1. 從一級緩存中獲取完全初始化的BeanObject singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {// 2. 從二級緩存中獲取早期Bean引用singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {// 3. 從三級緩存中獲取Bean工廠ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 調用工廠方法創建早期Bean引用singletonObject = singletonFactory.getObject();// 將早期Bean引用放入二級緩存this.earlySingletonObjects.put(beanName, singletonObject);// 從三級緩存中移除this.singletonFactories.remove(beanName);}}}}return singletonObject;}/*** 添加單例Bean工廠到三級緩存*/protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);}}}/*** 將完全初始化的Bean添加到一級緩存*/protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);}}
}

循環依賴解決過程詳解

讓我們通過一個具體的例子來看看Spring是如何解決循環依賴的:

/*** 模擬Spring解決循環依賴的完整過程*/
public class CircularDependencyResolver {// 三級緩存private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();private Map<String, Object> earlySingletonObjects = new HashMap<>();private Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();private Set<String> singletonsCurrentlyInCreation = new HashSet<>();/*** 創建Bean的主要方法*/public Object getBean(String beanName) {// 先嘗試從緩存中獲取Object singleton = getSingleton(beanName);if (singleton != null) {return singleton;}// 緩存中沒有,開始創建return createBean(beanName);}private Object createBean(String beanName) {// 標記Bean正在創建中singletonsCurrentlyInCreation.add(beanName);try {// 1. 實例化BeanObject bean = instantiateBean(beanName);// 2. 將Bean工廠放入三級緩存(關鍵步驟)addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, bean));// 3. 屬性填充(可能觸發循環依賴)populateBean(bean, beanName);// 4. 初始化BeaninitializeBean(bean, beanName);// 5. 將完成的Bean放入一級緩存addSingleton(beanName, bean);return bean;} finally {// 移除創建中標記singletonsCurrentlyInCreation.remove(beanName);}}private Object instantiateBean(String beanName) {// 模擬通過反射創建Bean實例System.out.println("實例化Bean: " + beanName);if ("serviceA".equals(beanName)) {return new ServiceA();} else if ("serviceB".equals(beanName)) {return new ServiceB();}throw new RuntimeException("Unknown bean: " + beanName);}private void populateBean(Object bean, String beanName) {System.out.println("填充Bean屬性: " + beanName);// 模擬依賴注入if (bean instanceof ServiceA) {ServiceA serviceA = (ServiceA) bean;// 注入ServiceB,這里會觸發ServiceB的創建Object serviceB = getBean("serviceB");serviceA.setServiceB((ServiceB) serviceB);} else if (bean instanceof ServiceB) {ServiceB serviceB = (ServiceB) bean;// 注入ServiceA,這里會從緩存中獲取早期引用Object serviceA = getBean("serviceA");serviceB.setServiceA((ServiceA) serviceA);}}private void initializeBean(Object bean, String beanName) {System.out.println("初始化Bean: " + beanName);// 執行初始化邏輯}/*** 獲取早期Bean引用(處理AOP代理)*/private Object getEarlyBeanReference(String beanName, Object bean) {System.out.println("獲取早期Bean引用: " + beanName);// 如果需要AOP代理,在這里創建代理對象if (needsProxy(beanName)) {return createProxy(bean);}return bean;}private boolean needsProxy(String beanName) {// 簡化的代理判斷邏輯return beanName.contains("service");}private Object createProxy(Object target) {// 簡化的代理創建邏輯System.out.println("創建代理對象: " + target.getClass().getSimpleName());return target; // 實際應該返回代理對象}// 其他輔助方法...private Object getSingleton(String beanName) {Object singletonObject = singletonObjects.get(beanName);if (singletonObject == null && singletonsCurrentlyInCreation.contains(beanName)) {singletonObject = earlySingletonObjects.get(beanName);if (singletonObject == null) {ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();earlySingletonObjects.put(beanName, singletonObject);singletonFactories.remove(beanName);}}}return singletonObject;}private void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {if (!singletonObjects.containsKey(beanName)) {singletonFactories.put(beanName, singletonFactory);earlySingletonObjects.remove(beanName);}}private void addSingleton(String beanName, Object singletonObject) {singletonObjects.put(beanName, singletonObject);singletonFactories.remove(beanName);earlySingletonObjects.remove(beanName);}
}

循環依賴解決的時序圖

ServiceA創建過程:
1. 實例化ServiceA
2. 將ServiceA工廠放入三級緩存
3. 填充ServiceA屬性,需要ServiceB|├─ ServiceB創建過程:│  1. 實例化ServiceB  │  2. 將ServiceB工廠放入三級緩存│  3. 填充ServiceB屬性,需要ServiceA│  4. 從三級緩存獲取ServiceA早期引用│  5. 完成ServiceB初始化,放入一級緩存│
4. 獲得ServiceB實例,完成ServiceA屬性填充
5. 完成ServiceA初始化,放入一級緩存

為什么需要三級緩存?

只有一級緩存的問題

public class SingleCacheExample {private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();public Object getBean(String beanName) {Object bean = singletonObjects.get(beanName);if (bean != null) {return bean;}// 創建Beanbean = createBean(beanName);singletonObjects.put(beanName, bean);return bean;}// 問題:在Bean完全創建完成之前,其他Bean無法獲取到它的引用// 導致循環依賴無法解決
}

只有二級緩存的問題

public class TwoCacheExample {private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();private Map<String, Object> earlySingletonObjects = new HashMap<>();// 問題:無法處理AOP代理的情況// 如果Bean需要被代理,早期引用和最終對象可能不是同一個實例
}

三級緩存的優勢

/*** 三級緩存解決的問題:* 1. 一級緩存:存儲完全初始化的Bean* 2. 二級緩存:存儲早期Bean引用,解決循環依賴* 3. 三級緩存:存儲Bean工廠,延遲決定是否需要代理*/
public class ThreeCacheAdvantages {/*** 三級緩存的關鍵作用:* 1. 延遲代理對象的創建* 2. 確保循環依賴中代理對象的一致性* 3. 避免不必要的代理對象創建*/private Object getEarlyBeanReference(String beanName, Object bean) {// 只有在真正需要早期引用時才決定是否創建代理if (hasCircularDependency(beanName)) {if (needsProxy(beanName)) {return createProxy(bean);}}return bean;}
}

AOP與循環依賴

Spring AOP為循環依賴的解決增加了復雜性:

@Component
@Service
public class UserService {@Autowiredprivate OrderService orderService;@Transactional  // 這個注解會導致創建AOP代理public void createUser(User user) {// 業務邏輯orderService.createDefaultOrder(user);}
}@Component
@Service  
public class OrderService {@Autowiredprivate UserService userService;@Transactionalpublic void createDefaultOrder(User user) {// 業務邏輯userService.validateUser(user);}
}

AOP代理處理過程:

public class AopCircularDependencyHandler {/*** 處理AOP代理的循環依賴*/public Object resolveAopCircularDependency(String beanName, Object bean) {// 1. 檢查是否需要創建代理if (shouldCreateProxy(bean)) {// 2. 創建代理對象Object proxy = createAopProxy(bean);// 3. 確保早期引用和最終對象的一致性ensureProxyConsistency(beanName, bean, proxy);return proxy;}return bean;}private boolean shouldCreateProxy(Object bean) {// 檢查是否有@Transactional、@Async等注解Class<?> beanClass = bean.getClass();// 檢查類級別注解if (beanClass.isAnnotationPresent(Transactional.class) ||beanClass.isAnnotationPresent(Async.class)) {return true;}// 檢查方法級別注解Method[] methods = beanClass.getDeclaredMethods();for (Method method : methods) {if (method.isAnnotationPresent(Transactional.class) ||method.isAnnotationPresent(Async.class)) {return true;}}return false;}private Object createAopProxy(Object target) {// 使用CGLIB或JDK動態代理創建代理對象ProxyFactory proxyFactory = new ProxyFactory(target);// 添加事務攔截器proxyFactory.addAdvice(new TransactionInterceptor());return proxyFactory.getProxy();}
}

Spring無法解決的循環依賴場景

1. 構造器循環依賴

@Component
public class ServiceA {private final ServiceB serviceB;// Spring無法解決構造器循環依賴public ServiceA(ServiceB serviceB) {this.serviceB = serviceB;}
}@Component
public class ServiceB {private final ServiceA serviceA;public ServiceB(ServiceA serviceA) {this.serviceA = serviceA;}
}// 啟動時會拋出異常:
// BeanCurrentlyInCreationException: Error creating bean with name 'serviceA': 
// Requested bean is currently in creation: Is there an unresolvable circular reference?

2. prototype作用域的循環依賴

@Component
@Scope("prototype")
public class PrototypeServiceA {@Autowiredprivate PrototypeServiceB serviceB;
}@Component
@Scope("prototype")
public class PrototypeServiceB {@Autowiredprivate PrototypeServiceA serviceA;
}// prototype作用域的Bean每次都會創建新實例,無法使用緩存解決循環依賴

3. @Async注解的特殊情況

@Component
public class AsyncServiceA {@Autowiredprivate AsyncServiceB serviceB;@Asyncpublic void doAsync() {serviceB.doSomething();}
}@Component
public class AsyncServiceB {@Autowiredprivate AsyncServiceA serviceA;public void doSomething() {serviceA.doAsync();}
}// 在某些情況下,@Async可能導致循環依賴解決失敗

循環依賴的解決方案

1. 使用@Lazy注解

@Component
public class ServiceA {private final ServiceB serviceB;// 使用@Lazy延遲注入,打破循環依賴public ServiceA(@Lazy ServiceB serviceB) {this.serviceB = serviceB;}
}@Component
public class ServiceB {private final ServiceA serviceA;public ServiceB(ServiceA serviceA) {this.serviceA = serviceA;}
}

2. 使用Setter注入替代構造器注入

@Component
public class ServiceA {private ServiceB serviceB;// 使用Setter注入替代構造器注入@Autowiredpublic void setServiceB(ServiceB serviceB) {this.serviceB = serviceB;}
}@Component
public class ServiceB {private ServiceA serviceA;@Autowiredpublic void setServiceA(ServiceA serviceA) {this.serviceA = serviceA;}
}

3. 使用@PostConstruct

@Component
public class ServiceA {@Autowiredprivate ServiceB serviceB;private ServiceA selfReference;@PostConstructpublic void init() {// 在初始化階段設置自引用this.selfReference = this;}
}

4. 重新設計架構

// 原始設計:循環依賴
@Component
public class UserService {@Autowiredprivate OrderService orderService;public void processUser(User user) {orderService.processOrder(user.getOrders());}
}@Component
public class OrderService {@Autowiredprivate UserService userService;public void processOrder(List<Order> orders) {for (Order order : orders) {userService.validateUser(order.getUser());}}
}// 重構后:提取公共服務
@Component
public class ValidationService {public void validateUser(User user) {// 用戶驗證邏輯}public void validateOrder(Order order) {// 訂單驗證邏輯}
}@Component
public class UserService {@Autowiredprivate ValidationService validationService;public void processUser(User user) {validationService.validateUser(user);// 處理用戶邏輯}
}@Component
public class OrderService {@Autowiredprivate ValidationService validationService;public void processOrder(Order order) {validationService.validateOrder(order);// 處理訂單邏輯}
}

循環依賴檢測和調試

1. 啟用循環依賴檢測

@Configuration
@EnableAutoConfiguration
public class AppConfig {@Beanpublic static BeanFactoryPostProcessor circularDependencyDetector() {return new BeanFactoryPostProcessor() {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {if (beanFactory instanceof DefaultListableBeanFactory) {DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;factory.setAllowCircularReferences(false); // 禁用循環依賴}}};}
}

2. 自定義循環依賴檢測器

@Component
public class CircularDependencyDetector implements BeanPostProcessor {private final Set<String> beansInCreation = new HashSet<>();private final Map<String, Set<String>> dependencyGraph = new HashMap<>();@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {detectCircularDependency(beanName);return bean;}private void detectCircularDependency(String beanName) {if (beansInCreation.contains(beanName)) {throw new BeanCurrentlyInCreationException("Circular dependency detected: " + beanName);}beansInCreation.add(beanName);// 分析依賴關系analyzeDependencies(beanName);beansInCreation.remove(beanName);}private void analyzeDependencies(String beanName) {// 通過反射分析Bean的依賴關系// 構建依賴圖,檢測環路}
}

3. 循環依賴調試工具

@Component
public class CircularDependencyDebugger {private static final Logger logger = LoggerFactory.getLogger(CircularDependencyDebugger.class);@EventListenerpublic void handleContextRefreshed(ContextRefreshedEvent event) {ApplicationContext context = event.getApplicationContext();// 分析所有Bean的依賴關系analyzeBeanDependencies(context);}private void analyzeBeanDependencies(ApplicationContext context) {String[] beanNames = context.getBeanDefinitionNames();for (String beanName : beanNames) {try {BeanDefinition beanDefinition = ((ConfigurableApplicationContext) context).getBeanFactory().getBeanDefinition(beanName);// 分析依賴關系Set<String> dependencies = extractDependencies(beanDefinition);logger.info("Bean {} depends on: {}", beanName, dependencies);} catch (Exception e) {logger.warn("Failed to analyze dependencies for bean: {}", beanName, e);}}}private Set<String> extractDependencies(BeanDefinition beanDefinition) {Set<String> dependencies = new HashSet<>();// 提取構造器依賴ConstructorArgumentValues constructorArgs = beanDefinition.getConstructorArgumentValues();for (ConstructorArgumentValues.ValueHolder valueHolder : constructorArgs.getGenericArgumentValues()) {if (valueHolder.getValue() instanceof RuntimeBeanReference) {RuntimeBeanReference ref = (RuntimeBeanReference) valueHolder.getValue();dependencies.add(ref.getBeanName());}}// 提取屬性依賴MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();for (PropertyValue pv : propertyValues.getPropertyValues()) {if (pv.getValue() instanceof RuntimeBeanReference) {RuntimeBeanReference ref = (RuntimeBeanReference) pv.getValue();dependencies.add(ref.getBeanName());}}return dependencies;}
}

性能考慮

1. 三級緩存的性能影響

public class CachePerformanceAnalysis {/*** 分析三級緩存對性能的影響*/public void analyzePerformance() {// 1. 內存開銷:三個Map需要額外的內存// 2. 查找開銷:需要依次查找三個緩存// 3. 同步開銷:緩存操作需要同步long startTime = System.nanoTime();// 模擬緩存查找Object bean = getSingletonFromCache("testBean");long endTime = System.nanoTime();long duration = endTime - startTime;System.out.println("緩存查找耗時: " + duration + " 納秒");}private Object getSingletonFromCache(String beanName) {// 三級緩存查找的性能開銷Object singleton = singletonObjects.get(beanName);if (singleton == null) {singleton = earlySingletonObjects.get(beanName);if (singleton == null) {ObjectFactory<?> factory = singletonFactories.get(beanName);if (factory != null) {singleton = factory.getObject();}}}return singleton;}
}

2. 優化建議

@Configuration
public class CircularDependencyOptimization {/*** 優化循環依賴處理的配置*/@Beanpublic static BeanFactoryPostProcessor optimizeCircularDependency() {return beanFactory -> {if (beanFactory instanceof DefaultListableBeanFactory) {DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;// 1. 預分析Bean依賴關系,提前發現循環依賴preAnalyzeDependencies(factory);// 2. 優化緩存大小optimizeCacheSize(factory);// 3. 啟用并發優化enableConcurrentOptimization(factory);}};}```javaprivate static void preAnalyzeDependencies(DefaultListableBeanFactory factory) {String[] beanNames = factory.getBeanDefinitionNames();Map<String, Set<String>> dependencyGraph = new HashMap<>();// 構建依賴圖for (String beanName : beanNames) {BeanDefinition bd = factory.getBeanDefinition(beanName);Set<String> dependencies = extractBeanDependencies(bd);dependencyGraph.put(beanName, dependencies);}// 檢測循環依賴detectCycles(dependencyGraph);}private static void detectCycles(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)) {System.out.println("檢測到循環依賴,涉及Bean: " + node);}}}private static 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;}private static void optimizeCacheSize(DefaultListableBeanFactory factory) {// 根據Bean數量優化緩存初始容量int beanCount = factory.getBeanDefinitionCount();int optimalCacheSize = Math.max(16, beanCount / 4);// 這里可以通過反射設置緩存的初始容量// 實際實現中需要訪問私有字段}private static void enableConcurrentOptimization(DefaultListableBeanFactory factory) {// 啟用并發創建Bean的優化factory.setAllowEagerClassLoading(true);factory.setAllowRawInjectionDespiteWrapping(true);}
}

實際應用案例

案例1:微服務中的循環依賴

/*** 微服務架構中常見的循環依賴場景*/
@Service
public class UserService {@Autowiredprivate OrderService orderService;@Autowiredprivate NotificationService notificationService;@Transactionalpublic User createUser(CreateUserRequest request) {User user = new User(request);user = userRepository.save(user);// 創建用戶后,創建默認訂單orderService.createWelcomeOrder(user);// 發送歡迎通知notificationService.sendWelcomeNotification(user);return user;}
}@Service
public class OrderService {@Autowiredprivate UserService userService; // 循環依賴@Autowiredprivate PaymentService paymentService;@Transactionalpublic Order createWelcomeOrder(User user) {Order order = new Order(user, getWelcomeProducts());order = orderRepository.save(order);// 更新用戶的訂單統計userService.updateOrderStatistics(user.getId());return order;}
}@Service
public class NotificationService {@Autowiredprivate UserService userService; // 另一個循環依賴public void sendWelcomeNotification(User user) {// 獲取用戶偏好設置UserPreference preference = userService.getUserPreference(user.getId());if (preference.isEmailEnabled()) {emailService.sendWelcomeEmail(user);}if (preference.isSmsEnabled()) {smsService.sendWelcomeSms(user);}}
}

解決方案:

/*** 重構后的解決方案:使用事件驅動架構*/
@Service
public class UserService {@Autowiredprivate ApplicationEventPublisher eventPublisher;@Transactionalpublic User createUser(CreateUserRequest request) {User user = new User(request);user = userRepository.save(user);// 發布用戶創建事件,而不是直接調用其他服務eventPublisher.publishEvent(new UserCreatedEvent(user));return user;}public void updateOrderStatistics(Long userId) {// 更新用戶訂單統計User user = userRepository.findById(userId);user.incrementOrderCount();userRepository.save(user);}public UserPreference getUserPreference(Long userId) {return userPreferenceRepository.findByUserId(userId);}
}@Service
public class OrderService {// 移除對UserService的直接依賴@EventListener@Asyncpublic void handleUserCreated(UserCreatedEvent event) {createWelcomeOrder(event.getUser());}@Transactionalpublic Order createWelcomeOrder(User user) {Order order = new Order(user, getWelcomeProducts());order = orderRepository.save(order);// 發布訂單創建事件eventPublisher.publishEvent(new OrderCreatedEvent(order));return order;}
}@Service
public class NotificationService {// 移除對UserService的直接依賴@Autowiredprivate UserPreferenceRepository userPreferenceRepository;@EventListener@Asyncpublic void handleUserCreated(UserCreatedEvent event) {sendWelcomeNotification(event.getUser());}public void sendWelcomeNotification(User user) {// 直接查詢用戶偏好,避免循環依賴UserPreference preference = userPreferenceRepository.findByUserId(user.getId());if (preference.isEmailEnabled()) {emailService.sendWelcomeEmail(user);}if (preference.isSmsEnabled()) {smsService.sendWelcomeSms(user);}}
}// 事件定義
public class UserCreatedEvent extends ApplicationEvent {private final User user;public UserCreatedEvent(Object source, User user) {super(source);this.user = user;}public User getUser() {return user;}
}

案例2:復雜的業務場景循環依賴

/*** 電商系統中的復雜循環依賴場景*/
@Service
public class ProductService {@Autowiredprivate CategoryService categoryService;@Autowiredprivate InventoryService inventoryService;@Autowiredprivate PriceService priceService;public ProductDTO getProductDetails(Long productId) {Product product = productRepository.findById(productId);// 獲取分類信息Category category = categoryService.getCategoryWithProducts(product.getCategoryId());// 獲取庫存信息Inventory inventory = inventoryService.getInventoryWithProductInfo(productId);// 獲取價格信息Price price = priceService.getPriceWithProductDetails(productId);return ProductDTO.builder().product(product).category(category).inventory(inventory).price(price).build();}
}@Service
public class CategoryService {@Autowiredprivate ProductService productService; // 循環依賴public Category getCategoryWithProducts(Long categoryId) {Category category = categoryRepository.findById(categoryId);// 獲取分類下的熱門產品List<ProductDTO> hotProducts = productService.getHotProductsByCategory(categoryId);category.setHotProducts(hotProducts);return category;}
}@Service
public class InventoryService {@Autowiredprivate ProductService productService; // 循環依賴public Inventory getInventoryWithProductInfo(Long productId) {Inventory inventory = inventoryRepository.findByProductId(productId);// 獲取產品基本信息用于庫存展示ProductDTO productInfo = productService.getBasicProductInfo(productId);inventory.setProductInfo(productInfo);return inventory;}
}

解決方案:使用Repository模式和DTO組裝器

/*** 重構后的解決方案:分離數據訪問和業務邏輯*/
@Service
public class ProductQueryService {@Autowiredprivate ProductRepository productRepository;@Autowiredprivate CategoryRepository categoryRepository;@Autowiredprivate InventoryRepository inventoryRepository;@Autowiredprivate PriceRepository priceRepository;@Autowiredprivate ProductDTOAssembler productDTOAssembler;/*** 獲取完整的產品信息,避免循環依賴*/public ProductDTO getProductDetails(Long productId) {// 1. 獲取基礎數據Product product = productRepository.findById(productId);Category category = categoryRepository.findById(product.getCategoryId());Inventory inventory = inventoryRepository.findByProductId(productId);Price price = priceRepository.findByProductId(productId);// 2. 組裝DTOreturn productDTOAssembler.assemble(product, category, inventory, price);}
}@Component
public class ProductDTOAssembler {public ProductDTO assemble(Product product, Category category, Inventory inventory, Price price) {return ProductDTO.builder().id(product.getId()).name(product.getName()).description(product.getDescription()).categoryName(category.getName()).categoryPath(category.getPath()).stockQuantity(inventory.getQuantity()).availableQuantity(inventory.getAvailableQuantity()).currentPrice(price.getCurrentPrice()).originalPrice(price.getOriginalPrice()).build();}public CategoryDTO assembleWithProducts(Category category, List<Product> products) {return CategoryDTO.builder().id(category.getId()).name(category.getName()).path(category.getPath()).productCount(products.size()).products(products.stream().map(this::toSimpleProductDTO).collect(Collectors.toList())).build();}private SimpleProductDTO toSimpleProductDTO(Product product) {return SimpleProductDTO.builder().id(product.getId()).name(product.getName()).build();}
}// 專門的查詢服務,避免循環依賴
@Service
public class CategoryQueryService {@Autowiredprivate CategoryRepository categoryRepository;@Autowiredprivate ProductRepository productRepository;@Autowiredprivate ProductDTOAssembler assembler;public CategoryDTO getCategoryWithProducts(Long categoryId) {Category category = categoryRepository.findById(categoryId);List<Product> products = productRepository.findByCategoryId(categoryId);return assembler.assembleWithProducts(category, products);}
}

監控和診斷工具

1. 自定義循環依賴監控

@Component
public class CircularDependencyMonitor {private static final Logger logger = LoggerFactory.getLogger(CircularDependencyMonitor.class);private final Map<String, Long> beanCreationTimes = new ConcurrentHashMap<>();private final Map<String, Set<String>> dependencyChains = new ConcurrentHashMap<>();@EventListenerpublic void onBeanCreationStart(BeanCreationStartEvent event) {String beanName = event.getBeanName();beanCreationTimes.put(beanName, System.currentTimeMillis());// 記錄依賴鏈recordDependencyChain(beanName, event.getDependencyChain());}@EventListenerpublic void onBeanCreationEnd(BeanCreationEndEvent event) {String beanName = event.getBeanName();Long startTime = beanCreationTimes.remove(beanName);if (startTime != null) {long duration = System.currentTimeMillis() - startTime;if (duration > 1000) { // 超過1秒的Bean創建logger.warn("Bean {} 創建耗時過長: {}ms, 可能存在循環依賴", beanName, duration);// 分析依賴鏈analyzeDependencyChain(beanName);}}}private void recordDependencyChain(String beanName, Set<String> chain) {dependencyChains.put(beanName, new HashSet<>(chain));}private void analyzeDependencyChain(String beanName) {Set<String> chain = dependencyChains.get(beanName);if (chain != null && chain.size() > 3) {logger.warn("Bean {} 的依賴鏈較長: {}", beanName, chain);// 檢測是否存在循環if (detectCircularInChain(chain)) {logger.error("檢測到循環依賴: {}", chain);}}}private boolean detectCircularInChain(Set<String> chain) {// 簡化的循環檢測邏輯return chain.size() != new HashSet<>(chain).size();}
}

2. Spring Boot Actuator集成

@Component
@Endpoint(id = "circular-dependencies")
public class CircularDependencyEndpoint {@Autowiredprivate ApplicationContext applicationContext;@ReadOperationpublic Map<String, Object> circularDependencies() {Map<String, Object> result = new HashMap<>();// 分析所有Bean的依賴關系Map<String, Set<String>> dependencyGraph = buildDependencyGraph();// 檢測循環依賴List<List<String>> cycles = detectAllCycles(dependencyGraph);result.put("totalBeans", dependencyGraph.size());result.put("circularDependencies", cycles);result.put("affectedBeans", cycles.stream().flatMap(List::stream).distinct().count());return result;}private Map<String, Set<String>> buildDependencyGraph() {Map<String, Set<String>> graph = new HashMap<>();if (applicationContext instanceof ConfigurableApplicationContext) {ConfigurableListableBeanFactory beanFactory = ((ConfigurableApplicationContext) applicationContext).getBeanFactory();String[] beanNames = beanFactory.getBeanDefinitionNames();for (String beanName : beanNames) {try {BeanDefinition bd = beanFactory.getBeanDefinition(beanName);Set<String> dependencies = extractDependencies(bd);graph.put(beanName, dependencies);} catch (Exception e) {// 忽略無法分析的Bean}}}return graph;}private List<List<String>> detectAllCycles(Map<String, Set<String>> graph) {List<List<String>> cycles = new ArrayList<>();Set<String> visited = new HashSet<>();for (String node : graph.keySet()) {if (!visited.contains(node)) {List<String> path = new ArrayList<>();Set<String> pathSet = new HashSet<>();findCycles(graph, node, visited, path, pathSet, cycles);}}return cycles;}private void findCycles(Map<String, Set<String>> graph, String current,Set<String> visited, List<String> path, Set<String> pathSet, List<List<String>> cycles) {if (pathSet.contains(current)) {// 找到循環int cycleStart = path.indexOf(current);List<String> cycle = new ArrayList<>(path.subList(cycleStart, path.size()));cycle.add(current);cycles.add(cycle);return;}if (visited.contains(current)) {return;}visited.add(current);path.add(current);pathSet.add(current);Set<String> neighbors = graph.get(current);if (neighbors != null) {for (String neighbor : neighbors) {findCycles(graph, neighbor, visited, path, pathSet, cycles);}}path.remove(path.size() - 1);pathSet.remove(current);}private Set<String> extractDependencies(BeanDefinition beanDefinition) {// 實現依賴提取邏輯Set<String> dependencies = new HashSet<>();// 提取構造器依賴ConstructorArgumentValues constructorArgs = beanDefinition.getConstructorArgumentValues();extractDependenciesFromConstructorArgs(constructorArgs, dependencies);// 提取屬性依賴MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();extractDependenciesFromPropertyValues(propertyValues, dependencies);return dependencies;}
}

3. 性能監控和優化

@Component
public class CircularDependencyPerformanceMonitor {private final MeterRegistry meterRegistry;private final Timer.Sample sample;public CircularDependencyPerformanceMonitor(MeterRegistry meterRegistry) {this.meterRegistry = meterRegistry;this.sample = Timer.start(meterRegistry);}@EventListenerpublic void onApplicationReady(ApplicationReadyEvent event) {sample.stop(Timer.builder("spring.context.startup.time").description("Spring context startup time").register(meterRegistry));// 分析啟動過程中的循環依賴處理analyzeStartupPerformance();}private void analyzeStartupPerformance() {ApplicationContext context = applicationContext;// 統計Bean創建耗時Map<String, Long> beanCreationTimes = getBeanCreationTimes();// 識別耗時最長的BeanString slowestBean = beanCreationTimes.entrySet().stream().max(Map.Entry.comparingByValue()).map(Map.Entry::getKey).orElse("unknown");Long slowestTime = beanCreationTimes.get(slowestBean);// 記錄指標Gauge.builder("spring.bean.creation.slowest.time").description("Slowest bean creation time").register(meterRegistry, slowestTime);Counter.builder("spring.circular.dependencies.resolved").description("Number of circular dependencies resolved").register(meterRegistry).increment(countResolvedCircularDependencies());}private Map<String, Long> getBeanCreationTimes() {// 實現Bean創建時間統計return new HashMap<>();}private int countResolvedCircularDependencies() {// 統計解決的循環依賴數量return 0;}
}

最佳實踐總結

1. 設計原則

/*** 避免循環依賴的設計原則*/
public class CircularDependencyBestPractices {/*** 1. 單一職責原則* 每個類應該只有一個明確的職責,避免過度耦合*/@Servicepublic class UserService {// 只負責用戶相關的核心業務邏輯public User createUser(CreateUserRequest request) {// 用戶創建邏輯}}/*** 2. 依賴倒置原則* 依賴抽象而不是具體實現*/@Servicepublic class OrderService {// 依賴接口而不是具體實現private final UserRepository userRepository;private final NotificationSender notificationSender;public OrderService(UserRepository userRepository, NotificationSender notificationSender) {this.userRepository = userRepository;this.notificationSender = notificationSender;}}/*** 3. 接口隔離原則* 使用小而專一的接口*/public interface UserValidator {boolean validateUser(User user);}public interface OrderValidator {boolean validateOrder(Order order);}/*** 4. 事件驅動架構* 使用事件解耦服務間的直接依賴*/@Servicepublic class EventDrivenUserService {@Autowiredprivate ApplicationEventPublisher eventPublisher;public User createUser(CreateUserRequest request) {User user = new User(request);user = userRepository.save(user);// 發布事件而不是直接調用其他服務eventPublisher.publishEvent(new UserCreatedEvent(user));return user;}}
}

2. 代碼檢查清單

/*** 循環依賴檢查清單*/
public class CircularDependencyChecklist {/*** 檢查項目:* * 1. 構造器注入檢查* - 避免構造器循環依賴* - 使用@Lazy注解打破循環* * 2. 服務層設計檢查* - 服務職責是否單一* - 是否存在相互調用* * 3. 依賴關系檢查* - 依賴層次是否清晰* - 是否違反分層架構原則* * 4. 配置檢查* - Bean作用域是否合適* - 是否正確使用@Lazy* * 5. 測試覆蓋* - 是否有集成測試覆蓋循環依賴場景* - 是否有性能測試驗證啟動時間*/public void performChecklist() {checkConstructorDependencies();checkServiceLayerDesign();checkDependencyHierarchy();checkConfiguration();checkTestCoverage();}private void checkConstructorDependencies() {// 檢查構造器循環依賴}private void checkServiceLayerDesign() {// 檢查服務層設計}private void checkDependencyHierarchy() {// 檢查依賴層次}private void checkConfiguration() {// 檢查配置}private void checkTestCoverage() {// 檢查測試覆蓋率}
}

結論

Spring通過精妙的三級緩存機制成功解決了單例Bean的循環依賴問題,這一設計體現了Spring框架的深度思考和技術實力。理解這一機制不僅有助于我們更好地使用Spring,還能幫助我們:

關鍵要點回顧

  1. 三級緩存的作用

    • 一級緩存:存儲完全初始化的Bean
    • 二級緩存:存儲早期Bean引用,解決循環依賴
    • 三級緩存:存儲Bean工廠,處理AOP代理
  2. 解決原理

    • 在Bean實例化后立即暴露早期引用
    • 通過工廠模式延遲決定是否創建代理
    • 確保循環依賴中對象引用的一致性
  3. 限制條件

    • 只能解決單例Bean的setter注入循環依賴
    • 無法解決構造器循環依賴
    • 無法解決prototype作用域的循環依賴
  4. 最佳實踐

    • 優先使用setter注入而非構造器注入
    • 合理使用@Lazy注解
    • 采用事件驅動架構解耦服務
    • 遵循SOLID設計原則

實際應用建議

在實際開發中,我們應該:

  1. 預防為主:通過良好的架構設計避免循環依賴
  2. 監控診斷:使用工具監控和診斷循環依賴問題
  3. 性能優化:關注循環依賴對啟動性能的影響
  4. 持續改進:定期重構代碼,消除不必要的依賴關系

Spring的循環依賴解決方案是一個經典的工程實踐案例,它告訴我們:面對復雜的技術問題,往往需要巧妙的設計和精細的實現。掌握這些知識不僅能幫助我們更好地使用Spring框架,還能提升我們的系統設計能力。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/921346.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/921346.shtml
英文地址,請注明出處:http://en.pswp.cn/news/921346.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HTML第六課:表格展示

HTML第六課&#xff1a;表格展示學生花名冊學生花名冊 效果示列 代碼展示 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html lang"zh-CN"> <head><meta …

醫療行業API管理優化:使用QuickAPI提高數據安全與接口性能

背景與挑戰在醫療行業&#xff0c;特別是醫院信息系統&#xff08;HIS&#xff09;或其他相關部門&#xff08;如實驗室信息系統LIS、藥品管理系統等&#xff09;&#xff0c;數據安全和隱私保護一直是核心問題。然而&#xff0c;許多醫療機構仍然面臨著以下問題&#xff1a;數…

docker 部署RustDesk服務

最近要用到遠程桌面服務&#xff0c;網上的資料很豐富&#xff0c;但是和我的情況有點點區別&#xff0c;我是要搭一臺局域網使用的遠程桌面服務。 首先是源的問題&#xff1a; 很多都是不能用的&#xff0c;我用的docker桌面版&#xff0c; 其他的不重要&#xff0c;源地址&…

Kubernetes 中為 ZenTao 的 Apache 服務器添加請求體大小限制

本文將詳細介紹如何通過修改 Apache 配置模板并在 Kubernetes 中使用 ConfigMap,為 ZenTao 系統添加請求體大小限制(LimitRequestBody)。 背景介紹 在企業級項目管理軟件 ZenTao 的部署過程中,我們經常需要對 Apache 服務器進行安全加固。其中一個重要的安全措施是限制客戶…

綜述 | Agentic RL for LLM的最新進展與未來挑戰,idea滿滿

近年來&#xff0c;大語言模型&#xff08;LLMs&#xff09;和強化學習&#xff08;RL&#xff09;的融合正在徹底改變我們構建和部署AI系統的方式。早期的LLM強化學習&#xff08;LLM-RL&#xff09;主要關注如何通過人類反饋&#xff08;如RLHF&#xff09;讓模型生成更符合人…

【代碼隨想錄算法訓練營——Day3】鏈表——203.移除鏈表元素、707.設計鏈表、206.反轉鏈表

LeetCode題目鏈接 https://leetcode.cn/problems/remove-linked-list-elements/ https://leetcode.cn/problems/design-linked-list/ https://leetcode.cn/problems/reverse-linked-list/ 題解 203.移除鏈表元素 重要的是創立頭結點&#xff0c;這點在寫題前已經經受過提示。 注…

CI/CD流水線驅動自動化流程深度解析:選型、競品、成本與資源消耗

目錄 一、CI/CD是什么&#xff1f;核心定位與價值 二、選型與競品分析 (GitLab CI vs. Jenkins vs. GitHub Actions vs. GitLab CI) 三、部署成本分析 四、服務器資源消耗分析 五、給您的最終建議 一、CI/CD是什么&#xff1f;核心定位與價值 CI/CD&#xff08;持續集成/…

工廠辦公環境如何實現一臺服務器多人共享辦公

在現代化工廠的辦公環境中&#xff0c;如何通過一臺服務器實現多人共享辦公是一個既實用又高效的需求。這種方案不僅能降低硬件成本&#xff0c;還能簡化IT管理&#xff0c;提高數據安全性。在工廠辦公環境中&#xff0c;通過云飛云共享云桌面實現一臺服務器多人共享辦公&#…

系統性學習數據結構-第三講-棧和隊列

系統性學習數據結構-第三講-棧和隊列1. 棧1.1 棧和隊列1.2 棧的實現2. 隊列2.1 概念與結構2.2 隊列的實現3. 棧和隊列算法題3.1 [有效的括號](https://leetcode.cn/problems/valid-parentheses/description/)3.2 [用隊列實現棧](https://leetcode.cn/problems/implement-stack-…

硬件(三) 通信方式、串口通信

一、通信類型&#xff08;一&#xff09;并行通信多個比特通過并行線同時傳輸&#xff0c;傳輸速率快&#xff0c;但會大量占用芯片資源&#xff0c;在對資源敏感的場景下不太適用。&#xff08;二&#xff09;串行通信把數據拆成單個比特&#xff0c;按順序在一根總線上發送。…

vsan default storage policy 具體是什么策略?

vSAN Default Storage Policy&#xff08;vSAN 默認存儲策略&#xff09;是 VMware vSAN 部署后自動創建的基礎存儲策略&#xff0c;其核心目標是在“通用性”和“可靠性”之間取得平衡&#xff0c;為大多數虛擬機提供默認的數據保護和存儲服務&#xff0c;無需管理員手動創建策…

雨后陽光為何更強烈?

1. 降雨后的輻射是否會增強一般來說&#xff0c;降雨時天空多云&#xff0c;云層對太陽輻射有強烈削弱作用&#xff0c;所以降雨時的短波輻射顯著下降。但雨后&#xff0c;空氣濕度大、顆粒物被沖刷、天空轉晴時&#xff0c;大氣透明度會提高&#xff0c;短波輻射相較于降雨前往…

美團發布 | LongCat-Flash最全解讀,硬剛GPT-4.1、Kimi!

一、導讀 本報告解析了美團LongCat團隊推出的LongCat-Flash模型&#xff0c;一個擁有5600億參數的混合專家模型&#xff08;Mixture-of-Experts, MoE&#xff09;。面對大規模語言模型在計算資源和效率上的挑戰&#xff0c;LongCat-Flash旨在實現計算效率與高級智能體&#xf…

Ubuntu 18.04 上升級 gcc 到 9.4

18.04 默認的源中可能沒有 GCC-9.3 或更新版本&#xff0c;在終端運行以下命令來添加 PPA&#xff1a; sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update2.安裝 GCC 和 G sudo apt install gcc-9 g-93.更新替代版本 如果系統中安裝了多個 GCC 版本&#x…

.NET GcPDF V8.2 新版本:人工智能 PDF 處理

一、GcPDF 產品簡介 GcPDF&#xff08;GrapeCity Documents for PDF&#xff09;是葡萄城&#xff08;GrapeCity&#xff09;推出的一款功能強大的 .NET PDF 開發組件&#xff0c;旨在為開發人員提供高效、靈活的 PDF 文檔處理解決方案。無論是創建全新 PDF 文檔、編輯現有 PD…

解鎖桐果云零代碼數據平臺能力矩陣——賦能零售行業數字化轉型新動能

在零售行業從“規模擴張”轉向“精細運營”的當下&#xff0c;數據已成為優化庫存、精準營銷、防控風險的核心抓手。但多數零售企業仍面臨“數據雜亂難治理、分析建模門檻高、場景適配性不足”等難題&#xff0c;導致大量訂單、商品、交易數據沉睡&#xff0c;難以轉化為經營決…

rabbitmq 入門知識點

RabbitMQ 是一個 消息隊列中間件&#xff08;Message Broker&#xff09;&#xff0c;實現了 AMQP 協議&#xff0c;常用于服務之間解耦、異步處理、流量削峰等場景。 我幫你分成兩個部分來講&#xff1a;核心原理 常見用法。&#x1f9e9; 一、核心原理 RabbitMQ 的核心是 生…

點控云智能客服:以AI重塑服務體驗,登頂行業第一的革新之路

在數字化浪潮席卷全球的今天&#xff0c;客戶服務已成為企業核心競爭力之一。智能客服作為連接企業與客戶的重要橋梁&#xff0c;其效能與體驗直接關系到企業的品牌形象與市場口碑。近日&#xff0c;權威機構發布的《中國智能客服市場競爭力報告》顯示&#xff0c;點控云智能客…

9.5 IO-線程day5

信號量打印ABC#include <stdio.h> #include <string.h> #include <stdlib.h> #include <25061head.h> sem_t sem[1]; void *callback(void *arg) {while(1){sem_wait(&sem[0]);printf("A\n");sleep(1);sem_post(&sem[1]);}pthread_e…

老師如何高效收集學生學籍信息,完成收集工作?

開學的時光總是忙碌而充實&#xff0c;除了要熱情地迎接新生、用心地備課&#xff0c;還有一件讓人頭疼不已的事情——學生學籍信息的收集。上學期開學&#xff0c;我承擔起了收集班級新生信息的重任&#xff0c;滿心以為提前準備好的紙質表格&#xff0c;在新生報到那天發給家…