SpringBoot中优雅的编写服务工厂的方法示例
作者:磊磊落落
在基于 Spring Boot 的业务开发中,我们有时会遇到这样的场景:即定义了一个通用接口,而该接口拥有多个实现类。在调用这些实现类时,我们通常需要编写一个工厂方法,该工厂方法可以根据指定的参数获取到对应的实现类。
那么,提供该工厂方法的类就是一个服务工厂,本文即是探讨如何优雅的编写这个服务工厂。
1 场景描述
为了将所描述的场景具像化,下面举一个易于理解的例子:「假定我们正在使用 Spring Boot 做一个对接多个第三方支付平台的支付服务。」
我们在实现这个支付服务时,定义了一个通用的支付接口 PaymentService
,其拥有一个 pay()
方法。该接口可以对订单(Order
)进行支付,支付后会得到一个支付结果 PaymentResult
。
public interface PaymentService { PaymentResult pay(Order order); } public class Order { } public class PaymentResult { private boolean success; private String message; }
目前这个支付服务需要支持三种支付类型:Alibaba、WeChat 和银联。
public enum PaymentType { ALIBABA, WECHAT, UNION }
那么 PaymentService
就会拥有三个不同的实现类: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 ...; } }
为了方便调用,我们需要编写一个工厂类 PaymentFactory
,其能够提供一个方法:可以根据不同的支付类型(PaymentType
)获取到 PaymentService
的具体实现。
public class PaymentFactory { public PaymentService getService(PaymentType paymentType) { return xxx; } }
这样调用者需要使用某种方式进行支付时,只需要指定支付类型,通过工厂类拿到 PaymentService
,然后调用 pay()
方法就可以了。
PaymentService paymentService = paymentFactory.getService(PaymentType.WECHAT); PaymentResult paymentResult = paymentService.pay(new Order()); System.out.println(paymentResult);
2 PaymentFactory 基础实现
那么如何编写这个 PaymentFactory
呢?一种最基础的写法就是在 PaymentFactory
中将 PaymentService
所有的实现类都以属性的方式注入进来,然后在 getService()
方法中使用 if-else
或 switch
语句根据 PaymentType
来返回不同的实现类。
@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"); }; } }
这种写法能用,但代码行数有点多且有点笨拙,有没有更高级一点的写法呢?
3 PaymentFactory 高级实现
PaymentFactory
稍微高级一点的写法是不用将实现类一一声明为属性,且不使用上述诸如 if-else
或 switch
等条件判断语句来根据不同参数返回不同的实现。
而是声明一个存放 PaymentType
和实现类的 Map
,然后在构造方法中将实现类注入为方法参数,然后建立该 Map
,这样在 getService()
方法中只需根据 PaymentType
从 Map
中直接获取实现类即可。
@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")); } }
上面的实现比较优雅,但代码行数仍有点多,有没有更简便的写法呢?
有。因为 PaymentService
的实现类命名是有规则的,所以更简便的写法即是借助 Spring BeanFactory
直接根据 Bean
名称获取对应的实现。
@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 实例化。在静态方法或由 Java 反射实例化的类中无法直接使用。
下面就尝试编写一个纯静态的 PaymentFactory
,使得调用者可以直接像下面这样通过 PaymentFactory.getService()
的方式获取 PaymentService
的实现类。
PaymentService paymentService = PaymentFactory.getService(PaymentType.WECHAT);
这样就需要依赖一个保存 Spring 应用上下文的工具类了:
@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
。然后在后期有需要时,调用者可以使用一个纯静态方法来获取任意 Spring 管理的 Bean。
这样,有了 SpringContextHolder
工具类后,我们的静态 PaymentFactory
就可以像下面这样实现了。
public class PaymentFactory { public static PaymentService getService(PaymentType paymentType) { String beanName = paymentType.name().toLowerCase() + "PaymentService"; return SpringContextHolder.getBean(beanName, PaymentService.class); } }
4 小结
综上,本文提出了如何在 Spring Boot 中编写一个服务工厂的问题。然后针对该问题,举了一个支付业务的例子,然后探索了 PaymentFactory
的基本写法和更高级的写法。以备有需要的同学在实际开发中做参考。
到此这篇关于SpringBoot中优雅的编写服务工厂的方法示例的文章就介绍到这了,更多相关SpringBoot服务工厂内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!