Pogodynka 03 artykuł
Pogodynka – działający termometr
19 marca 2017
pogodynka w spring mvc
Pogodynka – szkielet aplikacji webowej
26 marca 2017
serwlety w aplikacjach webowych

W artykule tym przeczytasz o serwletach. Poznasz podstawy protokołu HTTP. Dowiesz się czym są serwlety i jak je pisać. Dowiesz się także czym jest plik war i przeczytasz o jego strukturze. Po lekturze tego artykułu zrozumiesz co kryje się pod spodem Spring MVC. Napiszesz też swoją aplikację webową, używającą serwletów. Zapraszam do lektury.

Chociaż artykuł ten pisany jest z myślą o początkujących do jego pełnego zrozumienia przyda się wiedza, którą zawarłem w kliku innych artykułach. Zachęcam do zapoznania się z nimi przed podejściem do tego artykułu:

Artykuł ten bazuje na specyfikacji serwletów w wersji 3.1, która jest częścią specyfikacji Java Enterprise Edition 7. Planowany termin wydania specyfikacji Java Enterprise Edition 8 to koniec 2017 roku, w ramach tej specyfikacji wydana ma być także nowa specyfikacja serwletów w wersji 4.0.

Wprowadzenie do protokołu HTTP

Aby zacząć poważnie myśleć o tworzeniu aplikacji webowych niezbędna jest wiedza dotycząca protokołu HTTP (ang. Hypertext Transfer Protocol). Poniżej znajdziesz kilka podstawowych informacji, które będą Ci potrzebne w pracy z aplikacją webową.

  • Protokół HTTP jest oparty na komunikacji pomiędzy klientem a serwerem. Klientem może być na przykład przeglądarka internetowa. Serwer to aplikacja, która odpowiada na żądania klienta.
  • Komunikacja pomiędzy klientem a serwerem oparta jest na żądaniach (ang. request) i odpowiedziach (ang. response). Klient wysyła żądanie, na które serwer udziela odpowiedzi.
  • Zarówno żądania, jak i odpowiedzi mogą zawierać nagłówki i treść. Nagłówki służą do przekazania części informacji. W nagłówku na przykład zawarte mogą być informacje o przeglądarce, z której wysłano żądanie. Treścią odpowiedzi może być na przykład zawartość strony internetowej.
  • Protokół HTTP oparty jest o tak zwane “czasowniki HTTP”. Można powiedzieć, że czasownik ten określa rodzaj żądania jakie wysyła klient. Wszystkich czasowników jest 9, podstawowe rodzaje żądań to GET, POST, PUT, DELETE.
  • W większości przypadków używane są żądania typu GET i POST. Na przykład do wysłania informacji, które uzupełniłeś w formularzu używa się żądania typu POST. Natomiast zwykłe otworzenie strony, wpisanie adresu strony w przeglądarce to żądanie typu GET.

Adres URL

Każde z żądań dotyczy jakiegoś zasobu. Na przykład otwierając stronę www.samouczekprogramisty.pl w przeglądarce wysyłasz żądanie GET http://www.samouczekprogramisty.pl. Ta część po GET to nic innego jak URL (ang. Uniform Resource Locator). Innymi słowy adres strony www.

Adres URL może składać się z kilku części

W ogromnej większości przypadków część z użytkownikiem i hasłem jest pomijana. Używana jest ona do uwierzytelniania, jednak metoda ta nie jest powszechnie używana. Port także jest pomijany. Pomijamy go ponieważ dla protokołu http domyślny port to właśnie 80 więc nie ma potrzeby go dodawać. Odrzucając rzadziej używane elementy adres url wygląda następująco

Mamy tutaj informację o protokole (http), serwerze (www.samouczekprogramisty.pl), ścieżce (/kurs-programowania-java) i parametrach (parametr=wartosc&innyParametr=wartosc).

Czym jest serwlet

Serwlet to klasa, która implementuje interfejs Servlet. Instancje tej klasy tworzone są przez kontener serwletów (na przykład Jetty). Instancje te wiedzą jak odpowiadać na żądania, które dostają od klienta.

Do obsługi żądania klienta służy metoda service. Metoda ta przyjmuje jako parametry żądanie i odpowiedź. Na podstawie parametrów żądania odpowiednio modyfikuje przekazany argument odpowiedzi.

Szczerze mówiąc do tej pory ani razu nie napisałem klasy, która bezpośrednio implementuje ten interfejs. Używa się do tego klas, które upraszczają tworzenie serwletów. Są to klasy GenericServlet i HttpServlet.

