SpringBoot中優(yōu)雅的編寫服務(wù)工廠的方法示例
在基于 Spring Boot 的業(yè)務(wù)開發(fā)中,我們有時(shí)會(huì)遇到這樣的場景:即定義了一個(gè)通用接口,而該接口擁有多個(gè)實(shí)現(xiàn)類。在調(diào)用這些實(shí)現(xiàn)類時(shí),我們通常需要編寫一個(gè)工廠方法,該工廠方法可以根據(jù)指定的參數(shù)獲取到對(duì)應(yīng)的實(shí)現(xiàn)類。
那么,提供該工廠方法的類就是一個(gè)服務(wù)工廠,本文即是探討如何優(yōu)雅的編寫這個(gè)服務(wù)工廠。
1 場景描述
為了將所描述的場景具像化,下面舉一個(gè)易于理解的例子:「假定我們正在使用 Spring Boot 做一個(gè)對(duì)接多個(gè)第三方支付平臺(tái)的支付服務(wù)?!?/p>
我們?cè)趯?shí)現(xiàn)這個(gè)支付服務(wù)時(shí),定義了一個(gè)通用的支付接口 PaymentService,其擁有一個(gè) pay() 方法。該接口可以對(duì)訂單(Order)進(jìn)行支付,支付后會(huì)得到一個(gè)支付結(jié)果 PaymentResult。
public interface PaymentService {
PaymentResult pay(Order order);
}
public class Order {
}
public class PaymentResult {
private boolean success;
private String message;
}
目前這個(gè)支付服務(wù)需要支持三種支付類型:Alibaba、WeChat 和銀聯(lián)。
public enum PaymentType {
ALIBABA,
WECHAT,
UNION
}
那么 PaymentService 就會(huì)擁有三個(gè)不同的實(shí)現(xiàn)類:AlibabaPaymentServiceImpl、WechatPaymentServiceImpl 和 UnionPaymentServiceImpl。
@Service("alibabaPaymentService")
public class AlibabaPaymentServiceImpl implements PaymentService {
@Override
public PaymentResult pay(Order order) {
return ...;
}
}
@Service("wechatPaymentService")
public class WechatPaymentServiceImpl implements PaymentService {
@Override
public PaymentResult pay(Order order) {
return ...;
}
}
@Service("unionPaymentService")
public class UnionPaymentServiceImpl implements PaymentService {
@Override
public PaymentResult pay(Order order) {
return ...;
}
}
為了方便調(diào)用,我們需要編寫一個(gè)工廠類 PaymentFactory,其能夠提供一個(gè)方法:可以根據(jù)不同的支付類型(PaymentType)獲取到 PaymentService 的具體實(shí)現(xiàn)。
public class PaymentFactory {
public PaymentService getService(PaymentType paymentType) {
return xxx;
}
}
這樣調(diào)用者需要使用某種方式進(jìn)行支付時(shí),只需要指定支付類型,通過工廠類拿到 PaymentService,然后調(diào)用 pay() 方法就可以了。
PaymentService paymentService = paymentFactory.getService(PaymentType.WECHAT); PaymentResult paymentResult = paymentService.pay(new Order()); System.out.println(paymentResult);
2 PaymentFactory 基礎(chǔ)實(shí)現(xiàn)
那么如何編寫這個(gè) PaymentFactory 呢?一種最基礎(chǔ)的寫法就是在 PaymentFactory 中將 PaymentService 所有的實(shí)現(xiàn)類都以屬性的方式注入進(jìn)來,然后在 getService() 方法中使用 if-else 或 switch 語句根據(jù) PaymentType 來返回不同的實(shí)現(xiàn)類。
@Component
public class PaymentFactory {
@Qualifier("alibabaPaymentService")
@Autowired
private PaymentService alibabaPaymentService;
@Qualifier("wechatPaymentService")
@Autowired
private PaymentService wechatPaymentService;
@Qualifier("unionPaymentService")
@Autowired
private PaymentService unionPaymentService;
public PaymentService getService(PaymentType paymentType) {
return switch (paymentType) {
case ALIBABA -> alibabaPaymentService;
case WECHAT -> wechatPaymentService;
case UNION -> unionPaymentService;
default -> throw new IllegalArgumentException("PaymentType is not supported");
};
}
}
這種寫法能用,但代碼行數(shù)有點(diǎn)多且有點(diǎn)笨拙,有沒有更高級(jí)一點(diǎn)的寫法呢?
3 PaymentFactory 高級(jí)實(shí)現(xiàn)
PaymentFactory 稍微高級(jí)一點(diǎn)的寫法是不用將實(shí)現(xiàn)類一一聲明為屬性,且不使用上述諸如 if-else 或 switch 等條件判斷語句來根據(jù)不同參數(shù)返回不同的實(shí)現(xiàn)。
而是聲明一個(gè)存放 PaymentType 和實(shí)現(xiàn)類的 Map,然后在構(gòu)造方法中將實(shí)現(xiàn)類注入為方法參數(shù),然后建立該 Map,這樣在 getService() 方法中只需根據(jù) PaymentType 從 Map 中直接獲取實(shí)現(xiàn)類即可。
@Component
public class PaymentFactory {
private final Map<PaymentType, PaymentService> paymentServices;
@Autowired
public PaymentFactory(
@Qualifier("alibabaPaymentService") PaymentService alibabaPaymentService,
@Qualifier("wechatPaymentService") PaymentService wechatPaymentService,
@Qualifier("unionPaymentService") PaymentService unionPaymentService) {
paymentServices = Map.of(
PaymentType.ALIBABA, alibabaPaymentService,
PaymentType.WECHAT, wechatPaymentService,
PaymentType.UNION, unionPaymentService
);
}
public PaymentService getService(PaymentType paymentType) {
return Optional.ofNullable(paymentServices.get(paymentType))
.orElseThrow(() -> new IllegalArgumentException("PaymentType is not supported"));
}
}
上面的實(shí)現(xiàn)比較優(yōu)雅,但代碼行數(shù)仍有點(diǎn)多,有沒有更簡便的寫法呢?
有。因?yàn)?PaymentService 的實(shí)現(xiàn)類命名是有規(guī)則的,所以更簡便的寫法即是借助 Spring BeanFactory 直接根據(jù) Bean 名稱獲取對(duì)應(yīng)的實(shí)現(xiàn)。
@Component
public class PaymentFactory {
@Autowired
private BeanFactory beanFactory;
public PaymentService getService(PaymentType paymentType) {
String beanName = paymentType.name().toLowerCase() + "PaymentService";
if (!beanFactory.containsBean(beanName)) {
throw new IllegalArgumentException("PaymentType is not supported");
}
return (PaymentService) beanFactory.getBean(beanName);
}
}
上述代碼的確簡潔了不少,但其與前面的寫法均有一個(gè)同樣的問題,即類上均含有 @Component 注解,即均需要交給 Spring 實(shí)例化。在靜態(tài)方法或由 Java 反射實(shí)例化的類中無法直接使用。
下面就嘗試編寫一個(gè)純靜態(tài)的 PaymentFactory,使得調(diào)用者可以直接像下面這樣通過 PaymentFactory.getService() 的方式獲取 PaymentService 的實(shí)現(xiàn)類。
PaymentService paymentService = PaymentFactory.getService(PaymentType.WECHAT);
這樣就需要依賴一個(gè)保存 Spring 應(yīng)用上下文的工具類了:
@Component
public class SpringContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(String beanName, Class<T> clazz) {
return context.getBean(beanName, clazz);
}
}
SpringContextHolder 工具類可以在 Spring 加載完成后自動(dòng)持有 Spring 的 ApplicationContext。然后在后期有需要時(shí),調(diào)用者可以使用一個(gè)純靜態(tài)方法來獲取任意 Spring 管理的 Bean。
這樣,有了 SpringContextHolder 工具類后,我們的靜態(tài) PaymentFactory 就可以像下面這樣實(shí)現(xiàn)了。
public class PaymentFactory {
public static PaymentService getService(PaymentType paymentType) {
String beanName = paymentType.name().toLowerCase() + "PaymentService";
return SpringContextHolder.getBean(beanName, PaymentService.class);
}
}
4 小結(jié)
綜上,本文提出了如何在 Spring Boot 中編寫一個(gè)服務(wù)工廠的問題。然后針對(duì)該問題,舉了一個(gè)支付業(yè)務(wù)的例子,然后探索了 PaymentFactory 的基本寫法和更高級(jí)的寫法。以備有需要的同學(xué)在實(shí)際開發(fā)中做參考。
到此這篇關(guān)于SpringBoot中優(yōu)雅的編寫服務(wù)工廠的方法示例的文章就介紹到這了,更多相關(guān)SpringBoot服務(wù)工廠內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java?Long類型轉(zhuǎn)為json后數(shù)據(jù)損失精度的處理方式
這篇文章主要介紹了java?Long類型轉(zhuǎn)為json后數(shù)據(jù)損失精度的處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
SpringCloud大文件分片斷點(diǎn)上傳實(shí)現(xiàn)原理
這篇文章主要介紹了SpringCloud大文件分片斷點(diǎn)上傳實(shí)現(xiàn)原理,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05

