场景介绍

实际项目上中遇到交易使用多种支付方式,如微信原生支付,微信小程序支付,微信公众号支付,支付宝之支付等等等。我们将支付做成接口,微信支付、支付宝支付做成不同的实现类,根据需求调用不同的实现类的方法;简化多个if判断,switch case 使用。 (上述思想怎么感觉也有点像设计模式—策略模式呢,改天对比一下)

我们有两种实现思路:
1 基于@Autowired
2 基于getBeansOfType+ApplicationContextAware接口的

https://blog.csdn.net/Better000/article/details/105247431
https://blog.csdn.net/puhaiyang/article/details/86697359
上述两个链接讲解的是基于@Autowired实现策略模式;
https://www.cnblogs.com/zhoading/p/14663454.html
基于getBeansOfType的看这个链接就能会了,这里需要补充得就是applicationContextAware得作用【https://www.jianshu.com/p/4c0723615a52

简单总结:

web项目中,spring管理项目所有bean,我们可以通过 ApplicationContext appContext = new ClassPathXmlApplicationContext(“applicationContext-common.xml”);
AbcService abcService = (AbcService)appContext.getBean(“abcService”);
的方式获取容器中的bean,但是这样就会存在一个问题:因为它会重新装载applicationContext-common.xml并实例化上下文bean,如果有些线程配置类也是在这个配置文件中,那么会造成做相同工作的的线程会被启两次。一次是web容器初始化时启动,另一次是上述代码显示的实例化了一次。当于重新初始化一遍!!!!这样就产生了冗余。

解决方法

不用类似new ClassPathXmlApplicationContext()的方式,从已有的spring上下文取得已实例化的bean。通过ApplicationContextAware接口进行实现。 当一个类实现了这个接口(ApplicationContextAware)之后,这个类就可以方便获得ApplicationContext中的所有bean。 换句话说,就是这个类可以直接获取spring配置文件中,所有有引用到的bean对象。


1基于@Autowired (简单)

首先说一下spring Autowired 注解除了我们正常使用的之外还有一点:
@Autowired标注作用于 Map 类型时,如果 Map 的 key 为 String 类型,则 Spring 会将容器中所有类型符合 Map 的 value 对应的类型的 Bean 增加进来,用 Bean 的 id 或 name 作为 Map 的 key。非常牛X的操作。
需要注意的是:应用于map的时候key必须为String类型(实现类的名字)
下面是网上的例子

  1. public class x{
  2. @Autowired //@Autowired标注作用于 Map 类型时,如果 Map 的 key 为 String 类型,则 Spring 会将容器中所有类型符合 Map 的 value 对应的类型的 Bean 增加进来,用 Bean 的 id 或 name 作为 Map 的 key。非常牛X的操作。
  3. private Map<String, PayStrategy> payStategies;
  4. @RequestMapping(value = "/pay",method = RequestMethod.GET)
  5. public Object pay(int storeType, String version, int channel,.....) {
  6. PayStrategy payStrategy = null;
  7. PayChannelStrategyEnum payChannelStrategyEnum = PayChannelStrategyEnum.getPayChannelStrategyEnum(channel);
  8. if (payChannelStrategyEnum == null) {
  9. outMap.put("code", "fail");
  10. outMap.put("messge", "支付通道异常");
  11. return outMap;
  12. }
  13. for (Map.Entry<String, PayStrategy> entry : payStategies.entrySet()) { //这里是关键了,便利这个map
  14. if (payChannelStrategyEnum.getPayStrategy().equals(entry.getValue().getClass().getSimpleName())) {
  15. payStrategy = entry.getValue();
  16. }
  17. }
  18. if (payStrategy != null) {
  19. Map<String, Object> params = new ConcurrentHashMap<String, Object>();
  20. params.put("storeType",storeType);
  21. params.put("version",version);
  22. 。。。。。。
  23. Map map = payStrategy.doPay(params);
  24. return map;
  25. }
  26. }
  27. }

下面是公司pay.core的实际例子
着重看看这个就够了

/**
*注册所有策略,并提供获取具体策略实现类的方法;
*/
@Component("fundPayServiceContext")
public class FundPayServiceContext {

    @Autowired
    private final Map<String, IFundPayService> strategyMap = new ConcurrentHashMap<>(PlatformEunm.values().length);  
    //这里其实不用实例化的;只写private final Map<String, IFundPayService> strategyMap;就够了
    //这里就是自动注册了所有策略接口的实现类;

