Spring 依赖注入与 Bean 管理详解(含自动装配与多 Bean 冲突解决)
一、Spring 中的 @Autowired
注解详解
在 Spring 框架里,@Autowired
是非常常用的依赖注入注解,用于实现自动装配 Bean,简化代码中的对象管理。
1. @Autowired
是什么?
@Autowired
用于将容器中已有的 Bean 自动注入到当前类的成员变量、构造器或 setter 方法中,实现依赖注入(DI)。
简而言之,就是告诉 Spring:“请帮我把这个对象自动找到,并注入进来。”
2. @Autowired
的用法
2.1 注入成员变量(Field Injection)
@Component
public class UserService {
@Autowired
private UserRepository userRepository;
public void printUser(Long id) {
System.out.println(userRepository.findUserById(id));
}
}
Spring 会自动查找类型匹配的 UserRepository
Bean 并注入。
2.2 注入构造器(Constructor Injection)
@Component
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
构造器注入是推荐的方式,更利于测试和保证依赖不可变。
2.3 注入 Setter 方法(Setter Injection)
@Component
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
3. @Autowired
的工作原理
Spring 容器启动时,会扫描并管理所有的 Bean,当遇到 @Autowired
注解时,会自动根据类型(byType)寻找匹配的 Bean 注入。
如果找到多个候选 Bean,会结合 @Qualifier
注解指定具体 Bean。
4. 简单示例
@Repository
public class UserRepository {
public String findUserById(Long id) {
return "用户" + id;
}
}
UserRepository
类被标注为 @Repository
,表示数据访问层组件,Spring 会自动扫描并注册为 Bean。
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void printUser(Long id) {
System.out.println(userRepository.findUserById(id));
}
}
UserService
是业务逻辑层,通过构造器注入 UserRepository
,Spring 会自动注入对应的 Bean。
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
@Autowired
private UserService userService;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) {
userService.printUser(1001L);
}
}
DemoApplication
是 Spring Boot 启动类,实现了 CommandLineRunner
接口,程序启动后执行 run
方法,调用 UserService
的 printUser
方法。
5. 运行结果
用户1001
6. 总结
- 使用
@Repository
标注数据访问类,@Service
标注业务逻辑类。 - 通过构造器注入(带
@Autowired
)实现依赖自动装配,推荐这种方式。 - Spring Boot 启动时自动扫描组件并管理生命周期。
- 运行时实现调用链:
DemoApplication
→UserService
→UserRepository
→ 输出用户信息。
二、Spring 中 @Configuration
与 @Bean
注解详解及生命周期示例
@Configuration
和 @Bean
注解用于手动注册和管理 Bean,提供灵活的配置能力。下面结合示例,介绍它们的用法及生命周期回调。
1. @Configuration
与 @Bean
基本用法
@Configuration
public class BeanConfig {
@Bean
public MyUserService getUserService() {
return new MyUserService("id1", "name1");
}
@Bean(name = {"u1"})
public MyUserService getUserService1() {
return new MyUserService("id2", "name2");
}
@Bean(name = {"u2"})
public MyUserService getUserService2() {
return new MyUserService("id3", "name3");
}
@Bean(initMethod = "init", destroyMethod = "cleanup")
public LifecycleBean getUserService3() {
return new LifecycleBean();
}
}
- 注册三个
MyUserService
Bean,默认名和自定义名u1
、u2
。 - Spring 根据方法名或自定义名管理和注入 Bean。
2. Bean 的初始化与销毁回调
public class LifecycleBean {
public void init() {
System.out.println("LifecycleBean init");
}
public void cleanup() {
System.out.println("LifecycleBean cleanup");
}
}
- Spring 创建
LifecycleBean
时调用init()
,关闭时调用cleanup()
,便于管理生命周期。
3. 自定义 Bean 类示例
public class MyUserService {
private String id;
private String name;
public MyUserService(String id, String name) {
this.id = id;
this.name = name;
System.out.printf("MyUserService: id=%s, name=%s\n", id, name);
}
}
构造函数打印日志,方便观察 Bean 创建过程。
4. 运行调用示例
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
@Autowired
private MyUserService getUserService;
@Autowired
@Qualifier("u1")
private MyUserService userService1;
@Autowired
@Qualifier("u2")
private MyUserService userService2;
@Autowired
private LifecycleBean lifecycleBean;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) {
System.out.println("调用默认Bean:");
System.out.printf("id=%s, name=%s\n", getUserService.id, getUserService.name);
System.out.println("调用自定义命名Bean u1:");
System.out.printf("id=%s, name=%s\n", userService1.id, userService1.name);
System.out.println("调用自定义命名Bean u2:");
System.out.printf("id=%s, name=%s\n", userService2.id, userService2.name);
}
}
5. 运行输出说明
MyUserService: id=id1, name=name1
MyUserService: id=id2, name=name2
MyUserService: id=id3, name=name3
LifecycleBean init
调用默认Bean:
id=id1, name=name1
调用自定义命名Bean u1:
id=id2, name=name2
调用自定义命名Bean u2:
id=id3, name=name3
- 构造函数日志确认三个
MyUserService
Bean 已创建。 LifecycleBean init
表示初始化方法被调用。@Qualifier
精确注入指定 Bean。- 容器关闭时会调用
LifecycleBean cleanup
。
6. 总结
@Bean
允许定义多个实例和自定义名称,方便注入。- 通过
initMethod
和destroyMethod
管理 Bean 生命周期。 - 自定义构造打印帮助观察 Bean 实例化。
当然!我帮你把“Spring 自动装配:什么时候根据 Bean 名称匹配?”的内容融合进你给的文章里,形成一篇更完整、更系统的文章,结构清晰且内容丰富。
三、Spring 中多 Bean 注入冲突及 @Qualifier
注解详解
在 Spring 应用中,遇到多个同类型 Bean 时,自动注入会出现冲突。本文通过示例讲解如何使用 @Qualifier
注解,精准指定注入的 Bean,并详细说明 Spring 自动装配时何时会根据 Bean 名称匹配。
1. 场景介绍
假设我们有一个基类 GreetingService
,有两个子类分别实现不同语言的问候语:
// 基类,未被标注为 @Service
public class GreetingService {
public String greet() {
return "Hello from GreetingService!";
}
}
注解说明
给@Service
指定名字(@Service("beanName")
)是给 Bean 取一个明确的标识符。这样在注入时用@Qualifier("beanName")
就可以精确指定注入哪个 Bean,解决多个同类型 Bean 冲突问题。
两个子类分别用 @Service
标注,Spring 会自动注册它们为 Bean:
@Service("englishGreetingService")
public class EnglishGreetingService extends GreetingService {
@Override
public String greet() {
return "Hello from EnglishGreetingService!";
}
}
@Service("spanishGreetingService")
public class SpanishGreetingService extends GreetingService {
@Override
public String greet() {
return "Hola SpanishGreetingService!";
}
}
2. 多个同类型 Bean 冲突问题
当你在其他组件注入 GreetingService
类型 Bean 时:
@Service
public class MyService {
@Autowired
private GreetingService greetingService; // 这里会报错,因有多个实现
public void sayHello() {
System.out.println(greetingService.greet());
}
}
Spring 会抛出异常:
No qualifying bean of type 'GreetingService' available: expected single matching bean but found 2: englishGreetingService,spanishGreetingService
3. Spring 自动装配的匹配机制
3.1 按类型匹配(byType)
Spring 首先根据类型查找 Bean。如果容器只有一个该类型 Bean,则直接注入。
3.2 按名称匹配(byName)
当存在多个相同类型 Bean 时,Spring 会尝试使用注入点的变量名或构造器参数名作为 Bean 名称,查找匹配的 Bean。
例如:
@Autowired
private GreetingService englishGreetingService;
此时,Spring 会寻找名为 englishGreetingService
的 Bean 进行注入。
注意:构造器参数名匹配需要编译时保留参数名(Java 8+默认或用 -parameters
参数)。
3.3 使用 @Qualifier
明确指定
@Qualifier("beanName")
可以强制指定注入哪个 Bean,优先级高于变量名匹配。
4. 解决方案:使用 @Qualifier
指定 Bean 名称
通过 @Qualifier
注解,告诉 Spring 注入具体哪个 Bean:
@Service
public class MyService {
@Autowired
@Qualifier("spanishGreetingService") // 指定注入的 Bean 名称
private GreetingService greetingService;
public void sayHello() {
System.out.println(greetingService.greet());
}
}
5. 运行结果
执行 MyService.sayHello()
输出:
Hola SpanishGreetingService!
6. Bean 名称来源及自定义
- Spring 默认以类名首字母小写作为 Bean 名称,比如
EnglishGreetingService
默认 Bean 名为englishGreetingService
。 - 你也可以通过在注解里自定义名称,如
@Service("englishGreetingService")
,明确指定 Bean 名称。 - 通过自定义名称,可以让注入时更精准,避免冲突。
7. 总结
场景 | 匹配规则 | 说明 |
---|---|---|
单一类型 Bean | 按类型(byType)匹配 | 只有一个匹配 Bean,直接注入 |
多个类型相同 Bean | 按变量名匹配 Bean 名称 | 变量名与 Bean 名称相同,匹配成功 |
多个类型相同 Bean + @Qualifier |
按 @Qualifier 指定名称匹配 |
明确指定 Bean,避免变量名依赖,灵活准确 |
多个类型相同 Bean,无匹配名称 | 抛出 NoUniqueBeanDefinitionException |
需加 @Qualifier 或调整变量名,否则注入失败 |
- 当容器中存在多个同类型 Bean,且不指定名称时,
@Autowired
会失败。 - 按名称匹配是 Spring 自动装配解决多 Bean 冲突的第一道防线。
- 结合
@Qualifier
,可以准确控制注入哪个 Bean,提升代码的灵活性和可维护性。
这样,你就能清晰理解在自动装配时,Spring 何时根据 Bean 名称匹配,如何结合 @Qualifier
注解解决多 Bean 冲突问题。