目錄
一、模式介紹
二、架構設計
三、Demo 示例
四、總結
一、模式介紹
上下文對象(Context Object)模式 最早由《Core J2EE Patterns》第二版提出,其核心目標是在多層或多組件間共享與當前作用域(如一次請求、一次會話、一次業務流程)相關的所有狀態和服務,消除各組件對底層環境細節(如協議、線程、本地存儲等)的直接依賴,從而提高系統的可復用性、可維護性和可測試性。Context Object 封裝了與某個 Scope 相關的數據與行為,使各層或模塊只需依賴該 Context,即可獲得所需服務或狀態,而無需顯式傳遞大量參數或直接引用環境 API。
使用場景與歷史背景:
-
在傳統多層應用中,每一層如果要訪問共享信息(如用戶憑證、事務、配置參數),通常需要將其作為參數顯式傳遞,導致方法簽名臃腫、可讀性差、易出錯。
-
隨著 J2EE 應用復雜度提升,設計者發現將環境細節直接嵌入業務邏輯耦合度過高,維護成本激增。
-
《Core J2EE Patterns》第二版總結了當時業界實踐,將這種“共享環境數據”抽象為 Context Object 模式,對應 ApplicationContext/ServletContext、Hibernate SessionContext 等典型用法。
解決問題:
-
解耦:業務組件不必直接依賴環境(HTTP、JNDI、線程等)接口,一律通過 Context 獲取所需信息或服務。
-
集中管理:所有上下文相關的信息集中維護,便于調試、監控及擴展。
-
生命周期一致性:Context 在作用域開始時創建,結束時統一銷毀,資源釋放更可控。
典型實現:
-
Apache Spark –
SparkContext
/SQLContext
/StreamingContext
聚合集群配置、調度、序列化、監控接口,并暴露 RDD/DataFrame/流計算等 API。
-
Flink –
StreamExecutionEnvironment
/ExecutionEnvironment
封裝執行引擎配置、并行度、checkpoint、狀態后端等,用于構建批/流作業。
二、架構設計
以下為 Context Object 模式的標準 UML 類圖:
主要組件說明:
-
Context(接口):定義獲取與存放上下文數據的方法。
-
ConcreteContext(實現類):內部維護一張屬性映射,負責實際存取。
-
ContextFactory(工廠類):(可選)根據環境創建對應的 Context 實例。
-
Client:業務組件,通過構造器或工廠獲取 Context 實例,并向其讀寫數據或調用存入的服務。
三、Demo 示例
問題場景:在一個 Web 應用或微服務中,常常需要在多個業務層(如控制層、服務層、數據訪問層)之間傳遞用戶會話信息、跟蹤操作日志對象以及可復用的工具服務實例。直接在每個方法簽名中傳遞這些參數,不僅會導致參數列表臃腫,還容易遺漏,增加維護成本。
解決方案:使用上下文對象模式,將所有需跨層共享的數據與服務封裝到一個 Context 對象中,由各層直接從 Context 中獲取,而不必在方法之間顯式傳遞這些參數。
// 1. 定義 Context 接口,提供讀寫屬性和服務獲取功能 public interface RequestContext {<T> T get(String key, Class<T> type);void set(String key, Object value); } ? // 2. Context 實現,內部維護屬性映射 public class RequestContextImpl implements RequestContext {private final Map<String, Object> data = new HashMap<>(); ?@Overridepublic <T> T get(String key, Class<T> type) {return type.cast(data.get(key));} ?@Overridepublic void set(String key, Object value) {data.put(key, value);} } ? // 3. Context 工廠,初始化必要屬性 public class RequestContextFactory {public static RequestContext create(String userId) {RequestContext ctx = new RequestContextImpl();// 初始化用戶信息和日志追蹤器ctx.set("userId", userId);ctx.set("traceId", UUID.randomUUID().toString());return ctx;} } ? // 4. 控制層:創建 Context 并啟動業務流程 public class Controller {public void handleRequest(String userId) {RequestContext ctx = RequestContextFactory.create(userId);new BusinessService(ctx).process();} } ? // 5. 業務層:直接從 Context 獲取 userId 和 TraceId,執行業務邏輯 public class BusinessService {private final RequestContext ctx; ?public BusinessService(RequestContext ctx) {this.ctx = ctx;} ?public void process() {String userId = ctx.get("userId", String.class);String traceId = ctx.get("traceId", String.class);// 輸出日志時無需額外傳參System.out.println("[" + traceId + "] Processing business logic for user " + userId); ?// 調用下一層服務new DataService(ctx).execute();} } ? // 6. 數據訪問層:繼續復用同一個 Context public class DataService {private final RequestContext ctx; ?public DataService(RequestContext ctx) {this.ctx = ctx;} ?public void execute() {String traceId = ctx.get("traceId", String.class);// 使用 traceId 進行 SQL 日志關聯System.out.println("[" + traceId + "] Executing SQL queries");} }
說明:
-
Controller
層只需創建并初始化一次RequestContext
,并傳遞給后續各層,不再維護多個參數。 -
任何業務或數據訪問類均可通過
ctx.get(...)
獲取所需信息,簡化方法簽名。 -
可擴展到添加更多上下文數據(如用戶權限列表、國際化配置、第三方服務客戶端等),只需在 Context 中新增屬性,而無需改動各層接口。
四、總結
上下文對象模式 通過封裝作用域相關的所有狀態與服務,實現組件與環境的最大解耦,提升系統的靈活性和可維護性。
-
價值:
-
模塊化:各功能模塊僅依賴 Context 接口,無須關注如何獲取底層資源。
-
擴展性:新增上下文數據或服務時,只需修改 Context 實現,無需改動業務層代碼。
-
測試友好:可為單元測試提供 Mock Context,對業務邏輯進行隔離測試。
-
-
注意事項:
-
避免 Context 過于龐大,必要時可拆分為多個子 Context(如 ConfigContext、SecurityContext)。
-
隨著系統復雜度上升,建議引入成熟的 IoC/DI 容器(如 Spring
ApplicationContext
)來管理對象生命周期和依賴注入。
-
Context Object 模式是企業級應用架構中常見且行之有效的方案,既可手寫實現,也可借助框架。它在《Core J2EE Patterns》一書中的提煉,為現代微服務與云原生開發中的“作用域數據共享”提供了理論基礎與實踐指導。