关闭JAX RS客户端/响应

时间:2015-10-12 14:43:47

标签: java memory-leaks jax-rs

我不清楚是否必须关闭JAX RS客户端/响应实例。如果我必须,永远或不是吗?

根据documentation关于客户端类:

  

有效地调用此方法会使所有资源目标无效   由客户端实例生成。

WebTarget类没有任何invalidate()/ close()方法,但Response类却有。 根据{{​​3}}:

  

关闭基础消息实体输入流(如果可用和   open)以及释放与之相关的任何其他资源   响应(例如缓冲的消息实体数据)。

     

... close()方法   应该在包含未消耗实体的所有实例上调用   输入流以确保与实例关联的资源   正确清理并防止潜在的内存泄漏。这是   典型的应用程序层代码的客户端方案   仅处理响应头并忽略响应实体。

最后一段对我来说并不清楚。 “未消耗的实体输入流”是什么意思?如果我从响应中获取InputSteam或String,是否应该明确关闭响应?

我们可以在无法访问Response实例的情况下获得响应结果:

Client client = ...;
WebTarget webTarget = ...;
Invocation.Builder builder = webTarget.request(MediaType.APPLICATION_JSON_TYPE);
Invocation invocation = builder.buildGet();
InputStream reso = invocation.invoke(InputStream.class);

我正在使用RESTeasy实现,我期望在resteasy实现中关闭响应,但我找不到它。谁能告诉我为什么? 我知道documentation 但即使知道,也会使用响应,而不关闭它。

3 个答案:

答案 0 :(得分:3)

根据文件close()是幂等的。

  

此操作是幂等的,即它可以多次调用并具有相同的效果,这也意味着在已经关闭的消息实例上调用close()方法是合法的并且没有进一步的效果。

所以你可以安全地关闭InputStream并且应该。

据说我风格明智不会invocation.invoke(InputStream.class),因为invoker(Class)是为了进行实体转换而做的。相反,如果你想要InputStream,你可能只需要调用invocation.invoke()并直接处理Response对象,因为在阅读流之前你可能需要一些头信息。 在处理响应时需要标题的原因InputStream是典型的,因为您不关心正文或正文需要特殊处理和大小考虑因素,这是文档提到的内容(例如{{1}请求ping服务器)。

另见link

  

将缓存从此方法返回的消息实例,以便通过getEntity()进行后续检索。除非提供的实体类型是输入流,否则此方法会在打开时自动关闭未使用的原始响应实体数据流。如果实体数据已被缓冲,则在使用缓冲数据之前将重置缓冲区,以便在此响应上启用readEntity(...)方法的后续调用。

因此,如果您选择HEAD以外的任何其他内容,则无需关闭InputStream(但无论如何都可以安全地将其作为幂等元素)。

答案 1 :(得分:1)

简而言之:请调用close()或在try-with-resources-statement中使用closeable。

  • 如果使用JAX-RS客户端引用,则在客户端上调用close()将关闭打开的套接字。
  • 在Response上调用close将释放连接,但不会释放任何打开的套接字
  • 不需要调用close(),因为Resteasy会在幕后释放连接。但是,如果result是InputStream或正在处理Response结果,则应该这样做。

资源/参考:

根据Resteasy documentation,应在Response引用上调用close()。 最后在第47.3节中指出

Resteasy将释放底盖下的连接。唯一的 反例是响应是以下实例的情况 InputStream,必须明确关闭

另一方面,如果调用的结果是的一个实例 Response,然后使用Response。 close ()方法来释放 连接。

您可能应该在try / finally块中执行此操作。再次, 释放连接只会使其可供其他使用。它 通常不会关闭套接字。

请注意,如果ApacheHttpClient4Engine创建了自己的实例, HttpClient,不必等待finalize() close 打开 插座。 ClientHttpEngine接口为此具有 close ()方法 目的。

最后,如果您的javax.ws.rs.client.Client类创建了 为您自动创建引擎,您应该致电Client。关闭()和 这将清除所有套接字连接。

答案 2 :(得分:0)

查看resteasy-client源代码,Invocation#invoke(Class<T>)只需调用Invocation#invoke()并调用Invocation#extractResult(GenericType<T> responseType, Response response, Annotation[] annotations)即可从Response中提取结果:

@Override
public <T> T invoke(Class<T> responseType)
{
   Response response = invoke();
   if (Response.class.equals(responseType)) return (T)response;
   return extractResult(new GenericType<T>(responseType), response, null);
}

Invocation#extractResult(GenericType<T> responseType, Response response, Annotation[] annotations)关闭finally块中的响应:

/**
 * Extracts result from response throwing an appropriate exception if not a successful response.
 *
 * @param responseType
 * @param response
 * @param annotations
 * @param <T>
 * @return
 */
public static <T> T extractResult(GenericType<T> responseType, Response response, Annotation[] annotations)
{
   int status = response.getStatus();
   if (status >= 200 && status < 300)
   {
      try
      {
         if (response.getMediaType() == null)
         {
            return null;
         }
         else
         {
            T rtn = response.readEntity(responseType, annotations);
            if (InputStream.class.isInstance(rtn)
                 || Reader.class.isInstance(rtn))
            {
               if (response instanceof ClientResponse)
               {
                  ClientResponse clientResponse = (ClientResponse)response;
                  clientResponse.noReleaseConnection();
               }
            }
            return rtn;

         }
      }
      catch (WebApplicationException wae)
      {
         try
         {
            response.close();
         }
         catch (Exception e)
         {

         }
         throw wae;
      }
      catch (Throwable throwable)
      {
         try
         {
            response.close();
         }
         catch (Exception e)
         {

         }
         throw new ResponseProcessingException(response, throwable);
      }
      finally
      {
         if (response.getMediaType() == null) response.close();
      }
   }
   try
   {
      // Buffer the entity for any exception thrown as the response may have any entity the user wants
      // We don't want to leave the connection open though.
      String s = String.class.cast(response.getHeaders().getFirst("resteasy.buffer.exception.entity"));
      if (s == null || Boolean.parseBoolean(s))
      {
         response.bufferEntity();
      }
      else
      {
         // close connection
         if (response instanceof ClientResponse)
         {
            try
            {
               ClientResponse.class.cast(response).releaseConnection();
            }
            catch (IOException e)
            {
               // Ignore
            }
         }
      }
      if (status >= 300 && status < 400) throw new RedirectionException(response);

      return handleErrorStatus(response);
   }
   finally
   {
      // close if no content
      if (response.getMediaType() == null) response.close();
   }

}