Działanie HttpServletRequest i Request Scope w Springu przy Autowired i wielu klientach jednocześnie

0

Cześć.

Używam Spring Boota. Mam zwykłe bezstanowe beany singletony (oznaczane w Spring Boot jako @Component - domyślnie są singletonem).
Jeden z takich beanów o których będę tutaj mówić nazywa się CustomerAuthService a drugi CustomerAuthController, który to jest springowym REST controllerem.
Wstrzykuję sobie przez konstruktor za pomocą adnotacji @Autowired (bo tak powinniście wstrzykiwać swoje zależności, aby komponenty były łatwo testowalne) pole
private final HttpServletRequest request; do CustomerAuthService.

W tym momencie jeżeli wykonam request do CustomerAuthController i on wywola metodę na CustomerAuthService to w
pole private final HttpServletRequest request; w CustomerAuthService jest wsktrzykiwany ten właśnie request, który poszedł do CustomerAuthController.

  1. Czy dobrze myślę, że w tym momencie CustomerAuthService przestaje być bezstanowym, bo mimo złudnego final przy polu HttpServletRequest request będzie przechowywać requesta w scopie request?

  2. Co w przypadku jeżeli dwa requesty wejdą jednocześnie - request A i request B? Powstaną dwa osobne wątki http-nio-8080-exec-5 aby obsłużyć request A i http-nio-8080-exec-6 aby obsłużyć request B. Dla wątku http-nio-8080-exec-5 pole request w serwisie CustomerAuthService będzie równe requestowi A a dla wątku http-nio-8080-exec-6 pole request w serwisie CustomerAuthService równe requestowi B? Czy sądzicie, że wstrzyknięcie pola HttpServletRequest jest bezpieczne i czy jest dobrym podejściem oraz jak rozwiązuje to Spring Framework (tak naprawdę wygląda na to, że pole HttpServletRequest request będzie się co chwila zmieniać - jest gdzieś dokumentacja opisująca takie zachowanie?

  3. Co jeżeli macie złożoną aplikacje i potrzebujecie przechowywać dane z requesta, dane kontekstu wywołania danego endpointu pomiędzy wieloma serwisami?
    Chodzi mi o rozwiązanie podobne do SecurityContextHolder.getContext().getAuthentication().getPrincipal() z Spring Security.
    Zamiast przekazywać 15 razy przez wszystkie metody username itd... łamie się takie bezsensowne przekazywania wywołań na rzecz jednego współdzielonego obiektu przechowującego stan na scope sesji.
    Ja zamiast przekazywać 15 parametrów między metodami myślałam, żeby zrobić sobie własnie coś na zasadzie takiego RequestContextHolder - na scope requestu, być może tylko z wyciągniętymi danymi których potrzebuje i tylko readonly.
    Co myślicie o takim podejściu i czy robicie tak w swoich aplikacjach?

Pozdrawiam

1
Czy dobrze myślę, że w tym momencie CustomerAuthService przestaje być bezstanowym, bo mimo złudnego final przy polu HttpServletRequest request będzie przechowywać requesta w scopie request?

Przecież jak wstrzykujesz request-scoped do singletona, to singleton dalej jest singletonem tylko potem operujesz na proxy do HttpServletRequest (jak to działa, nie mam pojęcia, domyślam się że jakieś śliczne ThreadLocal) - to dalej masz bezstanowy CustomerAuthService (nie uważasz że to co robisz to jakiś hardcore ?) - chociaż pewnie się myle, w sumie nigdy poza tutorialem z tego nie korzystałem.

P.S dalej się używa aop:scoped-proxy/ ?

Powstaną dwa osobne wątki 

Generalnie to one już tam pewnie istnieją - kontenery tworzą sobie jakieś pule wątków - ale to akurat nie ważne.
Jak założysz że jeden wątek obsługuje jedno żądanie (SICK!!!!) to możesz te session-scoped beany wsadzić to Mapy która wiąże wątek z HttpServletRequest - Albo ThreadLocal i każdy wątek wie na jakim HttpServletRequest operuje.
Może ten wątek Ci pomoże https://stackoverflow.com/questions/33038856/how-does-a-scopedproxy-decide-what-session-to-use ?

a zamiast przekazywać 15 parametrów między metodami myślałam, żeby zrobić sobie własnie coś na zasadzie takiego

A potem masz jakiś black-box w którym trzymasz 1000000 zmienny (WTF) - jeżeli masz takie problemy to znaczy że coś w którymś momencie poszło nie tak jak powinno.
Naprawdę, lepiej przekazać ten jeden obiekt więcej w sygnaturze niż wsadzać coś do request-contex-variable, potem wyobraź sobie że masz jakieś asynchroniczne przetwarzanie, te mapy per thread muszą być kopiowane - a pomyśl że potesz przejść na non-blocking io - i nie masz request-per-thread, co zrobisz ? bydziesz orać ?

P.S.2 nie opieraj się o jakieś ciężkie mechanizmy springa i scopowania - jeżeli musisz robić takie rzeczy, zawróć !
P.S.3 jeszcze jeden argument za tym żeby nie robić takich rzeczy - wyobraź sobie że Twoje requesty zajmują troche czasu żeby zwrócić response, przez cały ten czas trzymasz w tej Mapie obiekty których już pewnie nie potrzebujesz, zwiększa się szansa że zostaną niepotrzebnie przenoszone podczas GC, bo przecież, ktoś do nich referuje.

2

To jest rak zwany inaczej magią Springa.

W niewielkim uproszczeniu (i troszkę na zasadzie o ile pamiętam):
Ten *Request *jest po prostu *Proxy *na prawdziwego HttpServletRequest, który to z kolei jest trzymany w ThreadLocal. Czyli masz jeden obiekt, który wywołany deleguje do właściwego, patrząc jaki faktycznie request jest skojarzony z obecnie przetwarzanym wątkiem.
Tak, że mimo, że wygląda to durnie to działa w miarę dobrze (do czasu jak nie zaczniesz się np. bawić w wątki, albo odkryjesz, że model 1 request - 1 wątek to trochę muł).

Generalnie wszelkie magiczne wstrzykiwania czegoś "request"/ thread scope / sesje, zwłaszcza przez ileś warstw to recepta na klęskę. Z programowaniem obiektowym i w szczególności enkapsulacją to nic wspólnego nie ma. Szukaj potem, kto coś gdzieś wynullował w obiekcie....

Przekazywanie przez ileś warstw tylko normalnie przez parametry. Nie potrzeba 15. Jeden wystarczy każdemu (ale do 3 jest ok). Jeśli jakiś cudem sobie zrobisz 4 to masz pewnie Intellij i on tam ma Extact Parameter Object.

1 użytkowników online, w tym zalogowanych: 0, gości: 1