本文主要对okhttp3进行封装代理,使得调用者只需要关注参数,无需关注具体实现,极大降低代码的耦合性,由于我们的项目主要跑在java的服务端,因此我们主要做的是同步的请求,当需要对接第三方平台的实话,该框架能够发挥出极大的作用,下面通过的代码的形式一步步剖析。
1.使用方法,定义一个api请求的类,并定义出接口,如下:
@ZoneMapping(url = "http://127.0.0.1:8800")
public interface Api {
@Get(path = "/pass/check/{id}")
Test get(@Query Map<String, Object> map);
@Post(path = "/pass/check")
Test post(@MultipartMap Map<String, File> fileMap);
@Header(contentType = "form-data",headMapName = "head")
@Put(path = "/pass/put/{id}")
Test put(@Query Map<String, Object> map,@PathVariable String id,Map<String,Object> head);
}
只需要如上所示就可以配置出接口的请求,面向接口进行http请求
@Query注解主要用来完成普通参数的请求,@MultipartMap标签主要实现了file类型的传输,通过map的形式,可以使用多个file一同进行传输,@Header标签需要注意一下,该标签有两个参数,一个是contentType,另外一个是其他的head参数,headMapName的值需要与方法中的map进行对应,代理后的接口会通过该名称去寻找head中的参数,并完成装配
2.调用过程及原理
@Bean
public NetAuxiliary netAuxiliary() throws ClassNotFoundException {
NetAuxiliary netAuxiliary =new NetAuxiliary();
netAuxiliary.setBasePackage("com.core.test.api");
return netAuxiliary;
}将核心类NetAuxiliary装配到ioc容器中,setBasePackage进行包扫描,创建出api接口的动态代理
/**
* 网络辅助核心类
* */
public class NetAuxiliary {
private final ConcurrentHashMap<String,Object> proxyObjMap =new ConcurrentHashMap<>();
public NetAuxiliary() {
}
public void setBasePackage(String basePackage) throws ClassNotFoundException {
List<String> classes = ReflectUtil.getClazzName(basePackage, true);
String[] beans = new String[classes.size()];
for (int i = 0; i < classes.size(); i++) {
Class<?> clsObj = Class.forName(classes.get(i));
Object obj = Proxy.createProxy(clsObj);
beans[i]=obj.getClass().getName();
proxyObjMap.put(classes.get(i),obj);
}
}
public <T> T getApi(Class<T> cls){
return (T) proxyObjMap.get(cls.getName());
}
}
public class HttpInvocationHandler implements InvocationHandler {
private ParameterSelector parameterSelector = new ParameterSelector();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//已实现的具体类
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
t.printStackTrace();
}
//接口
} else {
return run(method, args);
}
return null;
}
public Object run(Method method, Object[] args) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
ZoneMapping zoneMapping = method.getDeclaringClass().getAnnotation(ZoneMapping.class);
if (null == zoneMapping) {
throw new RuntimeException("Domain name not found,please set ZoneMapping annotation");
}
Get get = method.getAnnotation(Get.class);
Post post = method.getAnnotation(Post.class);
Put put = method.getAnnotation(Put.class);
Delete delete = method.getAnnotation(Delete.class);
if (null != get) {
return HttpUtil.getInstance().get(parsePathVariable(zoneMapping.url() + get.path(), args, method), parseQuery(args, method), (Class<? extends HttpResp>) method.getReturnType(), head(args, method));
} else if (null != post) {
return HttpUtil.getInstance().post(parsePathVariable(zoneMapping.url() + post.path(), args, method), parseQuery(args, method), parseMultipartMap(args, method), (Class<? extends HttpResp>) method.getReturnType(), head(args, method));
} else if (null != put) {
return HttpUtil.getInstance().put(parsePathVariable(zoneMapping.url() + put.path(), args, method), parseQuery(args, method), (Class<? extends HttpResp>) method.getReturnType(), head(args, method));
} else if (null != delete) {
return HttpUtil.getInstance().delete(parsePathVariable(zoneMapping.url() + delete.path(), args, method), parseQuery(args, method), (Class<? extends HttpResp>) method.getReturnType(), head(args, method));
} else {
throw new RuntimeException("Request method comment not found");
}
}
/**
* 组装head参数
*/
private Map<String, Object> head(Object[] args, Method method) {
//定义head的map
Map<String, Object> headMap = new HashMap<>();
if (args != null) {
if (!method.isAnnotationPresent(Header.class)) {
return headMap;
}
Header header = method.getAnnotation(Header.class);
String contentType = header.contentType();
if (!TextUtils.isEmpty(contentType)) {
headMap.put("Content-Type", contentType);
}
String headMapName = header.headMapName();
if (TextUtils.isEmpty(headMapName)) {
return headMap;
}
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
if (!parameter.getName().equals(headMapName)) {
continue;
}
if (!(args[i] instanceof Map)) {
throw new RuntimeException("head must be map values");
}
headMap.putAll((Map<? extends String, ?>) args[i]);
}
}
return null;
}
/**
* 查询map组装
*
* @param args :参数集合
*/
private Map<String, Object> parseQuery(Object[] args, Method method) {
if (args != null) {
for (Object o : args) {
if (null == o) {
continue;
}
Annotation[][] annotations = method.getParameterAnnotations();
for (Annotation[] annotation : annotations) {
if (annotation.length == 0) {
continue;
}
Annotation parameter = annotation[0];
if (parameter instanceof Query) {
if (o instanceof Map) {
return (Map<String, Object>) o;
}
}
}
}
}
return null;
}
/**
* 查询是否包含@PathVariable标签,使用rest方式url
*/
private String parsePathVariable(String url, Object[] args, Method method) {
if (args != null) {
for (Object o : args) {
if (null == o) {
continue;
}
Annotation[][] annotations = method.getParameterAnnotations();
for (Annotation[] annotation : annotations) {
if (annotation.length == 0) {
continue;
}
Annotation parameter = annotation[0];
if (parameter instanceof PathVariable) {
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
Parameter parameter1 = parameters[i];
if (null != parameter1.getAnnotation(PathVariable.class)) {
url = url.replace("{" + parameter1.getName() + "}", args[i].toString());
}
}
}
}
}
}
return url;
}
/**
* 查询map组装Map<String,File>
*
* @param args :参数集合
*/
private Map<String, File> parseMultipartMap(Object[] args, Method method) {
if (args != null) {
for (Object o : args) {
if (null == o) {
continue;
}
Annotation[][] annotations = method.getParameterAnnotations();
for (Annotation[] annotation : annotations) {
if (annotation.length == 0) {
continue;
}
Annotation parameter = annotation[0];
if (parameter instanceof MultipartMap) {
if (o instanceof Map) {
return (Map<String, File>) o;
}
}
}
}
}
return null;
}
}
3.代码调用
Map<String, File> fileMap =new HashMap<>();
fileMap.put("rootImage",new File("/Users/gfh/Downloads/fj.jpeg"));
Test test= IocBeanUtil.getBean(NetAuxiliary.class).getApi(Api.class).post(fileMap);
其中Test类需要继承并实现
public class Test extends HttpResp implements HttpListener {
private String msg;
private String code;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public String toString() {
return "Test{" +
"msg='" + msg + '\'' +
", code='" + code + '\'' +
", isSuccess=" + isSuccess() +
", statusCode=" + statusCode +
", httpErrorMsg='" + httpErrorMsg + '\'' +
'}';
}
@Override
public boolean isSuccess() {
if(isSuccessful()&&"0".equals(code)){
return true;
}
return false;
}
}
实现HttpListener后,可以自定义成功失败的状态,即isSuccess方法,后续将把源码上传至git,感兴趣的同学可以关注一下,github地址:
GitHub - sgfh/NetAuxiliary: 网络请求辅助