雖然 Spring Boot 推廣了基於註解和 Java Config 的配置方式,但在大量存量項目、傳統企業級應用中,XML 配置依然佔據重要地位。本文系統梳理 Spring XML 配置的核心機制、命名空間體系、常見場景與最佳實踐。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:context="https://www.springframework.org/schema/context"
xmlns:tx="https://www.springframework.org/schema/tx"
xsi:schemaLocation="
https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
https://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
https://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- Bean 定義與配置 -->
</beans>
核心屬性解析:
id:Bean 唯一標識符(XML ID 類型約束,不可包含特殊字符)name:別名支援,可包含逗號/分號/空格分隔的多個名稱class:全限定類名scope:作用域(singleton/prototype/request/session/application)lazy-init:延遲初始化(僅 singleton 有效)init-method / destroy-method:生命週期回調factory-bean / factory-method:工廠模式創建實例<bean id="userService" class="com.example.service.UserServiceImpl">
<constructor-arg index="0" ref="userDao"/>
<constructor-arg index="1" value="defaultValue"/>
<constructor-arg type="java.lang.String" value="config"/>
</bean>
<bean id="userService" class="com.example.service.UserServiceImpl">
<property name="userDao" ref="userDao"/>
<property name="maxRetry" value="3"/>
<property name="configMap">
<map>
<entry key="timeout" value="5000"/>
<entry key="retry" value="true"/>
</map>
</property>
<property name="serverList">
<list>
<value>server1</value>
<value>server2</value>
</list>
</property>
</bean>
<beans xmlns:p="https://www.springframework.org/schema/p"
xmlns:c="https://www.springframework.org/schema/c">
<!-- p:property (Setter) -->
<bean id="userService" class="com.example.service.UserServiceImpl"
p:userDao-ref="userDao" p:maxRetry="3"/>
<!-- c:constructor-arg (構造器) -->
<bean id="orderService" class="com.example.service.OrderServiceImpl"
c:orderDao-ref="orderDao" c:timeout="3000"/>
</beans>
| 命名空間 | 前綴 | 核心功能 | 典型標籤 |
|---|---|---|---|
| beans | 默認 | Bean 定義基礎 | <bean>, <alias>, <import> |
| context | context: | 掃描、屬性占位、注解驅動 | <component-scan>, <property-placeholder>, <annotation-config> |
| aop | aop: | 切面編程配置 | <config>, <aspect>, <pointcut>, <advisor> |
| tx | tx: | 聲明式事務管理 | <annotation-driven>, <advice>, <attributes> |
| jdbc | jdbc: | 數據源、模板初始化 | <initialize-database>, <embedded-database> |
| mvc | mvc: | Spring MVC 基礎設施 | <annotation-driven>, <interceptors>, <view-resolvers> |
| task | task: | 排程與異步執行 | <annotation-driven>, <executor>, <scheduler> |
| jms | jms: | 消息佇列整合 | <listener-container>, <jca-listener-container> |
| cache | cache: | 緩存抽象 | <annotation-driven>, <advice> |
<!-- 1. 組件掃描:排除 Controller,只掃描 Service/Repository -->
<context:component-scan base-package="com.example">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 2. 屬性文件占位符 -->
<context:property-placeholder location="classpath:jdbc.properties,classpath:redis.properties"
ignore-unresolvable="true"/>
<!-- 3. 啟用 @Autowired/@Resource/@PostConstruct 等注解處理 -->
<context:annotation-config/>
<!-- 4. 載入時編織 (LTW) -->
<context:load-time-weaver aspectj-weaving="on"/>
<!-- 數據源與事務管理器 -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 聲明式事務:注解驅動 -->
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<!-- 或 XML 配置式事務(細粒度控制) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceMethods" expression="execution(* com.example.service..*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>
</aop:config>
<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" c:_0="yyyy-MM-dd HH:mm:ss"/>
</property>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 靜態資源處理 -->
<mvc:resources mapping="/static/**" location="/static/,classpath:/META-INF/resources/webjars/"/>
<mvc:resources mapping="/favicon.ico" location="/favicon.ico"/>
<!-- 視圖解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
<property name="exposeContextBeansAsAttributes" value="true"/>
</bean>
<!-- 攔截器配置 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/api/**"/>
<mvc:exclude-mapping path="/api/login"/>
<bean class="com.example.interceptor.AuthInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
載入:Service、DAO、DataSource、TransactionManager 等業務層 Bean
<servlet>
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
載入:Controller、ViewResolver、Interceptor、HandlerMapping 等 Web 層 Bean
<beans profile="dev">
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/dev_db"/>
</bean>
</beans>
<beans profile="prod">
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<property name="jdbcUrl" value="${prod.jdbc.url}"/>
</bean>
</beans>
<!-- web.xml 啟動指定 profile -->
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>prod</param-value>
</context-param>
<bean id="appConfig" class="com.example.config.AppConfig">
<property name="appName" value="#{systemProperties['app.name'] ?: 'DefaultApp'}"/>
<property name="serverPort" value="#{ @serverConfig.port + 100 }"/>
<property name="activeProfiles" value="#{ @environment.activeProfiles }"/>
<property name="timeout" value="#{ T(java.lang.Math).random() * 1000 }"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mappers/*.xml"/>
<property name="typeAliasesPackage" value="com.example.domain"/>
</bean>
<!-- 暴露 FactoryBean 本身而非產出對象 -->
<bean id="&sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" />
<!-- 1. 導入單個文件 -->
<import resource="classpath:datasource.xml"/>
<!-- 2. 通配符批量導入 -->
<import resource="classpath*:config/*-context.xml"/>
<!-- 3. 條件導入 (Spring 4.3+) -->
<beans profile="!test">
<import resource="classpath:production-beans.xml"/>
</beans>
| 問題現象 | 根因分析 | 解決方案 |
|---|---|---|
| 循環依賴 | A 構造器注入 B,B Setter 注入 A | 改用 Setter 注入、@Lazy、或重構設計 |
| Bean 重複定義 | 多文件定義同 id、掃描與 XML 衝突 | 統一命名規範、使用 allow-bean-definition-overriding="false" |
| 屬性占位符無法解析 | PropertyPlaceholderConfigurer 未加載、路徑錯誤 | 檢查 context:property-placeholder 配置、文件編碼 |
| 事務不生效 | 調用內部方法、非 public 方法、異常吞噬 | 確保 public 方法、正確 rollback-for、避免 try-catch 吞異常 |
| 靜態資源 404 | DispatcherServlet 攔截所有請求 | 配置 <mvc:resources/> 或 <mvc:default-servlet-handler/> |
| XML 配置 | Java Config 等價 |
|---|---|
| <bean id="x" class="X"> | @Bean(name="x") public X x() { return new X(); } |
| <property name="y" ref="y"/> | @Autowired private Y y; 或 @Bean public X x(Y y) { return new X(y); } |
| <context:component-scan/> | @ComponentScan("com.example") |
| <tx:annotation-driven/> | @EnableTransactionManagement |
| <mvc:annotation-driven/> | @EnableWebMvc |
| <import resource="..."/> | @Import({ConfigA.class, ConfigB.class}) |
| <beans profile="dev"> | @Profile("dev") |