JavaRush /Blog Java /Random-PL /Od Hello World do Spring Web MVC i co mają z tym wspólneg...
Viacheslav
Poziom 3

Od Hello World do Spring Web MVC i co mają z tym wspólnego serwlety

Opublikowano w grupie Random-PL
Od Hello World do Spring Web MVC i co mają z tym wspólnego serwlety - 1

Wstęp

Jak wiemy, sukces Javy nastąpił właśnie dzięki ewolucji oprogramowania, które stara się łączyć z siecią. Dlatego za podstawę przyjmiemy zwykłą aplikację konsolową „ Hello World ” i zrozumiemy, czego potrzebuje, aby stać się aplikacją sieciową z aplikacji konsolowej. Najpierw musisz utworzyć projekt Java. Programiści to leniwi ludzie. W czasach prehistorycznych, gdy jedni polowali na mamuty, inni siedzieli i starali się nie pomylić w całej gamie bibliotek Java i struktur katalogów. Aby programista mógł kontrolować proces tworzenia aplikacji, aby mógł po prostu napisać „Chcę bibliotekę takiej a takiej wersji 2”, wymyślili specjalne narzędzia - buduj systemy. Dwa najbardziej znane to Maven i Gradle . W tym artykule użyjemy Gradle. Gdybyśmy wcześniej musieli sami tworzyć strukturę katalogów, teraz Gradle, korzystając z wtyczki Gradle Init, pozwala nam w jednym poleceniu utworzyć projekt Java ze strukturą katalogów i bazową klasą Main: gradle init --type java-application To polecenie wykonuje inicjalizację (init) dla nam aplikację Java (aplikacja Java) z konsolą Hello World. Po zakończeniu w katalogu pojawi się plik - build.gradle . To jest nasz skrypt build - czyli pewien skrypt służący do tworzenia aplikacji z opisem jakie czynności należy w tym celu wykonać. Otwórzmy go i dodajmy do niego linijkę: jar.baseName = 'webproject' Gradle pozwala na wykonywanie różnych akcji na projekcie i te akcje nazywane są zadaniami . Po wykonaniu polecenia (zadania) gradle buildw katalogu /build/libs zostanie utworzony plik JAR . I jak się domyślacie, jego nazwa będzie teraz brzmieć webproject.jar . Ale jeśli wykonamy java -jar ./build/libs/webproject.jar, otrzymamy błąd: no main manifest attribute. Dzieje się tak dlatego, że do aplikacji Java trzeba dołączyć manifest - jest to opis tego, jak pracować z aplikacją, jak ją postrzegać. Wtedy JVM, która wykona aplikację Java, będzie wiedziała, która klasa jest punktem wejścia do programu i inne informacje (na przykład ścieżkę klasy). Jeśli przyjrzymy się bliżej zawartości skryptu budującego, zobaczymy podłączone wtyczki. Na przykład: apply plugin: 'java' Jeśli przejdziemy na stronę wtyczki Gradle Java , zobaczymy, że możemy skonfigurować manifest:
jar {
    manifest {
        attributes 'Main-Class': 'App'
    }
}
Główna klasa będąca punktem wejścia do programu została dla nas wygenerowana przez Gradle Init Plugin. Jest to nawet określone w parametrze mainClassName. Nam to jednak nie odpowiadało, bo... to ustawienie odnosi się do innej wtyczki, wtyczki aplikacji Gradle . Mamy więc aplikację Java, która wyświetla Hello World na ekranie. Ta aplikacja Java jest spakowana w formacie JAR (Java ARchive). Jest prosty, oparty na konsoli i nieaktualny. Jak zamienić to w aplikację internetową?
От Hello World до Spring Web MVC и при чём тут сервлеты - 2

API serwletów

