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

Spring 2.0 中 EL 表達式 ${jdbc.name} 未解析問題:根因分析與解決方案

     編輯:Java 綜合教學

Spring 2.0 中 EL 表達式 ${jdbc.name} 未解析問題:根因分析與解決方案

在使用 Spring 2.0 的 PropertyPlaceholderConfigurer 時,若發現 XML 配置中的 ${jdbc.name} 以原樣字符串輸出而非屬性值,通常是由於 BeanFactory 與 ApplicationContext 的差異 導致後置處理器未自動觸發。本文深度剖析源碼機制,提供三種解決方案並對比優劣。

問題現場重現

<!-- applicationContext.xml -->
<bean id="iDaoImp" class="service.IDaoImp">
    <property name="name" value="${jdbc.name}"/>
</bean>

<bean id="property" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="classpath:jdbc.properties"/>
</bean>

<!-- jdbc.properties -->
jdbc.name=王昊
// 測試代碼
XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
IDaoImp imp = (IDaoImp) beanFactory.getBean("iDaoImp");
System.out.println(imp.getName()); // 輸出:${jdbc.name} ❌

核心根因:BeanFactory 不自動調用 BeanFactoryPostProcessor

Spring 容器體系分兩層:

  • BeanFactory:基礎 IoC 容器,**懶加載**,**不自動執行** BeanFactoryPostProcessor(如 PropertyPlaceholderConfigurer)
  • ApplicationContext:高級容器,**啟動時自動掃描並執行** 所有 BeanFactoryPostProcessor 與 BeanPostProcessor

源碼證據(AbstractApplicationContext.refresh()):

// 1. 調用 BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// 2. 註冊 BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 3. 初始化單例 Bean(此時 ${...} 已被替換)
finishBeanFactoryInitialization(beanFactory);

而 XmlBeanFactory 僅完成 Bean 定義讀取,**完全跳過** 步驟 1,導致佔位符未解析。

三種解決方案對比

方案一:手動觸發後置處理器(治標不治本)

XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
PropertyPlaceholderConfigurer ppc = (PropertyPlaceholderConfigurer) beanFactory.getBean("property");
ppc.postProcessBeanFactory(beanFactory); // 手動調用
IDaoImp imp = (IDaoImp) beanFactory.getBean("iDaoImp"); // 輸出:王昊 ✅

缺點:侵入業務代碼,違背 IoC 原則,若有多個後置處理器需手動排序調用,維護成本極高。

方案二:升級為 ClassPathXmlApplicationContext(推薦)

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
IDaoImp imp = ctx.getBean("iDaoImp", IDaoImp.class);
System.out.println(imp.getName()); // 輸出:王昊 ✅

優點:零代碼修改,自動完成所有後置處理,符合 Spring 設計初衷,支援國際化、事件發布、AOP 等企業級特性。

方案三:Spring 3.0+ 使用 (現代寫法)

<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 自動註冊 PropertySourcesPlaceholderConfigurer,功能更強 -->

支援多文件、系統環境變量覆蓋、默認值、文件編碼等高級特性。

深度避坑指南

誤區真相
以為 PropertyPlaceholderConfigurer 是 BeanPostProcessor它是 BeanFactoryPostProcessor,**在 Bean 實例化前** 修改 BeanDefinition 的屬性值
以為 value="${jdbc.name}" 是 SpEL 表達式它是 Spring 專用佔位符語法,解析器為 PropertyPlaceholderHelper,非 SpEL
覺得 XmlBeanFactory 輕量適合測試測試也應用 ApplicationContext,或使用 @ContextConfiguration 整合 Spring Test

最佳實踐總結

  1. 生產/測試統一使用 ApplicationContext,拒絕直接實例化 BeanFactory
  2. Spring 3.0+ 採用 <context:property-placeholder> 或 @PropertySource 註解
  3. 屬性文件編碼統一 UTF-8,避免中文亂碼(Spring 5+ 默認 UTF-8)
  4. 敏感配置(密碼、Key)走環境變量或配置中心,不寫入 properties 文件

「Spring 的強大在於約定優於配置,但前提是你用對了容器。BeanFactory 是 SPI 接口,ApplicationContext 才是開發者應直接面對的編程模型。」

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