隨著項目規模增長,單一巨大的 applicationContext.xml 會導致:讀取緩慢、合併衝突頻繁、職責不清、啟動耗時。拆分為功能內聚的多配置文件是最佳實踐:
application-core.xml:核心組件 (DataSource、TransactionManager、SessionFactory)application-dao.xml:DAO 掃描、MyBatis/Hibernate 配置application-service.xml:Service 掃描、事務切面application-web.xml:MVC 配置、ViewResolver、Interceptorapplication-security.xml:Spring Security 配置application-cache.xml:Redis/Ehcache 配置application-async.xml:線程池、@Async、@Scheduled<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:application-core.xml,
classpath:application-dao.xml,
classpath:application-service.xml,
classpath:application-*.xml
</param-value>
</context-param>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application-web.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<import> 引入 (推薦)<!-- applicationContext.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"
xsi:schemaLocation="https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="application-core.xml"/>
<import resource="application-dao.xml"/>
<import resource="application-service.xml"/>
<import resource="application-web.xml"/>
<import resource="application-security.xml"/>
<import resource="application-cache.xml"/>
</beans>
@Configuration
@Import({CoreConfig.class, DaoConfig.class, ServiceConfig.class, WebConfig.class})
public class AppConfig {
// 也可以使用 @ComponentScan 基礎包掃描
}
@Configuration
@PropertySource("classpath:jdbc.properties")
public class CoreConfig {
@Bean
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl("${jdbc.url}");
ds.setUsername("${jdbc.username}");
ds.setPassword("${jdbc.password}");
return ds;
}
@Bean
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
// 方式一:ClassPathXmlApplicationContext
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"application-core.xml", "application-dao.xml", "application-service.xml");
// 方式二:FileSystemXmlApplicationContext (指定文件系統路徑)
ApplicationContext ctx = new FileSystemXmlApplicationContext(
"/opt/config/application-core.xml", "/opt/config/application-dao.xml");
// 方式三:GenericApplicationContext + XmlBeanDefinitionReader (最大靈活性)
GenericApplicationContext ctx = new GenericApplicationContext();
new XmlBeanDefinitionReader(ctx).loadBeanDefinitions(
"classpath:application-core.xml", "classpath:application-dao.xml");
ctx.refresh();
<!-- application-dao.xml -->
<bean id="userDao" class="com.dao.UserDaoImpl">
<property name="sessionFactory" ref="sessionFactory"/> <!-- 引用 core 中的 Bean -->
</bean>
<!-- application-service.xml -->
<bean id="userService" class="com.service.UserServiceImpl">
<property name="userDao" ref="userDao"/> <!-- 引用 dao 中的 Bean -->
</bean>
<import> 的文件合併為一個邏輯 BeanFactory,ref 可跨文件引用任何已定義的 Bean<import> 順序不影響引用,但循環引用會報錯@Configuration
public class ServiceConfig {
@Autowired // 或 @Qualifier 指定名字
private UserDao userDao; // 自動從其他 @Configuration 類注入
@Bean
public UserService userService() {
return new UserServiceImpl(userDao);
}
}
// 或使用 @Bean 方法參數自動裝配
@Configuration
public class ServiceConfig {
@Bean
public UserService userService(UserDao userDao) { // Spring 自動注入參數
return new UserServiceImpl(userDao);
}
}
<!-- application-core.xml -->
<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>
<!-- JavaConfig -->
@Configuration
@Profile("dev")
public class DevDataSourceConfig { @Bean DataSource dataSource() { ... } }
@Configuration
@Profile("prod")
public class ProdDataSourceConfig { @Bean DataSource dataSource() { ... } }
# web.xml
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>prod</param-value>
</context-param>
# JVM 參數
-Dspring.profiles.active=prod
# 程序化
ctx.getEnvironment().setActiveProfiles("prod");
<!-- XML -->
<context:property-placeholder location="classpath:jdbc.properties,classpath:redis.properties"
ignore-unresolvable="true" system-properties-mode="OVERRIDE"/>
<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>
<!-- JavaConfig -->
@Configuration
@PropertySource({"classpath:jdbc.properties", "classpath:redis.properties"})
public class CoreConfig {
@Autowired Environment env;
@Bean
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(env.getProperty("jdbc.url"));
return ds;
}
}
| 場景 | 獲取方式 | 代碼示例 |
|---|---|---|
| 獨立應用啟動類 | 直接持有 | ApplicationContext ctx = new ClassPathXmlApplicationContext(...); |
| Web 應用 Servlet/Filter/Listener | WebApplicationContextUtils | WebApplicationContextUtils.getWebApplicationContext(servletContext) |
| Spring Bean 內部 | 實現 ApplicationContextAware | public class MyBean implements ApplicationContextAware { void setApplicationContext(ApplicationContext ctx) {...} } |
| Spring Bean 內部 (Web) | 實現 WebApplicationContextAware | void setWebApplicationContext(WebApplicationContext ctx) |
| 繼承便利基類 | ApplicationObjectSupport | getApplicationContext() |
| 繼承便利基類 (Web) | WebApplicationObjectSupport | getWebApplicationContext() |
| 任意位置 (不推薦) | 靜態持有器 | SpringContextHolder.getContext() (需自行實現) |
| 問題 | 原因 | 解決 |
|---|---|---|
| Bean 重複定義 | 多文件定義同 id、import 循環、組件掃描重複 | 使用 name 別名、@Primary、排除掃描包、檢查 import 循環 |
| 循環依賴 | A 引用 B,B 引用 A (構造器注入) | 改用 Setter/Field 注入、@Lazy、重構設計、@DependsOn |
| 屬性占位符無法解析 | PropertyPlaceholderConfigurer 未加載、文件路徑錯誤、Profile 不匹配 | 檢查 location、ignore-unresolvable、Profile 激活狀態 |
| 跨文件 ref 失效 | 引用的 Bean 在未導入的文件中、拼寫錯誤 | 確認 import 路徑、Bean id 拼寫、上下文層級關係 |
| 啟動慢 | 配置文件過多、掃描包過廣、Bean 初始化耗時 | 合並文件、縮小掃描範圍、@Lazy 延遲初始化、異步初始化 |
dao-userDao、service-userService,避免衝突Spring 多配置文件機制通過 <import>、@Import、通配符加載、Profile 隔離等多種方式,提供了極其靈活的模塊化裝配能力。合理劃分模塊邊界、統一命名規範、外部化環境配置,是構建可維護、可擴展、可測試企業級應用的基石。