文章目錄
- 一、問題的描述
- 二、相對低級解決方案
- 2.1. 方案一:使用`@Primary`注解
- 2.2. 方案二:使用`@Resource`注解
- 2.3.方案三:使用`@Qualifier`注解
- 三、相對進階的解決方案
一、問題的描述
在實際的系統應用開發中我經常會遇到這樣的一類需求,相信大家在工作中也會經常遇到:
- 同一個系統在多個省份部署。
- 一個業務在北京是一種實作方式,是基于北京使用者的需求。
- 同樣的業務在上海是另外一種實作方式,與北京的實作方式大同小異
遇到這樣的需求,我們通常會定義一個業務實作的接口,比如:
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
注解
@Primary
假如在北京部署系統的時候,在DemoServiceBeijing的類上面加上
@Primary
,該注解的作用就是強迫從多個實作類裡面選一個實作類,如果Spring不知道選哪一個,我們告訴它一個預設的。
@Primary
@Component
public class DemoServiceBeijing implements IDemoService {
2.2. 方案二:使用 @Resource
注解
@Resource
因為Resource注解預設使用名稱進行依賴注入,是以變量名明确叫做demoServiceBeijing(首字母小寫),使用的就是DemoServiceBeijing實作類。
@Resource
IDemoService demoServiceBeijing; //這裡的變量名稱指定了bean名稱
//IDemoService demoService; 被替換掉
或者
@Resource(name = "demoServiceBeijing") //使用resource注解明确指定名稱
IDemoService demoService;
2.3.方案三:使用 @Qualifier
注解
@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
注解實作)。