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


    一、问题的描述

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

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

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

    1. class="hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", 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 {
    2. public void doSomething();
    3. }

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

    1. class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", 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
    2. public class DemoServiceBeijing implements IDemoService {
    3. @Override
    4. public void doSomething() {System.out.println("北京的业务实现");}
    5. }

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

    1. class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", 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
    2. public class DemoServiceShanghai implements IDemoService {
    3. @Override
    4. public void doSomething() {System.out.println("上海的业务实现");}
    5. }

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

    1. class="prettyprint hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", 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
    2. class DemoApplicationTests {
    3. //这里注入的demoService是DemoServiceShanghai,还是DemoServiceBeijing?
    4. @Resource
    5. IDemoService demoService;
    6. @Test
    7. void testDemoService() {
    8. demoService.doSomething();
    9. }
    10. }

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

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

    二、相对低级解决方案

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

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

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

    1. class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", 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
    2. @Component
    3. public class DemoServiceBeijing implements IDemoService {

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

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

    1. <pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", 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
    2. IDemoService demoServiceBeijing; //这里的变量名称指定了bean名称
    3. //IDemoService demoService; 被替换掉

    或者

    1. <pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", 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注解明确指定名称
    2. IDemoService demoService;

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

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

    1. <pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", 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注解明确指定名称
    2. @Resource
    3. IDemoService demoService;

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

    三、相对高级的解决方案

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

    1. <pre class="hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", 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:
    2. province: beijing

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

    • 在北京的实现类上面加上ConditionalOnProperty注解,havingValue的值为beijing
    1. <pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", 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
    2. @ConditionalOnProperty(value="deploy.province",havingValue = "beijing")
    3. public class DemoServiceBeijing implements IDemoService {
    • 在上海的实现类上面加上ConditionalOnProperty注解,havingValue的值为shanghai
    1. <pre class="prettyprint hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", 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
    2. @ConditionalOnProperty(value="deploy.province",havingValue = "shanghai")
    3. public class DemoServiceShanghai implements IDemoService {

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

  • 相关阅读:
    构建全真互联数字地图底座 腾讯地图产业版WeMap重磅升级
    Windows与Linux行尾换行符引发Git的一系列惨案
    Django day08
    JavaWeb中的VUE快速入门
    python教程:封装与解构
    docker删除镜像和容器
    免费享受企业级安全:雷池社区版WAF,高效专业的Web安全的方案
    稻草熊娱乐股价再创新低:年内累计跌幅达80%,赵丽颖曾是其股东
    分类预测 | Matlab实现RBF-Adaboost多特征分类预测
    【全开源】JAVA打车小程序APP打车顺风车滴滴车跑腿源码微信小程序打车源码
  • 原文地址:https://blog.csdn.net/Java_ttcd/article/details/126502515