RestTemplate源码分析

背景:之前restTemplate调用,json序列化的时候,出了问题,然后临时匆忙没有研究restTemplate的源码,回过头来,想看看。

debug走起。

 public static void main(String[] args) {
        RestTemplate restTemplate1 = new RestTemplate();
        String url = "http://***";
        CheckAccountSaveRpcParam checkAccountSaveRpcParam = new CheckAccountSaveRpcParam(StringUtils.genId(),
                1, LocalDateTime.now(), 1);
        ResponseEntity<String> responseEntity =
                restTemplate1.postForEntity(url, checkAccountSaveRpcParam, String.class);
        String responseBody = responseEntity.getBody();
        Result obj = JSON.parseObject(responseBody, Result.class);
        Assert.isTrue(obj.getCode() == 0, null);
    }

看RestTemplate的构造函数。可以看到,RestTemplate维护了一个List的变量messageConverters,再构造方法中,往这个list中添加了各种MessageConverter,总共有八个。

private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();

// 如果引入这个jackson包,有这两个类,则在构造方法中,就会把添加jackson的httpmessageConverter   
private static final boolean jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", RestTemplate.class.getClassLoader()) && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", RestTemplate.class.getClassLoader());
// gson同理
private static final boolean gsonPresent =
            ClassUtils.isPresent("com.google.gson.Gson", RestTemplate.class.getClassLoader());
public RestTemplate() {
        this.messageConverters.add(new ByteArrayHttpMessageConverter());
        this.messageConverters.add(new StringHttpMessageConverter());
        this.messageConverters.add(new ResourceHttpMessageConverter());
        this.messageConverters.add(new SourceHttpMessageConverter<Source>());
// 这个转换器里面其实包含了很多转换器,其中就包括MappingJackson2HttpMessageConverter转换器,但是因为它没有继承GenericHttpMessageConverter,不是通用转换器,所以不会对post的json进行转换,这个应该是对form类型的数据进行转换用的。
        this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());

        if (romePresent) {
            this.messageConverters.add(new AtomFeedHttpMessageConverter());
            this.messageConverters.add(new RssChannelHttpMessageConverter());
        }

        if (jackson2XmlPresent) {
            this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
        }
        else if (jaxb2Present) {
            this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
        }

        if (jackson2Present) {
            this.messageConverters.add(new MappingJackson2HttpMessageConverter());
        }
        else if (gsonPresent) {
            this.messageConverters.add(new GsonHttpMessageConverter());
        }
    }

发起post请求

@Override
    public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
            throws RestClientException {
// 构造一个request回调,requestCallback包含了请求数据和响应类型,
// 请求数据用HttpEntity包裹,HttpEntity又包含了header和body。
        RequestCallback requestCallback = httpEntityCallback(request, responseType);
// 构造了一个响应提取器,响应提取器包含了响应类型,以及把restTemplate
// 里的messageConvert引用过来了,所以自然可想到,请求响应最后会使用这个类来进行转换提取数据
        ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType);
        return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
    }

private HttpEntityRequestCallback(Object requestBody, Type responseType) {
            super(responseType);
            if (requestBody instanceof HttpEntity) {
                this.requestEntity = (HttpEntity<?>) requestBody;
            }
            else if (requestBody != null) {
                this.requestEntity = new HttpEntity<Object>(requestBody);
            }
            else {
                this.requestEntity = HttpEntity.EMPTY;
            }
        }
public ResponseEntityResponseExtractor(Type responseType) {
            if (responseType != null && Void.class != responseType) {
                this.delegate = new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger);
            }
            else {
                this.delegate = null;
            }
        }

@SuppressWarnings("unchecked")
    HttpMessageConverterExtractor(Type responseType, List<HttpMessageConverter<?>> messageConverters, Log logger) {
        Assert.notNull(responseType, "'responseType' must not be null");
        Assert.notEmpty(messageConverters, "'messageConverters' must not be empty");
        this.responseType = responseType;
        this.responseClass = (responseType instanceof Class) ? (Class<T>) responseType : null;
        this.messageConverters = messageConverters;
        this.logger = logger;
    }

构造了请求回调包装、响应提取器之后,继续发起请求

@Override
    public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
            ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
// 这里获取到默认的url模板处理器,然后扩展(其实就是替换)url变量,也就是说,              
// 之前restTemplate1.postForEntity(url, checkAccountSaveRpcParam, String.class)发送请求的时候,
//方法可变参数里再添加参数之后,会在这里将url的变量替换。

        URI expanded = getUriTemplateHandler().expand(url, uriVariables);
        return doExecute(expanded, method, requestCallback, responseExtractor);
    }

