IOC依赖注入问题:@Primary 和 @Qualifier的使用

假设现在有一个接口:

java 复制代码
public interface TestRepository {
    void save();
}

然后你写了两个实现类:

java 复制代码
@Repository
public class MysqlTestRepository implements TestRepository {

    @Override
    public void save() {
        System.out.println("保存到 MySQL");
    }
}

以及:

java 复制代码
@Repository
public class MemoryTestRepository implements TestRepository {

    @Override
    public void save() {
        System.out.println("保存到内存");
    }
}

此时 Spring 容器里实际上有两个 Bean:

java 复制代码
MysqlTestRepository
MemoryTestRepository

而且它们都属于:

java 复制代码
TestRepository

这时候如果你写:

java 复制代码
@Service
public class StudentService {

    private final TestRepository repository;

    public StudentService(TestRepository repository) {
        this.repository = repository;
    }
}

Spring 就懵了:

你要 TestRepository,但我这里有两个,到底给你哪个?

这时通常会启动失败。


1.@Primary:设置默认实现

你可以告诉 Spring:

如果有多个同类型 Bean,默认优先用这个。

例如:

java 复制代码
@Repository
@Primary
public class MysqlTestRepository implements TestRepository {

    @Override
    public void save() {
        System.out.println("保存到 MySQL");
    }
}

另一个:

java 复制代码
@Repository
public class MemoryTestRepository implements TestRepository {
}

那么:

java 复制代码
public StudentService(TestRepository repository)

Spring 会默认注入:

java 复制代码
MysqlTestRepository

因为它被标记了:

java 复制代码
@Primary

你可以理解成:

@Primary = 默认候选人。


2.@Qualifier:明确指定我要哪个

有时候不能只设置一个默认值。

比如:

java 复制代码
StudentService
→ 想用 MySQL

TestService
→ 想用 Memory

这时可以明确指定。

例如:

java 复制代码
@Service
public class StudentService {

    private final TestRepository repository;

    public StudentService(
            @Qualifier("mysqlTestRepository")
            TestRepository repository) {

        this.repository = repository;
    }
}

这表示:

我要名字叫 mysqlTestRepository 的那个 Bean。

Spring 默认会把:

复制代码
MysqlTestRepository

生成的 Bean 名字变成:

复制代码
mysqlTestRepository

也就是类名首字母小写。

3. 还可以自己指定 Bean 名字

比如:

java 复制代码
@Repository("mysqlRepo")
public class MysqlTestRepository
        implements TestRepository {
}

然后:

java 复制代码
public StudentService(
        @Qualifier("mysqlRepo")
        TestRepository repository) {
}

这样就不依赖默认的:

复制代码
mysqlTestRepository

名字了。

4.可以把 Spring 注入理解成一个匹配过程

Spring 创建:

复制代码
StudentService

发现构造器需要:

复制代码
TestRepository

于是去容器里找:

复制代码
有没有 TestRepository 类型的 Bean?

情况一:

复制代码
找到 0 个
→ 报错:没有 Bean

情况二:

复制代码
找到 1 个
→ 直接注入

情况三:

复制代码
找到 2 个或更多
→ 看有没有 @Qualifier
→ 没有就看有没有 @Primary
→ 还无法确定就报错

这套逻辑掌握以后,Spring 的依赖注入其实就很清楚了。

相关推荐
booboosui37 分钟前
Android 悬浮窗藏不住自己:一个标志位与 140 毫秒
android
传奇开心果42 分钟前
【Flutter踩坑排雷】第5课:扩展方法冲突坑|同名扩展引发编译歧义,故障规避手段
android·flutter
.痞老板.3 小时前
mysql初阶--数据类型
android·数据库·mysql
StarsGreen3 小时前
Android 启动流程详解
android·linux·单片机·嵌入式硬件
booboosui1 天前
Android 上来电怎么判:7 个条件加 5 个中文词
android
MardaWang1 天前
App 内嵌 H5 的信任边界设计(ArkWeb/HarmonyOS 实战)
android
石头慢慢说1 天前
不写 XPath,让 AI 在真机上跑测试:Appium MCP 全流程实战
android
90后的晨仔1 天前
解决 Android 构建报错:Unable to delete file .../file-map.txt
android·前端
mg6681 天前
安卓玩机工具----安卓手机变成桌面电脑!多窗口镜像+音频投射+游戏操控,无需 Root
android·智能手机·电脑
peakmain91 天前
异常处理 - Native 层的崩溃捕获机制及实现 (一)
android·性能优化