业务背景:

前段时间我方需要与第三方对接,双方数据通过接口交互较多。最开始我方接收第三方只有简单的几个分类,我方采取switch case进行解决。

但是,由于业务的深耕,导致用户需求的不断细化,从而让对接的分类越来越多,每次都需要在switch里面去添加,整个case已经越来越长。

为了解决日益增多的接口分类以及不确定的接口分类,需要用一种方式进行处理。由于我方与对方约定,我方需提供一个统一的接口给对方,那么在此基础上,能选择的就是利用SpringBoot的特性结合一点点设计模式,对我方的代码进行优化。

想要达到的效果:

1、必须兼容以前的实现,各个模块改动小;

2、各个子模块使用方便,调用方便,学习成本低;

3、具有一定的扩展性

具体实现方式一:

自定义注解类:

package net.dlet.system.annotation;

import org.springframework.context.annotation.Configuration;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Copyright(c) dlet.net, 2020.  All rights reserved. .
 *
 * @description: 自定义注解接口,用以标识特殊的需要启动扫描的自定义类
 * @author: crue
 * @createTime: 2020/7/23 21:28
 * @version: 1.0
 */
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration
public @interface NetDletBootScan {
}
package net.dlet.system.io;

import com.alibaba.fastjson.JSONObject;

/**
 * Copyright(c) dlet.net, 2020.  All rights reserved. .
 *
 * @description: 第三方对接接口类
 * @author: crue
 * @createTime: 2020/7/23 22:20
 * @version: 1.0
 */
public interface IPushTypeManage {
    /**
     * 设置接收类型
     */
    void setType();

    /**
     * 具体处理
     * @param jsonData 数据流
     * @param mCode
     */
    String deal(JSONObject jsonData, String mCode);
}
package net.dlet.system.process;

import net.dlet.system.io.IPushTypeManage;

/**
 * Copyright(c) dlet.net, 2020.  All rights reserved. .
 *
 * @description: 第三方对接抽象层
 * @author: crue
 * @createTime: 2020/7/23 22:28
 * @version: 1.0
 */
public abstract class AbstractResponseThirdpartManage implements IPushTypeManage {

    /**
     * 类型
     */
    private String type;

    /**
     * 默认构造
     */
    public AbstractResponseThirdpartManage() {
        setType();
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}
package net.dlet.system.component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import lombok.SneakyThrows;
import net.dlet.system.process.AbstractResponseThirdpartManage;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

/**
 * Copyright(c) dlet.net, 2020.  All rights reserved. .
 *
 * @description: 查找所有实现了AbstractResponseThirdpartManage的类
 * @author: crue
 * @createTime: 2020/7/23 22:00
 * @version: 1.0
 */
@Component
public class NetDletThirdpartyMangFactoryComp implements BeanPostProcessor {

    /**
     * 接收类型map
     */
    private static Map<String, Object> responseMangFac = new ConcurrentHashMap<>();

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
        throws BeansException {
        return bean;
    }

    /**
     * 在具体子类初始化之后 确认该对象是否是对应父类的子类
     */
    @SneakyThrows
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
        throws BeansException {

        if (bean instanceof AbstractResponseThirdpartManage) {
            AbstractResponseThirdpartManage manageFactory = (AbstractResponseThirdpartManage) bean;
            responseMangFac.put(manageFactory.getType(), bean);
        }

        return bean;
    }

    /**
     * 根据类型得到具体的类
     * @param type
     * @return
     */
    public static Object getBeanFromTypeMangFactory(String type) {
        return responseMangFac.get(type);
    }
}
package net.dlet.system.process;

import net.dlet.system.component.NetDletThirdpartyMangFactoryComp;

/**
 * Copyright(c) dlet.net, 2020.  All rights reserved. .
 *
 * @description: 获取具体的处理类,暴露给外面调用
 * @author: crue
 * @createTime: 2020/7/23 23:45
 * @version: 1.0
 */
public class FactoryProducer {