Aby Java mogła współpracować z siecią, już w starożytności pojawiła się specyfikacja zwana Servlet API . To właśnie ta specyfikacja opisuje interakcję klient-serwer, otrzymanie wiadomości od klienta (na przykład przeglądarki) i wysłanie odpowiedzi (na przykład z tekstem strony). Oczywiście od tego czasu wiele się zmieniło, ale chodzi o to, że aby aplikacja Java stała się aplikacją internetową, wykorzystuje się Servlet API. Aby nie spekulować bezpodstawnie, sięgnijmy po tę właśnie specyfikację: JSR-000340 JavaTM Servlet 3.1 . Przede wszystkim interesuje nas „ Rozdział 1: Przegląd ”. Opisuje podstawowe pojęcia, które musimy zrozumieć. Po pierwsze, czym jest serwlet? Rozdział „ 1.1 Czym jest serwlet? ” mówi, że serwlet to komponent Java zarządzany przez kontener i generujący dynamiczną zawartość. Podobnie jak inne komponenty Java, serwlet jest klasą Java skompilowaną do kodu bajtowego i można ją załadować na serwer WWW przy użyciu technologii Java. Ważne jest, aby serwlety współdziałały z klientem sieciowym (na przykład przeglądarką) w ramach paradygmatu żądanie/odpowiedź, który jest implementowany przez kontener serwletów. Okazuje się, że serwlety żyją w pewnego rodzaju kontenerze serwletów. Co to jest? W rozdziale „ 1.2 Co to jest kontener serwletów? ” jest powiedziane, że kontener serwletów to część serwera WWW lub serwera aplikacji, która zapewnia usługi sieciowe, za pośrednictwem których wysyłane są żądania i odpowiedzi. Ten właśnie kontener serwletów zarządza cyklem życia serwletów. Wszystkie kontenery serwletów muszą obsługiwać przynajmniej protokół HTTP, ale mogą obsługiwać inne. Na przykład HTTPS. Ważne jest również, aby Kontener Serwletów mógł nakładać wszelkie ograniczenia związane z bezpieczeństwem na środowisko, w którym wykonywane są serwlety. Ważne jest również, że zgodnie z „ 10.6 Web Application Archive File ” aplikacja internetowa musi być spakowana w pliku WAR (Web ARchive). Oznacza to, że teraz musimy usunąć nasze wtyczki jar i aplikacji i zająć się czymś innym. A to jest wtyczka Gradle WAR . I zamiast jar.baseName określ war.baseName Ponieważ Ponieważ nie używamy już wtyczki jar, usunęliśmy również ustawienia manifestu. Kiedy uruchomiliśmy plik JAR, trzeba było poinformować wirtualną maszynę Java (JVM) za pośrednictwem manifestu, jak pracować z naszą aplikacją. Ponieważ obsługiwał go JVM. Najwyraźniej aplikacja internetowa jest wykonywana przez jakiś serwer sieciowy. Okazuje się, że musi mu w jakiś sposób podpowiedzieć, jak pracować z naszą aplikacją webową? I okazuje się, że tak. Aplikacje internetowe mają swój własny, specjalny manifest. Nazywa się to deskryptorem wdrażania . Poświęcono mu całą sekcję: „ 14. Deskryptor wdrożenia ”. Jest ważny rozdział: „ Rozdział 10:". Opowiada o tym, czym jest aplikacja internetowa z punktu widzenia Servlet API. Na przykład w rozdziale " 10.5 Struktura katalogów " wskazano, gdzie powinien znajdować się deskryptor wdrażania: /WEB-INF/web.xml. Gdzie umieścić WEB-INF? Jak podano we wtyczce Gradle WAR, dodaje ona nowy układ : src/main/webappStwórzmy zatem taki katalog, w środku utworzymy katalog WEB-INF, a w środku utworzymy plik web.xml.Ważne jest, aby katalog nazywa się WEB-INF, a nie META-INF! Skopiujmy go z „ 14.5.1 Przykład podstawowy ” Przykład XML:
От Hello World до Spring Web MVC и при чём тут сервлеты - 3
Jak widzimy, do konfiguracji używany jest dokument XML. Dokument XML, aby mógł zostać uznany za ważny (Valid), musi być zgodny z jakimś „schematem”. Można to traktować jako rodzaj interfejsu dla dokumentu XML. Schemat określa, jakie elementy mogą znajdować się w dokumencie XML, jaki typ danych może definiować element, kolejność, wymagania i inne aspekty. Przykład skopiowany z dokumentacji wskazuje wersję 2.5, ale my chcemy użyć wersji 3.1. Naturalnie specyfikacja zmieniała się wraz ze zmianą wersji i dodawaniem nowych funkcji. Dlatego musisz użyć innego schematu niż ten używany w wersji 2.5 (web-app_2_5.xsd). Jakiego schematu powinienem użyć dla wersji 3.1? Pomoże nam w tym dokumentacja, rozdział „ 14.3 Deskryptor wdrażania ”, który stwierdza specification is available at http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd To znaczy, że musimy wszędzie zastąpić link do schematu określonym xsd, nie zapominając o jego zmianie version="2.5"na 3.1, a także wszędzie zmienić przestrzeń nazw ( xmlns i w xsi:schemaLocation). Wskazują w jakiej przestrzeni nazw będziemy pracować (w dużym uproszczeniu jakich nazw elementów możemy używać). Jeśli otworzysz plik schematu, targetNamespace będzie zawierać tę samą przestrzeń nazw, którą powinniśmy określić:
От Hello World до Spring Web MVC и при чём тут сервлеты - 4
Jak pamiętamy, w pliku Manifest of the Jar napisaliśmy, jakiej klasy chcemy użyć. Co tu robić? Tutaj musimy określić, której klasy serwletu chcemy użyć, gdy otrzymamy żądanie od klienta sieciowego. Opis można przeczytać w rozdziale " 14.4 Diagram deskryptora wdrożenia ". Będzie to wyglądać tak:
От Hello World до Spring Web MVC и при чём тут сервлеты - 5
Tutaj wszystko jest proste. Serwerlet jest deklarowany, a następnie mapowany na określony szablon. W tym przypadku na /app. Po wykonaniu szablonu zostanie wykonana metoda serwletu. Dla urody należy przenieść klasę App do pakietu, nie zapominając o poprawieniu konfiguracji XML. Ale to nie wszystko. Aplikacja musi być serwletem. Co to znaczy być serwletem? Oznacza to, że musimy dziedziczyć po HttpServlet . Przykład można zobaczyć w rozdziale „ 8.1.1 @WebServlet ”. Zgodnie z nim nasza klasa App będzie wyglądać następująco:
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class App extends HttpServlet {
    public String getGreeting() {
        return "Hello world.";
    }

	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		response.setContentType("text/html");
		try {
			response.getWriter().println(getGreeting());
		} catch (IOException e) {
			throw new IllegalStateException(e);
		}
	}
}
Ale nasz projekt nie jest jeszcze gotowy. Ponieważ teraz polegamy na Servlet API w wersji 3.1. Oznacza to, że w naszym skrypcie kompilacji musimy wskazać zależność od Servlet API. JVM musi wiedzieć, że to, co napisałeś w kodzie, jest poprawne i jak z tego korzystać. Jak pamiętamy specyfikacja to w zasadzie tylko interfejsy opisujące jak to wszystko powinno działać. Implementacje leżą po stronie serwera WWW. Dlatego bez Servlet API będzie dostępna Znajdź wymaganą bibliotekę w Maven Central: javax.servlet-api . I dodaj wpis do bloku zależności . Jak widziałeś, w repozytorium Maven jest napisane „dostarczone”. Przed użyciem zależności należy określić zakres. Gradle nie ma zakresu o nazwie „pod warunkiem”, ale ma zakres „ tylko do kompilacji ”. Dlatego wskażemy: providedCompile 'javax.servlet:javax.servlet-api:3.1.0' Ugh, wszystko wydaje się być w porządku? Gradle Build zbuduje nasz projekt w pliku WAR. I co powinniśmy z tym dalej zrobić? Po pierwsze potrzebujemy serwera WWW. W Google piszemy „ lista serwerów WWW Java ” i wyświetlamy listę serwerów WWW. Wybierzmy z tej listy na przykład TomCat . Wejdź na stronę Apache Tomcat , pobierz najnowszą wersję (obecnie wersja 9) jako archiwum zip (jeśli dla Windows). Rozpakuj go do jakiegoś katalogu. Hurra, mamy serwer WWW. Z katalogu serwera WWW w podkatalogu bin uruchamiamy catalinę z wiersza poleceń i sprawdzamy dostępne opcje. Zróbmy: catalina start. Każdy serwer WWW ma katalog, który monitoruje. Jeśli pojawi się tam plik aplikacji internetowej, serwer WWW rozpocznie jego instalację. Ta instalacja nazywa się wdrożeniem lub wdrożeniem . Tak, właśnie dlatego „ deskryptor wdrożenia ”. Czyli jak prawidłowo wdrożyć aplikację. W Tomcat ten katalog to webapps . Skopiujmy tam wojnę, którą stworzyliśmy, używając kompilacji stopniowej. Następnie w logu zobaczymy coś takiego: Deployment of web application archive [tomcat\webapps\webproject.war] has finished in [время] ms Aby jeszcze lepiej zrozumieć, w katalogu Tomcat dokonamy edycji pliku \conf\tomcat-users.xml, dodając następujące wiersze:
От Hello World до Spring Web MVC и при чём тут сервлеты - 6
Teraz restartujemy serwer (catalina stop, catalina start) i udajemy się pod wskazany adres.Tutaj http://127.0.0.1:8080/manager zobaczymy ścieżki wszystkich aplikacji. Nasz projekt internetowy najprawdopodobniej otrzymał ścieżkę /webproject. Jaka jest ta ścieżka? Specyfikacja w rozdziale „ 10.1 Aplikacje internetowe na serwerach sieciowych ” stwierdza, że ​​aplikacja internetowa jest powiązana z pewną ścieżką wewnątrz aplikacji (w tym przypadku /webproject). Wszystkie żądania przechodzące przez tę ścieżkę będą powiązane z tym samym kontekstem ServletContext. Ta ścieżka jest również nazywana kontekstowym Rootem . Zgodnie z „ 10.2 Związek z ServletContext ” kontener serwletów łączy aplikację internetową i ServletContext jeden do jednego. Oznacza to, że każda aplikacja internetowa ma swój własny ServletContext. Co to jest kontekst serwletu ? Jak podaje specyfikacja, ServletContext to obiekt udostępniający serwletom „widok aplikacji ”, w której działają. Kontekst serwletu opisano bardziej szczegółowo w rozdziale 4 specyfikacji API serwletu. Co zaskakujące, interfejs API serwletów w wersji 3.1 nie wymaga już obecności pliku web.xml. Na przykład możesz zdefiniować serwlet za pomocą adnotacji:
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/app2")
public class App2 extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");
        response.getWriter().println("app2");
    }
}
Polecany również na temat: „ Wywiad Java EE - JEE Servlet API (pytania i odpowiedzi) ”. Mamy więc serwlet - jest on odpowiedzialny za to, jaką odpowiedź dać klientowi WWW. Mamy kontener ServletContainer, który odbiera żądania od użytkownika, dopasowuje ścieżkę, do której uzyskano dostęp, do ścieżki do serwletu i jeśli zostanie znaleziona zgodność, uruchamia serwlet. Cienki. Jakie miejsce w tym obrazie świata zajmuje wiosna ?

