org.springframework.remoting.httpinvoker.HttpInvokerRequestExecutor
用于实际执行HTTP调用程序请求的策略接口。由HttpInvokerClientInterceptor及其子类HttpInvokerProxyFactoryBean使用。提供了两个开箱即用的实现:SimpleHttpInvokerRequestExecutor:使用JDK工具执行POST请求,不支持HTTP身份验证或高级配置选项。HttpComponentsHttpInvokerRequestExecutor:使用Apache的Commons HttpClient执行POST请求,允许使用预配置的HttpClient实例(可能使用身份验证、HTTP连接池等)。
RemoteInvocationResult executeRequest(HttpInvokerClientConfiguration config, RemoteInvocation invocation) throws Exception;
执行请求。
org.springframework.remoting.httpinvoker.AbstractHttpInvokerRequestExecutor
HttpInvokerRequestExecutor接口的抽象基础实现。预实现RemoteInvocation对象的序列化和RemoteInvocationResults对象的反序列化。
@Override public final RemoteInvocationResult executeRequest( HttpInvokerClientConfiguration config, RemoteInvocation invocation) throws Exception { ByteArrayOutputStream baos = getByteArrayOutputStream(invocation); if (logger.isDebugEnabled()) { logger.debug("Sending HTTP invoker request for service at [" + config.getServiceUrl() + "], with size " + baos.size()); } return doExecuteRequest(config, baos); }
执行request,这里是模板方法实现。创建输出流写入RemoteInvocation,org.springframework.remoting.httpinvoker.AbstractHttpInvokerRequestExecutor#doExecuteRequest 执行请求发送给定的序列化远程调用。实现通常调用readRemoteInvocationResult来反序列化返回的RemoteInvocationResult对象。需要子类实现这个模板方法。org.springframework.remoting.httpinvoker.AbstractHttpInvokerRequestExecutor#writeRemoteInvocation
将给定的RemoteInvocation序列化到给定的OutputStream。默认实现使decorateOutputStream有机会首先修饰流(例如,用于自定义加密或压缩)。为最终流创建ObjectOutputStream并调用doWriteRemoteInvocation来实际编写对象。可以为调用的自定义序列化重写。
protected void writeRemoteInvocation(RemoteInvocation invocation, OutputStream os) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(decorateOutputStream(os)); try { doWriteRemoteInvocation(invocation, oos); } finally { oos.close(); } }
将给定的调用对象实际写入给定的ObjectOutputStream。默认实现只调用writeObject。可以覆盖自定义包装器对象的序列化,而不是普通调用,例如支持加密的持有者。
protected void doWriteRemoteInvocation(RemoteInvocation invocation, ObjectOutputStream oos) throws IOException { oos.writeObject(invocation); }
org.springframework.remoting.httpinvoker.AbstractHttpInvokerRequestExecutor#decorateOutputStream 返回OutputStream以用于编写远程调用,潜在地修饰给定的原始OutputStream。默认实现按原样返回给定的流。可以覆盖,例如,用于自定义加密或压缩。
protected OutputStream decorateOutputStream(OutputStream os) throws IOException { return os; }
org.springframework.remoting.httpinvoker.AbstractHttpInvokerRequestExecutor#readRemoteInvocationResult 从给定的InputStream反序列化RemoteInvocationResult对象。为decorateInputStream提供了首先修饰流的机会(例如,用于自定义加密或压缩)。通过createObjectInputStream创建一个ObjectInputStream,并调用doReadRemoteInvocationResult来实际读取该对象。可以为调用的自定义序列化重写。
protected RemoteInvocationResult readRemoteInvocationResult(InputStream is, String codebaseUrl) throws IOException, ClassNotFoundException { ObjectInputStream ois = createObjectInputStream(decorateInputStream(is), codebaseUrl); try { return doReadRemoteInvocationResult(ois); } finally { ois.close(); } }
org.springframework.remoting.httpinvoker.AbstractHttpInvokerRequestExecutor#doReadRemoteInvocationResult 从给定的ObjectInputStream执行调用对象的实际读取。默认实现只调用readObject。可以覆盖自定义包装器对象的反序列化,而不是普通调用,例如支持加密的持有者。
protected RemoteInvocationResult doReadRemoteInvocationResult(ObjectInputStream ois) throws IOException, ClassNotFoundException { Object obj = ois.readObject(); if (!(obj instanceof RemoteInvocationResult)) { throw new RemoteException("Deserialized object needs to be assignable to type [" + RemoteInvocationResult.class.getName() + "]: " + ClassUtils.getDescriptiveType(obj)); } return (RemoteInvocationResult) obj; }
org.springframework.remoting.httpinvoker.AbstractHttpInvokerRequestExecutor#decorateInputStream 返回用于读取远程调用结果的InputStream,潜在地修饰给定的原始InputStream。默认实现按原样返回给定的流。可以覆盖,例如,用于自定义加密或压缩。
protected InputStream decorateInputStream(InputStream is) throws IOException { return is; }
org.springframework.remoting.httpinvoker.SimpleHttpInvokerRequestExecutor HttpInvokerRequestExecutor实现,它使用标准Java工具来执行POST请求,不支持HTTP身份验证或高级配置选项。设计用于简单的子类化,定制特定的模板方法。但是,考虑HttpComponentsHttpInvokerRequestExecutor以获得更复杂的需求:标准HttpURLConnection类的功能相当有限。
private int connectTimeout = -1;
connectTimeout
设置底层URLConnection的连接超时(单位为毫秒)。超时值0指定无限超时。Default是系统的默认超时。
private int readTimeout = -1;
readTimeout
@Override protected RemoteInvocationResult doExecuteRequest( HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws IOException, ClassNotFoundException { // 创建HttpURLConnection HttpURLConnection con = openConnection(config); // 预处理HttpURLConnection prepareConnection(con, baos.size()); // 写requestBody writeRequestBody(config, con, baos); // 验证响应 validateResponse(config, con); // 读取响应信息 InputStream responseBody = readResponseBody(config, con); // 读取执行结果 return readRemoteInvocationResult(responseBody, config.getCodebaseUrl()); }
通过HttpURLConnection执行请求返回结果。
org.springframework.remoting.httpinvoker.SimpleHttpInvokerRequestExecutor#openConnection 根据服务url创建HttpURLConnection
protected HttpURLConnection openConnection(HttpInvokerClientConfiguration config) throws IOException { URLConnection con = new URL(config.getServiceUrl()).openConnection(); if (!(con instanceof HttpURLConnection)) { throw new IOException( "Service URL [" + config.getServiceUrl() + "] does not resolve to an HTTP connection"); } return (HttpURLConnection) con; }
准备给定的HTTP连接。默认实现将POST指定为方法,将“application/x-java-serialize -object”指定为“content - type”报头,将给定的内容长度指定为“content - length”报头org.springframework.remoting.httpinvoker.SimpleHttpInvokerRequestExecutor#prepareConnection
protected void prepareConnection(HttpURLConnection connection, int contentLength) throws IOException { // 设置连接超时参数 if (this.connectTimeout >= 0) { connection.setConnectTimeout(this.connectTimeout); } // 设置读取超时参数 if (this.readTimeout >= 0) { connection.setReadTimeout(this.readTimeout); } connection.setDoOutput(true); // 设置http请求方式为post connection.setRequestMethod(HTTP_METHOD_POST); // 设置contentType为application/x-java-serialized-object connection.setRequestProperty(HTTP_HEADER_CONTENT_TYPE, getContentType()); // 设置Content-Length connection.setRequestProperty(HTTP_HEADER_CONTENT_LENGTH, Integer.toString(contentLength)); LocaleContext localeContext = LocaleContextHolder.getLocaleContext(); if (localeContext != null) { Locale locale = localeContext.getLocale(); if (locale != null) { connection.setRequestProperty(HTTP_HEADER_ACCEPT_LANGUAGE, StringUtils.toLanguageTag(locale)); } } // 设置Accept-Encoding为gzip if (isAcceptGzipEncoding()) { connection.setRequestProperty(HTTP_HEADER_ACCEPT_ENCODING, ENCODING_GZIP); } }
org.springframework.remoting.httpinvoker.SimpleHttpInvokerRequestExecutor#writeRequestBody 写入请求体
protected void writeRequestBody( HttpInvokerClientConfiguration config, HttpURLConnection con, ByteArrayOutputStream baos) throws IOException { baos.writeTo(con.getOutputStream()); }
org.springframework.remoting.httpinvoker.SimpleHttpInvokerRequestExecutor#validateResponse 设置响应
protected void validateResponse(HttpInvokerClientConfiguration config, HttpURLConnection con) throws IOException { if (con.getResponseCode() >= 300) { throw new IOException( "Did not receive successful HTTP response: status code = " + con.getResponseCode() + ", status message = [" + con.getResponseMessage() + "]"); } }
org.springframework.remoting.httpinvoker.SimpleHttpInvokerRequestExecutor#readResponseBody 读取响应体
protected InputStream readResponseBody(HttpInvokerClientConfiguration config, HttpURLConnection con) throws IOException { // 判断请求头Content-Encoding是不是gzip if (isGzipResponse(con)) { // GZIP response found - need to unzip. return new GZIPInputStream(con.getInputStream()); } else { // Plain response found. return con.getInputStream(); } }
org.springframework.remoting.httpinvoker.AbstractHttpInvokerRequestExecutor#readRemoteInvocationResult 从给定的InputStream反序列化RemoteInvocationResult对象。为decorateInputStream提供了首先修饰流的机会(例如,用于自定义加密或压缩)。通过createObjectInputStream创建一个ObjectInputStream,并调用doReadRemoteInvocationResult来实际读取该对象。可以为调用的自定义序列化重写。
protected RemoteInvocationResult readRemoteInvocationResult(InputStream is, String codebaseUrl) throws IOException, ClassNotFoundException { ObjectInputStream ois = createObjectInputStream(decorateInputStream(is), codebaseUrl); try { return doReadRemoteInvocationResult(ois); } finally { ois.close(); } }
org.springframework.remoting.httpinvoker.AbstractHttpInvokerRequestExecutor#doReadRemoteInvocationResult 从给定的ObjectInputStream执行调用对象的实际读取。默认实现只调用readObject。可以覆盖自定义包装器对象的反序列化,而不是普通调用,例如支持加密的持有者。
protected RemoteInvocationResult doReadRemoteInvocationResult(ObjectInputStream ois) throws IOException, ClassNotFoundException { Object obj = ois.readObject(); if (!(obj instanceof RemoteInvocationResult)) { throw new RemoteException("Deserialized object needs to be assignable to type [" + RemoteInvocationResult.class.getName() + "]: " + ClassUtils.getDescriptiveType(obj)); } return (RemoteInvocationResult) obj; }
org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor 使用Apache HttpComponents HttpClient执行POST请求的HttpInvokerRequestExecutor实现。允许使用预先配置的HttpClient实例,可能包括身份验证、HTTP连接池等。还设计为易于子类化,提供特定的模板方法。从Spring 4.1开始,这个请求执行器需要Apache HttpComponents 4.3或更高版本。
private static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 100;
默认最大连接数
private static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 5;
默认每个route最大连接数
private static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = (60 * 1000);
默认读超时参数
private HttpClient httpClient;
httpClient
private RequestConfig requestConfig;
requestConfig
public HttpComponentsHttpInvokerRequestExecutor() { this(createDefaultHttpClient(), RequestConfig.custom() .setSocketTimeout(DEFAULT_READ_TIMEOUT_MILLISECONDS).build()); }
org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor#createDefaultHttpClient
private static HttpClient createDefaultHttpClient() { Registry<ConnectionSocketFactory> schemeRegistry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", SSLConnectionSocketFactory.getSocketFactory()) .build(); PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(schemeRegistry); connectionManager.setMaxTotal(DEFAULT_MAX_TOTAL_CONNECTIONS); connectionManager.setDefaultMaxPerRoute(DEFAULT_MAX_CONNECTIONS_PER_ROUTE); return HttpClientBuilder.create().setConnectionManager(connectionManager).build(); }
创建httpClient
public void setConnectTimeout(int timeout) { Assert.isTrue(timeout >= 0, "Timeout must be a non-negative value"); this.requestConfig = cloneRequestConfig().setConnectTimeout(timeout).build(); setLegacyConnectionTimeout(getHttpClient(), timeout); }
设置httpClient连接超时参数
public void setConnectionRequestTimeout(int connectionRequestTimeout) { this.requestConfig = cloneRequestConfig().setConnectionRequestTimeout(connectionRequestTimeout).build(); }
设置连接请求超时参数
public void setReadTimeout(int timeout) { Assert.isTrue(timeout >= 0, "Timeout must be a non-negative value"); this.requestConfig = cloneRequestConfig().setSocketTimeout(timeout).build(); setLegacySocketTimeout(getHttpClient(), timeout); }
设置读取超时参数
org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor#doExecuteRequest 通过httpClient执行request
@Override protected RemoteInvocationResult doExecuteRequest( HttpInvokerClientConfiguration config, ByteArrayOutputStream baos) throws IOException, ClassNotFoundException { // 创建httpPost HttpPost postMethod = createHttpPost(config); // 设置请求体 setRequestBody(config, postMethod, baos); try { // 执行httpPost请求返回httpResponse HttpResponse response = executeHttpPost(config, getHttpClient(), postMethod); // 验证响应 validateResponse(config, response); // 获取请求体 InputStream responseBody = getResponseBody(config, response); // 读取执行结果 return readRemoteInvocationResult(responseBody, config.getCodebaseUrl()); } finally { // 释放连接 postMethod.releaseConnection(); } }
org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor#createHttpPost 为给定的配置创建HttpPost。默认实现创建一个标准HttpPost,其“application/x-java-serialize -object”作为“Content-Type”报头。
protected HttpPost createHttpPost(HttpInvokerClientConfiguration config) throws IOException { // 根据serviceUrl创建HttpPost HttpPost httpPost = new HttpPost(config.getServiceUrl()); // 创建requestConfig RequestConfig requestConfig = createRequestConfig(config); if (requestConfig != null) { httpPost.setConfig(requestConfig); } LocaleContext localeContext = LocaleContextHolder.getLocaleContext(); if (localeContext != null) { Locale locale = localeContext.getLocale(); if (locale != null) { httpPost.addHeader(HTTP_HEADER_ACCEPT_LANGUAGE, StringUtils.toLanguageTag(locale)); } } // 设置请求头Accept-Encoding为gzip if (isAcceptGzipEncoding()) { httpPost.addHeader(HTTP_HEADER_ACCEPT_ENCODING, ENCODING_GZIP); } return httpPost; }
org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor#createRequestConfig 为给定的配置创建RequestConfig。可以返回null,以指示不应该设置自定义请求配置,并且应该使用HttpClient的默认值。默认实现尝试将客户机的默认值与实例的本地自定义(如果有的话)合并。
protected RequestConfig createRequestConfig(HttpInvokerClientConfiguration config) { HttpClient client = getHttpClient(); if (client instanceof Configurable) { RequestConfig clientRequestConfig = ((Configurable) client).getConfig(); return mergeRequestConfig(clientRequestConfig); } return this.requestConfig; }
org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor#mergeRequestConfig
private RequestConfig mergeRequestConfig(RequestConfig defaultRequestConfig) { if (this.requestConfig == null) { // nothing to merge return defaultRequestConfig; } RequestConfig.Builder builder = RequestConfig.copy(defaultRequestConfig); int connectTimeout = this.requestConfig.getConnectTimeout(); if (connectTimeout >= 0) { builder.setConnectTimeout(connectTimeout); } int connectionRequestTimeout = this.requestConfig.getConnectionRequestTimeout(); if (connectionRequestTimeout >= 0) { builder.setConnectionRequestTimeout(connectionRequestTimeout); } int socketTimeout = this.requestConfig.getSocketTimeout(); if (socketTimeout >= 0) { builder.setSocketTimeout(socketTimeout); } return builder.build(); }
org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor#setRequestBody 将给定的序列化远程调用设置为请求体。默认实现只是将序列化的调用设置为HttpPost的请求体。例如,可以重写它来编写特定的编码并可能设置适当的HTTP请求头。
protected void setRequestBody( HttpInvokerClientConfiguration config, HttpPost httpPost, ByteArrayOutputStream baos) throws IOException { ByteArrayEntity entity = new ByteArrayEntity(baos.toByteArray()); entity.setContentType(getContentType()); httpPost.setEntity(entity); }
org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor#executeHttpPost 执行httpPost请求
protected HttpResponse executeHttpPost( HttpInvokerClientConfiguration config, HttpClient httpClient, HttpPost httpPost) throws IOException { return httpClient.execute(httpPost); }
org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor#validateResponse 验证响应
protected void validateResponse(HttpInvokerClientConfiguration config, HttpResponse response) throws IOException { StatusLine status = response.getStatusLine(); if (status.getStatusCode() >= 300) { throw new NoHttpResponseException( "Did not receive successful HTTP response: status code = " + status.getStatusCode() + ", status message = [" + status.getReasonPhrase() + "]"); } }
org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor#getResponseBody 获取响应体
protected InputStream getResponseBody(HttpInvokerClientConfiguration config, HttpResponse httpResponse) throws IOException { if (isGzipResponse(httpResponse)) { return new GZIPInputStream(httpResponse.getEntity().getContent()); } else { return httpResponse.getEntity().getContent(); } }
org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor#isGzipResponse 判断请求头Content-Encoding是不是gzip
protected boolean isGzipResponse(HttpResponse httpResponse) { Header encodingHeader = httpResponse.getFirstHeader(HTTP_HEADER_CONTENT_ENCODING); return (encodingHeader != null && encodingHeader.getValue() != null && encodingHeader.getValue().toLowerCase().contains(ENCODING_GZIP)); }
本次解析仅代表个人观点,仅供参考。
发表评论