Skip to content

Spring @Autowired 注解详解 🎉

什么是 @Autowired?

@Autowired 是 Spring Framework 中最核心的依赖注入注解之一。它的设计哲学很简单:让 Spring 容器自动帮我们"连接"所需要的组件,而不需要手动创建和管理这些依赖关系

NOTE

想象一下,如果没有 @Autowired,我们每次需要使用一个服务时,都要手动 new 一个对象,还要管理这些对象的生命周期。这不仅繁琐,还容易出错,更难以进行单元测试。

核心原理与设计哲学

解决的核心痛点

设计原理

@Autowired 基于 控制反转(IoC)依赖注入(DI) 的设计模式:

  • 控制反转:对象的创建控制权从开发者转移到 Spring 容器
  • 依赖注入:Spring 容器负责将依赖的对象注入到需要的地方
  • 类型匹配:根据类型自动找到合适的 Bean 进行注入

@Autowired 的使用方式

1. 构造器注入(推荐方式)

kotlin
@Service
class MovieRecommender @Autowired constructor(
    private val customerPreferenceDao: CustomerPreferenceDao, 
    private val movieCatalog: MovieCatalog
) {
    
    fun recommendMovies(customerId: Long): List<Movie> {
        val preferences = customerPreferenceDao.findByCustomerId(customerId)
        return movieCatalog.findMoviesByPreferences(preferences)
    }
}
kotlin
@Service
class MovieRecommender {
    
    // 传统方式:手动创建依赖
    private val customerPreferenceDao = CustomerPreferenceDaoImpl() 
    private val movieCatalog = MovieCatalogImpl() 
    
    fun recommendMovies(customerId: Long): List<Movie> {
        val preferences = customerPreferenceDao.findByCustomerId(customerId)
        return movieCatalog.findMoviesByPreferences(preferences)
    }
}

TIP

从 Spring Framework 4.3 开始,如果类只有一个构造器,@Autowired 注解可以省略!

2. 属性注入

kotlin
@Service
class SimpleMovieLister {
    
    @Autowired
    lateinit var movieFinder: MovieFinder
    
    fun findMovies(genre: String): List<Movie> {
        return movieFinder.findByGenre(genre)
    }
}

WARNING

属性注入虽然简洁,但不利于单元测试,因为无法在测试中轻松替换依赖。

3. Setter 方法注入

kotlin
@Service
class SimpleMovieLister {
    
    @set:Autowired // [!code highlight]
    lateinit var movieFinder: MovieFinder
    
    fun findMovies(genre: String): List<Movie> {
        return movieFinder.findByGenre(genre)
    }
}

4. 任意方法注入

kotlin
@Service
class MovieRecommender {
    
    private lateinit var movieCatalog: MovieCatalog
    private lateinit var customerPreferenceDao: CustomerPreferenceDao
    
    @Autowired
    fun prepare(movieCatalog: MovieCatalog, customerPreferenceDao: CustomerPreferenceDao) {
        this.movieCatalog = movieCatalog
        this.customerPreferenceDao = customerPreferenceDao
    }
}

高级用法

1. 注入集合类型

当容器中有多个相同类型的 Bean 时,可以注入为集合:

kotlin
@Service
class MovieRecommender {
    
    // 注入所有 MovieCatalog 类型的 Bean
    @Autowired
    private lateinit var movieCatalogs: Array<MovieCatalog> 
    
    // 或者使用 List
    @Autowired
    private lateinit var catalogList: List<MovieCatalog> 
    
    // 或者使用 Set
    @Autowired
    private lateinit var catalogSet: Set<MovieCatalog> 
    
    fun recommendFromAllCatalogs(customerId: Long): List<Movie> {
        return movieCatalogs.flatMap { catalog ->
            catalog.findRecommendations(customerId)
        }
    }
}

2. 注入 Map 类型

kotlin
@Service
class MovieRecommender {
    
    // Key 是 Bean 的名称,Value 是 Bean 实例
    @Autowired
    lateinit var movieCatalogs: Map<String, MovieCatalog> 
    
    fun getSpecificCatalog(catalogName: String): MovieCatalog? {
        return movieCatalogs[catalogName]
    }
}

3. 可选依赖注入

有时某些依赖可能不存在,我们可以将其标记为可选:

kotlin
@Service
class SimpleMovieLister {
    
    @Autowired(required = false) 
    var movieFinder: MovieFinder? = null
    
    fun findMovies(genre: String): List<Movie> {
        return movieFinder?.findByGenre(genre) ?: emptyList()
    }
}
kotlin
@Service
class SimpleMovieLister {
    
    @Autowired
    var movieFinder: MovieFinder? = null
    
    fun findMovies(genre: String): List<Movie> {
        return movieFinder?.findByGenre(genre) ?: emptyList()
    }
}

4. 注入 Spring 内置组件

Spring 允许直接注入一些内置的重要组件:

kotlin
@Service
class ApplicationInfoService {
    