    //这里是获取具体策略的实现对象
    public IFundPayService getService(Long tenantId) { //参数是传进来的,不用纠结
        FundPayServiceEunm parkEnum = FundPayServiceEunm.valueOf(tenantId); //枚举
        if (Objects.isNull(parkEnum)) {
            parkEnum = FundPayServiceEunm.ERP_BASE_PLATFORM;//枚举
        }
        return strategyMap.get(parkEnum.getServiceName()); // 从map中取出想要的bean   枚举
    }
}

-------------------------------------------------------------------
    /*枚举   这个就是个辅助,上一个类用到这个而已*/
    public enum FundPayServiceEunm {

    ERP_BASE_PLATFORM(PlatformConstants.ERP_TENANT_ID, "fundPayService", "ERP平台"),

    MALL_PLATFORM(PlatformConstants.MALL_TENANT_ID, "mallFundPayService", "商城平台"),

    ;
     private Long tenantId;

    private String serviceName;

    private String desc;

    FundPayServiceEunm(Long tenantId, String serviceName, String desc) {
        this.tenantId = tenantId;
        this.serviceName = serviceName;
        this.desc = desc;
    }
   --------------------------------------------------------------------
       /*策略接口*/
       public interface IFundPayService {

        /** 接口方法:支付 */
        FundTransferQRVo pay(AllinPayQRCodeDto allinPayQRCodeDto) throws Exception;
   ----------------------------------------------------------------------------
     /*具体策略实现类1    erp平台*/
     @Service("fundPayService")  //本实例中根据这个value获取bean
     @Slf4j
    public class FundPayService implements IFundPayService {
    --------------------------------------------------------------------------
      /*具体策略实现类2     商城平台*/
      @Service("mallFundPayService")//本实例中根据这个value获取bean
      @Slf4j
      public class MallFundPayService implements IFundPayService {

2 基于getBeansOfType (代码多,但是可读性高)

这个就是基于spring得技术(需要配合ApplicationContextAware.java一起),相对来说代码稍微多了点, 但是可读性要好了很多!! 后续再补充吧;
https://www.cnblogs.com/zhoading/p/14663454.html
https://blog.csdn.net/qq_39237801/article/details/112299013

1 需求:

在springboot项目中,为了方便,我们可能需要获取某一个接口下面的所有实现类,根据名称进行匹配使用。

2 示例 (参照下面的实例,我写了一个demo放到了gitee上;https://gitee.com/binC_2016/getServiceImpls)

  1. 1 ```java package com.yang.config;

import com.yang.workOrder.service.IRootService; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component;

import java.util.Map;

/**

  • explain:获取应用上下文并获取相应的接口实现类 */ @Component public class ServiceLocator implements ApplicationContextAware {

    /**

    • 用于保存接口实现类名及对应的类 */ private Map map;

      /**

    • 获取应用上下文并获取相应的接口实现类
    • @param applicationContext
    • @throws BeansException */ @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { //根据接口类型返回相应的所有bean map = applicationContext.getBeansOfType(IRootService.class); //关键点 getBeansOfType }

      /**

    • 获取所有实现集合
    • @return */ public Map getMap() { return map; }

      /**

    • 获取对应服务
    • @param key
    • @return */ public IRootService getService(String key) { return map.get(key); } }

2. 接口
```java
package com.yang.workOrder.service;

import com.alibaba.fastjson.JSONObject;
import com.yang.workOrder.entity.WorkOrder;

/**
 * service层接口
 */
public interface IRootService {

    /** 开始流程*/
    boolean startProcess(WorkOrder workOrder);
}
  1. 实现1 (策略实现1) ```java package com.yang.workOrder.service.impl;

import com.alibaba.fastjson.JSONObject; import com.yang.workOrder.entity.WorkOrder; import com.yang.workOrder.service.IRootService; import org.springframework.stereotype.Service; import lombok.extern.slf4j.Slf4j;

/**

  • service层接口实现类A1 */ @Service(“A_001”) @Slf4j public class RootA001ServiceImpl implements IRootService { @Override public boolean startProcess(WorkOrder workOrder) {
     return false;
    
    } }

4. 实现2    (策略实现2)
```java
package com.yang.workOrder.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.yang.workOrder.entity.WorkOrder;
import com.yang.workOrder.service.IRootService;

import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
/**
 * service层接口实现类A2
 */
@Service("A_002")
public class RootA002ServiceImpl implements IRootService {

    @Override
    public boolean startProcess(WorkOrder workOrder) {
        return false;
    }
}
  1. 获取到了所有IRootService的实现类

image.png