首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 獲取 Spring 容器 Bean 的五種方式:從 ApplicationContextAware 到靜態工具類最佳實踐

     編輯:Java 綜合教學

前言:非 Spring 管理類如何優雅獲取 Bean?五種方式按優雅度遞減排序

在非 Spring 管理的類中(工具類、第三方庫回調、舊代碼遷移)獲取 Spring Bean 是常見需求。本文系統梳理 5 種方式,對比優缺點,推薦 實現 ApplicationContextAware + 靜態工具類 作為通用最佳實踐,並解析 WebApplicationContextUtils、ContextLoader 陷阱。

一、 五種方式全景對比

方式 核心原理 適用場景 優點 缺點 推薦指數
1. 實現 ApplicationContextAware (推薦) Spring 初始化時回調 setApplicationContext 注入上下文 通用工具類、靜態方法獲取 Bean 非侵入、靜態訪問、生命周期受 Spring 管理、支持多上下文隔離 需在 XML/配置類註冊 Bean ⭐⭐⭐⭐⭐
2. WebApplicationContextUtils 從 ServletContext 屬性獲取 WebApplicationContext Web 環境、有 ServletContext 時 (Filter、Servlet、Listener) 無需額外 Bean 註冊、標準 API 依賴 Web 環境、需傳 ServletContext、非靜態友好 ⭐⭐⭐⭐
3. 繼承 ApplicationObjectSupport 父類提供 getApplicationContext() 自定義 Spring 擴展類 (如自定義 Repository 基類) 代碼復用、IDE 友好 強制繼承、單繼承限制、仍需註冊 Bean ⭐⭐⭐
4. 繼承 WebApplicationObjectSupport 父類提供 getWebApplicationContext() Web 環境下的 Spring 擴展類 同 3,專為 Web 設計 同 3,更窄 ⭐⭐
5. 手動 new ClassPathXmlApplicationContext 獨立構建容器 獨立應用、測試、批處理、無 Web 容器 完全獨立、無依賴 重複創建容器、資源浪費、Bean 不共享、配置文件耦合 ⭐ (慎用)

二、 最佳實踐:SpringContextHolder 靜態工具類

2.1 核心實現


package com.example.util;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

@Component // 關鍵:註冊為 Spring Bean,觸發 setApplicationContext
public class SpringContextHolder implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(@NonNull ApplicationContext ctx) throws BeansException {
        applicationContext = ctx;
    }

    // 靜態獲取 Bean
    public static  T getBean(String name) {
        checkContext();
        return (T) applicationContext.getBean(name);
    }

    public static  T getBean(Class clazz) {
        checkContext();
        return applicationContext.getBean(clazz);
    }

    public static  T getBean(String name, Class clazz) {
        checkContext();
        return applicationContext.getBean(name, clazz);
    }

    // 檢查上下文是否就緒
    private static void checkContext() {
        if (applicationContext == null) {
            throw new IllegalStateException("SpringContextHolder 未初始化,請確認:\n" +
                    "1. 已掃描到 @Component 包路徑\n" +
                    "2. 未在靜態塊/構造函數中調用 getBean()\n" +
                    "3. 非 Web 環境需手動調用 refresh()");
        }
    }

    // 可選:獲取環境屬性
    public static String getProperty(String key) {
        return getBean(org.springframework.core.env.Environment.class).getProperty(key);
    }
}

2.2 使用示例


// 任意非 Spring 管理類中
UserService userService = SpringContextHolder.getBean(UserService.class);
String url = SpringContextHolder.getProperty("app.url");

2.3 關鍵注意事項

  1. @Component 掃描路徑:確保工具類所在包被 <context:component-scan> 或 @ComponentScan 覆蓋。
  2. 生命周期陷阱:絕不在靜態塊、構造函數、字段初始化中調用 getBean(),此時 SpringContextHolder 尚未注入上下文。需在方法內調用。
  3. 多上下文隔離:父子容器 (如 SpringMVC DispatcherServlet 子容器) 共用同一個靜態引用,若需區分可改用 ThreadLocal 或 key 前綴區分。
  4. 泛型擦除:getBean(String, Class) 比單純 getBean(String) 更安全,避免強轉異常。

三、 Web 環境替代方案:WebApplicationContextUtils


// 在 Filter、Servlet、Listener 中有 ServletContext 時
WebApplicationContext ctx = WebApplicationContextUtils
        .getRequiredWebApplicationContext(servletContext); // 找不到拋異常
// 或
WebApplicationContext ctx = WebApplicationContextUtils
        .getWebApplicationContext(servletContext); // 找不到返回 null

UserService service = ctx.getBean(UserService.class);

優勢:無需額外 Bean 註冊,標準 SPI。
劣勢:耦合 Servlet API,非 Web 環境 (測試、批處理) 不可用。

四、 反模式:ContextLoader.getCurrentWebApplicationContext()


// 嚴禁在容器啟動階段調用!
WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
wac.getBean("xxx"); // 啟動時 wac 為 null,拋 NPE

原因:ContextLoaderListener.contextInitialized() 完成前,ContextLoader.currentContext 為 null。若在 Bean 的構造函數、@PostConstruct、靜態塊中調用,必現 NPE。

五、 總結決策樹

需要在非 Spring 類獲取 Bean?
├─ 是 Web 環境且有 ServletContext (Filter/Servlet)?
│   ├─ 是 → WebApplicationContextUtils (零配置)
│   └─ 否 → 通用場景
│       └─ 實現 ApplicationContextAware + 靜態工具類 SpringContextHolder (推薦標準化)
├─ 獨立應用/測試/批處理?
│   └─ new ClassPathXmlApplicationContext (僅此場景)
└─ 正在開發 Spring 擴展基類?
    └─ 繼承 ApplicationObjectSupport (框架開發)

結語

獲取 Spring Bean 的本質是 「如何拿到 ApplicationContext 引用」。SpringContextHolder 以 ApplicationContextAware 為橋樑,將上下文「推」給靜態持有者,實現了「非 Spring 類零成本訪問 Spring Bean」的工程化平衡。記住三條鐵律:註冊 Bean、靜態持有、方法內調用,拒絕手動構建容器、拒絕啟動期調用 ContextLoader,你的代碼將既優雅又健壯。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved