1. 循环依赖的深度解析与实战解决方案
循环依赖是Spring框架中一个经典且棘手的问题,它发生在两个或多个Bean相互依赖的情况下。让我们通过一个典型示例来理解这个问题:
java复制@Service
public class ServiceA {
private final ServiceB b;
public ServiceA(@Lazy ServiceB b) {
this.b = b;
}
}
@Service
public class ServiceB {
private final ServiceA a;
public ServiceB(ServiceA a) {
this.a = a;
}
}
1.1 循环依赖的本质
当Spring容器尝试创建ServiceA时,发现它需要ServiceB,于是开始创建ServiceB。但在创建ServiceB时,又发现它需要ServiceA,这就形成了一个闭环。Spring默认情况下会抛出BeanCurrentlyInCreationException异常。
1.2 三级缓存机制详解
Spring通过三级缓存机制巧妙地解决了这个问题:
- 一级缓存(singletonObjects):存储完全初始化好的Bean
- 二级缓存(earlySingletonObjects):存储提前暴露的原始Bean(尚未完成属性注入)
- 三级缓存(singletonFactories):存储Bean工厂对象,用于生成原始Bean
具体流程如下:
- 创建ServiceA时,将它的ObjectFactory放入三级缓存
- 当ServiceB需要注入ServiceA时,从三级缓存获取ObjectFactory并生成原始对象
- 这个原始对象被放入二级缓存,同时从三级缓存移除
- ServiceB完成创建后,ServiceA完成属性注入
1.3 @Lazy注解的妙用
@Lazy注解通过延迟初始化打破了循环依赖的链条。它告诉Spring:
- 不要立即创建真实的Bean
- 先注入一个代理对象
- 当第一次真正调用方法时,才创建真实对象
这种方案特别适合解决构造函数注入导致的循环依赖问题。
1.4 AOP代理的特殊处理
当Bean需要AOP代理时,Spring会进行特殊处理:
- 检查是否有匹配的Advisor
- 如果有,工厂返回代理对象
- 如果没有,返回原始对象
常见的AOP场景包括:
- @Transactional事务管理
- @Cacheable缓存处理
- @Async异步方法
- 自定义切面逻辑
重要提示:虽然Spring提供了循环依赖的解决方案,但在设计上仍应尽量避免循环依赖,因为它会导致代码耦合度增高,难以维护。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Spring MVC工作流程深度剖析
Spring MVC的核心是围绕DispatcherServlet(前端控制器)构建的请求处理管道。让我们详细解析这个流程:
2.1 完整请求处理流程
-
请求接收阶段:
- 用户发起HTTP请求
- 请求到达DispatcherServlet(简称DD)
-
处理器映射阶段:
- DD调用HandlerMapping
- 根据URL找到对应的Controller方法
- 返回HandlerExecutionChain(包含处理器和拦截器)
-
处理器适配阶段:
- DD调用HandlerAdapter
- 适配器执行Controller方法
- 返回ModelAndView
-
视图解析阶段:
- DD将ModelAndView传给ViewResolver
- 解析出具体的View对象
-
视图渲染阶段:
- View使用Model数据进行渲染
- 生成最终响应内容
2.2 核心组件详解
2.2.1 HandlerMapping实现
- RequestMappingHandlerMapping:处理@RequestMapping注解
- BeanNameUrlHandlerMapping:根据Bean名称映射
- SimpleUrlHandlerMapping:显式URL映射
2.2.2 HandlerAdapter实现
- RequestMappingHandlerAdapter:处理基于注解的控制器
- HttpRequestHandlerAdapter:处理HttpRequestHandler
- SimpleControllerHandlerAdapter:处理Controller接口
2.2.3 ViewResolver实现
- InternalResourceViewResolver:JSP视图解析
- ThymeleafViewResolver:Thymeleaf模板解析
- FreeMarkerViewResolver:FreeMarker模板解析
2.3 拦截器工作机制
拦截器(Interceptor)提供了三个切入点:
- preHandle:处理器执行前
- postHandle:处理器执行后,视图渲染前
- afterCompletion:请求完成后
典型应用场景:
- 权限检查
- 日志记录
- 性能监控
- 通用数据处理
3. 线程池的深度配置与生产实践
Java线程池是并发编程的核心组件,合理配置对系统性能至关重要。
3.1 核心参数解析
| 参数 | 说明 | 推荐值 | 注意事项 |
|---|---|---|---|
| corePoolSize | 核心线程数 | CPU密集型:N+1 IO密集型:2N+1 |
长期存活,不会被回收 |
| maximumPoolSize | 最大线程数 | 一般≤200 | 包含核心线程 |
| keepAliveTime | 空闲线程存活时间 | 60秒 | 仅对非核心线程有效 |
| workQueue | 工作队列 | ArrayBlockingQueue | 必须设置合理大小 |
| threadFactory | 线程工厂 | 自定义命名 | 便于问题排查 |
| handler | 拒绝策略 | CallerRunsPolicy 或自定义 |
避免直接丢弃 |
3.2 队列选型指南
-
ArrayBlockingQueue:
