Appearance
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)
}
}
在这个例子中,我们可以看到:
- OrderService 通过构造器注入了四个依赖
- OrderController 注入了 OrderService
- 每个组件都专注于自己的职责,依赖关系由 Spring 管理
- 便于单元测试,可以轻松 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 应用程序! 💯