Spring Boot 开发必知:那些高频使用的核心上下文类 🐛 从一个 NPE 说起 同事在 IdUtils 工具类里写了一个生成订单号的方法,需要调用数据库序列服务。代码部署到生产环境后,每隔几天就会抛出一个 NullPointerException,而且总是在凌晨 2 点左右。
排查后发现问题:生成订单号的逻辑需要从 Spring 容器中获取 SequenceService,但 IdUtils 是一个纯静态工具类,不归 Spring 管理。同事的写法是:
public class IdUtils { // 这样永远拿不到 Bean——IdUtils 自己都没被 Spring 管理,谁来注入? @Autowired private static SequenceService sequenceService; public static String genOrderId() { return sequenceService.nextVal("order"); // NPE! sequenceService == null } } 这是一个典型场景: 需要在不受 Spring 管理的类中获取 Spring Bean 。解决它的钥匙就是本篇要讲的"上下文类"(Context Classes)——Spring 框架提供的一系列能让你在任何位置获取框架运行时状态的工具。
flowchart LR classDef root fill:#0f172a,stroke:#3b82f6,stroke-width:2px,color:#bfdbfe,font-weight:bold; classDef branch fill:#2d1a05,stroke:#f59e0b,stroke-width:2px,color:#fde68a,font-weight:bold; classDef leaf fill:#1e1e24,stroke:#6b7280,stroke-width:1.5px,color:#e5e7eb; classDef highlight fill:#450a0a,stroke:#dc2626,stroke-width:1.5px,color:#fecaca,font-weight:bold; ROOT[Spring Boot 核心上下文类] ROOT --> B1(1. Web 请求上下文) B1 --> L1["RequestContextHolder\n持有当前请求的 ThreadLocal"] B1 --> L2["ServletRequestAttributes\n封装 HttpServletRequest/Response"] B1 --> L3["RequestContextUtils\nLocale / FlashMap / 输入输出流"] ROOT --> B2(2. Security 安全上下文) B2 --> L4["SecurityContextHolder\n持有当前认证信息的 ThreadLocal"] B2 --> L5["Authentication\nPrincipal / Credentials / Authorities"] ROOT --> B3(3. 事务上下文) B3 --> L6["TransactionSynchronizationManager\n事务状态判断 / 回调注册\n事务资源绑定"] ROOT --> B4(4. 容器上下文) B4 --> L7["ApplicationContext\nSpring 容器本身"] B4 --> L8["ApplicationContextAware\n回调注入容器引用"] B4 --> L9["Environment\n配置属性 / Profile"] ROOT --> B5(5. 其他) B5 --> L10["LocaleContextHolder\n国际化语言上下文"] B5 --> L11["BeanFactory\n底层 IoC 容器"] class ROOT root; class B1,B2,B3,B4,B5 branch; class L1,L2,L3,L4,L5,L6,L7,L8,L9,L10,L11 leaf; class L1,L4,L7 highlight; 🌐 一、Web 请求上下文 ⚙️ 1.1 核心类与底层原理 RequestContextHolder(请求上下文持有者)通过 ThreadLocal(线程局部变量)将当前请求的 ServletRequestAttributes 绑定到当前线程。DispatcherServlet(Spring MVC 的前端控制器)在处理每个请求时,会自动调用 RequestContextHolder.setRequestAttributes() 将请求对象"挂"到当前线程上。
...