透過持有 Spring ApplicationContext,打破依賴注入的作用域限制,實現在非 Spring 管理類別、靜態方法、工具類等任意位置獲取 Bean 實例。採用單例模式與 ApplicationContextAware 接口結合,確保上下文全局可訪問且生命週期安全。
package com.example.util;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* 服務定位器 - 全局 Spring Bean 訪問入口
* 支援按名稱/類型獲取 Bean,自動處理類型轉換
*/
public final class ServiceLocator implements ApplicationContextAware, DisposableBean {
private static final Log logger = LogFactory.getLog(ServiceLocator.class);
private static ApplicationContext context = null;
/**
* Spring 容器啟動時自動注入 ApplicationContext
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
logger.debug("Injected the ApplicationContext into ServiceLocator: " + applicationContext);
if (ServiceLocator.context != null) {
logger.warn("ApplicationContext in ServiceLocator is being overridden. Original: "
+ ServiceLocator.context);
}
ServiceLocator.context = applicationContext;
}
/**
* 容器關閉時清理靜態引用,防止記憶體洩漏
*/
@Override
public void destroy() throws Exception {
ServiceLocator.clear();
}
/**
* 獲取原始 ApplicationContext 實例
*/
public static ApplicationContext getApplicationContext() {
assertContextInjected();
return context;
}
/**
* 按 Bean 名稱獲取實例(自動類型轉換)
* @param name Bean 名稱
* @param 目標類型
* @return Bean 實例
*/
@SuppressWarnings("unchecked")
public static T getService(String name) {
assertContextInjected();
return (T) context.getBean(name);
}
/**
* 按 Bean 類型獲取實例
* @param requiredType Bean 類別
* @param 目標類型
* @return Bean 實例
*/
public static T getService(Class requiredType) {
assertContextInjected();
return context.getBean(requiredType);
}
/**
* 手動清理上下文引用
*/
public static void clear() {
logger.debug("Clear ApplicationContext in ServiceLocator: " + context);
context = null;
}
/**
* 斷言上下文已注入,未注入則拋出異常
*/
private static void assertContextInjected() {
if (context == null) {
throw new IllegalStateException("ApplicationContext not injected. " +
"Please define ServiceLocator in context.xml");
}
}
}
// 在任意非 Spring 管理類別中使用
public class SomeUtilityClass {
public void doSomething() {
// 按名稱獲取
UserService userService = ServiceLocator.getService("userService");
// 按類型獲取(單例 Bean)
OrderService orderService = ServiceLocator.getService(OrderService.class);
// 獲取原始上下文進行進階操作
ApplicationContext ctx = ServiceLocator.getApplicationContext();
}
}
| 優勢 | 說明 |
|---|---|
| 零侵入式 | 無需修改現有 Bean 定義,不影響 Spring 依賴注入機制 |
| 全局可訪問 | 靜態方法調用,無需傳遞上下文引用 |
| 類型安全 | 泛型支援自動轉換,減少強制轉型錯誤 |
| 生命週期安全 | 實現 DisposableBean,容器關閉自動清理 |
getService