依赖注入与 Bean 管理
依赖注入与 Bean 管理
依赖注入(Dependency Injection,DI)是 Spring Framework 最核心的概念,也是 Spring Boot 一切魔法的基础。理解 DI,你就能理解为什么 Spring Boot 能「约定优于配置」——框架自动发现、管理和组装组件,你只需声明依赖,容器负责注入。
IoC 容器核心概念
什么是 IoC
控制反转(Inversion of Control,IoC)是一种设计原则,将对象的创建和依赖关系的管理从应用代码中转移到框架容器。在传统模式中:
// ❌ 传统方式:对象在代码内部创建,高耦合
public class UserService {
private UserRepository repository = new UserRepositoryImpl(); // 硬编码依赖
private EmailService email = new EmailService(); // 难以替换
public void register(User user) {
repository.save(user);
email.sendWelcome(user);
}
}// ✅ IoC 方式:依赖由外部注入,低耦合,可测试
public class UserService {
private final UserRepository repository;
private final EmailService email;
// 依赖通过构造函数从外部传入
public UserService(UserRepository repository, EmailService email) {
this.repository = repository;
this.email = email;
}
public void register(User user) {
repository.save(user);
email.sendWelcome(user);
}
}Bean 是什么
在 Spring 中,Bean 是由 Spring IoC 容器创建、组装和管理的对象。所有被 @Component、@Service、@Repository、@Controller、@Configuration + @Bean 标注的类,或者用 Java @Bean 方法返回的对象,都是 Spring 容器中的 Bean。
Bean 的注册方式
1. @Component 及其派生注解
这是最常用的方式。@Component 是最通用的组件标注,@Service、@Repository、@Controller 都是 @Component 的特殊化:
// @Service:标注业务逻辑组件(语义更明确)
@Service
public class UserServiceImpl implements UserService {
// ...
}
// @Repository:标注数据访问组件(额外支持异常转换)
@Repository
public class JpaUserRepository implements UserRepository {
// ...
}
// @Controller:标注 Spring MVC 控制器
@Controller
public class UserController {
// ...
}
// @RestController:@Controller + @ResponseBody 的组合
@RestController
@RequestMapping("/api/users")
public class UserApiController {
// ...
}2. @Configuration + @Bean
当需要引入第三方库或需要更细粒度控制 Bean 的创建逻辑时,使用 Java 配置类:
@Configuration
public class AppConfig {
// @Bean 标注的方法返回值会被注册为 Bean,方法名即为 Bean 名称
@Bean
public RestTemplate restTemplate() {
RestTemplate template = new RestTemplate();
// 自定义配置
template.getMessageConverters().add(new Jackson2HttpMessageConverter());
return template;
}
// 同一个配置类中,Bean 方法可以相互调用(Spring 会处理依赖)
@Bean
public UserService userService(UserRepository userRepository) {
return new UserServiceImpl(userRepository);
}
// 使用 @Qualifier 精确指定要注入的 Bean
@Bean
public PaymentService paymentService(
@Qualifier("alipay") PaymentGateway alipayGateway) {
return new AlipayPaymentService(alipayGateway);
}
@Bean("alipay") // 指定 Bean 名称为 "alipay"
public PaymentGateway alipay() {
return new AlipayGateway();
}
@Bean("wechatpay")
public PaymentGateway wechatpay() {
return new WechatPayGateway();
}
}3. @Import 导入配置
将多个配置类聚合在一起,或导入第三方配置:
// 主配置类
@SpringBootApplication
@Import({ DataConfig.class, SecurityConfig.class })
public class AppConfig {
// ...
}
// DataConfig.java
@Configuration
public class DataConfig {
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
}依赖注入的方式
构造器注入(推荐)
Spring Boot 2.6+ 推荐使用构造器注入,优势在于:
- 不可变性:final 字段必须在构造时初始化
- 强制依赖声明:缺少必要依赖时编译期即报错
- 天然可测试:无需 Spring 容器即可实例化测试
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentGateway paymentGateway;
private final MessagePublisher messagePublisher;
// ✅ 构造器注入(推荐)
public OrderService(
OrderRepository orderRepository,
PaymentGateway paymentGateway,
MessagePublisher messagePublisher) {
this.orderRepository = orderRepository;
this.paymentGateway = paymentGateway;
this.messagePublisher = messagePublisher;
}
// 只有一个构造器时,@Autowired 可省略(Spring 自动注入)
public void placeOrder(Order order) {
orderRepository.save(order);
paymentGateway.process(order.getPayment());
messagePublisher.publish("order.placed", order);
}
}Spring Framework 4.x 就引入了构造器注入的隐式支持,当类只有一个构造器时,@Autowired 可以省略。
Setter 注入
通过 setXxx() 方法注入,字段可选:
@Service
public class NotificationService {
private EmailService emailService;
private SmsService smsService;
// Setter 注入(可选依赖)
@Autowired(required = false)
public void setEmailService(EmailService emailService) {
this.emailService = emailService;
}
@Autowired
public void setSmsService(SmsService smsService) {
this.smsService = smsService;
}
}字段注入(不推荐)
@Service
public class BadExample {
// ❌ 字段注入:难以测试、不支持不可变字段
@Autowired
private UserRepository userRepository;
// ❌ @Inject 是 Java 标准 CDI 注解,与 @Autowired 等效
@Inject
private CacheService cacheService;
}@Autowired 与 @Qualifier 精确注入
当存在多个同类型 Bean 时,需要精确指定注入哪一个:
// 定义两个实现了同一接口的类
@Component
public class InMemoryUserRepository implements UserRepository {
// 内存实现
}
@Component
public class JpaUserRepository implements UserRepository {
// JPA 实现
}
// 方式1:@Qualifier 指定 Bean 名称
@Service
public class UserServiceImpl {
@Autowired
@Qualifier("jpaUserRepository") // 指定注入 JpaUserRepository
private UserRepository userRepository;
}
// 方式2:构造器参数 + @Qualifier
@Service
public class UserServiceImpl {
private final UserRepository userRepository;
public UserServiceImpl(
@Qualifier("jpaUserRepository") UserRepository userRepository) {
this.userRepository = userRepository;
}
}
// 方式3:@Primary 指定默认实现
@Component
@Primary // 标注为默认实现,@Autowired 未指定时使用此 Bean
public class JpaUserRepository implements UserRepository {
// ...
}@Resource 与 @Inject
Java EE 标准注解也可用于注入:
// @Resource:默认按字段名匹配 Bean(JSK 330 标准)
@Resource
private UserRepository userRepository;
// @Inject:功能同 @Autowired(Java CDI 标准)
@Inject
private UserRepository userRepository;Bean 作用域
Spring 默认创建的 Bean 是单例(singleton)的,整个容器只有一个实例:
@Component
@Scope("singleton") // 默认作用域,可省略
public class Counter {
private int count = 0;
}
// @Configuration 类默认也是 singleton
@Configuration
public class AppConfig {
// ...
}Spring 支持的 Bean 作用域:
| 作用域 | 说明 | 使用场景 |
|---|---|---|
singleton | 容器中只有一个实例 | 默认,适用于无状态 Bean |
prototype | 每次注入都创建新实例 | 有状态 Bean |
request | 每次 HTTP 请求创建一个实例 | Spring MVC Web 请求作用域 |
session | 每次 HTTP Session 创建一个实例 | Web 会话作用域 |
application | ServletContext 生命周期内唯一 | Web 应用级全局状态 |
websocket | WebSocket 生命周期内唯一 | WebSocket 作用域 |
// 原型作用域:每次注入/获取都创建新实例
@Component
@Scope("prototype")
public class ShoppingCart {
private final List<Item> items = new ArrayList<>();
// ...
}
// 使用 ObjectFactory / ObjectProvider 按需获取原型 Bean
@Service
public class OrderService {
@Autowired
private ObjectProvider<ShoppingCart> cartProvider;
public void checkout() {
// 每次调用 getObject() 都创建新的 ShoppingCart 实例
ShoppingCart cart = cartProvider.getObject();
// ...
}
}@Primary 与 @Order 优先级
@Primary 指定默认 Bean
@Component
@Primary // 同类型 Bean 中优先级最高
public class DefaultCacheService implements CacheService {
// ...
}@Order 控制 Bean 注册顺序
@Order 控制同一类型的多个 Bean 被 List<Interface> 注入时的顺序(数值越小优先级越高):
@Configuration
public class ValidatorConfig {
@Bean
@Order(1)
public Validator emailValidator() {
return new EmailValidator();
}
@Bean
@Order(2)
public Validator phoneValidator() {
return new PhoneValidator();
}
@Bean
@Order(3)
public Validator passwordValidator() {
return new PasswordValidator();
}
}
// 注入所有 Validator,按 @Order 顺序排列
@Service
public class RegistrationService {
@Autowired
private List<Validator> validators; // [email, phone, password]
public void validate(User user) {
validators.forEach(v -> v.validate(user));
}
}@Configuration 的代理机制
@Configuration 类默认被 CGLIB 代理,以保证 @Bean 方法之间的调用也经过容器代理:
@Configuration
public class AppConfig {
@Bean
public ServiceA serviceA() {
// 调用 serviceB() 时返回的是容器中的 Bean,不是 new ServiceB()
return new ServiceA(serviceB());
}
@Bean
public ServiceB serviceB() {
return new ServiceB();
}
}如果将 @Configuration 改为 @Component,CGLIB 代理会被禁用,@Bean 方法调用会创建新的实例,这通常不是你想要的行为。
小结
- 构造器注入是 Spring Boot 3.x 推荐的方式,支持不可变性,强制声明依赖
@Component体系适合业务组件,@Configuration + @Bean适合第三方库配置- 多个同类型 Bean 使用
@Qualifier按名称精确注入,@Primary标记默认实现 - Bean 作用域中
singleton是默认且最常用的,prototype适合有状态对象 @Order控制同类型多个 Bean 的注册和应用顺序
下一篇文章我们将学习 RESTful API 与 Web 开发,掌握控制器层开发的全部核心技能。
评论
Written by
AI-Writer
Related Articles
依赖注入与 Bean 管理
深入理解 Spring IoC 容器核心概念、@Bean/@Component/@Configuration 注解使用、构造器注入与 Setter 注入、@Autowired 与 @Qualifier 精确注入、Bean 作用域与优先级控制
Read MoreSpring Security 安全认证
Spring Security 6.x 核心架构、认证与授权概念、基于 JWT 的无状态登录实现、OAuth 2.0 资源服务器入门、@PreAuthorize 方法级安全注解
Read More