目录
一、背景
二、分析
三、编码
InterfaceDescriptor
ServiceProviderInterface
ServiceProviderInterfaceClient
ServiceProviderInterfaceClientImpl
四、测试
SendSmsRequest
SendSmsResult
SendSmsProvider
TestController
五、总结
一、背景
一般我们的项目中,肯定少不了调第三方的http接口,比如说:调阿里云短信接口机发送短信,调邮件接口发送邮件,获取用户的某些数据,向第三方上报数据等等。
随着我们需要调的接口逐渐增多,代码将会越来越乱,不好好整理的话对于每个接口的维护成本将会增加。
下面我们就来优雅的去调用第三方接口,写出好维护、可扩展的代码。
二、分析
调用http接口一般就分为三步:
1、组装参数
2、按照接口 URl 和 调用方式,使用 httpClient 工具类去调用
3、处理响应返回
三、编码
InterfaceDescriptor
定义注解 InterfaceDescriptor,这里定义 接口地址 和 请求方式,是给业务调用方用的,用于标识这个业务需要调的接口地址和方式是什么
package com.maple.client.third;import java.lang.annotation.*;@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface InterfaceDescriptor {String interfaceUrl();String interfaceMethod();
}
ServiceProviderInterface
定义公共接口 ServiceProviderInterface,每次调用都需要组装请求和处理响应,所以我们这里定义公共方法,这里是给业务调用方实现的,因为每个业务所需要的 入参 和 响应 不同
package com.maple.client.third;import com.alibaba.fastjson.JSONObject;public interface ServiceProviderInterface<REQ, RES> {/*** 构建请求** @param request* @return*/JSONObject buildRequest(final REQ request);/*** 处理响应** @param jsonObject* @return*/RES processResponse(final JSONObject jsonObject);
}
ServiceProviderInterfaceClient
定义 ServiceProviderInterfaceClient,这个也是暴露给业务调用方的统一的接口,这个接口的请求参数是 上面的 ServiceProviderInterface 和 request
package com.maple.client.third;public interface ServiceProviderInterfaceClient {<REQ, RES> RES invoke(ServiceProviderInterface<REQ, RES> serviceProviderInterface, REQ request);
}
ServiceProviderInterfaceClientImpl
定义 ServiceProviderInterfaceClientImpl,是 ServiceProviderInterfaceClient 接口的实现类,这个实现类是我们具体的内部处理,而这个ServiceProviderInterface 接口的里包含了很多的数据可供我们获取到,实现类里的主要逻辑就是:
1、从注解里获取到 接口地址 和 请求方式
2、然后根据不同的请求方式去使用不同的httpClient工具类
3、通过 接口地址 和 请求参数 去做真实的调用,调用完成之后再去处理响应
package com.maple.client.third;import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Preconditions;
import org.springframework.stereotype.Component;@Component
public class ServiceProviderInterfaceClientImpl implements ServiceProviderInterfaceClient {@Overridepublic <REQ, RES> RES invoke(ServiceProviderInterface<REQ, RES> serviceProviderInterface, REQ request) {Preconditions.checkNotNull(serviceProviderInterface, "interface cannot be null");Preconditions.checkNotNull(request, "request cannot be null");String interfaceUrl, interfaceMethod;InterfaceDescriptor interfaceDescriptor = serviceProviderInterface.getClass().getAnnotation(InterfaceDescriptor.class);Preconditions.checkNotNull(interfaceDescriptor, "【interface】missing interface descriptor on ServiceProviderInterface class, class=" + getClass());interfaceUrl = interfaceDescriptor.interfaceUrl();interfaceMethod = interfaceDescriptor.interfaceMethod();RES result = null;try {result = doInvoke(interfaceUrl, interfaceMethod, serviceProviderInterface, request);return result;} catch (Exception e) {System.out.println("【interface】" + interfaceUrl + " invoke failed, error=" + e.getMessage());throw new RuntimeException("【interface】" + interfaceUrl + " 接口不可用");} finally {System.out.println("【interface】" + interfaceUrl + " invoke finished, result=" + result);}}private <RES, REQ> RES doInvoke(String interfaceUrl, String interfaceMethod, ServiceProviderInterface<REQ, RES> serviceProviderInterface, REQ request) {Preconditions.checkNotNull(interfaceUrl, "interface cannot be null");Preconditions.checkNotNull(interfaceMethod, "request cannot be null");//组装请求参数JSONObject requestJson = serviceProviderInterface.buildRequest(request);JSONObject result = new JSONObject();if ("GET".equalsIgnoreCase(interfaceMethod)) {System.out.println("GET方式调用http接口,入参为:" + requestJson);result.put("result", "GET方式调用http接口成功");} else if ("POST".equalsIgnoreCase(interfaceMethod)) {System.out.println("POST方式调用http接口,入参为:" + requestJson);result.put("result", "POST方式调用http接口成功");} else {throw new RuntimeException("【interface】" + interfaceUrl + ", " + interfaceMethod + "方式不支持http调用");}RES res = serviceProviderInterface.processResponse(result);return res;}
}
四、测试
我们这里就以调接口发送短信为例
SendSmsRequest
定义入参 SendSmsRequest
package com.maple.client.third.request;import java.io.Serializable;public class SendSmsRequest implements Serializable {private String mobile;private String content;public String getMobile() {return mobile;}public void setMobile(String mobile) {this.mobile = mobile;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}@Overridepublic String toString() {return "SendSmsRequest{" +"mobile='" + mobile + '\'' +", content='" + content + '\'' +'}';}
}
SendSmsResult
定义出参 SendSmsResult
package com.maple.client.third.result;import java.io.Serializable;public class SendSmsResult implements Serializable {private String status;public String getStatus() {return status;}public void setStatus(String status) {this.status = status;}@Overridepublic String toString() {return "SendSmsResult{" +"status='" + status + '\'' +'}';}
}
SendSmsProvider
定义发送短信核心类 SendSmsProvider
package com.maple.client.third.provider;import com.alibaba.fastjson.JSONObject;
import com.maple.client.third.InterfaceDescriptor;
import com.maple.client.third.ServiceProviderInterface;
import com.maple.client.third.request.SendSmsRequest;
import com.maple.client.third.result.SendSmsResult;
import org.springframework.stereotype.Component;@Component
@InterfaceDescriptor(interfaceUrl = "http://yunpian/send/sms", interfaceMethod = "POST")
public class SendSmsProvider implements ServiceProviderInterface<SendSmsRequest, SendSmsResult> {@Overridepublic JSONObject buildRequest(SendSmsRequest request) {JSONObject jsonObject = new JSONObject();jsonObject.put("mobile", request.getMobile());jsonObject.put("content", request.getContent());return jsonObject;}@Overridepublic SendSmsResult processResponse(JSONObject jsonObject) {SendSmsResult result = new SendSmsResult();String status = jsonObject.getString("status");result.setStatus(status);return result;}
}
TestController
定义 测试类 TestController
package com.maple.client.controller;import com.maple.client.third.ServiceProviderInterfaceClient;
import com.maple.client.third.provider.SendSmsProvider;
import com.maple.client.third.request.SendSmsRequest;
import com.maple.client.third.result.SendSmsResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestController
public class TestController {@Resourceprivate ServiceProviderInterfaceClient serviceProviderInterfaceClient;@Resourceprivate SendSmsProvider sendSmsProvider;@GetMapping("/sendSms")public SendSmsResult sendSms() {SendSmsRequest sendSmsRequest = new SendSmsRequest();sendSmsRequest.setMobile("123");sendSmsRequest.setContent("写代码");SendSmsResult result = serviceProviderInterfaceClient.invoke(sendSmsProvider, sendSmsRequest);return result;}
}

五、总结
通过以上的编码,我们的思路就很清晰了,接入方只需要实现 ServiceProviderInterfaceClient 接口,不需要关注具体调用的细节方面;
只需要编写类似 SendSmsProvider的类 ,关注于自己的业务(接口地址,调用方式,接口入参,接口响应)即可,思路更加的清晰,代码更好的维护和扩展。


![[SpringBoot系列]项目测试](https://img-blog.csdnimg.cn/cf9864ce05654078bd3961c98e1d283f.png)












