spring接口多實現類,該依賴注入哪一個?

一、問題的描述

在實際的系統應用開發中我經常會遇到這樣的一類需求,相信大家在工作中也會經常遇到:

  • 同一個系統在多個省份部署。
  • 一個業務在北京是一種實現方式,是基於北京用戶的需求。
  • 同樣的業務在上海是另外一種實現方式,與北京的實現方式大同小異

遇到這樣的需求,我們通常會定義一個業務實現的接口,比如:

public interface IDemoService {
  public void doSomething();
}

在北京環境下這樣實現,比如:

@Component
public class DemoServiceBeijing implements IDemoService {
  @Override
  public void doSomething() {System.out.println("北京的業務實現");}
}

在上海環境下這樣實現,比如:

@Component
public class DemoServiceShanghai implements IDemoService {
  @Override
  public void doSomething() {System.out.println("上海的業務實現");}
}

然後我們寫一個模擬業務測試用例

@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不知道選哪一個,我們告訴它一個默認的。

@Primary
@Component
public class DemoServiceBeijing implements IDemoService {

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

因爲Resource註解默認使用名稱進行依賴注入,所以變量名明確叫做demoServiceBeijing(首字母小寫),使用的就是DemoServiceBeijing實現類。

@Resource
IDemoService demoServiceBeijing;  //這裏的變量名稱指定了bean名稱
//IDemoService demoService;  被替換掉

或者

@Resource(name = "demoServiceBeijing")  //使用resource註解明確指定名稱
IDemoService demoService;

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

與上文同樣的道理,使用@Qualifier註解,指定bean的名稱進行依賴注入

@Qualifier("demoServiceBeijing")  //使用Qualifier註解明確指定名稱
@Resource
IDemoService demoService;

上面所提到的三個方案雖然都可以解決:在不同的部署環境下使用不同的接口實現類完成依賴注入的問題。但是這樣不好,因爲一旦我們要把部署環境從beijing(北京)換成shanghai(上海),就需要把上面的註解的位置或者內容全都修改一遍(所有的實現類代碼都要修改)。

三、相對高級的解決方案

我們提出進一步的期望:就是隻修改一個配置就能完成部署環境切換的操作。比如:

deploy:
  province: beijing

當我們期望把部署環境從北京切換到上海的時候,只需要將上文配置中的beijing 改成 shanghai ,這該怎麼實現呢?

  • 在北京的實現類上面加上ConditionalOnProperty註解,havingValue的值爲beijing
@Component
@ConditionalOnProperty(value="deploy.province",havingValue = "beijing")
public class DemoServiceBeijing implements IDemoService {
  • 在上海的實現類上面加上ConditionalOnProperty註解,havingValue的值爲shanghai
@Component
@ConditionalOnProperty(value="deploy.province",havingValue = "shanghai")
public class DemoServiceShanghai implements IDemoService {

ConditionalOnProperty註解在這裏的作用就是:讀取配置文件發現deploy.province,並將該配置的值與havingValue匹配,匹配上哪一個就實例化哪一個類作爲該接口的實現類bean注入到Spring容器中(當然注入過程需要配合@Component註解實現)。

歡迎關注我的公告號:字母哥雜談,回覆003贈送作者專欄《docker修煉之道》的PDF版本,30餘篇精品docker文章。字母哥博客:zimug.com

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章