    /**
     * 得到具体处理的类
     *
     * @param type 推送类型
     */
    public static AbstractResponseThirdpartManage getFactoryInstance(String type) {
        AbstractResponseThirdpartManage manageFactory = (AbstractResponseThirdpartManage) NetDletThirdpartyMangFactoryComp.getBeanFromTypeMangFactory(type);

        return manageFactory;
    }
}

使用方法:

1、定义公共接收接口以及service层

/**
 * 在公共模块定义controller,接收信息
 */
package net.dlet.system.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.servlet.http.HttpServletRequest;

import net.dlet.common.core.domain.AjaxResult;
import net.dlet.system.service.thrid.ResponseThirdpartyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * Copyright(c) dlet.net, 2020.  All rights reserved. .
 *
 * @description: 对接第三方平台
 * @author: crue
 * @createTime: 2020/8/31 23:20
 * @version: 1.0
 */
@Api("对接第三方平台")
@RequestMapping(value = "/api/third")
@RestController
public class ResponseThirdpartyController {

    @Autowired
    private ResponseThirdpartyService thirdpartyService ;

    @ApiOperation(value = "对接第三方平台", httpMethod = "POST", response = AjaxResult.class)
    @RequestMapping(value = "receive", method = RequestMethod.POST)
    @ResponseBody
    public String receiveData(HttpServletRequest request) {
        return thirdpartyService.receiveData(request);
    }
}
package net.dlet.system.service.thrid;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;

import net.dlet.common.utils.StreamUtils;
import net.dlet.system.process.FactoryProducer;
import org.springframework.stereotype.Service;

/**
 * Copyright(c) dlet.net, 2020.  All rights reserved. .
 * 公共模块定义service
 * @description: 处理对三方推送
 * @author: crue
 * @createTime: 2020/7/23 23:20
 * @version: 1.0
 */
@Service
public class ResponseThirdpartyService {
    private static final String HEADER_MCODE = "mdata";

    public String receiveData(HttpServletRequest request) {
        String mCode = "";

        try {
            //--------------------根据自己功能具体实现 start-------------------------
            // 取Body数据 读出数据流
            ServletInputStream inputStream = request.getInputStream();
            byte[] buffer = StreamUtils.readInputStream(inputStream);

            String receive = new String(buffer, "utf-8");
            JSONObject receiveJson = JSON.parseObject(receive);
            JSONObject receiveHead = JSON.parseArray(receiveJson.getString("head")).getJSONObject(0);
            mCode = receiveHead.getString(HEADER_MCODE);
            JSONObject receiveBody = JSON.parseArray(receiveJson.getString("body")).getJSONObject(0);
            //--------------------根据自己功能具体实现 end-------------------------
            
            return FactoryProducer.getFactoryInstance(mCode).deal(receiveBody, mCode);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return "";
    }
}

2、定义具体的实现类,继承自 AbstractResponseThirdpartManage ,并添加NetDletBootScan注解

至少写两个demo类,看看是否能正常发现bean,如果不行,那么需要自定义Register与BeanDefinitionScanner。

测试效果如下:

子模块示例

package net.dlet.web.service.demo;

import com.alibaba.fastjson.JSONObject;
import net.dlet.system.annotation.NetDletBootScan;
import net.dlet.system.process.AbstractResponseThirdpartManage;

/**
 * Copyright(c) dlet.net, 2021.  All rights reserved. .
 *
 * @description: 接收第三方推送消息示例
 * @author: crue
 * @createTime: 2021/8/31 22:10
 * @version: 1.0
 */
@NetDletBootScan
public class ResponseThirdpartyServiceDemo extends AbstractResponseThirdpartManage {

    private final static String TEST_TYPE = "test";

    @Override
    public void setType() {
        //注意类型管理要做好,不能重复,可以是大类
        this.setType(TEST_TYPE);
    }

    @Override
    public String deal(JSONObject jsonData, String mCode) {
        return null;
    }
}

3、所用到的工具类 

package net.dlet.common.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * Copyright(c) dlet.net, 2020.  All rights reserved. .
 *
 * @description: 读取流的工具类
 * @author: crue
 * @createTime: 2019/09/07 23:45
 * @version: 1.0
 */
public class StreamUtils {
    public static byte[] readInputStream(InputStream inStream) throws IOException {
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];

        int len;
        while((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }

        outSteam.close();
        inStream.close();
        return outSteam.toByteArray();
    }
}

效果:

postman模拟请求

统一接入服务 架构图 统一对接_统一接口

可以看见已经进入了断点

统一接入服务 架构图 统一对接_统一接入服务 架构图_02

统一接入服务 架构图 统一对接_接收数据_03

统一接入服务 架构图 统一对接_接收数据_04