Appearance
Spring IoC 容器的 Java 配置:告别 XML,拥抱注解 🚀
前言:为什么需要 Java 配置?
在 Spring 的早期版本中,我们主要通过 XML 文件来配置 Bean 和依赖关系。虽然 XML 配置功能强大,但随着项目规模的增长,XML 文件会变得冗长且难以维护。想象一下,你需要在一个巨大的 XML 文件中寻找某个 Bean 的配置,就像在图书馆里寻找一本没有索引的书一样令人头疼!
NOTE
Java 配置是 Spring 3.0 引入的特性,它让我们可以用纯 Java 代码来配置 Spring 容器,享受 IDE 的智能提示、编译时检查等优势。
核心概念:@Bean
和 @Configuration
🎯 设计哲学
Spring 的 Java 配置基于两个核心注解:
@Configuration
:标记一个类作为配置类,相当于 XML 配置文件@Bean
:标记一个方法来定义 Bean,相当于 XML 中的<bean>
标签
这种设计让配置更加类型安全,并且可以利用 Java 的所有特性(如条件判断、循环等)来创建复杂的配置逻辑。
📊 对比:XML vs Java 配置
xml
<!-- applicationContext.xml -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.example.UserService">
<constructor-arg ref="userRepository"/>
</bean>
<bean id="userRepository" class="com.example.UserRepository"/>
</beans>
kotlin
@Configuration
class AppConfig {
@Bean
fun userRepository(): UserRepository {
return UserRepository()
}
@Bean
fun userService(userRepository: UserRepository): UserService {
return UserService(userRepository) // 自动注入依赖
}
}
TIP
注意 Kotlin 版本中的 userService
方法参数 userRepository
,Spring 会自动从容器中查找对应的 Bean 并注入!
实战应用:构建一个完整的服务层
让我们通过一个实际的电商订单系统来演示 Java 配置的强大功能:
1. 定义业务组件
kotlin
// 数据访问层
class OrderRepository {
fun save(order: Order): Order {
println("保存订单: ${order.id}")
return order
}
fun findById(id: String): Order? {
println("查询订单: $id")
return Order(id, "商品A", 100.0)
}
}
// 业务逻辑层
class OrderService(
private val orderRepository: OrderRepository,
private val notificationService: NotificationService
) {
fun createOrder(productName: String, price: Double): Order {
val order = Order(generateId(), productName, price)
val savedOrder = orderRepository.save(order)
notificationService.sendOrderConfirmation(savedOrder)
return savedOrder
}
private fun generateId() = "ORDER-${System.currentTimeMillis()}"
}
// 通知服务
class NotificationService {
fun sendOrderConfirmation(order: Order) {
println("📧 发送订单确认邮件: ${order.id}")
}
}
// 数据模型
data class Order(
val id: String,
val productName: String,
val price: Double
)
2. Java 配置类
kotlin
@Configuration
class OrderConfig {
@Bean
fun orderRepository(): OrderRepository {
return OrderRepository()
}
@Bean
fun notificationService(): NotificationService {
return NotificationService()
}
@Bean
fun orderService(
orderRepository: OrderRepository,
notificationService: NotificationService
): OrderService {
// Spring 自动注入这两个依赖
return OrderService(orderRepository, notificationService)
}
}
3. 容器启动与使用
kotlin
@SpringBootApplication
class OrderApplication
fun main(args: Array<String>) {
runApplication<OrderApplication>(*args)
// 或者手动创建应用上下文(用于演示)
val context = AnnotationConfigApplicationContext(OrderConfig::class.java)
val orderService = context.getBean(OrderService::class.java)
val order = orderService.createOrder("MacBook Pro", 12999.0)
println("✅ 订单创建成功: $order")
context.close()
}
高级特性:条件化配置
Java 配置的一个巨大优势是可以使用 Java 的控制流来创建条件化的 Bean:
kotlin
@Configuration
class DatabaseConfig {
@Bean
@Profile("dev")
fun devDataSource(): DataSource {
return HikariDataSource().apply {
jdbcUrl = "jdbc:h2:mem:devdb"
username = "sa"
password = ""
}
}
@Bean
@Profile("prod")
fun prodDataSource(): DataSource {
return HikariDataSource().apply {
jdbcUrl = System.getenv("DATABASE_URL")
username = System.getenv("DATABASE_USER")
password = System.getenv("DATABASE_PASSWORD")
}
}
@Bean
@ConditionalOnProperty(name = ["cache.enabled"], havingValue = "true")
fun cacheManager(): CacheManager {
return ConcurrentMapCacheManager("orders", "users")
}
}
IMPORTANT
条件化配置让我们可以根据不同的环境(开发、测试、生产)或配置属性来创建不同的 Bean,这在 XML 配置中是很难实现的。
配置组合:模块化管理
当项目变大时,我们可以将配置分散到多个配置类中,然后通过 @Import
来组合它们:
kotlin
// 数据库相关配置
@Configuration
class DatabaseConfig {
@Bean
fun dataSource(): DataSource = HikariDataSource()
@Bean
fun jdbcTemplate(dataSource: DataSource): JdbcTemplate {
return JdbcTemplate(dataSource)
}
}
// Web 相关配置
@Configuration
class WebConfig : WebMvcConfigurer {
@Bean
fun corsConfigurationSource(): CorsConfigurationSource {
// CORS 配置
return UrlBasedCorsConfigurationSource()
}
}
// 主配置类
@Configuration
@Import(DatabaseConfig::class, WebConfig::class)
class MainConfig {
@Bean
fun applicationService(jdbcTemplate: JdbcTemplate): ApplicationService {
return ApplicationService(jdbcTemplate)
}
}
最佳实践与注意事项
✅ 推荐做法
配置类命名规范
- 配置类以
Config
结尾,如DatabaseConfig
、SecurityConfig
- 按功能模块划分配置类,避免单个配置类过于庞大
Bean 方法设计
kotlin
@Configuration
class ServiceConfig {
// ✅ 好的做法:方法名即 Bean 名称
@Bean
fun userService(userRepository: UserRepository): UserService {
return UserService(userRepository)
}
// ✅ 显式指定 Bean 名称
@Bean("customUserService")
fun createUserService(): UserService {
return UserService()
}
}
⚠️ 常见陷阱
Bean 循环依赖
kotlin
@Configuration
class BadConfig {
@Bean
fun serviceA(serviceB: ServiceB): ServiceA {
return ServiceA(serviceB)
}
@Bean
fun serviceB(serviceA: ServiceA): ServiceB {
return ServiceB(serviceA) // 循环依赖!
}
}
解决方案:使用 @Lazy
注解或重新设计类结构
容器交互流程
让我们通过时序图来理解 Spring 容器如何处理 Java 配置:
总结:Java 配置的价值
Java 配置为 Spring 应用带来了以下核心价值:
- 类型安全 🛡️:编译时就能发现配置错误,而不是运行时
- IDE 友好 💡:享受代码补全、重构、导航等 IDE 特性
- 灵活性 🔧:可以使用 Java 的所有特性来创建复杂的配置逻辑
- 可测试性 🧪:配置类本身就是普通的 Java 类,易于单元测试
- 模块化 📦:通过
@Import
实现配置的组合和复用
NOTE
Java 配置并不是要完全替代 XML 配置,而是提供了一种更现代、更灵活的选择。在实际项目中,你可以根据具体需求选择最适合的配置方式,甚至可以混合使用。
通过掌握 Java 配置,你将能够构建更加清晰、可维护的 Spring 应用,让代码配置成为开发过程中的助力而非阻力! 🎉