业务背景:
前段时间我方需要与第三方对接,双方数据通过接口交互较多。最开始我方接收第三方只有简单的几个分类,我方采取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模拟请求
可以看见已经进入了断点