文章目錄
- 前言
- 1. 圖示
- 2. 源碼坐標
- 后記
前言
今天看了一段老業務代碼,HttpServletRequest 被注入后直接用于業務邏輯。
好奇Spring是如何解決線程安全問題。
@Controller
public class TestController {@ResourceHttpServletRequest request;@ResponseBody@GetMapping("/test")public String test() {return request.getQueryString();}
}
本質問題是 Spring 如何裝配 Controller 中 HttpServletRequest 的bean依賴
1. 圖示
2. 源碼坐標
可以在代碼層面打斷點驗證,版本:SpringBoot 2.7.5 (自行集成WebStarter)
- 加載web相關的bean定義
ServletWebServerApplicationContext.java:141
- 設置工廠
WebApplicationContextUtils#registerWebApplicationScopes(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, javax.servlet.ServletContext)
- 具體的工廠方法定義(定義是從線程中取request)
RequestObjectFactory#getObject
// 關鍵代碼塊
@Override
public ServletRequest getObject() {return currentRequestAttributes().getRequest();
}
- 啟動時解析依賴,將工廠方法植入
org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
org.springframework.beans.factory.support.DefaultListableBeanFactory#findAutowireCandidates
org.springframework.beans.factory.support.AutowireUtils#resolveAutowiringValue
- 接收請求
FrameworkServlet.java:1003 將響應放在ThreadLocal中
// 關鍵代碼塊
if (requestAttributes != null) {RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);
}
- 回調工廠方法創建 Request
AutowireUtils.java:292
// 關鍵代碼
return method.invoke(this.objectFactory.getObject(), args)
后記
- 解決 HttpServletRequest 的線程安全問題的思路
- 工廠方法定義, 將 Request 的創建約束為從 ThreadLocal 中取。
- 收到請求 ,將 Request 放入ThreadLocal 中。(請求時設置 Request)
- Controller 實例化,使用動態代理技術回調工廠方法。(業務邏輯消費 Request)
- 建議按官方建議替換成方法參數,語義更加清晰。
@ResponseBody@GetMapping("/test")public String test(HttpServletRequest request) {return request.getQueryString();}