2015-01-12 2 views
10

я использую кэш пружинный с помощью этого метода, который возвращает запрошенное значение как JSON:Как иметь кэш весной хранить ResponseBody, а не промежуточный объект

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

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

Я использую Джексон с весны Ботинок 1.2 и Spring 4.1

ответ

6

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

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

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

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

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

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

+0

Спасибо за подробные ответы и отличные альтернативы. Я буду ждать немного дольше, если появятся другие ответы, но пока мне нравятся все альтернативы, которые вы описываете, и третья идея, кажется, подходит, хотя есть некоторые работы, связанные с ее внедрением. Поэтому лень - единственная причина для меня ждать возможного четвертого ответа, который может потребовать меньше работы ;-). Кэш браузера не то, что я хочу, потому что у меня есть тысячи пользователей, которые получат тот же результат, и кеш браузера не будет работать идеально, потому что HugeValue нужно будет получить и настроить для каждого пользователя. – Marged

+0

Конфигурация WebContentInterceptor фактически сообщает клиенту, что он не кэширует ответ ... –

1

Я думаю, что это может сработать.

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

Удача !!!

1

Кэш 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(); 
} 
+0

Это работает, но не очень «загрузочный стиль». Который я, по общему признанию, не упомянул в качестве предварительного условия ;-) – Marged

+0

Хотя обычно не используется (и правильно, поскольку это не обязательно в большинстве случаев), это правильный весенний и весенне-загрузочный код. Это также чистый, очевидный и хорошо читаемый. – Dariusz