Skip to content

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 结尾,如 DatabaseConfigSecurityConfig
  • 按功能模块划分配置类,避免单个配置类过于庞大

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 应用带来了以下核心价值:

  1. 类型安全 🛡️:编译时就能发现配置错误,而不是运行时
  2. IDE 友好 💡:享受代码补全、重构、导航等 IDE 特性
  3. 灵活性 🔧:可以使用 Java 的所有特性来创建复杂的配置逻辑
  4. 可测试性 🧪:配置类本身就是普通的 Java 类,易于单元测试
  5. 模块化 📦:通过 @Import 实现配置的组合和复用

NOTE

Java 配置并不是要完全替代 XML 配置,而是提供了一种更现代、更灵活的选择。在实际项目中,你可以根据具体需求选择最适合的配置方式,甚至可以混合使用。

通过掌握 Java 配置,你将能够构建更加清晰、可维护的 Spring 应用,让代码配置成为开发过程中的助力而非阻力! 🎉