spring容器初始化时候遇到的死锁问题

在启动Spring项目时遇到死锁问题,源于DefaultSingletonBeanRegistry.getSingleton()方法中的锁与DataLocalcacheInit.afterPropertiesSet()方法中开启线程加载数据导致的并发冲突。当主线程初始化bean并持有锁时,新开线程尝试获取相同锁,造成死锁。表现为Spring容器初始化受阻。解决方法是明确FirstBean对ConfigHelper和SecondBean的依赖关系,调整bean加载顺序,避免并发问题。

最近启动spring项目的时候遇到一个死锁问题,使用jstack获取线程堆栈的时候,可以看到2个线程出现了死锁:



DefaultSingletonBeanRegistry.getSingleton()源码如下,可以看到这个方法需要对singletonObjects加锁




第二处xxx.subject.core.cache.DataLocalcacheInit.afterPropertiesSet源码如下:

<think>我们使用BeanPostProcessor来干预MongoDB相关Bean的初始化过程,确保加载顺序正确。核心思路:通过实现BeanPostProcessor接口,在Bean初始化前后插入自定义逻辑,对特定的MongoDB相关Bean(如MongoTemplate、MongoClient等)进行顺序控制。步骤:1.创建一个自定义的BeanPostProcessor,例如MongoBeanPostProcessor。2.在postProcessBeforeInitialization和postProcessAfterInitialization方法中,我们可以识别特定的Bean(通过beanName或类型),并执行相应的操作。3.为了控制顺序,我们可以在Bean初始化之前(postProcessBeforeInitialization)检查依赖是否已经准备好。例如,如果某个Bean依赖于MongoTemplate,而MongoTemplate还没有初始化,我们可以等待或者抛出异常。4.但是请注意,BeanPostProcessor本身也是Bean,它的加载顺序可能早于其他普通Bean。因此,我们需要确保这个后置处理器本身不会依赖于那些需要控制顺序的Bean。然而,直接通过BeanPostProcessor来控制Bean的加载顺序并不直观,因为BeanPostProcessor主要作用于Bean初始化的前后(即属性设置之后,init方法调用前后),而不是Bean的创建顺序。Bean的创建顺序主要由依赖关系(@DependsOn)和声明顺序决定。因此,我们通常使用BeanPostProcessor来增强Bean的功能(如AOP),而不是控制加载顺序。但是,我们可以利用它来确保在某个Bean初始化之前,它所依赖的另一个Bean已经完成初始化(通过检查)。下面是一个示例,展示如何通过BeanPostProcessor确保MongoTemplate在初始化之前,MongoClient已经初始化完成:注意:这个示例仅用于演示,实际中我们更推荐使用@DependsOn或构造器注入来控制顺序。</think>### 通过 BeanPostProcessor 控制 MongoDB Bean 初始化顺序 在 Spring 中,可通过自定义 `BeanPostProcessor` 干预 MongoDB 相关 Bean 的初始化过程,确保加载顺序正确。以下是具体实现方案: --- #### 一、核心实现原理 1. **BeanPostProcessor 工作机制** 在 Bean 初始化前后插入自定义逻辑: ```java public interface BeanPostProcessor { // Bean初始化前执行 Object postProcessBeforeInitialization(Object bean, String beanName); // Bean初始化后执行 Object postProcessAfterInitialization(Object bean, String beanName); } ``` - `postProcessBeforeInitialization`:在 `@PostConstruct` 和 `InitializingBean.afterPropertiesSet()` 前执行[^1] - `postProcessAfterInitialization`:在初始化完成后执行,常用于代理包装[^2] 2. **控制顺序的关键点** - 在 `postProcessBeforeInitialization` 中检查依赖状态 - 通过同步机制阻塞未满足依赖条件的 Bean - 维护自定义的依赖关系映射表 --- #### 二、MongoDB 顺序控制实现示例 ```java @Component public class MongoDependencyProcessor implements BeanPostProcessor { // 存储已初始化的关键Bean private final Set<String> initializedBeans = Collections.synchronizedSet(new HashSet<>()); // 定义依赖关系:BeanA 依赖 BeanB private final Map<String, String[]> dependencies = Map.of( "mongoTemplate", new String[]{"mongoClient"}, "userRepository", new String[]{"mongoTemplate"} ); @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { // 检查当前Bean是否有依赖要求 if (dependencies.containsKey(beanName)) { String[] requiredBeans = dependencies.get(beanName); // 等待依赖Bean完成初始化 synchronized (this) { while (!allDependenciesReady(requiredBeans)) { try { this.wait(100); // 每100ms检查一次 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) { // 标记当前Bean为已初始化 initializedBeans.add(beanName); synchronized (this) { this.notifyAll(); // 唤醒等待线程 } return bean; } // 检查所有依赖是否就绪 private boolean allDependenciesReady(String[] beanNames) { return Arrays.stream(beanNames).allMatch(initializedBeans::contains); } } ``` --- #### 三、执行流程说明 1. **初始化阶段** ```mermaid sequenceDiagram Spring容器->>MongoClient: 创建实例 Spring容器->>MongoDependencyProcessor: postProcessAfterInitialization(mongoClient) MongoDependencyProcessor->>initializedBeans: 添加"mongoClient" Spring容器->>MongoTemplate: 创建实例 MongoDependencyProcessor->>MongoTemplate: 检查依赖(postProcessBeforeInitialization) MongoDependencyProcessor->>检查: 依赖"mongoClient"已存在? MongoDependencyProcessor->>MongoTemplate: 允许初始化 Spring容器->>MongoDependencyProcessor: postProcessAfterInitialization(mongoTemplate) ``` 2. **依赖阻塞机制** 当 `UserRepository` 初始化时: - 检测到依赖 `mongoTemplate` - 若 `mongoTemplate` 未就绪,线程进入等待状态 - 当 `mongoTemplate` 初始化完成后唤醒线程 --- #### 四、结合 MongoDB 专用 Bean ```java @Configuration public class MongoConfig { // 1. 最先初始化 @Bean public MongoClient mongoClient() { return new MongoClient("localhost:27017"); } // 2. 依赖 mongoClient @Bean public MongoTemplate mongoTemplate(MongoClient client) { return new MongoTemplate(client, "mydb"); } // 3. 自定义Repository(依赖 mongoTemplate) @Bean public UserRepository userRepository(MongoTemplate template) { return new UserRepositoryImpl(template); } } ``` --- #### 五、最佳实践建议 1. **依赖声明** 在 `BeanPostProcessor` 中明确定义依赖关系映射: ```java private final Map<String, String[]> dependencies = new ConcurrentHashMap<>(); @PostConstruct public void initDependencies() { dependencies.put("mongoTemplate", new String[]{"mongoClient"}); dependencies.put("userRepository", new String[]{"mongoTemplate"}); } ``` 2. **循环依赖处理** 使用 `@Lazy` 避免死锁: ```java @Bean @Lazy public ServiceA serviceA(ServiceB serviceB) { ... } ``` 3. **性能优化** - 使用 `ConcurrentHashMap` 替代同步集合 - 设置合理的等待超时时间 - 避免在 `BeanPostProcessor` 中执行耗时操作 --- #### 六、与其他机制对比 | 方法 | 优点 | 缺点 | |------|------|------| | `@DependsOn` | 声明简单 | 硬编码依赖关系 | | 构造器注入 | Spring官方推荐 | 无法动态调整顺序 | | **BeanPostProcessor** | 动态控制<br>集中管理 | 实现复杂度高 | > 关键点:**`BeanPostProcessor` 适用于需要动态控制复杂依赖关系的场景**,简单场景优先使用 `@DependsOn` 或构造器注入[^3][^4]。 --- ### 相关问题 1. 如何在 `BeanPostProcessor` 中处理 MongoDB 连接池的特殊初始化需求? 2. `BeanPostProcessor` 与 `BeanFactoryPostProcessor` 在控制 Bean 顺序时的区别是什么? 3. 如何通过 Spring 的 `@Order` 注解控制多个 `BeanPostProcessor` 的执行顺序? 4. MongoDB 的 `MongoTemplate` 初始化失败时如何在 `BeanPostProcessor` 中优雅处理? 5. 在 Spring Boot 自动配置环境下如何确保自定义 `BeanPostProcessor` 优先于 MongoDB 自动配置执行? [^1]: Spring 官方文档 - BeanPostProcessor 生命周期 [^2]: Bean 初始化过程中的后置处理器调用规则 [^3]: Spring 单例 Bean 的创建流程分析 [^4]: Spring Bean 实例化过程中的反射机制应用 [^5]: Spring Boot 与 MongoDB 集成实践
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值