Spring 依赖注入与 Bean 管理详解(含自动装配与多 Bean 冲突解决)

发布于:2025-08-15 ⋅ 阅读:(14) ⋅ 点赞:(0)

一、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 方法,调用 UserServiceprintUser 方法。


5. 运行结果

用户1001

6. 总结

  • 使用 @Repository 标注数据访问类,@Service 标注业务逻辑类。
  • 通过构造器注入(带 @Autowired)实现依赖自动装配,推荐这种方式。
  • Spring Boot 启动时自动扫描组件并管理生命周期。
  • 运行时实现调用链:DemoApplicationUserServiceUserRepository → 输出用户信息。

二、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,默认名和自定义名 u1u2
  • 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 允许定义多个实例和自定义名称,方便注入。
  • 通过 initMethoddestroyMethod 管理 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 冲突问题。


网站公告

今日签到

点亮在社区的每一天
去签到