RestTemplate的使用 转换带ParameterType/参数类型/泛型的处理

探索过程,

以前是用 apache 阿帕奇的 HttpClient 

然后同事提到了 Spring 的RestTemplate 比较方便 

后来查这方面的资料也是确实如此,看名字(xxTemplate)也知道使用了模板设计模式,简化了重复的代码的编写,不用每次都将体转换成流再发到请求execute方法,也不用每次都在finally里面 close 所有资源

 

了解了RestTemplate的使用,查看了源码,发现里面的所有方法最后都是执行execute,针对简单的rest请求可以用封装好的template方法(get/post/put/deleteForEntity/Object)

对于复杂的请求(需要自定义请求头,返回类型带泛型)最好还是自行重新用exchange方法封装一次

例如这样:

这里省略了请求异常的处理(其实是自己也还没研究怎么自定义4开头和5开头的状态返回的处理方法。。。哈哈)先暂时用Spring默认的异常处理

public <T> T get(String url, Class<T> respClass, Map<String, Object> urlParams){
    return restTemplate.exchange(
        url,
        HttpMethod.GET,
        null, // HttpEntity 如果用默认头可以置空,自定义头可以 new HttpEntity(new HttpHeaders())
        respClass,
        urlParams == null ? new HashMap() : urlParams
    )
}

public <T> T post(String url, Class<T>,HttpEntity httpEntity respClass){
    return restTemplate.exchange(
        url,
        HttpMethod.POST,
        httpEntity , 
        respClass        
    )
}

但是发现对接的接口的返回类型都是同样的结构 code msg data

就想到用带类型参数的类 (泛型类) 来统一处理请求的响应体,

Bean:BaseResp 

public class BaseResp<T> {
    
    private int code;
    private String msg;
    private T data;

//省略 get set 方法。。。

}

但是如果在调用自己封装方法,直接传入 BaseResp.class 就会报错

LinkedHashMap can not cast to BaseResp

因为我传入的class没有指定对应的<T> (不可以BaseResp<RespBody>.class,编译器报错),对于带有泛型的data属性 exchange 方法无法解析将转换成什么类型,就会转换成默认的LinkedHashMap了

查了网络资源

然后看exchange的实现方法 (这里安利一下idea的ctrl+p)

发现处了传class 还可以传type

网络上查了很多资料,调试了很多次。。。

有这个方法 

尝试1:

new ParameterizedTypeReference<T>(){};

但是T还是T,却不是我实例化的RespBody

也试过用反射(又是一番学习,还好年前的这段时间没有什么任务可以随意划水)

反射出传入的respClass的Type类型

public <T> T getHttpResp2(String url, Class<T> clazz){     
        Method thisMethod = null;
        Method[] methods = this.getClass().getMethods();
        for (Method method : methods) {
            if(method.getName().equals("getHttpResp2")){
                thisMethod = method;
                break;
            }
        }
        Type[] genericParameterTypes = thisMethod.getGenericParameterTypes(); // String url,Class<T> clazz
        Type[] actualTypeArguments = ((ParameterizedType) genericParameterTypes[1]).getActualTypeArguments(); // T
        Type ttype = actualTypeArguments[0]; // T.class

        ParameterizedTypeReference<T> objectParameterizedTypeReference = ParameterizedTypeReference.forType(ttype);

...
...
}

然后用

尝试2:

ParameterizedTypeReference.forType(ttype);

还是不行

然后看到Class类是Type类的实现(网上有人直接cast  (Class)Type)

所以尝试直接传class到forType里面(这时候发现之前的反射真是饶了一大圈,也算是重新学习了泛型的反射吧,class好像是不能直接穿换成type)

 

有用过这样的方法

尝试3:

ParameterizedTypeReference.forType(respClass);

但是返回的typeReference还是能反射到我实例化的泛型的类型

 

上面三种尝试都是不能解析成我想要的RespBody

都是报错 LinkedHashMap can not cast to 

 

最后突然想起看到过exchange的源码,里面也是要一个具体的Type才可以解释

exchange的源码

 

就想到我的方法是不是过度封装了,最后换成这样子

最终方法实现:

public <T> T post(String url, @NotNull HttpEntity requestEntity, ParameterizedTypeReference<T> parameterizedTypeReference){

        ResponseEntity<T> responseEntity = restTemplate.exchange(
                url,
                HttpMethod.POST,
                requestEntity,
                parameterizedTypeReference
        );
        return responseEntity.getBody();
    }

让调用者先指定好了type

方法调用:

ParameterizedTypeReference<TestModelType<List<TestModelBody>>> parameterizedTypeReference = 
    new ParameterizedTypeReference<TestModelType<List<TestModelBody>>>() {};
TestModelType<List<TestModelBody>> listTestModelType = 
    restTemplateUtils.get(testUrl, parameterizedTypeReference);

再传入我行封装好的方法。

估计即使在实例化的时候指定了泛型的类型,java底层虚拟机也是不能反射出的吧。不然Spring也不用提供ParameterizedTypeReference这个方法了。

同样阿里的json工具也是有个这个方法

 


import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;

String jsonString = jsonStringEntity.getBody();
T body = JSONObject.parseObject(jsonString, clazz); // 直接传Class不能确定泛型
JSONObject.parseObject(
    jsonString, 
    new TypeReference<TestModelType<List<TestModelBody>>>(){}
);

 

第一次写博客,年前这个星期真实划水划到没力了。

放假前来一编划水一周的总结

 

总结:

对有泛型类需求的处理

不能过度封装直接传入一个class

需要用ParameterizedTypeReference来指定具体的泛型

后续:

// TODO    

年后再研究一下怎么处理自定义请求异常

 

参考:

你真的了解Java泛型参数? :  https://blog.csdn.net/u012881904/article/details/80813294

Spring RestTemplate详解 : https://www.cnblogs.com/caolei1108/p/6169950.html

T和Class以及Class的理解 : https://blog.csdn.net/witewater/article/details/53462385

RestTemplate中使用ParameterizedTypeReference参数化类型支持泛型,主要是List https://www.jianshu.com/p/886922facc5f

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值