    @Autowired
    private lateinit var applicationContext: ApplicationContext
    
    @Autowired
    private lateinit var environment: Environment
    
    fun getApplicationInfo(): Map<String, Any> {
        return mapOf(
            "applicationName" to environment.getProperty("spring.application.name", "Unknown"),
            "beanCount" to applicationContext.beanDefinitionCount,
            "activeProfiles" to environment.activeProfiles.toList()
        )
    }
}

实际业务场景示例

让我们通过一个完整的电商订单服务来看看 @Autowired 的实际应用:

完整的订单服务示例
kotlin
// 数据访问层
@Repository
interface OrderRepository {
    fun save(order: Order): Order
    fun findById(orderId: Long): Order?
}

@Repository
interface ProductRepository {
    fun findById(productId: Long): Product?
    fun updateStock(productId: Long, quantity: Int)
}

// 业务服务层
@Service
class PaymentService {
    fun processPayment(orderId: Long, amount: BigDecimal): PaymentResult {
        // 支付处理逻辑
        return PaymentResult.SUCCESS
    }
}

@Service
class NotificationService {
    fun sendOrderConfirmation(order: Order) {
        // 发送订单确认通知
        println("订单确认邮件已发送: ${order.id}")
    }
}

// 主要的订单服务
@Service
class OrderService @Autowired constructor( 
    private val orderRepository: OrderRepository, 
    private val productRepository: ProductRepository, 
    private val paymentService: PaymentService, 
    private val notificationService: NotificationService
) {
    
    @Transactional
    fun createOrder(customerId: Long, items: List<OrderItem>): Order {
        // 1. 验证商品库存
        items.forEach { item ->
            val product = productRepository.findById(item.productId)
                ?: throw IllegalArgumentException("商品不存在: ${item.productId}")
            
            if (product.stock < item.quantity) {
                throw IllegalStateException("库存不足: ${product.name}")
            }
        }
        
        // 2. 创建订单
        val order = Order(
            customerId = customerId,
            items = items,
            totalAmount = items.sumOf { it.price * it.quantity.toBigDecimal() }
        )
        
        val savedOrder = orderRepository.save(order)
        
        // 3. 更新库存
        items.forEach { item ->
            productRepository.updateStock(item.productId, -item.quantity)
        }
        
        // 4. 处理支付
        val paymentResult = paymentService.processPayment(savedOrder.id, savedOrder.totalAmount)
        
        if (paymentResult == PaymentResult.SUCCESS) {
            // 5. 发送通知
            notificationService.sendOrderConfirmation(savedOrder)
        }
        
        return savedOrder
    }
}

// 控制器层
@RestController
@RequestMapping("/api/orders")
class OrderController @Autowired constructor( 
    private val orderService: OrderService
) {
    
    @PostMapping
    fun createOrder(@RequestBody request: CreateOrderRequest): ResponseEntity<Order> {
        val order = orderService.createOrder(request.customerId, request.items)
        return ResponseEntity.ok(order)
    }
}

在这个例子中,我们可以看到:

  1. OrderService 通过构造器注入了四个依赖
  2. OrderController 注入了 OrderService
  3. 每个组件都专注于自己的职责,依赖关系由 Spring 管理
  4. 便于单元测试,可以轻松 Mock 各个依赖

注意事项与最佳实践

1. 循环依赖问题

WARNING

避免循环依赖!如果 A 依赖 B,B 又依赖 A,会导致 Spring 无法正确初始化 Bean。

kotlin
// 错误示例:循环依赖
@Service
class ServiceA @Autowired constructor(
    private val serviceB: ServiceB
)

@Service
class ServiceB @Autowired constructor(
    private val serviceA: ServiceA
)

解决方案

  • 重新设计类的职责,消除循环依赖
  • 使用 @Lazy 注解延迟初始化
  • 提取公共逻辑到第三个服务中

2. 类型匹配问题

IMPORTANT

确保 @Autowired 注入点的类型与容器中的 Bean 类型匹配。

kotlin
// 确保返回类型足够具体
@Configuration
class DatabaseConfig {
    
    @Bean
    fun movieCatalog(): MovieCatalog { 
        return DatabaseMovieCatalog() // 返回具体实现
    }
}

3. 推荐使用构造器注入

TIP

优先使用构造器注入的原因:

  • 不可变性:依赖在对象创建后不能被修改
  • 必需依赖:确保所有必需的依赖都被提供
  • 测试友好:便于在测试中注入 Mock 对象
  • 线程安全:避免了属性注入可能的线程安全问题

总结

@Autowired 是 Spring 依赖注入的核心机制,它通过以下方式简化了我们的开发:

自动化依赖管理:无需手动创建和管理对象 ✅ 松耦合设计:组件之间通过接口依赖,便于替换和测试
配置灵活:支持多种注入方式和高级特性 ✅ 类型安全:编译时就能发现类型不匹配的问题

通过合理使用 @Autowired,我们可以构建出更加模块化、可测试、可维护的 Spring 应用程序! 💯