Spring 配置文件分層架構
src/main/resources/
├── application.yml # 主配置,環境無關
├── application-dev.yml # 開發環境
├── application-prod.yml # 生產環境
├── application-test.yml # 測試環境
├── spring/
│ ├── spring-mvc.xml # Web 層:DispatcherServlet 載入
│ ├── spring-service.xml # 業務層:Service、AOP、事務
│ └── spring-dao.xml # 資料層:DataSource、MyBatis、Redis
└── mybatis/
└── mapper/ # XML 映射檔
web.xml 雙容器啟動機制
<web-app>
<!-- 父容器:業務層 + 資料層 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-service.xml,classpath:spring/spring-dao.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 子容器:Web 層 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
spring-mvc.xml 核心配置
<beans xmlns:mvc="https://www.springframework.org/schema/mvc"
xmlns:context="https://www.springframework.org/schema/context"
xsi:schemaLocation="...
https://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 1. 註解驅動:@Controller、@RequestMapping、@RequestBody 等 -->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg value="yyyy-MM-dd HH:mm:ss"/>
</bean>
</property>
<property name="serializationInclusion" value="NON_NULL"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 2. 掃描 Controller 包 -->
<context:component-scan base-package="com.example.controller" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.RestController"/>
</context:component-scan>
<!-- 3. 靜態資源處理 (關鍵!) -->
<mvc:default-servlet-handler/>
<mvc:resources mapping="/static/**" location="/static/" cache-period="31536000"/>
<mvc:resources mapping="/favicon.ico" location="/favicon.ico"/>
<!-- 4. 視圖解析器 (Thymeleaf 範例) -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver"/>
</bean>
<bean id="templateResolver" class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="classpath:/templates/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
<!-- 5. 攔截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/api/**"/>
<bean class="com.example.interceptor.AuthInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
spring-dao.xml 資料層配置
<beans xmlns:tx="https://www.springframework.org/schema/tx"
xsi:schemaLocation="...
https://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 1. 屬性佔位符 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 2. Druid DataSource -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" 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="initialSize" value="5"/>
<property name="maxActive" value="20"/>
<property name="validationQuery" value="SELECT 1"/>
<property name="testWhileIdle" value="true"/>
<property name="filters" value="stat,wall"/>
</bean>
<!-- 3. MyBatis SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mybatis/mapper/**/*.xml"/>
<property name="typeAliasesPackage" value="com.example.entity"/>
<property name="configurationProperties">
<props>
<prop key="mapUnderscoreToCamelCase">true</prop>
<prop key="cacheEnabled">true</prop>
</props>
</property>
</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" proxy-target-class="true"/>
</beans>
常見錯誤與解決方案
| 錯誤現象 | 根因 | 解決方案 |
| 404 靜態資源無法訪問 | 缺少 <mvc:default-servlet-handler/> 或 <mvc:resources> | 添加靜態資源映射,或調整 DispatcherServlet url-pattern |
| Controller 未被掃描 | component-scan 包路徑錯誤、缺少 @Controller/@RestController | 核對 base-package、確認註解存在 |
| 事務不生效 | 1) @Transactional 在 private 方法 2) 同類內部調用 3) 異常吞噬 4) 事務管理器 Bean 名稱不匹配 | 移至 public 方法、注入自身代理、rollbackFor=Exception.class、核對 tx-manager 名稱 |
| Mapper 注入失敗 | MapperScannerConfigurer 掃描路徑錯、介面無 @Mapper 註解 (MyBatis 3.4+) | 修正 basePackage、添加 @Mapper 或配置 annotation-class |
| 中文亂碼 | 缺少 CharacterEncodingFilter、Tomcat URI 編碼未設 | web.xml 加入 Filter (forceEncoding=true)、server.xml URIEncoding=UTF-8 |
| 循環依賴 | 建構子注入循環、Setter 注入循環 | 重構設計、使用 @Lazy、改用 Setter/Field 注入 |
| Bean 重複定義 | XML 與 @Configuration 類重複宣告、多環境配置衝突 | 統一配置方式、使用 @Profile 隔離 |
Spring Boot 零 XML 配置對應關係
| XML 配置 | Spring Boot 替代 |
<context:component-scan> | @SpringBootApplication(scanBasePackages=...) / @ComponentScan |
<mvc:annotation-driven> | 自動啟用 (WebMvcAutoConfiguration) |
<mvc:resources> | spring.mvc.static-path-pattern, spring.web.resources.static-locations |
ViewResolver Bean | spring.thymeleaf.prefix/suffix 等屬性 |
DataSource Bean | spring.datasource.* 屬性 + spring.datasource.type |
SqlSessionFactory Bean | mybatis.mapper-locations, mybatis.type-aliases-package |
MapperScannerConfigurer | @MapperScan 或 mybatis.mapper-locations 自動掃描 |
tx:annotation-driven | 自動啟用 (TransactionAutoConfiguration) |
PropertyPlaceholderConfigurer | application.yml / @Value / @ConfigurationProperties |
最佳實踐清單
- 分層解耦:Web/Service/DAO 配置分離,便於單元測試與模組化。
- 環境隔離:
application-{env}.yml + spring.profiles.active,杜絕硬編碼。
- 屬性外部化:敏感資訊 (密碼、金鑰) 用環境變數或配置中心 (Nacos/Apollo)。
- 註解優先:新專案優先 Spring Boot 註解配置,僅在整合舊系統時保留 XML。
- 版本鎖定:
dependencyManagement 統一管理 Spring、MyBatis、Jackson 版本。
- 啟動檢查:
spring.main.allow-bean-definition-overriding=false 防止 Bean 覆蓋。