Pytanie W jaki sposób mieć pamięć podręczną sprężyny, zapisz element ResponseBody, a nie obiekt pośredniczący


Używam Spring Cache z tą metodą, która zwraca wartość zapytania jako JSON:

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

Działa to dobrze, a obiekt HugeValue jest przechowywany w pamięci podręcznej (w tym przypadku Hazelcast). Chcę dalej to poprawić, ponieważ czas potrzebny na utworzenie JSON z HugeValue jest dość wysoki. Czy mogę nakazać wiosennej pamięci podręcznej cache JSON-ified wersji mojego obiektu?

Używam Jacksona z Spring Boot 1.2 i Spring 4.1


11
2018-01-12 08:59


pochodzenie




Odpowiedzi:


Bez znajomości konkretnego przypadku użycia (nie mogę jeszcze dodawać komentarzy do niestety), staram się przedstawić krótkie podsumowanie pomysłów, które mam na myśli. Wszyscy zakładają, że używasz Jackson'a do mapowania jsonów Wiosna 3.1.

W SpringMVC nie ma funkcji enableResponseBodyCaching, o ile wiem.

Pierwsza alternatywa: Użyj buforowania http, ponieważ wygląda na to, że naprawdę chcesz buforować całą odpowiedź HTTP. Wiosna oferuje prosty sposób globalnej konfiguracji:

<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>

Jeśli chcesz kontrolować to na kontroler, dziedzicz ze sprężyny AbstractController i ustaw właściwość cacheSeconds zgodnie z javaDoc.

Prawdziwa moc buforowania http pochodzi oczywiście od serwera proxy http przed serwerem.

Drugi pomysł: Zaimplementuj własną podklasę MappingJackson2HttpMessageConverter. W writeInternal() można dodać logikę, która uzyskuje dostęp do pamięci podręcznej w celu pobrania już zmapowanej wersji zamiast mapowania obiektu wejściowego. To podejście oznacza, że ​​trafisz na swoje usługi, aby pobrać obiekt java za strumieniem Json. Jeśli jest to w porządku dla ciebie, ponieważ w pewnym momencie istnieje również buforowanie, to podejście jest warte spróbowania.

Trzeci pomysł: Wykonuj mapowanie json na własną rękę w specjalnej usłudze wrapper, która zapewnia nieprzetworzone ciągi / strumienie json. Możesz łatwo wstrzyknąć mappera Jacksona (nazwa klasy ObjectMapper) i uzyskać pełną kontrolę nad mapowaniem. Adnotowanie tej usługi pozwala na buforowanie wyników. W kontrolerze zapewniasz tylko ResponseEntity odpowiedniego typu, którego chcesz użyć (String lub jakiś strumień). Zapobiegłoby to nawet głębszemu dostępowi do usługi, jeśli obecny jest wynik z pamięci podręcznej.

Edycja: Prawdopodobnie MappingJackson2JsonView może również się przydać. Szczerze mówiąc, nigdy wcześniej z nim nie pracowałem, więc nie mogę naprawdę powiedzieć czegoś na temat jego użycia.

Nadzieja, która pomaga i / lub daje inspirację! Twoje zdrowie


7
2018-01-13 08:05



Dzięki za dokładne odpowiedzi i świetne alternatywy. Poczekam trochę dłużej, jeśli pojawią się inne odpowiedzi, ale na razie lubię wszystkie alternatywy, które opisujesz, a trzeci pomysł wydaje się pasować, chociaż jest trochę pracy wymagającej wdrożenia tego. Więc lenistwo to jedyny powód, dla którego muszę poczekać na czwartą odpowiedź, która może wymagać mniej pracy ;-). Pamięć podręczna przeglądarki nie jest tym, czego potrzebuję, ponieważ mam tysiące użytkowników, którzy otrzymają ten sam wynik, a pamięć podręczna przeglądarki nie będzie działać w sposób doskonały, ponieważ HugeValue będzie musiała być pobrana i zesynchronizowana dla każdego użytkownika. - Marged
Konfiguracja WebContentInterceptor faktycznie informuje klienta, aby nie buforował odpowiedzi ... - Brian Clozel


Myślę, że to może zadziałać.

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

Powodzenia!!!


1
2018-06-08 21:30





Cache json i umieścić go w odpowiedzi ręcznie.

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

1
2018-06-09 06:53



To działa, ale nie jest bardzo "stylem startowym". Które co prawda nie wspominałem jako warunek wstępny ;-) - Marged
Chociaż nie jest powszechnie używany (i poprawnie, ponieważ nie jest potrzebny przez większość czasu), jest to właściwy kod sprężyny i sprężyny rozruchowej. Jest także czysty, oczywisty i dobrze czytelny. - Dariusz