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

Spring 獲取 ApplicationContext 三種方式:Aware 接口、靜態工具類、依賴注入

     編輯:Java 綜合教學

前言

在非 Spring 管理的類別 (工具類、舊代碼、第三方庫回調) 中需要存取 Spring Bean 時,必須獲取 `ApplicationContext`。本文介紹三種標準方式,**強烈推薦方式一 (實作 Aware 接口)**。

方式一:實作 ApplicationContextAware (推薦、最標準)

@Component
public class SpringContextUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        SpringContextUtil.applicationContext = context;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @SuppressWarnings("unchecked")
    public static  T getBean(String name) {
        return (T) applicationContext.getBean(name);
    }

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

    public static  Map getBeansOfType(Class clazz) {
        return applicationContext.getBeansOfType(clazz);
    }
}

原理

  • Spring 容器啟動時,檢測 Bean 是否實作 `ApplicationContextAware`。
  • 若是,自動回調 `setApplicationContext()` 注入上下文實例。
  • 靜態欄位保存引用,提供靜態方法供全域存取。

優點

  • **單例、線程安全**、初始化時機確定 (容器啟動階段)。
  • 無循環依賴風險 (非 `@Autowired` 注入)。
  • 符合 Spring 設計哲學,官方文檔推薦模式。

方式二:靜態工具類 + 主動初始化 (適用無組件掃描環境)

public class SpringContextHolder {
    private static ApplicationContext context;

    public static void setContext(ApplicationContext ctx) {
        if (context != null) throw new IllegalStateException("已初始化");
        context = ctx;
    }

    public static ApplicationContext getContext() {
        if (context == null) throw new IllegalStateException("未初始化,請在啟動類呼叫 setContext()");
        return context;
    }
    // getBean 方法同上...
}

啟動類主動注入

@SpringBootApplication
public class Application implements CommandLineRunner {
    @Autowired private ApplicationContext context;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) {
        SpringContextHolder.setContext(context); // 容器就緒後初始化
    }
}

缺點

  • 需手動確保初始化順序,易忘記導致 NPE。
  • 測試環境需額外配置。

方式三:直接注入 ApplicationContext (僅限 Spring 管理的 Bean)

適用場景

  • 在 **Spring 管理的 Bean** 內部臨時需要上下文。
  • **不可**用於非 Spring 管理的類別 (如 `static` 方法、舊代碼)。

❌ 反模式:ClassPathXmlApplicationContext 重建上下文

// 錯誤示範
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

嚴重後果

  • **建立第二個獨立容器**,Bean 重複創建 (單例失效、資料源重複、定時任務重複執行)。
  • 效能極差 (掃描、解析、實例化)。
  • 與主容器 Bean 不共享狀態。

最佳實踐總結表

場景推薦方式關鍵點
工具類、靜態方法、非 Spring 代碼需取 Bean方式一:實作 ApplicationContextAware單例、線程安全、容器自動回調
Spring Bean 內部需取其他 Bean方式三:直接 @Autowired ApplicationContext簡潔、型別安全
無組件掃描、手動裝配環境方式二:靜態工具類 + 啟動類初始化需嚴格控制初始化時機
單元測試@SpringBootTest 注入 ApplicationContext測試切片自動管理

進階:泛型 Bean 獲取工具方法

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

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

使用:`UserService service = SpringContextUtil.getBean(UserService.class);` 避免強轉。

結語

**「實作 `ApplicationContextAware` + 靜態工具類」** 是獲取 Spring 上下文的**黃金標準**。切記:**絕不**在運行時手動 `new ClassPathXmlApplicationContext()`,這會破壞單例語意並導致嚴重資源洩漏與行為錯誤。

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