Chociaż specyfikacja serwletów, nie wymaga użycia serwletów z protokołem HTTP w praktyce nie spotkałem się z innym zastosowaniem. Zatem z dwóch wyżej wspomnianych klas powinieneś zapamiętać HttpServlet.1

Interfejs serwletów

Wcześniej wspomniałem Ci już o metodzie service. Metodę tę musiałbyś zaimplementować jeśli utworzyłbyś klasę, która implementuje interfejs Servlet bezpośrednio. W przypadku klasy, która dziedziczy po HttpServlet wystarczy nadpis odpowiednie metody. Na przykład, jeśli twój serwlet ma obsłużyć żądania typu GET musisz zaimplementować metodę doGet. Istnieją też metody dla pozostałych “czasowników”, na przykład doPost czy doPut.

W interfejsie serwletów znajdują się też metody, które są wykorzystywane w trakcie cyklu życia serwletu. Jak wspomniałem wyżej kontener odpowiedzialny jest za tworzenie instancji serwletu. Ponadto kontener zarządza cyklem życia serwletu używając metod z tego interfejsu.

Cykl życia serwletu

Każda instancja serwletu ma swój cykl życia. Jest to jasno zdefiniowana lista etapów, przez które przechodzi każdy serwelt. Lista ta wygląda następująco:

Utworzenie instancji serwletu

Kontener wyszukuje klas serwletów i następnie tworzy jedną instancję serwletu2.

Inicjalizacja serwletu

Z racji tego, że to kontener serwletów odpowiedzialny jest za tworzenie instancji klasy serwletu nie ma możliwości przekazania odpowiednich parametrów do konstruktora. Do inicjalizacji stanu serwletu służy metoda init i jest ona wywoływana przez kontener przed rozpoczęciem obsługi żądań przez dany serwlet.

Obsługa żądań

W trakcie tego etapu kontener serwletów może wielokrotnie użyć tej samej instancji to obsługi wielu żądań. Pociąga to za sobą dość poważne konsekwencje. Możliwa jest sytuacja, w której w tym samym czasie instancja serwletu będzie obsługiwała kilka żądań jednocześnie. Na przykład jest to możliwe gdy kilku użytkowników wejdzie na ten sam adres. Obsługa każdego żądania do wywołanie przez kontener metody service.

Zniszczenie serwletu

Kontener może usunąć daną instancję serwletu. Przed zniszczeniem instancji wywołana zostanie metoda destroy. Dzięki temu wewnątrz serweltu masz szansę na “posprzątanie”. Metoda ta może na przykład służyć do zamknięcia połączenia z bazą danych. Nie masz pewności jak długo serwlet będzie żył, o tym decyduje kontener.

Kontener serwletów

Z poprzednich paragrafów dowiedziałeś się już, że kontener serwletów zarządza cyklem życia serwletów. Nie jest to jedyna odpowiedzialność kontenera. Kontener serwletów odpowiedzialny jest za “wyszukanie” klas odpowiedzialnych za działanie aplikacji.

W pierwszych wersjach specyfikacji niezbędny był do tego plik web.xml (tak zwany deskryptor wdrożenia), teraz aplikację webową można skonfigurować przy pomocy adnotacji. Dalej jednak to kontener musi “znaleźć” te klasy.

Poza serwletami istnieją też inne komponenty aplikacji webowej opisane w specyfikacji serlwetów. Na przykład filtry czy “listnenery” (ang. listener) (ma ktoś z was pomysł jak przetłumaczyć to słowo na polski :)?). Także i tutaj kontener serwletów pełni kluczową rolę. Kontener zarządza cyklem życia tych elementów.

Przy konstruowaniu odpowiedzi na żądania pomocne są pliki typu JSP (ang. Java Server Pages). Powtórzę się – kontener zarządza cyklem życia takich plików.

W przypadku aplikacji webowych możemy mówić o kilku kontekstach. Możemy wyróżnić na przykład kontekst żądania czy kontekst aplikacji. Także tutaj kontener serwletów za nie odpowiada.

Jest jeszcze wiele innych aspektów, za które odpowiada kontener wybiegają jednak poza zakres tego artykułu. Napiszę jeszcze o jednym z nich. Kontener także odpowiedzialny jest za instalowanie aplikacji webowej, wiąże się to z “czytaniem” zawartości pliku war, w którym znajduje się aplikacja webowa.

Plik war

