java 关闭 JAX RS 客户端/响应

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33083961/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 21:09:41  来源:igfitidea点击:

Closing JAX RS Client/Response

javamemory-leaksjax-rs

提问by Alexandr

It's not clear must I close JAX RS Client/Response instances or not. And if I must, always or not?

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

According to documentationabout the Client class:

根据有关 Client 类的文档

Calling this method effectively invalidates all resource targets produced by the client instance.

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

The WebTarget class does not have any invalidate()/close() method, but the Response class does. According to documentation:

WebTarget 类没有任何 invalidate()/close() 方法,但 Response 类有。根据文档

Close the underlying message entity input stream (if available and open) as well as releases any other resources associated with the response (e.g. buffered message entity data).

... The close() method should be invoked on all instances that contain an un-consumed entity input stream to ensure the resources associated with the instance are properly cleaned-up and prevent potential memory leaks. This is typical for client-side scenarios where application layer code processes only the response headers and ignores the response entity.

关闭底层消息实体输入流(如果可用并打开)并释放与响应相关的任何其他资源(例如缓冲的消息实体数据)。

... close() 方法应该在包含未使用实体输入流的所有实例上调用,以确保与实例关联的资源被正确清理并防止潜在的内存泄漏。这对于应用层代码仅处理响应标头而忽略响应实体的客户端场景来说是典型的。

The last paragraph is not clear to me. What does "un-consumed entity input stream" mean? If I get an InputSteam or a String from response, should I close the response explicitly?

最后一段我不清楚。“未消费的实体输入流”是什么意思?如果我从响应中得到 InputSteam 或 String,我应该明确关闭响应吗?

We can get a response result without getting access to Response instance:

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

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

I'm working with RESTeasy implementation, and I expected that response will be closed inside of resteasy implementation, but I could not find it. Could anyone tell me why? I know that the Response class will implement Closeable interfaceBut even know, the Response is used, without closing it.

我正在使用 RESTeasy 实现,我预计响应将在 resteasy 实现内关闭,但我找不到它。谁能告诉我为什么?我知道Response 类将实现 Closeable 接口但即使知道,使用了 Response ,而不关闭它。

回答by Adam Gent

According to the documentation close()is idempotent.

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

This operation is idempotent, i.e. it can be invoked multiple times with the same effect which also means that calling the close() method on an already closed message instance is legal and has no further effect.

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

So you can safely close the InputStreamyourself and should.

所以你可以安全地关闭InputStream自己和应该。

That being said I style wise would not do invocation.invoke(InputStream.class)as the invoker(Class)is made for doing entity transformation. Instead if you want InputStream you should probably just call invocation.invoke()and deal with the Responseobject directly as you may want some header info before reading the stream. The reason you want headers when dealing with a response InputStreamis typical because you either don't care about the body or the body requires special processing and size considerations which is what the documentation is alluding to (e.g. HEADrequest to ping server).

话虽如此,我的风格明智不会做,invocation.invoke(InputStream.class)因为它invoker(Class)是为进行实体转换而设计的。相反,如果您想要 InputStream,您可能应该直接调用invocation.invoke()并处理Response对象,因为在读取流之前您可能需要一些标头信息。在处理响应时需要标头的原因InputStream是典型的,因为您要么不关心正文,要么正文需要特殊处理和大小考虑,这就是文档所暗示的(例如,HEAD请求 ping 服务器)。

See also link

另见链接

A message instance returned from this method will be cached for subsequent retrievals via getEntity(). Unless the supplied entity type is an input stream, this method automatically closes the an unconsumed original response entity data stream if open. In case the entity data has been buffered, the buffer will be reset prior consuming the buffered data to enable subsequent invocations of readEntity(...) methods on this response.

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

So if you choose anything other than InputStreamyou will not have to close the Response(but regardless its safe to do it anyways as its idempotent).

因此,如果您选择除此之外的任何其他内容InputStream,则不必关闭Response(但无论如何它都是安全的,因为它是幂等的)。

回答by Daniel Shuy

Looking into the resteasy-clientsource code, Invocation#invoke(Class<T>)is simply calling Invocation#invoke()and calling Invocation#extractResult(GenericType<T> responseType, Response response, Annotation[] annotations)to extract the result from the Response:

查看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)closes the Response in the finallyblock:

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();
   }

}