Как иметь кеш spring хранить ResponseBody, а не промежуточный объект

Я использую кэш spring с этим методом, который возвращает значение queried как JSON:

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

Это отлично работает, и объект HugeValue хранится в кеше (Hazelcast в этом случае). Я хочу еще больше улучшить это, потому что время, необходимое для создания JSON от HugeValue, довольно велико. Могу ли я сказать кешу spring кэшировать версию моего объекта JSON-ified?

Я использую Jackson с spring Boot 1.2 и spring 4.1

Ответ 1

Не зная вашего точного варианта использования (мне еще не разрешено добавлять комментарии для запроса, к сожалению), я стараюсь кратко рассказать о тех идеях, которые имею в виду. Все они предполагают, что вы используете 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 и установите свойство cacheSeconds в соответствии с javaDoc.

Истинная мощь кэширования http поставляется с прокси-сервером http перед вашим сервером, конечно.

Вторая идея: Внедрите свой собственный подкласс MappingJackson2HttpMessageConverter. В writeInternal() вы можете добавить некоторую логику, которая обращается к кешу, чтобы получить уже сопоставленную версию вместо сопоставления входного объекта. Этот подход означает, что вы ударите свои службы, чтобы получить java-объект за потоком Json. Если это хорошо для вас, потому что в какой-то момент есть кеширование, этот подход стоит попробовать imho.

Третья идея: Сделайте собственное сопоставление json в специализированной службе обертки, которая предоставляет сырые строки/потоки json. Вы можете легко вводить картограф Jackson (имя класса ObjectMapper) и получать полный контроль над отображением. Аннотирование этой службы позволяет вам кэшировать результаты. В вашем контроллере вы предоставляете только ResponseEntity соответствующего типа, который вы хотите использовать (String или некоторый поток). Это даже предотвратит более глубокий доступ к сервису, если будет присутствовать полученный в кэше результат.

Изменить: возможно, MappingJackson2JsonView также может оказаться полезным. Честно говоря, я никогда не работал с ним, поэтому я не могу сказать что-то о его использовании.

Надеюсь, что это помогает и/или дает вдохновение! Приветствия

Ответ 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();
}