RestTemplate是Spring5默认的http请求工具,从Spring6开始默认http请求工具变成了WebClient。
首先,在RestTemplate调用处打上断点
这里以postForEntity方法为入口跟踪。F7进入
一眼望去,execute肯定是关键代码,继续跟进
do开头的一定是核心逻辑(Spring都是这个风格)
这个方法里,当调用request.execute()后就获得了response,说明调用的具体方法在execute()方法里。其中createRequest方法是创建http调用的客户端方法,RestTemplate只是一个壳,也就是Spring封装的一个工具而已,底层实现还是以第三方的为主,例如:HttpClient、OKHttp等,本项目中使用的事HttpClient。具体创建过程这里略过,贴一下创建的代码。如下:
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { HttpClient client = getHttpClient(); HttpUriRequest httpRequest = createHttpUriRequest(httpMethod, uri); postProcessHttpRequest(httpRequest); HttpContext context = createHttpContext(httpMethod, uri); if (context == null) { context = HttpClientContext.create(); } // Request configuration not set in the context if (context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) { // Use request configuration given by the user, when available RequestConfig config = null; if (httpRequest instanceof Configurable) { config = ((Configurable) httpRequest).getConfig(); } if (config == null) { config = createRequestConfig(client); } if (config != null) { context.setAttribute(HttpClientContext.REQUEST_CONFIG, config); } } if (this.bufferRequestBody) { return new HttpComponentsClientHttpRequest(client, httpRequest, context); } else { return new HttpComponentsStreamingClientHttpRequest(client, httpRequest, context); } }
另外,requestCallback.doWithRequest方法是完善请求内容的,例如请求头处理,最重要的事设置消息处理器。代码如下:
public void doWithRequest(ClientHttpRequest httpRequest) throws IOException { super.doWithRequest(httpRequest); Object requestBody = this.requestEntity.getBody(); if (requestBody == null) { HttpHeaders httpHeaders = httpRequest.getHeaders(); HttpHeaders requestHeaders = this.requestEntity.getHeaders(); if (!requestHeaders.isEmpty()) { requestHeaders.forEach((key, values) -> httpHeaders.put(key, new LinkedList<>(values))); } if (httpHeaders.getContentLength() < 0) { httpHeaders.setContentLength(0L); } } else { Class<?> requestBodyClass = requestBody.getClass(); Type requestBodyType = (this.requestEntity instanceof RequestEntity ? ((RequestEntity<?>)this.requestEntity).getType() : requestBodyClass); HttpHeaders httpHeaders = httpRequest.getHeaders(); HttpHeaders requestHeaders = this.requestEntity.getHeaders(); MediaType requestContentType = requestHeaders.getContentType(); for (HttpMessageConverter<?> messageConverter : getMessageConverters()) { if (messageConverter instanceof GenericHttpMessageConverter) { GenericHttpMessageConverter<Object> genericConverter = (GenericHttpMessageConverter<Object>) messageConverter; if (genericConverter.canWrite(requestBodyType, requestBodyClass, requestContentType)) { if (!requestHeaders.isEmpty()) { requestHeaders.forEach((key, values) -> httpHeaders.put(key, new LinkedList<>(values))); } if (logger.isDebugEnabled()) { if (requestContentType != null) { logger.debug("Writing [" + requestBody + "] as \"" + requestContentType + "\" using [" + messageConverter + "]"); } else { logger.debug("Writing [" + requestBody + "] using [" + messageConverter + "]"); } } genericConverter.write(requestBody, requestBodyType, requestContentType, httpRequest); return; } } else if (messageConverter.canWrite(requestBodyClass, requestContentType)) { if (!requestHeaders.isEmpty()) { requestHeaders.forEach((key, values) -> httpHeaders.put(key, new LinkedList<>(values))); } 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); } }
回到request.execute()方法中:
此时入参已经被转换成了字节,
又看到了熟悉的do开头的方法
这时方法已经进入到了httpClient包内了,说明开始调用第三方框架了。继续跟进这个方法
看到了pool终于见到了连接池,
pool里有很多属性,比如:maxTotal 、defaultMaxPerRoute等这是项目配置的参数。注意看这里有个routeToPool,这个路径和连接池的对照。也就是你要放某个ip地址,这个地址会生成一个连接池,他们是共享maxTotal这个参数,这里http连接池和数据库连接池还是有区别。数据库连接池是有初始化连接的,但是这里没有,因为http请求在初始化时并不能完全知道要调用哪个地址也就无法完成初始化,所以这里只能根据具体的请求来初始化routeToPool这个连接池,在http连接池里你是看不到初始连接这个参数的。leased、available、pending这几个是连接池的核心参数