16. Wprowadzenie do Springa MVC
16.1. Rola Springa MVC w aplikacji internetowej
Umieśćmy Spring MVC w kontekście tworzenia aplikacji internetowej. Najczęściej będzie ona oparta na architekturze wielowarstwowej, takiej jak poniższa:
![]() |
- warstwa [Web] stanowi warstwę kontaktową z użytkownikiem aplikacji internetowej. Użytkownik ten wchodzi w interakcję z aplikacją internetową za pośrednictwem stron internetowych wyświetlanych w przeglądarce. To właśnie w tej warstwie znajduje się Spring MVC i wyłącznie w tej warstwie;
- warstwa [métier] realizuje reguły biznesowe aplikacji, takie jak obliczanie wynagrodzenia lub faktury. Warstwa ta wykorzystuje dane pochodzące od użytkownika za pośrednictwem warstwy [Web] oraz dane z warstw SGBD za pośrednictwem warstwy [DAO];
- Warstwa [DAO] (Data Access Objects), warstwa [ORM] (Object Relational Mapper) oraz sterownik JDBC zarządzają dostępem do danych z warstwy SGBD. Warstwa [ORM] stanowi pomost między obiektami obsługiwanymi przez warstwę [DAO] a wierszami i kolumnami tabel w relacyjnej bazie danych. Specyfikacja o nazwie JPA (Java Persistence API) pozwala na abstrakcję od używanego ORM, o ile ten ostatni implementuje te specyfikacje. Tak będzie w przypadku niniejszego samouczka, dlatego od tej pory będziemy nazywać warstwę ORM warstwą JPA;
- integracja warstw odbywa się za pośrednictwem frameworka Spring;
16.2. Model rozwoju Spring MVC
Spring MVC implementuje model architektury zwany MVC (Model – Widok – Kontroler) w następujący sposób:
![]() |
Przetwarzanie żądania klienta przebiega w następujący sposób:
- żądanie – żądane obiekty URL mają postać http://machine:port/contexte/Action/param1/param2/....?p1=v1&p2=v2&.... [Front Controller] wykorzystuje plik konfiguracyjny lub adnotacje Java do „przekierowania” żądania do właściwego kontrolera i właściwej akcji w ramach tego kontrolera. W tym celu wykorzystuje pole [Action] z URL. Pozostała część URL i [/param1/param2/...] składa się z opcjonalnych parametrów, które zostaną przekazane do akcji. C w MVC to w tym przypadku ciąg znaków [Front Controller, Contrôleur, Action]. Jeśli żaden kontroler nie jest w stanie obsłużyć żądanej akcji, serwer WWW odpowie, że żądana akcja URL nie została znaleziona.
- Przetwarzanie
- (ciąg dalszy)
- Wybrana akcja może wykorzystać parametry parami, które przekazał jej [Front Controller]. Mogą one pochodzić z kilku źródeł:
- ze ścieżki [/param1/param2/...] pliku URL,
- z parametrów [p1=v1&p2=v2] akcji URL,
- z parametrów przesłanych przez przeglądarkę wraz z żądaniem;
- podczas przetwarzania żądania użytkownika akcja może wymagać warstwy [métier] [2b]. Po przetworzeniu żądania klienta może ono wywołać różne odpowiedzi. Klasycznym przykładem jest:
- strona błędu, jeśli żądanie nie mogło zostać poprawnie przetworzone
- w przeciwnym razie strona potwierdzenia
- akcja żąda wyświetlenia określonego widoku [3]. Widok ten wyświetli dane, które nazywamy modelem widoku. To właśnie litera M w MVC. Akcja utworzy ten model M [2c] i zażąda wyświetlenia widoku V [3];
- odpowiedź – wybrany widok V wykorzystuje model M utworzony przez akcję do zainicjowania dynamicznych części odpowiedzi HTML, którą musi wysłać do klienta, a następnie wysyła tę odpowiedź.
W przypadku usługi internetowej / jSON powyższa architektura ulega niewielkiej modyfikacji:
![]() |
- w [4a] model, który jest klasą Java, jest przekształcany w ciąg znaków jSON przez bibliotekę jSON;
- w [4b] ten ciąg znaków jSON jest wysyłany do przeglądarki;
Teraz wyjaśnijmy związek między architekturą internetową MVC a architekturą warstwową. W zależności od definicji modelu te dwie koncepcje mogą być ze sobą powiązane lub nie. Weźmy na przykład jednowarstwową aplikację internetową Spring MVC:
![]() |
Jeśli zaimplementujemy warstwę [Web] przy użyciu Springa MVC, otrzymamy wprawdzie architekturę internetową MVC, ale nie będzie to architektura wielowarstwowa. W tym przypadku warstwa [web] zajmie się wszystkim: prezentacją, logiką biznesową oraz dostępem do danych. Zadania te będą realizowane przez akcje.
Rozważmy teraz wielowarstwową architekturę internetową:
![]() |
Warstwa [Web] może zostać zaimplementowana bez frameworka i bez stosowania się do modelu MVC. Mamy więc architekturę wielowarstwową, ale warstwa internetowa nie implementuje modelu MVC.
Na przykład w środowisku .NET warstwa [Web]może być zaimplementowana przy użyciu ASP.NET i MVC, co daje architekturę warstwową z warstwą [Web] typu MVC. Po wykonaniu tej czynności można zastąpić tę warstwę ASP.NET MVC klasyczną warstwą ASP.NET (WebForms), zachowując pozostałe elementy (biznesową, DAO, ORM) bez zmian. Otrzymujemy wówczas architekturę warstwową z warstwą [Web], która nie jest już typu MVC.
W MVC stwierdziliśmy, że model M odpowiada widokowi V, c.a.d, czyli zbiorowi danych wyświetlanych przez widok V. Podano kolejną definicję modelu M dla MVC:
![]() |
Wielu autorów uważa, że to, co znajduje się po prawej stronie warstwy [Web], tworzy model M modelu MVC. Aby uniknąć niejasności, można mówić o:
- o modelu domeny, gdy mamy na myśli wszystko, co znajduje się po prawej stronie warstwy [Web]
- o modelu widoku, gdy mamy na myśli dane wyświetlane przez widok V
W dalszej części termin „model M” będzie odnosił się wyłącznie do modelu widoku V.
16.3. Projekt internetowy / jSON z wykorzystaniem Springa MVC
Strona [http://spring.io/guides] zawiera samouczki dla początkujących, pozwalające zapoznać się z ekosystemem Spring. Skorzystamy z jednego z nich, aby poznać konfigurację Maven niezbędną dla projektu Spring MVC.
16.3.1. Projekt demonstracyjny
![]() |
- w [1] importujemy jeden z przewodników Spring;
![]() |
- w [2] wybieramy przykład [Rest Service];
- w [3] wybieramy projekt Maven;
- w [4] wybieramy ostateczną wersję przewodnika;
- w [5] zatwierdzamy;
- w [6] – zaimportowany projekt;
Usługi internetowe dostępne za pośrednictwem standardowych interfejsów URL i dostarczające tekst jSON są często nazywane usługami REST (REpresentational State Transfer). Usługa jest określana jako RESTful, jeśli spełnia określone zasady.
Przyjrzyjmy się teraz zaimportowanemu projektowi, a najpierw jego konfiguracji Maven.
16.3.2. Konfiguracja Maven
Plik [pom.xml] ma następującą treść:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-rest-service</artifactId>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<properties>
<start-class>hello.Application</start-class>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-releases</id>
<url>https://repo.spring.io/libs-release</url>
</pluginRepository>
</pluginRepositories>
</project>
- wiersze 6–8: właściwości projektu Maven. Brakuje tagu [<packaging>] określającego typ pliku generowanego przez kompilację Maven. W przypadku jego braku stosowany jest typ [jar]. Aplikacja jest zatem aplikacją konsolową, a nie aplikacją internetową, w przypadku której pakiet miałby typ [war];
- wiersze 10–14: projekt Maven ma projekt nadrzędny o nazwie [spring-boot-starter-parent]. To właśnie on definiuje większość zależności projektu. Mogą one być wystarczające, w którym to przypadku nie dodaje się żadnych nowych, lub mogą być niewystarczające, w którym to przypadku dodaje się brakujące zależności;
- wiersze 17–20: artefakt [spring-boot-starter-web] zawiera biblioteki niezbędne dla projektu Spring o nazwie MVC, będącego serwisem internetowym, w którym nie generowane są widoki. Ten artefakt zawiera bardzo dużą liczbę bibliotek, w tym biblioteki wbudowanego serwera Tomcat. To właśnie na tym serwerze będzie uruchamiana aplikacja;
Biblioteki dostarczane w ramach tej konfiguracji są bardzo liczne:
![]() | ![]() |
Powyżej widoczne są trzy archiwa serwera Tomcat.
16.3.3. Architektura serwisu Spring [web / jSON]
W przypadku serwisu internetowego / jSON, Spring MVC implementuje model MVC w następujący sposób:
![]() |
- w [4a] model, który jest klasą Java, jest przekształcany w ciąg znaków jSON przez bibliotekę jSON;
- w [4b] ten ciąg znaków jSON jest wysyłany do przeglądarki;
16.3.4. Kontroler C
![]() |
Zaimportowana aplikacja posiada następujący kontroler:
package hello;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
@RequestMapping("/greeting")
public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
- wiersz 9: adnotacja [@RestController] sprawia, że klasa [GreetingController] staje się kontrolerem Springa, co oznacza, że jej metody są zarejestrowane do obsługi obiektów typu URL. Widzieliśmy już podobną adnotację [@Controller]. Wynikiem działania metod tego kontrolera był typ [String], który stanowił nazwę widoku do wyświetlenia. Tutaj jest inaczej. Metody kontrolera typu [@RestController] zwracają obiekty, które są serializowane w celu wysłania do przeglądarki. Typ przeprowadzanej serializacji zależy od konfiguracji Springa MVC. W tym przypadku zostaną one zserializowane do formatu jSON. To właśnie obecność biblioteki jSON wśród zależności projektu sprawia, że Spring Boot, poprzez autokonfigurację, skonfiguruje projekt w ten sposób;
- wiersz 14: adnotacja [@RequestMapping] wskazuje na URL, który jest przetwarzany przez metodę, w tym przypadku URL [/greeting];
- wiersz 15: adnotację [@RequestParam] wyjaśniliśmy już wcześniej. Wynikiem zwracanym przez metodę jest obiekt typu [Greeting].
- wiersz 12: liczba całkowita typu long o charakterze atomowym. Oznacza to, że obsługuje współbieżność dostępu. Kilka wątków może jednocześnie próbować zwiększyć wartość zmiennej [counter]. Odbędzie się to poprawnie. Wątek może odczytać wartość licznika dopiero wtedy, gdy wątek dokonujący modyfikacji zakończył swoją operację.
16.3.5. Model M
Model M wygenerowany przez poprzednią metodę to następujący obiekt [Greeting]:
![]() |
package hello;
public class Greeting {
private final long id;
private final String content;
public Greeting(long id, String content) {
this.id = id;
this.content = content;
}
public long getId() {
return id;
}
public String getContent() {
return content;
}
}
Transformacja jSON tego obiektu utworzy ciąg znaków {"id":n,"content":"tekst"}. Ostatecznie ciąg znaków jSON wygenerowany przez metodę kontrolera będzie miał postać:
lub
16.3.6. Wykonanie
![]() |
Klasa [Application.java] jest klasą wykonywalną projektu. Jej kod wygląda następująco:
package hello;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Ten kod omówiliśmy już w poprzednim przykładzie. Uruchommy projekt:
![]() |
Otrzymujemy następujące logi konsoli:
- wiersz 13: serwer Tomcat uruchamia się na porcie 8080 (wiersz 12);
- wiersz 17: serwlet [DispatcherServlet] jest obecny;
- wiersz 20: wykryto metodę [GreetingController.greeting];
Aby przetestować aplikację internetową, wysyłamy żądanie do serwletu URL [http://localhost:8080/greeting]:
![]() | ![]() |
Otrzymujemy oczekiwany ciąg znaków jSON. Warto przyjrzeć się nagłówkom HTTP wysyłanym przez serwer. W tym celu użyjemy rozszerzenia do przeglądarki Chrome o nazwie [Advanced Rest Client] (Chrome / Ctrl-T / Menu [Applications] / [Advanced Rest Client] – patrz załączniki, punkt 23.11):
![]() |
- w [1], żądany plik URL;
- w [2] zastosowano metodę GET;
- w [3] – odpowiedź jSON;
- w przypadku [4] serwer poinformował, że wysyła odpowiedź w formacie jSON;
- w [5] żądana jest ta sama odpowiedź URL, ale tym razem z POST;
- w [7] informacje są wysyłane do serwera w postaci [urlencoded];
- w [6] – parametr „name” wraz z jego wartością;
- w [8] przeglądarka informuje serwer, że przesyła mu informacje [urlencoded];
- w [9] – odpowiedź serwera jSON;
16.3.7. Tworzenie archiwum wykonywalnego
Teraz tworzymy archiwum wykonywalne:
![]() |
![]() |
- w [1]: uruchamiamy cel Maven;
- w [2]: występują dwa cele (goals): [clean] w celu usunięcia folderu [target] z projektu Maven oraz [package] w celu jego ponownego wygenerowania;
- w [3]: wygenerowany folder [target] zostanie umieszczony w tym folderze;
- w [4]: generowany jest cel;
W logach wyświetlanych w konsoli ważne jest, aby pojawiła się wtyczka [spring-boot-maven-plugin]. To właśnie ona generuje archiwum wykonywalne (patrz [pom.xml] poniżej):
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
W konsoli należy przejść do wygenerowanego folderu:
D:\Temp\wksSTS\gs-rest-service\target>dir
...
11/06/2014 15:30 <DIR> classes
11/06/2014 15:30 <DIR> generated-sources
11/06/2014 15:30 11 073 572 gs-rest-service-0.1.0.jar
11/06/2014 15:30 3 690 gs-rest-service-0.1.0.jar.original
11/06/2014 15:30 <DIR> maven-archiver
11/06/2014 15:30 <DIR> maven-status
...
- wiersz 5: wygenerowany plik archiwum;
Archiwum to uruchamia się w następujący sposób:
D:\Temp\wksSTS\gs-rest-service-complete\target>java -jar gs-rest-service-0.1.0.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.1.0.RELEASE)
2014-06-11 15:32:47.088 INFO 4972 --- [ main] hello.Application
: Starting Application on Gportpers3 with PID 4972 (D:\Temp\wk
sSTS\gs-rest-service-complete\target\gs-rest-service-0.1.0.jar started by ST in
D:\Temp\wksSTS\gs-rest-service-complete\target)
...
Teraz, gdy aplikacja internetowa została uruchomiona, można ją wywołać za pomocą przeglądarki:
![]() |
16.3.8. Wdrożenie aplikacji na serwerze Tomcat
Podobnie jak w przypadku poprzedniego projektu, modyfikujemy plik [pom.xml] w następujący sposób:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-rest-service</artifactId>
<version>0.1.0</version>
<packaging>war</packaging>
...
</project>
- wiersz 9: należy wskazać, że zamierzamy wygenerować archiwum WAR (Web ARchive);
Należy również skonfigurować aplikację internetową. W przypadku braku pliku [web.xml] odbywa się to za pomocą klasy dziedziczącej po [SpringBootServletInitializer]:
![]() |
Klasa [ApplicationInitializer] ma następującą postać:
package hello;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
public class ApplicationInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}
- wiersz 6: klasa [ApplicationInitializer] rozszerza klasę [SpringBootServletInitializer];
- wiersz 9: metoda [configure] została ponownie zdefiniowana (wiersz 8);
- wiersz 10: podano klasę konfigurującą projekt;
Aby uruchomić projekt, można postępować w następujący sposób:
![]() |
- w [1-2] uruchamia się projekt na jednym z serwerów zarejestrowanych w IDE Eclipse;
Po wykonaniu tej czynności można wywołać URL [http://localhost:8080/gs-rest-service/greeting/?name=Mitchell] w przeglądarce:
![]() |
16.4. Conclusion
Wprowadziliśmy rodzaj projektów Spring o nazwie MVC, w których aplikacja internetowa wysyła strumień jSON do przeglądarki. Teraz opracujemy aplikację internetową / jSON w celu udostępnienia w sieci bazy danych [dbproduitscategories] omówionej w poprzednich rozdziałach.























