<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:context="https://www.springframework.org/schema/context"
xmlns:p="https://www.springframework.org/schema/p"
xmlns:tx="https://www.springframework.org/schema/tx"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 1. 加密屬性佔位符配置器 -->
<bean class="com.example.config.EncryptPropertyPlaceholderConfigurer">
<property name="locations">
<value>file:/opt/config/app.properties</value>
</property>
</bean>
<!-- 2. 數據源 -->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- 連接池調優 -->
<property name="maxTotal" value="${pool.maxTotal:20}"/>
<property name="maxIdle" value="${pool.maxIdle:10}"/>
<property name="minIdle" value="${pool.minIdle:5}"/>
<property name="initialSize" value="${pool.initialSize:5}"/>
<property name="validationQuery" value="SELECT 1"/>
<property name="testOnBorrow" value="true"/>
<property name="testOnReturn" value="false"/>
<property name="testWhileIdle" value="true"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="minEvictableIdleTimeMillis" value="300000"/>
</bean>
<!-- 3. SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath*:mapper/**/*.xml"/>
<property name="typeAliasesPackage" value="com.example.entity"/>
</bean>
<!-- 4. Mapper 掃描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.example.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<!-- 5. 事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 6. 註解驅動事務 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 7. Spring 上下文持有器 (工具類) -->
<bean id="springContextHolder" class="com.example.util.SpringContextHolder"/>
</beans>
PropertyPlaceholderConfigurer,重寫 convertPropertyValue 解密 {cipher}xxx 格式屬性。file: 前綴指定絕對路徑,避免將密碼打包進 WAR/JAR。| 參數 | 推薦值 | 說明 |
|---|---|---|
| maxTotal / maximumPoolSize | 20-50 | 最大連接數,視 CPU 核數與業務並發設定 |
| maxIdle / maxIdle | 10-20 | 最大閒置連接 |
| minIdle / minimumIdle | 5-10 | 最小閒置連接,保持熱備 |
| validationQuery | SELECT 1 | 驗證連接有效性,MySQL/Oracle 通用 |
| testOnBorrow | true | 借用時驗證,犧牲少量性能換穩定 |
| testWhileIdle | true | 後台線程定期檢測閒置連接 |
| timeBetweenEvictionRunsMillis | 60000 | 驗證間隔 1 分鐘 |
| minEvictableIdleTimeMillis | 300000 | 閒置 5 分鐘後可被驅逐 |
強烈建議:生產環境替換為 HikariCP (com.zaxxer.hikari.HikariDataSource),性能優 2-3 倍,配置更簡潔。
dataSource:注入連接池。configLocation:指向 mybatis-config.xml (全局設定:緩存、類型別名、插件等)。mapperLocations:Mapper XML 文件通配符路徑。typeAliasesPackage:實體類包路徑,自動註冊別名 (類名首字母小寫)。basePackage:Mapper 接口所在包,掃描生成代理對象註冊到 Spring 容器。sqlSessionFactoryBeanName:指定工廠 Bean 名稱,避免多數據源衝突。sqlSessionFactory 屬性 (而非 BeanName),可能導致 PropertyPlaceholderConfigurer 失效 (${xxx} 不解析)。解決:改用 sqlSessionFactoryBeanName 或不設置 (默認尋找唯一工廠)。DataSourceTransactionManager:單數據源場景標準管理器。JtaTransactionManager:分布式事務 (XA) 需求時使用。@Transactional 關鍵屬性:@Transactional(
propagation = Propagation.REQUIRED, // 傳播行為
isolation = Isolation.READ_COMMITTED, // 隔離級別
timeout = 30, // 超時秒數
rollbackFor = Exception.class, // 回滾異常類
readOnly = false // 唯讀優化
)
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext ctx;
@Override public void setApplicationContext(ApplicationContext ctx) { SpringContextHolder.ctx = ctx; }
public static ApplicationContext getContext() { return ctx; }
public static <T> T getBean(String name) { return (T) ctx.getBean(name); }
public static <T> T getBean(Class<T> clazz) { return ctx.getBean(clazz); }
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 下劃線轉駝峰 -->
<setting name="logImpl" value="SLF4J"/>
</settings>
<typeAliases>
<package name="com.example.entity"/>
</typeAliases>
</configuration>
| 現象 | 可能原因 | 排查方向 |
|---|---|---|
| Cannot load JDBC driver class '${driver}' | MapperScannerConfigurer 過早初始化導致佔位符未解析 | 改用 sqlSessionFactoryBeanName 或升級 MyBatis-Spring |
| Transaction rollback not work | @Transactional 在 private/非 public 方法 / 同類內部調用 / 異常被捕獲未拋出 | 檢查方法修飾符、調用方式、異常處理 |
| Mapper 接口無法注入 | basePackage 掃描路徑錯誤 / 接口無 @Mapper / 未掃描到 XML | 檢查包結構、XML 路徑、命名空間 |
| 連接泄漏 | testOnBorrow=false / 無驗證查詢 / 長事務未提交 | 開啟驗證、縮短事務、監控連接池指標 |
application-context.xml 是 Spring MVC 數據訪問層的「心臟」。掌握加密配置、連接池調優、SqlSessionFactory 整合、Mapper 掃描機制、聲明式事務邊界與常見坑點,能構建穩健、安全、高性能的持久層架構。現代項目建議遷移至 Spring Boot 自動配置 (DataSourceAutoConfiguration, MybatisAutoConfiguration),但理解 XML 原理對排查疑難雜症仍不可或缺。