【问题标题】:How to have spring cache store the ResponseBody and not the intermediary object如何让弹簧缓存存储 ResponseBody 而不是中间对象
【发布时间】:2023-03-21 18:42:02
【问题描述】:

我用这个方法使用spring cache,它以JSON形式返回查询值:

@RequestMapping("/getById")
@ResponseBody
@Cacheable
public HugeValue getHugeValueFromSlowFoo( @RequestParam(value = "id", defaultValue = "") String id ) {
  return Foo.getById( id );
}

这很好用,并且 HugeValue 对象存储在缓存中(在本例中为 Hazelcast)。我想进一步改进这一点,因为从 HugeValue 创建 JSON 所花费的时间非常长。 我可以告诉 spring 缓存缓存我的对象的 JSON 化版本吗?

我将 Jackson 与 Spring Boot 1.2 和 Spring 4.1 一起使用

【问题讨论】:

    标签: java json spring caching spring-cache


    【解决方案1】:

    在没有真正了解您的确切用例的情况下(不幸的是,我还不允许添加 cmets 来询问),我尝试对我的想法进行简短总结。他们都假设您使用 Jackson 进行 json 映射,并且至少使用 Spring 3.1

    据我所知,SpringMVC 中没有 enableResponseBodyCaching 功能。

    第一种选择:使用 http 缓存,因为您似乎真的想缓存整个 http 响应。 Spring 提供了一种直接的全局配置方式:

    <mvc:interceptors>
        <bean id="webContentInterceptor"
              class="org.springframework.web.servlet.mvc.WebContentInterceptor">
            <property name="cacheSeconds" value="0"/>
            <property name="useExpiresHeader" value="true"/>
            <property name="useCacheControlHeader" value="true"/>
            <property name="useCacheControlNoStore" value="true"/>
        </bean>
    </mvc:interceptors>
    

    如果你想控制每个控制器,继承自 Spring AbstractController 并根据 javaDoc 设置 cacheSeconds 属性。

    http 缓存的真正强大之处当然在于您的服务器前的 http 代理。

    第二个想法:实现你自己的MappingJackson2HttpMessageConverter子类。在writeInternal() 中,您可以添加一些逻辑来访问缓存以检索已映射的版本,而不是映射输入对象。这种方法意味着您将访问您的服务以检索 Json 流后面的 java 对象。如果这对您来说很好,因为在某些时候也有缓存,那么这种方法值得一试恕我直言。

    第三个想法: 在提供原始 json 字符串/流的专用包装服务中自行进行 json 映射。您可以轻松地注入 Jackson 映射器(类名 ObjectMapper)并获得对映射的完全控制。注释此服务然后允许您缓存结果。在您的控制器中,您只需提供您希望使用的相应类型的 ResponseEntity (字符串或某些流)。如果存在缓存结果,这甚至会阻止更深入的服务访问。

    编辑:可能 MappingJackson2JsonView 也可以派上用场。老实说,我以前从未使用过它,所以我不能真正说出它的用法。

    希望能有所帮助和/或提供灵感! 干杯

    【讨论】:

    • 感谢您提供详尽的答案和出色的替代方案。如果有其他答案,我会再等一会儿,但现在我喜欢你描述的所有替代方案,第三个想法似乎很合适,尽管实现这一点需要做一些工作。所以懒惰是我等待可能需要更少工作的第四个答案的唯一原因;-)。浏览器端缓存不是我想要的,因为我有成千上万的用户会收到相同的结果,并且浏览器端缓存不会以完美的方式工作,因为需要为每个用户检索和 jsonified HugeValue。
    • WebContentInterceptor 配置实际上告诉客户端不缓存响应...
    【解决方案2】:

    我认为这可行。

    public interface HugeValueService{
        public String getHugeValueFromSlowFoo(String id);
    }
    
    public class HugeValueServiceImpl implements HugeValueService{
        @Cacheable
        public String getHugeValueFromSlowFoo(String id ) {
          return Foo.getById( id );
        }
    }
    
    Your Class
    ----------
    @RequestMapping("/getById")
    @ResponseBody
    public HugeValue getHugeValueFromSlowFoo( @RequestParam(value = "id", defaultValue = "") String id ) {
      return hugeValueService.getHugeValueFromSlowFoo(id);
    }
    

    祝你好运!!!

    【讨论】:

      【解决方案3】:

      缓存 json 并手动放入响应中。

      public void getHugeValueFromSlowFoo( @RequestParam(value = "id", defaultValue = "") String id, ServletResponse resp ) 
      {
        String jsonSerializedHugeValue = Bar.getById(id); // serialize manually with ObjectMapper
        resp.getWriter().write(jsonSerializedHugeValue);
        resp.setContentType("application/json");
        resp.flushBuffer();
      }
      

      【讨论】:

      • 这可行,但不是很“引导风格”。我承认没有提到这是一个先决条件;-)
      • 虽然不常用(并且正确,因为大多数时候没有必要),但它是一个合适的弹簧和弹簧启动代码。它也干净、明显且可读性好。
      猜你喜欢
      • 2012-04-19
      • 2014-03-03
      • 2012-07-07
      • 1970-01-01
      • 1970-01-01
      • 2012-07-05
      • 1970-01-01
      • 2014-09-02
      • 1970-01-01
      相关资源
      最近更新 更多