原来使用 Spring 实现策略模式可以这么简单!

开发 架构
Spring 中 @Autowired注解,大家应该不会陌生,用过 Spring 的肯定也离不开这个注解,通过这个注解可以帮我们自动注入我们想要的 Bean。

 [[343796]]

 本文转载自微信公众号「Java极客技术」,作者鸭血粉丝 。转载本文请联系Java极客技术公众号。

Hello,大家好,我是鸭血粉丝~

最近看同事的代码时候,学到了个小技巧,在某些场景下非常挺有用的,这里分享一下给大家。

Spring 中 @Autowired注解,大家应该不会陌生,用过 Spring 的肯定也离不开这个注解,通过这个注解可以帮我们自动注入我们想要的 Bean。

除了这个基本功能之外,@Autowired 还有更加强大的功能,还可以注入指定类型的数组,List/Set 集合,甚至还可以是 Map 对象。

比如说当前应用有一个支付接口 PayService,分别需要对接支付宝、微信支付、银行卡,所以分别有三个不同实现类 AliPayService,WechatPayservice,BankCardPayService。

四个类的关系如下图所示:

如果此时我需要获取当前系统类所有 PayService Bean,老的方式我们只能通过 BeanFactory或者 ApplicationContext 获取。

  1. // 首先通过 getBeanNamesForType 获取 PayService 类型所有的 Bean 
  2. String[] names = ctx.getBeanNamesForType(PayService.class); 
  3. List<PayService> anotherPayService = Lists.newArrayList(); 
  4. for (String beanName : names) { 
  5.     anotherPayService.add(ctx.getBean(beanName, PayService.class)); 
  6. // 或者通过 getBeansOfType 获取所有 PayService 类型 
  7. Map<String, PayService> beansOfType = ctx.getBeansOfType(PayService.class); 
  8. for (Map.Entry<String, PayService> entry : beansOfType.entrySet()) { 
  9.     anotherPayService.add(entry.getValue()); 

但是现在我们可以不用这么麻烦了,我们可以直接使用 @Autowired 注入 PayService Bean 数组,或者 PayService List/Set 集合,甚至,我们还可以注入 PayService 的 Map 集合。

  1. @Autowired 
  2. List<PayService> payServices; 
  3.  
  4. @Autowired 
  5. PayService[] payServicesArray; 

知道了这个功能,当我们需要使用 Spring 实现策略模式就非常简单。

可能有的小伙伴不太了解策略模式,没关系,这类阿粉介绍一个业务场景,通过这个场景给大家介绍一下策略模式。

还是上面的例子,我们当前系统需要对接微信支付、支付宝、以及银行卡支付。

当接到这个需求,我们首先需要拿到相应接口文档,分析三者的共性。

假设我们这里发现,三者模式比较类似,只是部分传参不一样。

所以我们根据三者的共性,抽象出一组公共的接口 PayService,

  1. public interface PayService { 
  2.     PayResult epay(PayRequest request); 

然后分别实现三个实现类,都继承这个接口。

那么现在问题来了,由于存在三个实现类,如何选择具体的实现类?

其实这个问题很好解决,请求参数传入一个唯一标识,然后我们根据标识选择相应的实现类。

比如说我们在请求类 PayRequest 搞个 channelNo 字段,这个代表相应支付渠道唯一标识,比如说支付宝为:00000001,微信支付为 00000002,银行卡支付为 00000003。

接着我们需要把唯一标识与具体实现类一一映射起来,刚好我们可以使用 Map 存储这种映射关系。

我们实现一个 RouteService,具体代码逻辑如下:

  1. @Service 
  2. public class RouteService { 
  3.  
  4.     @Autowired 
  5.     Map<String, PayService> payServiceMap; 
  6.  
  7.     public PayResult epay(PayRequest payRequest) { 
  8.         PayService payService = payServiceMap.get(payRequest.getChannelNo()); 
  9.         return  payService.epay(payRequest); 
  10.     } 
  11.  

我们在 RouteService 自动注入 PayService 所有相关 Bean,然后使用唯一标识查找实现类。

这样我们对外就屏蔽了支付渠道的差异,其他服务类只要调用 RouteService 即可。

但是这样实现还是有点小问题,由于我们唯一标识为一串数字,如果像我们上面直接使用 @Autowired注入 Map,这就需要我们实现类的 Bean 名字为 00000001 这些。

但是这样命名不是很优雅,这样会让后来同学很难看懂,不好维护。

所以我们需要做个转换,我们可以这么实现。

首先我们改造一下 PayService 这个接口,增加一个方法,每个具体实现类通过这个方法返回其唯一标识。

  1. public interface PayService { 
  2.  
  3.     PayResult epay(PayRequest request); 
  4.  
  5.     String channel(); 

具体举个支付宝实现类的代码,其他实现类实现类似。

  1. @Service("aliPayService"
  2. public class AliPayService implements PayService { 
  3.  
  4.     @Override 
  5.     public PayResult epay(PayRequest request) { 
  6.         // 业务逻辑 
  7.         return new PayResult(); 
  8.     } 
  9.     @Override 
  10.     public String channel() { 
  11.         return "00000001"
  12.     } 

最后我们改造一下 RouteService,具体逻辑如下:

  1. @Service 
  2. public class RouteService { 
  3.  
  4.     @Autowired 
  5.     Set<PayService> payServiceSet; 
  6.      
  7.     Map<String, PayService> payServiceMap; 
  8.  
  9.     public PayResult epay(PayRequest payRequest) { 
  10.         PayService payService = payServiceMap.get(payRequest.getChannelNo()); 
  11.         return  payService.epay(payRequest); 
  12.     } 
  13.  
  14.     @PostConstruct 
  15.     public void init() { 
  16.         for (PayService payService : payServiceSet) { 
  17.             payServiceMap = new HashMap<>(); 
  18.             payServiceMap.put(payService.channel(), payService); 
  19.         } 
  20.     } 

上面代码首先通过自动注入 PayService 一个集合,然后我们再将其转为一个 Map,这样内部存储刚好是唯一标识与实现类的映射了。

 

责任编辑:武晓燕 来源: Java极客技术
相关推荐

2021-02-01 12:18:55

策略模式Spring

2022-06-17 07:32:39

策略模式SpringBoot

2014-10-08 15:00:50

SUSE操作系统云计算

2016-03-21 11:09:52

Tableau/大数据

2010-08-02 13:55:20

2021-04-19 05:42:51

Mmap文件系统

2023-11-01 14:49:07

2020-11-02 14:38:56

Java 深度学习模型

2021-06-10 06:57:39

Redis存储数据库

2022-12-06 17:30:04

2020-09-24 06:44:54

HTTPS网站 HTTP

2020-11-27 10:34:01

HTTPHTTPS模型

2023-09-22 08:00:00

分布式锁Redis

2019-03-15 10:55:12

通信系统手机

2014-11-25 15:02:01

客服系统

2020-10-22 08:01:52

XMLJSON转换

2018-10-28 17:54:00

分布式事务数据

2019-05-27 14:03:48

开发技能代码

2021-12-30 10:55:54

Python游戏脚本

2022-01-27 14:12:49

Python游戏脚本
点赞
收藏

51CTO技术栈公众号