@Override
    public URI expand(String uriTemplate, Object... uriVariables) {
        URI url = expandInternal(uriTemplate, uriVariables);
        return insertBaseUrl(url);
    }

@Override
    protected URI expandInternal(String uriTemplate, Object... uriVariables) {
    // 打碎拆分url  
            UriComponentsBuilder uriComponentsBuilder = initUriComponentsBuilder(uriTemplate);
        // 从方法名可以看出,这是扩充变量以及编码
            UriComponents uriComponents = expandAndEncode(uriComponentsBuilder, uriVariables);
        return createUri(uriComponents);
    }


    /** 从这个javadoc注释里,可以看到,这个方法就是把url打碎拆分成一个个部分,比如scheme、host、port、path等,然后维护在UriComponentsBuilder成员变量中
     * Create a {@code UriComponentsBuilder} from the URI template string.
     * This implementation also breaks up the path into path segments depending
     * on whether {@link #setParsePath parsePath} is enabled.
     */
    protected UriComponentsBuilder initUriComponentsBuilder(String uriTemplate) {
        UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(uriTemplate);
        if (shouldParsePath() && !isStrictEncoding()) {
            List<String> pathSegments = builder.build().getPathSegments();
            builder.replacePath(null);
            for (String pathSegment : pathSegments) {
                builder.pathSegment(pathSegment);
            }
        }
        return builder;
    }

public static UriComponentsBuilder fromUriString(String uri) {
        Assert.notNull(uri, "URI must not be null");
        Matcher matcher = URI_PATTERN.matcher(uri);
        if (matcher.matches()) {
            UriComponentsBuilder builder = new UriComponentsBuilder();
            String scheme = matcher.group(2);
            String userInfo = matcher.group(5);
            String host = matcher.group(6);
            String port = matcher.group(8);
            String path = matcher.group(9);
            String query = matcher.group(11);
            String fragment = matcher.group(13);
            boolean opaque = false;
            if (StringUtils.hasLength(scheme)) {
                String rest = uri.substring(scheme.length());
                if (!rest.startsWith(":/")) {
                    opaque = true;
                }
            }
            builder.scheme(scheme);
            if (opaque) {
                String ssp = uri.substring(scheme.length()).substring(1);
                if (StringUtils.hasLength(fragment)) {
                    ssp = ssp.substring(0, ssp.length() - (fragment.length() + 1));
                }
                builder.schemeSpecificPart(ssp);
            }
            else {
                builder.userInfo(userInfo);
                builder.host(host);
                if (StringUtils.hasLength(port)) {
                    builder.port(port);
                }
                builder.path(path);
                builder.query(query);
            }
            if (StringUtils.hasText(fragment)) {
                builder.fragment(fragment);
            }
            return builder;
        }
        else {
            throw new IllegalArgumentException("[" + uri + "] is not a valid URI");
        }
    }

扩充替换变量以及编码

protected UriComponents expandAndEncode(UriComponentsBuilder builder, Object[] uriVariables) {
        if (!isStrictEncoding()) {
// 进入这里,获取到的是HierarchicalUriComponents变量
            return builder.buildAndExpand(uriVariables).encode();
        }
        else {
            Object[] encodedUriVars = new Object[uriVariables.length];
            for (int i = 0; i < uriVariables.length; i++) {
                encodedUriVars[i] = applyStrictEncoding(uriVariables[i]);
            }
            return builder.buildAndExpand(encodedUriVars);
        }
    }
public final UriComponents expand(Object... uriVariableValues) {
        Assert.notNull(uriVariableValues, "'uriVariableValues' must not be null");
// VarArgsTemplateVariables也就是维护了一个list的迭代器
        return expandInternal(new VarArgsTemplateVariables(uriVariableValues));
    }

HierarchicalUriComponents分层uri组件一层一层拆分、替换url变量。

protected HierarchicalUriComponents expandInternal(UriTemplateVariables uriVariables) {
        Assert.state(!this.encoded, "Cannot expand an already encoded UriComponents object");

        String schemeTo = expandUriComponent(getScheme(), uriVariables);
        String userInfoTo = expandUriComponent(this.userInfo, uriVariables);
        String hostTo = expandUriComponent(this.host, uriVariables);
        String portTo = expandUriComponent(this.port, uriVariables);
        PathComponent pathTo = this.path.expand(uriVariables);
        MultiValueMap<String, String> paramsTo = expandQueryParams(uriVariables);
        String fragmentTo = expandUriComponent(getFragment(), uriVariables);

        return new HierarchicalUriComponents(schemeTo, userInfoTo, hostTo, portTo,
                pathTo, paramsTo, fragmentTo, false, false);
    }
