在Spring Boot中具有多个实现的接口正确注入组件的六种方式

开发 前端
通常,当我们有多个接口实现并尝试将该接口自动注入到组件中时,会遇到一个错误——“需要单个bean,但找到了X个”。原因很简单:Spring不知道我们想要在该组件中使用哪个实现类。不过,Spring对于这种情况还是提供了多种方式来让我们更具体地指定所需要的类。

环境:Spring Boot3.2.5

1. 简介

本篇文章,我们将探讨学习在 Spring Boot 中自动装配时当一个接口有多个实现时如何选择正确的实现类进行注入。这是一项强大的功能,允许开发人员将接口的不同实现动态注入应用程序中。

通常,当我们有多个接口实现并尝试将该接口自动注入到组件中时,会遇到一个错误——“需要单个bean,但找到了X个”。原因很简单:Spring不知道我们想要在该组件中使用哪个实现类。不过,Spring对于这种情况还是提供了多种方式来让我们更具体地指定所需要的类。

接下来,我将详细的介绍6种实现方式。

2. 实战案例

2.1 准备环境

首先,定义一个接口DAO

public interface DAO<T> {
  List<T> queryList() ;
}

接下来,定义2个实现类

public class MySQLDAO implements DAO<User> {
  @Override
  public List<User> queryList()
    // TODO
  }
}
public class OracleDAO implements DAO<User> {
  @Override
  public List<Date> queryList() {
    // TODO
  }
}

这里针对DAO接口,定义了2个接口实现,接下来的案例中我们将围绕这2个进行讲解。

2.2 使用@Qualifier注解

使用@Qualifier注解,我们可以在多个实现类中指定要自动装配哪个bean。我们可以将其应用于组件本身,为其提供一个自定义的限定符名称,如下示例:

@Repository
@Qualifier("mysqlDAO-1")
public class MySQLDAO implements DAO<User> {}
@Repository
public class OracleDAO implements DAO<User> {}

这里2个实现使用@Repository注册为bean,其中MySQLDAO实现使用了@Qualifier限定了bean名称。接下来,在注入时,我们就可以使用@Qualifier来限定注入的bean

@Component
public class CompDAO {


  private final DAO dao1 ;
  private final DAO dao2 ;
  public CompDAO(
    @Qualifier("mysqlDAO-1") DAO dao1, 
    DAO oracleDAO) {
    this.dao1 = dao1 ;
    this.dao2 = dao2 ;
  }
}

需要注意的是,这里的第二个DAO参数并没有添加@Qualifier注解,它也能正确的注入,这是因为默认情况Spring会按照名称进行装配(确保定义bean的名称与你这参数名称一致)。

2.3 使用@Primary注解

此外,我们还可以用 @Primary 来注解其中一个实现。如果有多个候选实现,且按参数名或限定符自动装配不适用,Spring 将使用该实现:

@Repository
@Primary
public class OracleDAO implements DAO<User> {}

当我们经常使用其中一种实现时,此种方式就会非常有用,这非常有助于避免出现 如下的错误信息:

图片图片

在这种情况下如果你还需要使用其它的实现,你可以通过ApplicationContext#getBean手动方式来获取。

2.4 使用@Profile注解

可以使用Spring的配置文件(profiles)来决定要自动装配哪个组件。如上示例,我们可以让OracleDAO实现仅在生产环境配置文件(prod profile)下激活,而MySQLDAO仅在开发环境配置文件(dev profile)下激活,如下示例:

@Repository
@Profile("dev")
public class MySQLDAO implements DAO<User> {}
@Repository
@Profile("prod")
public class OracleDAO implements DAO<User> {}

具体哪个会生效,就看你当前环境配置的spring.profiles.active属性是dev还是prod了。

2.5 所有实现装配到集合中

我们可以将特定类型的所有可用 Bean 注入到一个集合中,如下示例:

@Component
public class CompDAO {


  private final List<DAO> daos ;
  public CompDAO(List<DAO> daos) {
    this.daos = daos ;
  }
}

此外,我们还可以将实现自动装配到Set、Map或Array中。使用Map时,格式通常是 Map<String, DAO>,其中键是 Bean 的名称,值是 Bean 实例本身,如下示例:

@Component
public class CompDAO {
  private final Map<String, DAO> daos ;
  public CompDAO(Map<String, DAO> daos) {
    this.daos = daos ;
  }
  
  public void use() {
    MySQLDAO mdao = this.daos.get("mySQLDAO") ;
    OracleDAO odao = this.daos.get("oracleDAO") ;
    // TODO
  }
}

注意:此时Spring不会考虑限定符或参数名称。它会忽略注释为 @Profile 的、与当前配置文件不匹配的 Bean。

2.6 使用@Priority注解

我们还可以使用jakarta.annotation.Priority注解,为每一个实现类定义优先级,该注解有一个Integer类型的参数,值越小优先级越高,当存在多个类型的优先级越高的会被优先注入,如下示例:

@Repository
@Priority(2)
public class OracleDAO implements DAO<Date> {}
@Repository
@Priority(1)
public class MySQLDAO implements DAO<Date> {}

如上示例,由于MySQLDAO设置优先级最小,所以注入的将是MySQLDAO。

2.7 自定义Condition

为了更精确地确定哪个 bean 成为自动装配的候选者,我们可以使用 @Conditional 注解对它们进行标注。@Conditional 注解应该有一个参数,该参数是一个实现了 Condition 接口(它是一个函数式接口)的类,如下示例:

public class PackCondition implements Condition {
  @Override 
  public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    return context.getEnvironment()
      .getProperty("pack.active")
      .toLowerCase()
      .equals("mysql") ;
  } 
}

使用

@Repository
@Conditional(PackCondition.class) 
public class MySQLDAO implements DAO<User> {
  // ...
}

在上面的示例中,只有配置文件的pack.active属性设置为mysql(matches方法返回true)时才会创建MySQLDAO。

责任编辑:武晓燕 来源: Spring全家桶实战案例源码
相关推荐

2024-05-30 08:51:28

Spring数据分布式

2011-06-03 11:53:06

Spring接口

2011-07-22 17:22:20

Spring

2023-06-01 16:45:11

React开发JavaScript

2023-05-10 13:58:13

服务限流系统

2012-08-22 10:32:34

2024-08-30 11:11:01

2019-05-15 08:00:00

vue组件间通信前端

2022-03-23 12:55:50

农业物联网

2019-05-16 13:00:18

异步编程JavaScript回调函数

2022-03-28 20:57:31

私有属性class属性和方法

2023-04-26 08:41:16

Git撤消更改

2016-05-10 14:38:13

大数据企业

2023-08-15 15:44:55

React开发

2009-02-11 09:46:00

ASON网络演进

2022-12-06 10:39:43

Spring事务失效

2017-06-26 10:35:58

前端JavaScript继承方式

2024-10-18 08:00:00

SpringBoot框架开发

2012-07-17 09:16:16

SpringSSH

2020-07-31 11:12:39

安全威胁网络攻击网络安全
点赞
收藏

51CTO技术栈公众号