spring接口多实现类,该依赖注入哪一个?

一、问题的描述

在实际的系统应用开发中我经常会遇到这样的一类需求,相信大家在工作中也会经常遇到:

  • 同一个系统在多个省份部署。
  • 一个业务在北京是一种实现方式,是基于北京用户的需求。
  • 同样的业务在上海是另外一种实现方式,与北京的实现方式大同小异

遇到这样的需求,我们通常会定义一个业务实现的接口,比如:

<pre class="hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">public interface IDemoService {
  public void doSomething();
}

在北京环境下这样实现,比如:

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">@Component
public class DemoServiceBeijing implements IDemoService {
  @Override
  public void doSomething() {System.out.println("北京的业务实现");}
}

在上海环境下这样实现,比如:

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">@Component
public class DemoServiceShanghai implements IDemoService {
  @Override
  public void doSomething() {System.out.println("上海的业务实现");}
}

然后我们写一个模拟业务测试用例

<pre class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">@SpringBootTest
class DemoApplicationTests {
    //这里注入的demoService是DemoServiceShanghai,还是DemoServiceBeijing?
    @Resource
    IDemoService demoService;  
    @Test
    void testDemoService() {
        demoService.doSomething();
    }
}

当我们执行这个测试用例的时候一定会报错,因为Spring发现了两个IDemoService的实现类。它不知道去实例化哪一个实现类,来作为IDemoService的实际业务处理bean。当然我们期望的状态是:

  • 在北京部署系统的时候,使用DemoServiceBeijing作为IDemoService的实现类完成依赖注入
  • 在上海部署系统的时候,使用DemoServiceShanghai作为IDemoService的实现类完成依赖注入

二、相对低级解决方案

面对上面的需求,先说几个相对低级的解决方案,这几个方案虽然可以实现我们期望的状态,但是对运维不够友好。

2.1. 方案一:使用 @Primary 注解

假如在北京部署系统的时候,在DemoServiceBeijing的类上面加上 @Primary ,该注解的作用就是强迫从多个实现类里面选一个实现类,如果Spring不知道选哪一个,我们告诉它一个默认的。

<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">@Primary
@Component
public class DemoServiceBeijing implements IDemoService {

2.2. 方案二:使用 @Resource 注解

因为Resource注解默认使用名称进行依赖注入,所以变量名明确叫做demoServiceBeijing(首字母小写),使用的就是DemoServiceBeijing实现类。

<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">@Resource
IDemoService demoServiceBeijing;  //这里的变量名称指定了bean名称
//IDemoService demoService;  被替换掉

或者

<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">@Resource(name = "demoServiceBeijing")  //使用resource注解明确指定名称
IDemoService demoService;

2.3.方案三:使用 @Qualifier 注解

与上文同样的道理,使用 @Qualifier 注解,指定bean的名称进行依赖注入

<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">@Qualifier("demoServiceBeijing")  //使用Qualifier注解明确指定名称
@Resource
IDemoService demoService;

上面所提到的三个方案虽然都可以解决:在不同的部署环境下使用不同的接口实现类完成依赖注入的问题。但是这样不好,因为一旦我们要把部署环境从beijing(北京)换成shanghai(上海),就需要把上面的注解的位置或者内容全都修改一遍(所有的实现类代码都要修改)。

三、相对高级的解决方案

我们提出进一步的期望:就是只修改一个配置就能完成部署环境切换的操作。比如:

<pre class="hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">deploy:
  province: beijing

当我们期望把部署环境从北京切换到上海的时候,只需要将上文配置中的beijing 改成 shanghai ,这该怎么实现呢?

  • 在北京的实现类上面加上ConditionalOnProperty注解,havingValue的值为beijing
<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">@Component
@ConditionalOnProperty(value="deploy.province",havingValue = "beijing")
public class DemoServiceBeijing implements IDemoService {
  • 在上海的实现类上面加上ConditionalOnProperty注解,havingValue的值为shanghai
<pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">@Component
@ConditionalOnProperty(value="deploy.province",havingValue = "shanghai")
public class DemoServiceShanghai implements IDemoService {

ConditionalOnProperty注解在这里的作用就是:读取配置文件发现 deploy.province ,并将该配置的值与havingValue匹配,匹配上哪一个就实例化哪一个类作为该接口的实现类bean注入到Spring容器中(当然注入过程需要配合 @Component 注解实现)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring框架通过使用依赖注入(DI)来实现解耦。DI允许外部实体在构造函数,字段或者集合属性注入相应的依赖,从而使得的实例化更加简单,代码更加清晰,维护更加容易。Spring框架实现DI的关键有三个:控制反转(IoC)、面向切面编程(AOP)和依赖查找(DL)。 ### 回答2: Spring框架是一个开源的Java应用开发框架,它使用了依赖注入的原理来管理对象之间的依赖关系。所谓依赖注入,就是让程序员不再需要手动创建和管理对象之间的关系,而是由框架来自动完成。 在Spring框架中,我们首先需要定义好我们的Java,声明它们之间的依赖关系。我们可以使用注解的方式,在需要依赖的属性或者构造方法上加上注解,告诉框架这个属性或者参数需要注入一个对象。 当我们启动程序的时候,Spring框架会根据我们的配置信息,遍历所有的Java,解析其中的注解信息。然后会根据这些信息创建一个对象的实例,并且将需要注入的属性或者参数自动赋值。这个过程是通过Java的反射机制来实现的。 具体来说,Spring框架会根据注解上的信息,找到合适的对象实例,然后通过调用对象的构造方法或者设值方法,将实例注入到被依赖的属性或者参数中。这样,我们就完成了对象之间的依赖关系的建立,可以方便地使用它们进行开发和业务处理。 借助依赖注入,我们不再需要手动创建和管理对象之间的依赖关系,大大简化了对象之间的耦合度。我们只需要关注对象的功能实现,而不需要过多关心它的依赖关系。这样可以提高开发效率,同时也方便了程序的维护和修改。 ### 回答3: Spring框架是一个用于简化Java开发的框架,其中的依赖注入是其中的一个核心特性。 依赖注入是将对象之间的依赖关系交由框架来管理,而不是由开发人员手动创建和管理。在Spring中,依赖注入是通过配置文件或注解的方式来实现的。 首先,需要将要注入所对应的bean配置为一个Spring的bean,这样框架就能够管理这个对象的生命周期。配置文件通常是一个XML文件,其中包含了对Bean的定义和属性的设置。 接下来,需要在需要注入中声明需要注入的属性,并为这些属性提供setter方法。Spring框架在启动时会扫描配置文件,找到需要注入,并创建对应的对象。 当需要使用某个对象时,Spring会自动将需要注入的属性通过反射的方式注入到对象中,而不需要开发人员手动创建和设置依赖关系。 通过注入,对象之间的依赖关系被解耦,每个对象只需要关注自己的业务逻辑,而不需要关心如何获取依赖的对象。这样可以提高代码的可维护性和可测试性,并且减少了对象之间的紧耦合。 总的来说,Spring框架的依赖注入是通过配置文件或注解的方式来管理对象之间的依赖关系,框架会自动将需要注入的属性注入到对象中。这样可以简化开发过程,提高代码的可维护性和可测试性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值