// 看着扩展url方法,可以看到就是正则匹配,然后匹配到就替换掉变量。
static String expandUriComponent(String source, UriTemplateVariables uriVariables) {
        if (source == null) {
            return null;
        }
        if (source.indexOf('{') == -1) {
            return source;
        }
        if (source.indexOf(':') != -1) {
            source = sanitizeSource(source);
        }
        Matcher matcher = NAMES_PATTERN.matcher(source);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String match = matcher.group(1);
            String variableName = getVariableName(match);
            Object variableValue = uriVariables.getValue(variableName);
            if (UriTemplateVariables.SKIP_VALUE.equals(variableValue)) {
                continue;
            }
            String variableValueString = getVariableValueAsString(variableValue);
            String replacement = Matcher.quoteReplacement(variableValueString);
            matcher.appendReplacement(sb, replacement);
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

替换了变量和编码之后,可以看到就是创建普通的URI对象。

private URI createUri(UriComponents uriComponents) {
        try {
            // Avoid further encoding (in the case of strictEncoding=true)
            return new URI(uriComponents.toUriString());
        }
        catch (URISyntaxException ex) {
            throw new IllegalStateException("Could not create URI object: " + ex.getMessage(), ex);
        }
    }

这里还会把base url添加到url前面,如果设置了的话。

@Override
    public URI expand(String uriTemplate, Object... uriVariables) {
        URI url = expandInternal(uriTemplate, uriVariables);
        return insertBaseUrl(url);
    }

url构造好了之后,就是发起请求了。

protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
            ResponseExtractor<T> responseExtractor) throws RestClientException {

        Assert.notNull(url, "'url' must not be null");
        Assert.notNull(method, "'method' must not be null");
        ClientHttpResponse response = null;
        try {
            ClientHttpRequest request = createRequest(url, method);
            if (requestCallback != null) {
                requestCallback.doWithRequest(request);
            }
            response = request.execute();
            handleResponse(url, method, response);
            if (responseExtractor != null) {
                return responseExtractor.extractData(response);
            }
            else {
                return null;
            }
        }
        catch (IOException ex) {
            String resource = url.toString();
            String query = url.getRawQuery();
            resource = (query != null ? resource.substring(0, resource.indexOf(query) - 1) : resource);
            throw new ResourceAccessException("I/O error on " + method.name() +
                    " request for \"" + resource + "\": " + ex.getMessage(), ex);
        }
        finally {
            if (response != null) {
                response.close();
            }
        }
    }
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
// 通过获取requestFactory工厂获取Request,如果,要设置http超时时间等参数,就可以通过改变这个requestFactory来达到效果
        ClientHttpRequest request = getRequestFactory().createRequest(url, method);
        if (logger.isDebugEnabled()) {
            logger.debug("Created " + method.name() + " request for \"" + url + "\"");
        }
        return request;
    }

@Override
    public ClientHttpRequestFactory getRequestFactory() {
        ClientHttpRequestFactory delegate = super.getRequestFactory();
        if (!CollectionUtils.isEmpty(getInterceptors())) {
            return new InterceptingClientHttpRequestFactory(delegate, getInterceptors());
        }
        else {
            return delegate;
        }
    }

默认的HttpRequestFactory是simpleClientHttpRequest,可以看到这个工厂里面维护了超时时间等参数。

public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {

    private static final int DEFAULT_CHUNK_SIZE = 4096;


    private Proxy proxy;

    private boolean bufferRequestBody = true;

    private int chunkSize = DEFAULT_CHUNK_SIZE;

    private int connectTimeout = -1;

    private int readTimeout = -1;
@Override
    public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
// 这个代码就比较熟悉了,开启连接
        HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
// 设置连接参数,比如连接超时时间等
        prepareConnection(connection, httpMethod.name());

        if (this.bufferRequestBody) {
            return new SimpleBufferingClientHttpRequest(connection, this.outputStreaming);
        }
        else {
            return new SimpleStreamingClientHttpRequest(connection, this.chunkSize, this.outputStreaming);
        }
    }
protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback,
            ResponseExtractor<T> responseExtractor) throws RestClientException {

        Assert.notNull(url, "'url' must not be null");
        Assert.notNull(method, "'method' must not be null");
        ClientHttpResponse response = null;
        try {
            ClientHttpRequest request = createRequest(url, method);
            if (requestCallback != null) {
    // 创建httpReques之后,这里使用requestCallback对body进行转换。
                requestCallback.doWithRequest(request);
            }
            response = request.execute();
            handleResponse(url, method, response);
            if (responseExtractor != null) {
                return responseExtractor.extractData(response);
            }
            else {
                return null;
            }
        }
        catch (IOException ex) {
            String resource = url.toString();
            String query = url.getRawQuery();
            resource = (query != null ? resource.substring(0, resource.indexOf(query) - 1) : resource);
            throw new ResourceAccessException("I/O error on " + method.name() +
                    " request for \"" + resource + "\": " + ex.getMessage(), ex);
        }
        finally {
            if (response != null) {
                response.close();
            }
        }
    }
public void doWithRequest(ClientHttpRequest httpRequest) throws IOException {
// 调用父类也就是
            super.doWithRequest(httpRequest);
        }

public void doWithRequest(ClientHttpRequest httpRequest) throws IOException {
            super.doWithRequest(httpRequest);
                        // requestEntity没有body
            if (!this.requestEntity.hasBody()) {
                。。。
            }
            else {
                Object requestBody = this.requestEntity.getBody();
                Class<?> requestBodyClass = requestBody.getClass();
                Type requestBodyType = (this.requestEntity instanceof RequestEntity ?
                        ((RequestEntity<?>)this.requestEntity).getType() : requestBodyClass);
                HttpHeaders requestHeaders = this.requestEntity.getHeaders();
                MediaType requestContentType = requestHeaders.getContentType();
// 这里可以看到,遍历转换器,查看是否可以转换
                for (HttpMessageConverter<?> messageConverter : getMessageConverters()) {
// 这个转换器是否实现了GenericHttpMessageConverter这个通用转换器接口,
                    if (messageConverter instanceof GenericHttpMessageConverter) {
                        GenericHttpMessageConverter<Object> genericMessageConverter = (GenericHttpMessageConverter<Object>) messageConverter;
                        if (genericMessageConverter.canWrite(requestBodyType, requestBodyClass, requestContentType)) {
                            if (!requestHeaders.isEmpty()) {
                                httpRequest.getHeaders().putAll(requestHeaders);
                            }
                            if (logger.isDebugEnabled()) {
                                if (requestContentType != null) {
                                    logger.debug("Writing [" + requestBody + "] as \"" + requestContentType +
                                            "\" using [" + messageConverter + "]");
                                }
                                else {
                                    logger.debug("Writing [" + requestBody + "] using [" + messageConverter + "]");
                                }

                            }
                            genericMessageConverter.write(
                                    requestBody, requestBodyType, requestContentType, httpRequest);
                            return;
                        }
                    }
// 不是通用转换器进入这里
                    else if (messageConverter.canWrite(requestBodyClass, requestContentType)) {
                        if (!requestHeaders.isEmpty()) {
                            httpRequest.getHeaders().putAll(requestHeaders);
                        }
                        if (logger.isDebugEnabled()) {
                            if (requestContentType != null) {
                                logger.debug("Writing [" + requestBody + "] as \"" + requestContentType +
                                        "\" using [" + messageConverter + "]");
                            }
                            else {
                                logger.debug("Writing [" + requestBody + "] using [" + messageConverter + "]");
                            }

                        }
                        ((HttpMessageConverter<Object>) messageConverter).write(
                                requestBody, requestContentType, httpRequest);
                        return;
                    }
                }
                String message = "Could not write request: no suitable HttpMessageConverter found for request type [" +
                        requestBodyClass.getName() + "]";
                if (requestContentType != null) {
                    message += " and content type [" + requestContentType + "]";
                }
                throw new RestClientException(message);
            }
        }
    }

摘取一个StringHttpMessageConverter看一下

@Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return supports(clazz) && canRead(mediaType);
    }
@Override
    public boolean supports(Class<?> clazz) {
        return String.class == clazz;
    }

protected boolean canRead(MediaType mediaType) {
        if (mediaType == null) {
            return true;
        }
        for (MediaType supportedMediaType : getSupportedMediaTypes()) {
            if (supportedMediaType.includes(mediaType)) {
                return true;
            }
        }
        return false;
    }

解析各种转换器支持的类型,然后防止到request的header。

            request.getHeaders().setAccept(allSupportedMediaTypes);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,706评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,002评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,462评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,375评论 0 216
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,763评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,849评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,033评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,768评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,490评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,734评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,204评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,566评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,227评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,137评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,934评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,926评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,774评论 2 274

推荐阅读更多精彩内容