嘗試在工具類 UtilsDao 的靜態方法中使用 @Autowired 注入 JdbcTemplate,發現靜態字段始終為 null。
Spring 容器管理的是 Bean 實例,依賴注入(DI)發生在 Bean 創建過程中。靜態字段屬於類,不屬於實例,Spring 無法對靜態字段執行標準注入。即使加上 @Autowired,容器也會忽略。
public class UtilsDao {
@Autowired @Qualifier("JdbcTemplate")
private static JdbcTemplate jdbcTemplate; // 永遠為 null
public static JSONArray queryList2Json(String sql) { ... }
}
@Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
applicationContext = context;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
}
public class UtilsDao {
public static JSONArray queryList2Json(String sql) {
JdbcTemplate jdbcTemplate = SpringContextUtil.getBean(JdbcTemplate.class);
List<Map<String, Object>> lists = jdbcTemplate.queryForList(sql);
return JSONArray.fromObject(lists);
}
}
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| SpringContextUtil (推薦) | 通用、非侵入、支援任意 Bean | 運行時才能檢測 Bean 是否存在 | 工具類、靜態方法、非 Spring 管理類 |
| 靜態 setter 手動注入 | 編碼簡單 | 需在配置類/初始化器中顯式調用,易忘、耦合強 | 極簡項目、單一工具類 |
| 改為實例方法 + @Component | 標準 DI、編譯期檢查 | 調用處需注入 UtilsDao 實例 | 新代碼、可重構時 |
| @PostConstruct 初始化靜態字段 | 利用實例方法注入後賦值給靜態字段 | 本質仍是靜態字段,並發安全需自行保證 | 舊代碼最小改動 |
SpringContextUtil 必須被 Spring 掃描到(@Component + 掃描路徑正確)。prototype,每次調用會創建新實例,注意效能。applicationContext 或使用 @SpringBootTest 整合測試。Spring 不支援直接注入靜態字段。標準解法是實現 ApplicationContextAware 封裝 SpringContextUtil,在靜態方法中通過 getBean() 獲取實例。若條件允許,優先將工具類改為 Spring Bean 使用實例方法,更符合依賴倒置原則。