W artykule opisującym Javę z linii poleceń możesz przeczytać o plikach jar. W przypadku aplikacji webowych plik war pełni kluczową rolę.

W skład aplikacji webowej mogą wchodzić:

  • serwlety,
  • strony jsp,
  • inne klasy Java,
  • zależności aplikacji webowej,
  • statyczne pliki (na przykład html czy css),
  • pliki konfiguracyjne opisujące aplikację webową.

Wszystkie te pliki pakowane są w odpowiednią strukturę wewnątrz pliku war.

Struktura aplikacji webowej

Podobnie jak w przypadku pliku jar jest to zwykłe archiwum zip ze zmienionym rozszerzeniem (war a nie zip). Buduje się go przy pomocy tych samych narzędzi jak plik jar . Struktura przykładowego pliku war jest następująca:

Pliki takie jak index.html, publiczny_ktalog/strona.html czy style.css są publicznie dostępne. Oznacza to tyle, że kontener serwletów może serwować te pliki.

Sprawa wygląda zupełnie inaczej w przypadku katalogu WEB-INF. Jest to katalog, który zawiera dane, które nigdy nie mogą być bezpośrednio “serwowane” przez kontener. Wewnątrz WEB-INF znajdują się inne katalogi:

  • classes – zawiera on skompilowane klasy aplikacji webowej,
  • lib – zawiera on spakowane pliki jar potrzebne do działania aplikacji webowej.

Dodatkowo aplikacja webowa może zawierać tak zwany deskryptor wdrożenia (ang. deployment descriptor). Jest to plik web.xml, który konfiguruje działanie aplikacji webowej. W przypadku prostych aplikacji nie jest on wymagany, całą konfigurację można dostarczyć przy pomocy adnotacji.

Sekret działania Spring MVC

W codziennej pracy z aplikacjami webowymi programiści bardzo rzadko (wcale?) tworzą swoje serwlety. W ogromnej większości przypadków to biblioteka pomagająca przybudowaniu aplikacji webowych zawiera “główny serwlet”.

Ten serwlet pośredniczy przy wszystkich zapytaniach do danej aplikacji webowej. Następnie w zależności od ścieżki, której dotyczy dane żądanie przekazuje je do odpowiedniej klasy. I to właśnie te klasy pisane są przez programistów.

Dla przykładu w Spring MVC takim “głównym serwletem” jest DispatcherServlet. Zachęcam do zajrzenia do źródeł tego serwletu. Zobaczyć możesz tam jakie mechanizmy użyte są do przekazania żądania dalej.

Jak przeszukasz DispatcherServlet i klasy po których dziedziczy dogrzebiesz się do dobrze znanych metod, takich jak service czy doGet.

Pierwsza aplikacja webowa

Nadszedł czas, żeby wykorzystać tę wiedzę w praktyce. Proszę spójrz na przykład poniżej:

DateServlet to serwlet, który odpowiada na żądania typu GET wysłane na adres /date. Oczywiście w produkcyjnych aplikacjach w inny sposób konstruuje się odpowiedzi, jednak przykład ten pokazuje ogólną zasadę działania.

response.getWriter() zwraca instancję klasy PrintWriter. Należy traktować ją jako zawartość pliku, która zostanie wysłana w odpowiedzi na żądanie. Jeśli użyjemy tej metody odpowiedź, którą wygenerujemy musi być tekstowa (nie binarna). Ostatnia linijka metody doGet to właśnie generowanie treści odpowiedzi, gdzie odpowiadamy aktualną datą umieszczoną wewnątrz podstawowych znaczników html.

Taką klasę serwletu umieszczamy w projekcie. W moim przypadku jego struktura wygląda następująco:

Na tym etapie proszę użyj przykładowych plików umieszczonych w repozytorium kodu. W osobnym artykule wytłumaczę zasadę działania Gradle w przypadku aplikacji webowych. Bazowy plik build.gradle i plik 01_serwlety/build.gradle pozwalają na uruchomienie tak utworzonej aplikacji webowej:

Następnie uruchomienie przeglądarki i wpisanie adresu http://localhost:8080/01_serwlety/date powinno pokazać działającą aplikację, która wyświetla datę:

dateservlet odpowiedz

Ćwiczenie do wykonania

Na podstawie przykładowej aplikacji napisz serwlet, który wyświetli liczbę sekund, od dnia Twojego urodzenia. Po wejściu na stronę, którą obsługuje dany serwlet powinna pokazać się liczba sekund, od Twoich urodzin.

Dodatkowe materiały do nauki

