• RestTemplate源码debug:可变形参引发的问题


    今天在做一个跨域请求的时候遇到一个问题,我需要同时从JDK7版本的项目接口中取出设置的请求头和响应的二进制流数据,按着源码分析了半天,把API倒是玩明白了,最后发现是因为没有注意可变形参引发的问题。。因为之前没有用过这个API,所以出这个问题的时候也没往这方面想。。。记录一下吧

    1.环境模拟

    1.1接口提供

    提供一个接口,设置一个请求头给response,把图片二进制流写入response

    @GetMapping("/pic1")
    public void getPic(HttpServletRequest request,HttpServletResponse response) throws IOException {
        response.setHeader("keyy","valuee");
        System.out.println(response.getHeader("keyy"));
        File file = new File("C:\\Users\\zhangjiahao\\Desktop\\变更图片\\班级活动.jpg");
        FileInputStream fis = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(fis);
        byte[] buf = new byte[2048];
        ServletOutputStream os = response.getOutputStream();
        BufferedOutputStream bos = new BufferedOutputStream(os);
        while(bis.read(buf) != -1){
            bos.write(buf);
        }
        bos.flush();
        bos.close();
        bis.close();
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    1.2工具类(2个)

    前提须知:

    • getForEntity() exchange()本质上都是调用的excute()
      返回值都是ResponseEntity
      产生的问题是,这个ResponseEntity最后在getBody()的时候,一个能读取到二进制流,一个读取不到

    1.2.1使用getForEntity()

    public static String getStreamAndHeader(String url,String headKey,HttpServletResponse response) {
        RestTemplate restTemplate = getSingleRestTemplate();
        ResponseEntity responseEntity = restTemplate.getForEntity(url, null, byte[].class, (Object) null);
    
        byte[] res = responseEntity.getBody();//获取不到。res=null
        HttpHeaders headers = responseEntity.getHeaders();
        String      value   = headers.getFirst(headKey);
        try {
            ServletOutputStream os = response.getOutputStream();
            os.write(res);
            os.flush();
            os.close();
        }
        catch (IOException e) {
            logger.error("请求接口二进制流写入response时异常{}",e);
        }
    
        return value;
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    1.2.2使用exchange()

    public static void getStreamWithNoParam(String url, HttpMethod method, HttpServletResponse response) {
        RestTemplate restTemplate = getSingleRestTemplate();
        try {
            ResponseEntity exchange = restTemplate.exchange(url, method, null, byte[].class, (Object) null);
    
    
            byte[]                 body     = exchange.getBody();//正常获取
            String value = exchange.getHeaders().getFirst("keyy");
            response.setHeader("keyy",value);
            response.getOutputStream().write(body);
        }
        catch (IOException e) {
            logger.error("RestTemplateUtils获取接口二进制流异常");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    2.源码分析思路

    2.1方法本身区别

    调用exchange()的时候,内部调用的方法是需要携带请求参数的,不过可以设为null

    getForEntity
    exchange
    两个方法本身的区别就是:requestEntity,即请求参数

    2.2分别看两个内部的方法

    2.2.1 exchange-带参的:httpEntityCallback

    到目前为止没有看到对返回值response进行操作的内容

    2.2.2getFroEntity-无参:acceptHeaderRequestCallback


    3.到此没有分析出异常,则看request回调相关的execute

    3.1分析理由

    因为两个方法的区别就是在于回调RequestCallback,本身分析不出来问题,就去找其他RequestCallback相关的即execute()执行方法本身

    3.2doExecute()

    doWithRequest()对于这两个方法来说,其实都是会执行的


    debug的时候能正常进去

    虽然这里没有指定requestEntity

    但是内部造了一个requestCallBack,和requestEntity没有关系

    4.定位到doWithRequest()

    这两个实现类其实是父子类关系,都会调用(内部用的super)两个实现类

    4.1 debug 发现支持的媒体类型

    发现在使用getForEntity()的时候,走到这里是false,因此没有指定返回值的类型

    而正常正确的应该是这样

    4.2回去看参数是否正确

    发现这里给responseType指定为了null,那么问题来了,为什么参数写错了但是没提示?


    这是因为最后最后一个形参是Object... uriVariables,这种形参放在最后一位就支持这种很多逗号的写法

    5.修改参数

    然后就跑通了

    6.结论

    其实这两个方法本质只有“是否携带请求参数”的区别,其他的所有东西都一样,执行的过程一样,返回值一样,返回值的使用方式一样。
    唯一值得需要注意的是,以后遇到这种最后一个形参是Object... uriVariables的,就要对形参的个数比较敏感一点

  • 相关阅读:
    MyBatis:缓存机制详解
    el-autocomplete 必填校验问题
    Redis_哨兵模式
    Recommended Azure Monitors
    ​力扣解法汇总808. 分汤
    Redis 常见问题
    MATLAB向量的运算
    【2023,学点儿新Java-48】变量与运算符 (阶段性复习):关键字和保留,回顾:标识符的命名规则,变量的基本使用
    软件设计23种设计模式
    最优性减枝
  • 原文地址:https://blog.csdn.net/m0_56079407/article/details/126052474