一、背景
在项目中需要调用外部接口,由于需要调用不同环境(生产、测试、开发)的相同接口(例如:向生、测试、开发环境的设备下发同一个 APP)。
1. 生产环境由 SpringCloud 注册中心,通过 Feign 调用,2. 其它环境直接通过 OKHttp 直接通过 Url 调用。
因此需要根据传入的环境调选择不同的调用方式。
优化前代码结构
下面以添加和删除设备接口为例(一切从简, 不代表真正业务代码):
public interface DeviceHandler {void remoteAddBatch(RemoteAddDeviceParam remoteAddDeviceParam, Integer envValue);
void remoteDeleteBatch(Integer envValue, List<String> snsList);
}
Feign 方式实现:
@Component
@Slf4j
public class DeviceHandlerFeignImpl implements DeviceHandler {
@Autowired
private DeviceFeignClient deviceFeignClient;
@Override
public void remoteAddBatch(RemoteAddDeviceParam remoteAddDeviceParam, Integer envValue) {RestResult restResult = deviceFeignClient.create(remoteAddDeviceParam);
...
}
@Override
public void remoteDeleteBatch(Integer envValue, List<String> snsList) {RestResult restResult = deviceFeignClient.deleteBySnList(snsList);
...
}
}
Url 方式实现
@Component
@Slf4j
public class DeviceHandlerUrlImpl implements DeviceHandler {
@Override
public void remoteAddBatch(RemoteAddDeviceParam remoteAddDeviceParam, Integer envValue) {String url = getAddUrlByEnvValue(envValue);
String response = OkHttpUtils.httpPostSyn(url, JSON.toJSONString(snsList), false);
RestResult restResult = JSON.parseObject(response, RestResult.class);
...
}
@Override
public void remoteDeleteBatch(Integer envValue, List<String> snsList) {String url = getDelUrlByEnvValue(envValue);
String response = OkHttpUtils.httpPostSyn(url, JSON.toJSONString(snsList), false);
RestResult restResult = JSON.parseObject(response, RestResult.class);
...
}
}
起到路由作用的 DeviceHandlerRouter(其实类似代理),选择具体调用哪种实现,对上传服务暴露的是 DeviceHandlerRouter。
@Component
public class DeviceHandlerRouter implements DeviceHandler {
...
@Autowired
private DeviceHandlerUrlImpl deviceHandlerUrlImpl;
@Autowired
private DeviceHandlerUrlImpl deviceHandlerUrlImpl;
@Override
public void remoteAddBatch(RemoteAddDeviceParam remoteAddDeviceParam, Integer envValue) {getDeviceHandler(envValue).remoteAddBatch(remoteAddDeviceParam,envValue);
}
@Override
public void remoteDeleteBatch(Integer envValue, List<String> snsList) {getDeviceHandler(envValue).remoteDeleteBatch(envValue,snsList);
}
private DeviceHandler getDeviceHandler(Integer envValue) {// 根据传入的环境返回 DeviceHandlerUrlImpl 或 DeviceHandlerUrlImpl}
}
上传服务调用 DeviceHandlerRouter 实现对设备的添加和删除操作。
存在问题
如果新增一直接口调用就需要新增实现 xxxRouter,但是代码基本上都是一样的。有没有什么方式不用写代码而提供默认实现?
动态代理提供默认实现
@Slf4j
public class DynamicProxyBeanFactory implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 各位客官别急,这里只是简单打印一下,真正的代码在下一篇
log.info("DynamicProxyBeanFactory------------------>invoke")
// 正儿八经的随便创建一个 DeviceHandlerUrlImpl
return new DeviceHandlerUrlImpl();}
public static <T> T newMapperProxy(Class<T> mapperInterface) {ClassLoader classLoader = mapperInterface.getClassLoader();
Class<?>[] interfaces = new Class[]{mapperInterface};
DynamicProxyBeanFactory proxy = new DynamicProxyBeanFactory();
return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
}
}
调用:
DeviceHandler deviceHandler = DynamicProxyBeanFactory.newMapperProxy(DeviceHandler.class);
deviceHandler.remoteAddBatch(…);
参考链接:动态代理提供接口默认实现
总结
以上我们只是抛出在实际开发中面临的问题,以及找到解决问题的第一步的方法。当然动态代理提供接口的默认实现只是演示,并没有什么实际内容。