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