Poniżej przygotowałem dla Ciebie kilka dodatkowych linków, które pomogą Ci rozszerzyć wiedzę z tego artykułu:

Podsumowanie

Gratulacje! Udało Ci się przeczytać cały artykuł, a nie należał on do najkrótszych ;). Po jego przeczytaniu wiesz czym jest serwlet. Poznałeś strukturę pliku war, znasz podstawowy zakres odpowiedzialności kontenera serwletów. Poznałeś też część magii, która kryje się pod spodem biblioteki Spring MVC. No i oczywiście utworzyłeś swoją pierwszą dynamiczną aplikację webowową!

Mimo objętości artykułu nie wyczerpałem tematu aplikacji webowych, jest to jeden z serii artykułów opisujących podstawy aplikacji webowych w Javie. Jeśli nie chcesz pominąć kolejnych artykułów polub Samouczka na facebooku i zapisz się do newslettera.

Do następnego razu!

Newsletter

  Jeśli chcesz otrzymywać informacje o nowych artykułach na blogu prosto na Twój email, zapisz się 🙂

Zdjęcie dzięki uprzejmości https://www.flickr.com/photos/rafa2010/15353313381/sizes/l

  1. Prawda jest taka, że używając bibliotek pomagających tworzyć aplikacje webowe sam nie będziesz pisał serwletów. Będą to zwykłe klasy, które będą przez bibliotekę wywoływane. Biblioteka dostarczy “główny” serwlet, który będzie przekazywał żądania dalej.
  2. Chodzi o zachowanie domyślne, kontener może utworzyć kilka instancji jeśli zaimplementujesz interfejs SingleThreadedModel. Takie podejście nie jest jednak polecane.

7 Komentarze

  1. Rafał napisał(a):

    Cześć! Świetny i ciekawy artykuł, niestety mam problem z przykładową aplikacją. Pobrałem przykład z GITa i próbowałem go odpalić. W konsoli wygląda, że wszystko działa w porządku (załączam print screen), jednak strona i tak się nie odpala, jakby lokalny serwer od razu został zamknięty. Masz może pomysł co jest nie tak? Pozdrawiam 😉

    https://zapodaj.net/d19c67f4ada69.png.html

    • Marcin Pietraszek napisał(a):

      Dziękuję, cieszę się, że się podoba :). Po uruchomieniu komendy gradle appRun odpali się serwer. Będzie on działał do momentu, kiedy naciściesz dowolny klawisz w terminalu.

      Troszkę inaczej to wygląda jeśli uruchomisz appRun jako komenda gradle z InteliJ. W tym przypadku InteliJ nie będzie czekał i od razu „wyłączy” serwer. Jeśli chcesz uruchomić to z InteliJ najszybciej zrobisz to używając okienka „Terminal” – wpisz w nim gradle appRun, wtedy powinno być OK 🙂

      • Rafał napisał(a):

        Dzięki wielkie 😉 Mała różnica, komenda to „gradlew appRun”, wtedy rzeczywiście wszystko działa, na szczęście dodałeś zrzut ekranu, więc udało mi się do tego dojść. Pozdrawiam miłego wieczoru

      • Rafał napisał(a):

        Mam jeszcze jedno pytanie, czy w zapowiadanych kolejnych artykułach o aplikacjach webowych mógłbyś nieco dokładniej opisać proces łączenia Springa przy użyciu Gradle? Jak to skonfigurować i od czego zacząć? Próbowałem przeglądać przykładowe zadanie, jednak przy pierwszym spotkaniu z tą technologią ciężko dojrzeć co i jak zostało zrobione. Pozdrawiam 😉

        • Marcin Pietraszek napisał(a):

          Jasne 🙂 Do momentu kiedy dojdziemy w tej serii do aplikacji opartych o Spring MVC jeszce trochę zostało. Zanim do tego dojdzie przyda się zrozumienie filtrów, „listenerów”, pojęcia kontekstu i zakresu (ang. scope). Później przyda się trochę wiedzy na temat IoC (ang. Inversion of Control). Mając to można zacząć bawić się ze Spring MVC (a i wtedy zrozumienie tego nie jest trywialne).

  2. Maciej S. napisał(a):

    listener – może po prostu słuchacz, nasłuchiwacz?

    • Marcin Pietraszek napisał(a):

      Słuchacz brzmi ciekawie. Szczerze mówiąc miałem z tym problem – do tej pory nie spotałem się z polskim tłumaczeniem tego terminu 😉

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *