#spring

0
1
Wpis dotyczy #java #spring
Cześć.
Chciałbym Was zapytać o jedną dość laicką kwestię.
Studiuję sobie trochę jak Spring działą pod spodem i ogólnie budowanie appek pure MVC / RESTful.
Ciekawi mnie jedna kwestia. W momencie kiedy tworzymy powiedzmy apkę opartą o architekturę czysto MVC, czyli mamy:
- Model (dane),
- View (JSP lub jakieś zewnętrze biblioteki np. Thymeleaf etc.)
- Controller (tu ogarniamy dane z modelu i przesyłamy te dane wraz ze wskazanie, który widok ma być wyświetlony).
Załużmy, że używamy Spring Boota ze starterem web, który konfiguruje Nam:
- DispatcherServleta (nie musimy mapować serwletów, wkazywać root configuracji itp.)
- ViewResolvera 
+ nie musimy generalnie config klasy dowozić wraz z @EnableWebMvc, @ComponentScan, @Configuration itd. 
Jeśli używamy Spring Bootowej domyślnej auto-konfiguracji to odpada Nam tworzenie beanów itd. 
Spring Boota ma wbudowany kontener servletów Tomcat, więc to również Nam odpada + mamy spakowane dependencje w startery. 
Świetnie. Teraz załużmy, że startujemy Naszą apkę. Mamy napisane wszystkie warstwy MVC. 
Nasza apka jest z automatu deployowana jako war na serwer Tomcat. 
(Wiem, że Spring Boot ogarnia większość tych rzeczy sam, ale żeby zadać odpowiednie pytanie, muszę to jakoś opisać ( ͡o ͜ʖ ͡o) )
Ok, to lecimy. 
- User wysyła requesta
- Nasza apka odbiera requesta, zczytuje konfigurację z web.xml lub java-based-config.class gdzie skonfigurowany jest DispatcherServlet.
- Teraz ten DispatcherServlet (innymi słowy Front Controller) tworzy tzw. WebApplicationContext container (ten kontekst rozszerza ApplicationContext) z pliku, który w XML musi wyglądać następująco "nazwaServletu-servlet.xml" np. "frontcontroller-dispacher-servlet.xml". W tym pliku konfiguracyjnym wskazujemy ścieżkę do zeskanowania, w której będziemy trzymać klasy z adnotacją @Controller. W momencie kiedy taka klasa zostanie znaleziona, to trafia do wspomnianego kontenera. 
- teraz powiedzmy, że user wykonał rządanie pod adresem /test.com/hello. Ścieżka w pliku konfiguracyjnym web.xml była następująca /test.com/*, a więc ten adress łapie się w zakresie danego DispatcherServleta. Jeśli się łapie, to uderzamy do konfiguracji dispachera, czyli pliku "frontcontroller-dispatcher-servlet.xml". W tym pliku jak już wyżej wspomniałem mamy ścieżkę, która jest skanowana pod kątem klas z @Controller. 
- teraz tzw. Handler Mapping znajduje pasujący controller na podstawie URL i tak jakby zwraca go z powrotem do DispatcherServletu. 
- teraz tzw. Handler Adapter egzekwuje/wywołuje logikę biznesową wewnątrz controllera.
- controller woła serwis 
- serwis woła warstwę DAO 
- DAO łączy się z baza danych i lecimy z powrotem do góry
- DAO -> service
- service -> controller 
- controller -> Handler Adapter 
- Handler Adapter -> Dispatcher
- teraz na podstawie zwróconego ModelAndView (lub samego Stringa wskazującego na nazwę widoku) Dispatcher komunikuje się z tzw. ViewResolverem. 
- ViewResolver znajduje po prefixie i sufixie odpowiedni View i zwraca lokację widoku do Dispatchera
- Dispatcher znając lokację docelowego widoku przesyła do niego model (dane) i następnie ten widok jest zwracany jako Response.
Czy dobrze rozumiem workflow MVC? 
Natomiast moje docelowe pytanie, to jak wewnętrznie wygląda workflow dla właśnie aplikacji RESTfulowej. Nie mamy wtedy żadnych widoków, używamy właśnie @RestController, który zawiera w sobie adnotacje @Controller + @RequestBody. Dzięki temu wiemy, że taki controller zwracać ma response w postaci JSON lub XML. Klient w headerze ("Accept") wysyła info o tym, w jakiej postaci chce uzyskać odpowiedź i wtedy uruchamiany jest odpowiedni HttpMessageConverter, np. MappingJackson2HttpMessageConverter. 
Okej, ale co z działaniem pod spodem. Coś musi wykrywać, do jakiego controllera uderzyć. Czy działa to na podobnej zasadzie, tylko pomijany jest po prostu widok i od strzała wysyłany jest JSON/XML?
Z góry dziękuje każdemu, któremu chciało się powyższy esej przeczytać.
splatch

@Yeboy Twoja dedukcja jest mniej więcej poprawna. Końcowy etap, o który pytasz wygląda w ten sposób że jest coś takiego co się nazywa ViewResolver, który jest odpowiedzialny za wybranie odpowiedniego widoku. Dodatkowo jest implementacja ContentNegotiatingViewResolver, która na podstawie nagłówka Accept szuka najlepiej pasującej implementacji widoku.

Jeśli bijesz przeglądarką - masz Accept: text/html, jak wywołujesz coś do API to zazwyczaj będziesz miał application/json i tak dalej.

Logikę view resolvera możesz prześledzić samodzielnie, jest ona odpalana przed samym widokiem.


Druga kwestia odnośnie samego mapowania metod - domyślnie kontrolery mają po jednej metodzie na każdą ścieżkę. W JAXRS, być może w Springu też, jest tak że metod do każdej ścieżki może być kilka i wybierana jest ta, która ma najlepsze dopasowanie do ścieżki i samej treści zapytania. Czyli przy GET /, Accept: text/html najpierw pójdzie metoda z @RequestMapping(consumes=text/html), później z @RequestMapping(consumes=text/*) a na końcu będzie @RequestMapping.


Co do samego widoku i tego jak on działa - wystarczy że wyrzuci coś do HttpServletResponse i output stream. Czyli dla JSP/freemarker itd będzie procesowanie szablonów a dla XML/JSON po prostu zrzucenie modelu prosto do odpowiedniego formatu.

Zaloguj się aby komentować