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;在新生报到那天发给家…