转载

SpringCloud学习之-使用RestTemplate发送http请求

上一节我们学习了如何使用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<T> responseType, Object... uriVariables
复制代码

getForObject方法有三个参数

参数名 作用
url 请求的地址
responseType 返回值的类型
uriVariables url里面的填充字段

进入方法

public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
		RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
		HttpMessageConverterExtractor<T> responseExtractor =
				new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
		return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
	}
复制代码

在该方法中有三行代码,第一行封装了一个AcceptHeaderRequestCallback对象,第二行封装了一个HttpMessageConverterExtractor对象,这两对象的作用我们可以先放一放,然后第三行就是执行请求。

所以我们进入execute方法

public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
			@Nullable ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables)
			throws RestClientException {

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

在excute方法中首先做了准备工作,将我们的字符串url转化成了URI对象。这个对象正是我们上一节用以发送http请求的对象。在扩展的时候有一个expand方法,该方法的作用大致如下

Map<String,String> 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<MediaType> 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转对象了吧。

原文  https://juejin.im/post/5cff89636fb9a07ef819f425
正文到此结束
Loading...