巧用 Spring 自動注入快速實現策略模式

寫在前面:2020年面試必備的Java後端進階面試題總結了一份複習指南在Github上,內容詳細,圖文並茂,有需要學習的朋友可以Star一下!
GitHub地址:https://github.com/abel-max/Java-Study-Note/tree/master

最近看同事的代碼時候,學到了個小技巧,在某些場景下非常挺有用的,這裏分享一下給大家。

Spring 中 @Autowired 註解,大家應該不會陌生,用過 Spring 的肯定也離不開這個註解,通過這個註解可以幫我們自動注入我們想要的 Bean。

除了這個基本功能之外, @Autowired 還有更加強大的功能,還可以注入指定類型的數組,List/Set 集合,甚至還可以是 Map 對象。

比如說當前應用有一個支付接口 PayService ,分別需要對接支付寶、微信支付、銀行卡,所以分別有三個不同實現類 AliPayService , WechatPayservice , BankCardPayService 。

四個類的關係如下圖所示:

如果此時我需要獲取當前系統類所有 PayService Bean,老的方式我們只能通過 BeanFactory或者 ApplicationContex t 獲取。

// 首先通過 getBeanNamesForType 獲取 PayService 類型所有的 Bean
String[] names = ctx.getBeanNamesForType(PayService.class);List<PayService> anotherPayService = Lists.newArrayList();
for (String beanName : names) {    anotherPayService.add(ctx.getBean(beanName, PayService.class));}// 或者通過 getBeansOfType 獲取所有 PayService 類型Map<String, PayService> beansOfType = ctx.getBeansOfType(PayService.class);
for (Map.Entry<String, PayService> entry : beansOfType.entrySet()) {
    anotherPayService.add(entry.getValue());}

但是現在我們可以不用這麼麻煩了,我們可以直接使用 @Autowired 注入 PayService Bean 數組,或者 PayService List/Set 集合,甚至,我們還可以注入 PayService 的 Map 集合。

@Autowired
List<PayService> payServices;
@Autowired
PayService[] payServicesArray;

知道了這個功能,當我們需要使用 Spring 實現策略模式就非常簡單。

可能有的小夥伴不太瞭解策略模式,沒關係,這類阿粉介紹一個業務場景,通過這個場景給大家介紹一下策略模式。

還是上面的例子,我們當前系統需要對接微信支付、支付寶、以及銀行卡支付。

當接到這個需求,我們首先需要拿到相應接口文檔,分析三者的共性。

假設我們這裏發現,三者模式比較類似,只是部分傳參不一樣。

所以我們根據三者的共性,抽象出一組公共的接口 PayService ,

public interface PayService {
    PayResult epay(PayRequest request);
}

然後分別實現三個實現類,都繼承這個接口。

那麼現在問題來了,由於存在三個實現類,如何選擇具體的實現類?

其實這個問題很好解決,請求參數傳入一個唯一標識,然後我們根據標識選擇相應的實現類。

比如說我們在請求類 PayRequest 搞個 channelNo 字段,這個代表相應支付渠道唯一標識,比如說支付寶爲: 00000001 ,微信支付爲 00000002 ,銀行卡支付爲 00000003

接着我們需要把唯一標識與具體實現類一一映射起來,剛好我們可以使用 Map 存儲這種映射關係。

我們實現一個 RouteService ,具體代碼邏輯如下:

@Service
public class RouteService {
    @Autowired
    Map<String, PayService> payServiceMap;    public PayResult epay(PayRequest payRequest) {
        PayService payService = payServiceMap.get(payRequest.getChannelNo());
        return  payService.epay(payRequest);
    }}

我們在 RouteService 自動注入 PayService 所有相關 Bean,然後使用唯一標識查找實現類。

這樣我們對外就屏蔽了支付渠道的差異,其他服務類只要調用 RouteService 即可。

但是這樣實現還是有點小問題,由於我們唯一標識爲一串數字,如果像我們上面直接使用 @Autowired 注入 Map ,這就需要我們實現類的 Bean 名字爲 00000001 這些。

但是這樣命名不是很優雅,這樣會讓後來同學很難看懂,不好維護。

所以我們需要做個轉換,我們可以這麼實現。

首先我們改造一下 PayService 這個接口,增加一個方法,每個具體實現類通過這個方法返回其唯一標識。

public interface PayService {
    PayResult epay(PayRequest request);
    String channel();
}

具體舉個支付寶實現類的代碼,其他實現類實現類似。

@Service("aliPayService")
public class AliPayService implements PayService {
    @Override
    public PayResult epay(PayRequest request) {
        // 業務邏輯
        return new PayResult();
    }
    @Override
    public String channel() {
        return "00000001";
    }
}

最後我們改造一下 RouteService ,具體邏輯如下:

@Service
public class RouteService {
    @Autowired
    Set<PayService> payServiceSet;        Map<String, PayService> payServiceMap;    public PayResult epay(PayRequest payRequest) {
        PayService payService = payServiceMap.get(payRequest.getChannelNo());        return  payService.epay(payRequest);
    }    @PostConstruct
    public void init() {
        for (PayService payService : payServiceSet) {
            payServiceMap = new HashMap<>();
            payServiceMap.put(payService.channel(), payService);        }    }}

上面代碼首先通過自動注入 PayService 一個集合,然後我們再將其轉爲一個 Map ,這樣內部存儲剛好是唯一標識與實現類的映射了。

來源:https://www.tuicool.com/articles/FjYNJbF

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