上一节我们学习了如何使用jdk的URI工具类发送http请求,这一节学习一下spring框架中对于jdk的网络请求工具类的封装RestTemplate.
@RestController
public class Controller {
@GetMapping("hello")
public String hello(){
return "hello";
}
}
复制代码
首先还是接着上一节的内容。先创建一个服务端,提供上述接口。
template.getForObject("http://127.0.0.1:8763/hello",String.class);
复制代码
接着使用RestTemplate来调用该服务。可以看出,代码量上,节省了非常多。那么RestTemplate到底进行了哪些封装呢?我们从源码来探究一下。
在这儿我就以基础的getForObject方法来进行分析。
String url, Class responseType, Object... uriVariables
复制代码
getForObject方法有三个参数
参数名 | 作用 |
---|---|
url | 请求的地址 |
responseType | 返回值的类型 |
uriVariables | url里面的填充字段 |
进入方法
public T getForObject(String url, Class responseType, Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
复制代码
在该方法中有三行代码,第一行封装了一个AcceptHeaderRequestCallback对象,第二行封装了一个HttpMessageConverterExtractor对象,这两对象的作用我们可以先放一放,然后第三行就是执行请求。
所以我们进入execute方法
public T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor responseExtractor, Map uriVariables)
throws RestClientException {
URI expanded = getUriTemplateHandler().expand(url, uriVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
复制代码
在excute方法中首先做了准备工作,将我们的字符串url转化成了URI对象。这个对象正是我们上一节用以发送http请求的对象。在扩展的时候有一个expand方法,该方法的作用大致如下
Map uriVariables = new HashMap<>();
uriVariables.put("name" ,"hello");
template.getForObject("http://127.0.0.1:8080/{name}",String.class,uriVariables);
复制代码
在url中我们使用{}输入了一个占位符name,而在uriVariables这个map中我们设置了name的值为hello,经过这个方法后,http://127.0.0.1:8080/{name} 就会变成 http://127.0.0.1:8080/hello 。
下面进入doExecute方法。
ClientHttpResponse response = null;
try {
//封装一个request对象
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
//对request对象进行回调处理
requestCallback.doWithRequest(request);
}
//执行request请求
response = request.execute();
//处理返回结果
handleResponse(url, method, response);
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
复制代码
除开异常处理相关代码,doExecute主要代码如上。可以看出主要逻辑还是很清晰的。
首先创建ClientHttpRequest对象
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
ClientHttpRequest request = getRequestFactory().createRequest(url, method);
if (logger.isDebugEnabled()) {
logger.debug("HTTP " + method.name() + " " + url);
}
return request;
}
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);
}
}
复制代码
openConnection和prepareConnection这两个方法可以自己看下,看看和上一节我们自己调用URI对象有啥区别。
接下来对request对象进行回调处理
public void doWithRequest(ClientHttpRequest request) throws IOException {
if (this.responseType != null) {
List allSupportedMediaTypes = getMessageConverters().stream()
.filter(converter -> canReadResponse(this.responseType, converter))
.flatMap(this::getSupportedMediaTypes)
.distinct()
.sorted(MediaType.SPECIFICITY_COMPARATOR)
.collect(Collectors.toList());
if (logger.isDebugEnabled()) {
logger.debug("Accept=" + allSupportedMediaTypes);
}
request.getHeaders().setAccept(allSupportedMediaTypes);
}
}
复制代码
get方法的回调处理只做了一件事,就是设置请求头的"Accept"属性。
执行request请求
public final ClientHttpResponse execute() throws IOException {
//校验,确认请求未执行过
assertNotExecuted();
ClientHttpResponse result = executeInternal(this.headers);
//执行完成,修改请求状态
this.executed = true;
return result;
}
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
byte[] bytes = this.bufferedOutput.toByteArray();
if (headers.getContentLength() < 0) {
//设置请求头属性
headers.setContentLength(bytes.length);
}
ClientHttpResponse result = executeInternal(headers, bytes);
this.bufferedOutput = new ByteArrayOutputStream(0);
return result;
}
protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
//将我们设置的请求头加入connection中
addHeaders(this.connection, headers);
if (getMethod() == HttpMethod.DELETE && bufferedOutput.length == 0) {
this.connection.setDoOutput(false);
}
if (this.connection.getDoOutput() && this.outputStreaming) {
this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
}
//建立连接,发送请求
this.connection.connect();
if (this.connection.getDoOutput()) {
FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
}
else {
this.connection.getResponseCode();
}
return new SimpleClientHttpResponse(this.connection);
}
复制代码
处理返回结果
最后对结果的处理,其实猜也能猜到了,就是将返回的信息流封装成我们可以直接操作的对象,一般最常用的应该就是json转对象了吧。