Wiosenny internetowy MVC

Świetnie, mamy aplikację internetową. Teraz musimy połączyć Springa. Jak możemy to zrobić? Najpierw musisz dowiedzieć się, jak prawidłowo podłączyć Springa do swojego projektu. Okazuje się, że wcześniej można było to zrobić zgodnie z dokumentacją projektu platformy Spring , ale obecnie „ Platforma zakończy swój okres wsparcia 9 kwietnia 2019 r. ”, czyli nie jest wskazane użyj go, ponieważ wkrótce nie będzie już obsługiwany. Jedynym wyjściem jest „ Zachęcanie użytkowników Platformy do rozpoczęcia korzystania z zarządzania zależnościami Spring Boot ”. Przejdźmy zatem do dokumentacji Spring Boot . Wyjaśnię, że nie używamy samego Spring Boot, a jedynie zarządzania zależnościami z Spring Boot. Oznacza to, że projekt Spring Boot może dostarczyć wiedzy o tym, jakich wersji bibliotek użyć (w tym Spring MVC). Tam znajdziemy 3.2. Korzystanie z zarządzania zależnościami Spring Boot w izolacji . Zgodnie z dokumentacją dodaj do skryptu kompilacji:
plugins {
    id 'org.springframework.boot' version '2.0.4.RELEASE' apply false
}
apply plugin: 'io.spring.dependency-management'
I
dependencyManagement {
    imports {
        mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
    }
}
Jak widać wskazaliśmy apply false, tj. Nie używamy samego Spring Boot, ale korzystamy z niego do zarządzania zależnościami. To zarządzanie zależnościami nazywane jest również BOM – „ Bill Of Materials ”. Teraz jesteśmy gotowi na podłączenie samego projektu Spring Web MVC. Spring Web MVC jest częścią projektu Spring Framework i interesuje nas sekcja „ Web Servlet ”. Dodajmy zależność do skryptu kompilacji: compile 'org.springframework:spring-webmvc'. Jak widzimy, ustawiamy kompilację zakresu, ponieważ serwer WWW nie zapewnia nam Springa. Nasz projekt zmuszony jest uwzględnić w sobie bibliotekę Spring. Następnie ważne jest, abyśmy przeczytali sekcję „ 1.2. DispatcherServlet ”, gdzie jest powiedziane, że Spring MVC jest zbudowany wokół wzorca „ Kontroler frontowy ”, w którym istnieje jakiś centralny serwlet zapewniający konfigurację i delegowanie do innych komponentów . Dyspozytor można przetłumaczyć jako dyspozytor. Zatem przede wszystkim w pliku web.xml deklarujemy:
От Hello World до Spring Web MVC и при чём тут сервлеты - 7
Jak widzimy, jest to w rzeczywistości zwykły odbiornik zdefiniowany w specyfikacji API serwletu. Mówiąc dokładniej, jest to ServletContextListener, czyli jest uruchamiany w celu zainicjowania kontekstu serwletu dla naszej aplikacji internetowej. Następnie musisz określić ustawienie, które powie Springowi, gdzie znajduje się jego specjalna konfiguracja XML z ustawieniami:
От Hello World до Spring Web MVC и при чём тут сервлеты - 8
Jak widać, jest to zwykłe ustawienie, które jest przechowywane na poziomie kontekstu serwletu, ale które będzie używane przez Springa podczas inicjowania kontekstu aplikacji. Teraz musisz zadeklarować zamiast wszystkich serwletów jednego dyspozytora, który będzie dystrybuował wszystkie pozostałe żądania.
От Hello World до Spring Web MVC и при чём тут сервлеты - 9
I nie ma tu żadnej magii. Jeśli się przyjrzymy, będzie to HttpServlet, w którym Spring wykonuje wiele rzeczy, co czyni go frameworkiem. Pozostaje tylko skorelować (zmapować) konkretny szablon adresu URL z serwletem:
От Hello World до Spring Web MVC и при чём тут сервлеты - 10
Wszystko jest tak samo, jak robiliśmy to wcześniej. Stwórzmy teraz coś, co powinien wyświetlić nasz serwer WWW. Przykładowo utwórzmy podkatalog stron w naszym WEB-INF i będzie tam plik hello.jsp. Treść może być najbardziej prymitywna. Na przykład wewnątrz znaczników HTML znajduje się znacznik h1 z tekstem „ Hello World ”. I nie zapomnij utworzyć pliku applicationContext.xml, który określiliśmy wcześniej. Weźmy przykład z dokumentacji Springa: „ 1.10.3. Automatyczne wykrywanie klas i rejestrowanie definicji fasoli ”.
От Hello World до Spring Web MVC и при чём тут сервлеты - 11
Ponieważ włączamy w ten sposób autodetekcję, możemy teraz stworzyć 2 klasy (będą one uważane za Spring Beans ze względu na zastosowanie specjalnych adnotacji Spring), które Spring sam teraz utworzy i za ich pomocą dostosuje naszą aplikację:
  1. Konfiguracja internetowa, na przykład konfiguracja w stylu Java:

    @Configuration
    @EnableWebMvc
    public class WebConfig implements WebMvcConfigurer {
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            registry.jsp("/WEB-INF/pages/", ".jsp");
        }
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        }
    }

    Ten przykład opisano w dokumentacji Spring Framework: „ 1.11. Konfiguracja MVC ”.

    Tutaj rejestrujemy ViewResolver, który pomoże określić, gdzie znajdują się strony jsp. Druga metoda zapewnia włączenie „ Domyślnego serwletu ”.

    Więcej na ten temat możesz przeczytać tutaj: „ Jaka jest potrzeba i zastosowanie domyślnej procedury obsługi serwletów ”.

  2. Kontroler HelloController do opisywania mapowania żądań na konkretną stronę JSP

    @Controller
    public class HelloController {
        @GetMapping("/hello")
        public String handle(Model model) {
            return "hello";
        }
    }

    Tutaj wykorzystaliśmy adnotację @Controller opisaną w dokumentacji w rozdziale „ 1.4. Kontrolery z adnotacjami ”.

Teraz, gdy nasza aplikacja zostanie wdrożona i wyślemy żądanie /webproject/hello(gdzie /webproject jest kontekstowym katalogiem głównym), najpierw zostanie przetworzony serwer DispatcherServlet. On, jako główny dyspozytor, ustali, że /* pasujemy do bieżącego żądania, co oznacza, że ​​DispatcherServlet musi coś zrobić. Następnie przejrzy wszystkie znalezione mapowania. Zobaczy, że istnieje HelloController z metodą uchwytu odwzorowaną na /hello i wykona ją. Ta metoda zwróci tekst „cześć”. Ten tekst zostanie odebrany przez ViewResolver, który powie serwerowi, gdzie szukać plików jsp, które należy wyświetlić klientowi. W ten sposób klient ostatecznie otrzyma tę bardzo cenną stronę.

Wniosek

Mam nadzieję, że z artykułu będzie jasno wynikać, że słowo „kontekst” nie jest straszne. Specyfikacje te okazują się bardzo przydatne. A dokumentacja jest naszym przyjacielem, a nie wrogiem. Mam nadzieję, że będzie jasne, na czym opiera się Spring, jak się łączy i co ma z tym wspólnego API Servletów.
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION