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 的依赖注入其实就很清楚了。

相关推荐
换元不配限1 小时前
ConstraintLayout核心用法详解(三):引导线、屏障、组和占位
android·placeholder·barrier·约束布局·guideline
心平气和量大福大3 小时前
android studio(AS)工具-调试-统计总行数
android·ide·android studio
你的坚定4 小时前
SurfaceFlinger合成一帧的完整流程
android
爱笑鱼6 小时前
Android 系统启动机制(七):进入 SystemServer.main() 以后,system_server 还是一个空进程吗?
android·linux
好安静6 小时前
SurfaceFlinger 合成一帧的完整流程
android
alexhilton6 小时前
架构测试为何需要双视图
android·kotlin·android jetpack
事圆则缓6 小时前
Jetpack Compose 快速上手
android·kotlin
y小花6 小时前
安卓HAL接口技术之HIDL&AIDL
android
YF02116 小时前
Android 系统自带机制的卸载更新都哪些实现方案
android