Spring MVC. Przewodnik dla początkujących 978-83-283-0520-5 [PDF]

Wykorzystaj możliwości Spring MVC! Nauka przez praktykę, mniej teorii, więcej ćwiczeń Spring MVC to szkielet dostarczają

135 29 5MB

Polish Pages 281 Year 2015

Report DMCA / Copyright

DOWNLOAD PDF FILE

Table of contents :
Spis treści......Page 3
Zespół......Page 6
O autorze......Page 7
O recenzentach......Page 8
Zawartość książki......Page 9
Konwencje......Page 11
Instalacja Javy......Page 15
Konfiguracja narzędzia do budowy......Page 18
Instalacja serwera WWW......Page 19
Konfiguracja środowiska programistycznego......Page 21
Tworzenie pierwszego projektu opartego na Springu MVC......Page 25
Podsumowanie......Page 36
Serwlet przekazujący......Page 37
Kontekst aplikacji internetowej......Page 40
Kontekst konfiguracji aplikacji internetowej......Page 44
Resolwery widoków......Page 46
Model – widok – kontroler......Page 49
Przegląd przepływu żądania w Springu MVC......Page 50
Warstwa domeny......Page 51
Warstwa danych......Page 57
Warstwa usług......Page 64
Rzut oka na architekturę aplikacji internetowej......Page 69
Podsumowanie......Page 71
Definiowanie kontrolera......Page 73
Rola kontrolera w Springu MVC......Page 78
Używanie szablonów wzorców URI......Page 79
Używanie zmiennych tablicowych......Page 84
Zrozumieć parametry żądania......Page 89
Podsumowanie......Page 96
Prezentowanie i przetwarzanie formularzy......Page 97
Dostosowywanie wiązania danych......Page 105
Wyodrębnianie napisów......Page 109
Używanie znaczników Spring Security......Page 111
Podsumowanie......Page 121
Odwzorowywanie widoków......Page 123
Widok przekierowujący......Page 125
Serwowanie statycznych zasobów......Page 128
Żądania typu multipart w praktyce......Page 131
ContentNegotiatingViewResolver w praktyce......Page 137
Praca z resolwerem obsługi wyjątków......Page 141
Podsumowanie......Page 147
Praca z przechwytywaczami......Page 149
Internacjonalizacja (i18n)......Page 155
Raportowanie zdarzeń......Page 160
Warunkowe przekierowanie......Page 163
Podsumowanie......Page 168
Walidacja beanów......Page 169
Własna walidacja z użyciem JSR-303/walidacji beanów......Page 175
Walidacja Springa......Page 179
Podsumowanie......Page 187
Wprowadzenie do REST......Page 189
Obsługa usługi internetowej za pomocą Ajaksa......Page 204
Podsumowanie......Page 212
Praca ze Spring Web Flow......Page 213
Zwiększanie możliwości ponownego użycia kodu interfejsu użytkownika za pomocą Apache Tiles......Page 239
Podsumowanie......Page 247
Testowanie jednostkowe......Page 249
Testy integracyjne z użyciem szkieletu Spring Test Context......Page 253
Podsumowanie......Page 265
Instalacja Gradle......Page 267
Skrypt budowy Twojego projektu w Gradle......Page 268
Zrozumieć skrypt Gradle......Page 269
Rozdział 2. Architektura Spring MVC — projektowanie Twojego sklepu internetowego......Page 271
Rozdział 6. Przechwytywacze w akcji......Page 272
Rozdział 9. Apache Tiles oraz Spring Web Flow w praktyce......Page 273
Skorowidz......Page 275
Papiere empfehlen

Spring MVC. Przewodnik dla początkujących
 978-83-283-0520-5 [PDF]

  • 0 0 0
  • Gefällt Ihnen dieses papier und der download? Sie können Ihre eigene PDF-Datei in wenigen Minuten kostenlos online veröffentlichen! Anmelden
Datei wird geladen, bitte warten...
Zitiervorschau

Tytuł oryginału: Spring MVC: Beginner's Guide Tłumaczenie: Andrzej Bobak ISBN: 978-83-283-0520-5 Copyright © 2014 Packt Publishing First published in the English language under the title ‘Spring MVC: Beginner's Guide’ (9781783284870). Polish edition copyright © 2015 by Helion S.A. All rights reserved. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Kościuszki 1c, 44-100 GLIWICE tel. 32 231 22 19, 32 230 98 63 e-mail: [email protected] WWW: http://helion.pl (księgarnia internetowa, katalog książek) Pliki z przykładami omawianymi w książce można znaleźć pod adresem: ftp://ftp.helion.pl/przyklady/sprimv.zip Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/sprimv_ebook Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.   

Poleć książkę na Facebook.com Kup w wersji papierowej Oceń książkę

 

Księgarnia internetowa Lubię to! » Nasza społeczność

Spis treści O autorze

7

O recenzentach

8

Przedmowa

9

Rozdział 1. Konfiguracja środowiska do programowania w Springu Instalacja Javy Konfiguracja narzędzia do budowy Instalacja serwera WWW Konfiguracja środowiska programistycznego Tworzenie pierwszego projektu opartego na Springu MVC Podsumowanie

15 15 18 19 21 25 36

Rozdział 2. Architektura Springa MVC — projektowanie Twojego sklepu internetowego 37 Serwlet przekazujący Kontekst aplikacji internetowej Kontekst konfiguracji aplikacji internetowej Resolwery widoków Model – widok – kontroler Przegląd przepływu żądania w Springu MVC Architektura aplikacji internetowej Warstwa domeny Warstwa danych Warstwa usług Rzut oka na architekturę aplikacji internetowej Podsumowanie

37 40 44 46 49 50 51 51 57 64 69 71

Spis treści

Rozdział 3. Kontroluj swój sklep za pomocą kontrolerów Definiowanie kontrolera Rola kontrolera w Springu MVC Interfejs HandlerMapping Używanie szablonów wzorców URI Używanie zmiennych tablicowych Zrozumieć parametry żądania Podsumowanie

Rozdział 4. Praca z bibliotekami znaczników Springa Prezentowanie i przetwarzanie formularzy Dostosowywanie wiązania danych Wyodrębnianie napisów Używanie znaczników Spring Security Podsumowanie

Rozdział 5. Praca z resolwerami widoków Odwzorowywanie widoków Widok przekierowujący Serwowanie statycznych zasobów Żądania typu multipart w praktyce ContentNegotiatingViewResolver w praktyce Praca z resolwerem obsługi wyjątków Podsumowanie

Rozdział 6. Przechwytywacze w akcji

73 73 78 79 79 84 89 96

97 97 105 109 111 121

123 123 125 128 131 137 141 147

149

Praca z przechwytywaczami Internacjonalizacja (i18n) Raportowanie zdarzeń Warunkowe przekierowanie Podsumowanie

149 155 160 163 168

Rozdział 7. Walidatory w akcji

169

Walidacja beanów Własna walidacja z użyciem JSR-303/walidacji beanów Walidacja Springa Podsumowanie

4

169 175 179 187

Spis treści

Rozdział 8. Ajax i usługi REST Wprowadzenie do REST Obsługa usługi internetowej za pomocą Ajaksa Podsumowanie

Rozdział 9. Apache Tiles oraz Spring Web Flow w praktyce Praca ze Spring Web Flow Zwiększanie możliwości ponownego użycia kodu interfejsu użytkownika za pomocą Apache Tiles Podsumowanie

Rozdział 10. Testowanie aplikacji Testowanie jednostkowe Testy integracyjne z użyciem szkieletu Spring Test Context Podsumowanie

Dodatek A. Gradle — alternatywne narzędzie do budowy projektów

189 189 204 212

213 213 239 247

249 249 253 265

267

Instalacja Gradle Skrypt budowy Twojego projektu w Gradle Zrozumieć skrypt Gradle

267 268 269

Dodatek B. Odpowiedzi do krótkich testów

271

Rozdział 2. Architektura Springa MVC — projektowanie Twojego sklepu internetowego Rozdział 3. Kontroluj swój sklep za pomocą kontrolerów Rozdział 5. Praca z resolwerami widoków Rozdział 6. Przechwytywacze w akcji Rozdział 9. Apache Tiles oraz Spring Web Flow w praktyce

Skorowidz

271 272 272 272 273

275

5

Spis treści

Zespół Author Amuthan G Reviewers Rafał Borowiec Pawan Chopra Rubén Clemente Serna Acquisition Editor Vinay Argekar Content Development Editor Azharuddin Sheikh Technical Editors Monica John Neha Mankare Shiny Poojary Copy Editors Gladson Monteiro Insiya Morbiwala Aditya Nair Stuti Srivastava

6

Project Coordinators Kinjal Bari Wendell Palmer Proofreaders Simran Bhogal Stephen Copestake Maria Gould Ameesha Green Paul Hindle Indexer Hemangini Bari Graphics Disha Haria Abhinash Sahu Producti on Coordinator Aparna Bhagat Cover Work Aparna Bhagat

O autorze Amuthan G ma ponad sześć lat doświadczenia jako profesjonalny programista. Obecnie pracuje dla dużej firmy rozwijającej platformę chmurową. Ma sporą wiedzę na temat rozwoju oprogramowania z użyciem Javy, Springa, JPA oraz wielu innych korporacyjnych technologii. W wolnych chwilach bloguje na swojej stronie (http://www.madebycode.in). Można się z nim skontaktować za pośrednictwem adresu e-mail: [email protected]. Chciałbym serdecznie podziękować panu Vincentowi Kokowi za cierpliwość, radę, zrozumienie oraz przyjaźń okazane mi podczas mej pierwszej pracy w Educator Inc. Poprzez mentoring pomógł mi w staniu się poprawnie ukształtowanym profesjonalistą. Zachęcał mnie, bym rozwijał się nie tylko jako programista, ale również jako niezależny myśliciel. Pragnę wyrazić wdzięczność zespołowi Packt Publishing za ich cierpliwość i gotowość do współpracy. Gdy rozpocząłem pracę nad książką, nie miałem pojęcia, jak sprawy się potoczą. Nie ukończyłbym pracy bez ich porad. Bardzo dziękuję rodzinie i przyjaciołom za niekończące się wsparcie i zachętę. Wszystkie wyzwania i osiągnięcia zawdzięczam kolegom, którzy w ostatnich latach wiele mnie nauczyli. Kieruję specjalne podziękowania dla Divyi i Aruna za ich zachętę, przyjaźń i wsparcie. Byli dla mnie oparciem podczas trudnych chwil w czasie pisania tej książki. Na koniec najważniejsze podziękowania należą się mojej żonie Manju. Wierzy we mnie bardziej niż ja sam. Jej wsparcie, zaangażowanie, milcząca cierpliwość oraz niezachwiana miłość są fundamentem, na którym buduję swoje życie.

Część XX • ???

O recenzentach Rafał Borowiec jest specjalistą IT z ośmioletnim komercyjnym doświadczeniem. Specjalizuje się w testowaniu aplikacji, zapewnianiu jakości, tworzeniu oprogramowania, zarządzaniu projektem oraz zespołem. Obecnie jest zatrudniony jako lider zespołu w Goyello, gdzie odpowiada za tworzenie zespołów programistów i testerów i zarządzanie nimi. Jest również odpowiedzialny za utrzymywanie relacji z klientami oraz zdobywanie nowych (przede wszystkim poprzez konsultacje). Wierzy w zwinne zarządzanie projektami. Jest wielkim fanem technologii, głównie (ale nie wyłącznie) powiązanych z Javą. Lubi dzielić się wiedzą o tworzeniu oprogramowania oraz dobrych praktykach na swym blogu (http://blog.codeleak.pl), koncie Twittera (@kolorobot) oraz wewnętrznych i zewnętrznych wydarzeniach, takich jak warsztaty lub konferencje. Pawan Chopra jest programistą wiernym metodologii Agile, posiadającym osiem lat doświadczenia w branży tworzenia oprogramowania. Obecnie pracuje w Webners (http://www.webnersolutions. com/). Zajmuje się interesującymi projektami wykorzystującymi JavaScript, Javę, HTML5, Node.js oraz AngularJS. Jest zwolennikiem wolnego oprogramowania. Uwielbia dzielić się wiedzą poprzez blogowanie oraz warsztaty. Jego silną stroną jest kodowanie po stronie serwera. Ma duże doświadczenie w korzystaniu ze Springa oraz Hibernate. Bloguje pod adresem: www.itspawan.com. Rubén Clemente Serna jest programistą z ponadośmioletnim doświadczeniem w tworzeniu oprogramowania. Niedawno przeprowadził się do Wielkiej Brytanii. Pracuje jako programista Javy w Pikselu, firmie zajmującej się tworzeniem rozwiązań wideo dla czołowych marek w branży mediów. Wcześniej pracował w Hiszpanii dla firmy GFI Informática. Realizował tam wiele javowych projektów, głównie dla branży telekomunikacyjnej oraz dla sektora rządowego. Więcej informacji na temat jego umiejętności i doświadczenia znajduje się pod adresem: http://www.linkedin.com/in/rubenclementeserna. Można się z nim skontaktować za pośrednictwem e-maila: [email protected].

8

Przedmowa Przesłaniem książki jest zaprezentowanie Ci niewyobrażalnej prostoty oraz użyteczności Springa MVC. Do dziś pamiętam pierwsze zetknięcie ze szkieletem Springa w 2009 r. Najlepszym sposobem, by przekonać się, czy rozumie się dane zagadnienie, jest próba przekazania wiedzy komuś innemu. W moim przypadku przedstawiłem MVC Springa MVC. Pozwól, że wyjaśnię ten rebus. W 2009 r. przedstawiłem Springa MVC mojej żonie Manju Viswambaran Chandrice (MVC). Podczas tego szkolenia poznałem wątpliwości rodzące się w głowie początkującej osoby. Zgromadziłem moje doświadczenie jako trenera i umieściłem je w książce tak, by treść była zrozumiana bezproblemowo. W książce będziesz krok po kroku rozwijać prosty sklep internetowy. W każdym rozdziale zapoznasz się z nowym zagadnieniem dotyczącym Springa MVC. Oczywiście celem książki jest poznanie Springa MVC, a nie napisanie kompletnej, gotowej do wdrożenia platformy handlu elektronicznego.

Zawartość książki Rozdział 1., „Konfiguracja środowiska do programowania w Springu”, zapozna Cię skrótowo ze Springiem MVC oraz jego architekturą. Dowiesz się, jak krok po kroku utworzyć środowisko programistyczne. Po zainstalowaniu wymaganych składników zapoznasz się z krótkim przykładem prezentującym tworzenie aplikacji za pomocą Springa MVC. Wprawdzie w rozdziale nie został wytłumaczony cały zaprezentowany kod źródłowy, ale zrozumiesz kilka spraw intuicyjnie. Rozdział 2., „Architektura Springa MVC — projektowanie Twojego sklepu internetowego”, zaprezentuje podwaliny przykładowej aplikacji, którą będziesz rozwijać w kolejnych rozdziałach. Treść rozdziału przybliży Ci zagadnienia odwzorowania żądań, kontekstu aplikacji internetowej, przepływu żądania w Springu MVC oraz warstwowej struktury typowej aplikacji internetowej.

Spring MVC. Przewodnik dla początkujących

Rozdział 3., „Kontroluj swój sklep za pomocą kontrolerów”, zapozna Cię z informacjami na temat kontrolerów. Dowiesz się, jak je zdefiniować, jak tworzyć wzorce szablonów URI oraz jak używać zmiennych tablicowych i parametrów żądania. Rozdział 4., „Praca z bibliotekami znaczników Springa”, nauczy Cię, jak wykorzystywać Springa oraz jego biblioteki znaczników podczas pracy z formularzami internetowymi. Zobaczysz, jak wiązać obiekty domenowe z widokami oraz jak używać paczek wiadomości, by wyodrębnić etykiety pól formularza. Na koniec dowiesz się, jak utworzyć ekran logowania użytkownika. Rozdział 5., „Praca z resolwerami widoków”, zaprezentuje Ci zasadę działania resolwera Inter nalResourceViewResolver. Dowiesz się, w jaki sposób widoki zostają odwzorowane, jak używać różnych typów widoków, np. widoków statycznych lub przekierowujących. Zapoznasz się również z resolwerami żądań typu multipart oraz resolwerami odpowiedzialnymi za negocjację treści strony. Na koniec poznasz resolwery służące do obsługi wyjątków. Rozdział 6., „Przechwytywacze w akcji”, przedstawia koncepcję przechwytywaczy. Dowiesz się, jak użyć ich do elastycznej transformacji żądań i odpowiedzi. Nauczysz się, jak wspierać w swojej aplikacji internacjonalizację oraz jak może Ci w tym zadaniu pomóc LocaleChangeIn terceptor. Oprócz tego użyjesz przechwytywacza w celu implementacji audytu danych. Rozdział 7., „Walidatory w akcji”, prezentuje koncepcję walidacji. Nauczysz się, jak walidować obiekty, wykorzystując beany, oraz jak dostosować mechanizm walidacji do swoich potrzeb. Poznasz również podstawy walidacji Springa oraz dowiesz się, jak połączyć ją z walidacją beanów. Rozdział 8., „Ajax i usługi REST”, przedstawi Ci podstawowe założenia koncepcji Ajax oraz REST. Nauczysz się, jak tworzyć aplikację z wykorzystaniem usług REST. Dowiesz się, jak wykorzystywać wybrane metody HTTP oraz jak są one połączone z operacjami CRUD. Nauczysz się, jak wywołać ajaksowe żądanie i jak je obsłużyć z poziomu strony internetowej. Rozdział 9., „Apache Tiles oraz Spring Web Flow w praktyce”, zaprezentuje Ci, jak napisać aplikację opierającą się na przepływach, używając Spring Web Flow. Dowiesz się, czym są stany oraz przejścia w przepływie. Nauczysz się również, jak zdefiniować przepływ. Następnie zapoznasz się z Apache Tiles. Dowiesz się, jak zdekomponować stronę. Nauczysz się wykorzystywać TileViewResolver oraz tworzyć szablony wielokrotnego użytku za pomocą Apache Tiles. Rozdział 10., „Testowanie aplikacji”, nauczy Cię wykorzystywać możliwości Springa podczas testowania kontrolerów. Dowiesz się, jak tworzyć testowy kontekst aplikacji oraz imitacje warstwy usług i repozytorium. Następnie nauczysz się, jak używać modułu testowego Springa MVC. Dodatek A, „Gradle — alternatywne narzędzie do budowy projektów”, zaprezentuje Ci narzędzie Gradle, służące do budowy Twojej przykładowej aplikacji. Zapoznasz się ze skryptem budowy projektu wymaganym przez to narzędzie. W dodatku B, „Odpowiedzi do krótkich testów”, znajdziesz poprawne odpowiedzi do testów zamieszczonych w niektórych rozdziałach.

10

Przedmowa

Co jest potrzebne, by korzystać z tej książki Aby uruchomić przykłady zaprezentowane w książce, będziesz potrzebować poniższych narzędzi:  Java SE Development Kit w wersji 7u45 lub nowszej.  Maven 3.1.0 lub nowsze.  Apache Tomcat 7.0.  STS 3.6.2 lub nowsze.

Dla kogo jest ta książka Książka została napisana tak, by zapoznawać się z nią chronologicznie. Osoby posiadające wiedzę o Springu MVC mogą rozpocząć od późniejszych rozdziałów, zawierających istotne dla nich informacje. Nie spodziewam się, że masz doświadczenie ze szkieletem Springa. Wiedza o programowaniu serwletów i wstrzykiwaniu zależności będzie pomocna, lecz nie jest niezbędna. W skrócie: książka dostarcza klarownych pomysłów. Jest idealna dla początkujących i średnio zaawansowanych programistów.

Konwencje W książce często znajdziesz rozmaite nagłówki. Szczegółowe instrukcje, jak przeprowadzić ćwiczenie lub akcję, są zaprezentowane w poniższy sposób.

Ćwiczenie praktyczne — nagłówek 1. Akcja 1 2. Akcja 2 3. Akcja 3 Instrukcje często wymagają dodatkowego omówienia lub wyjaśnienia. W takim wypadku są umieszczone po nagłówku.

Co się właśnie wydarzyło? Ten nagłówek poprzedza dodatkowe wyjaśnienia do treści ćwiczenia, które właśnie zrealizowałeś.

11

Spring MVC. Przewodnik dla początkujących

W książce znajdziesz również inne pomoce naukowe.

Krótki test — nagłówek Krótkie testy wielokrotnego wyboru mają na celu sprawdzenie Twojego zrozumienia danego fragmentu.

Dla ambitnych — nagłówek Praktyczne wyzwania pozwalają na poeksperymentowanie z zagadnieniami, które właśnie poznałeś. Natkniesz się również na rozmaite style, wyróżniające niektóre informacje. Poniżej znajdziesz przykłady wybranych stylów oraz wyjaśnienie ich zastosowania. Fragmenty kodu w tekście, nazwy zmiennych, katalogów, tabel w bazie danych, plików, rozszerzeń plików, ścieżek, testowych adresów URL, danych wejściowych oraz identyfikatorów Twittera są prezentowane w następujący sposób: „Gdy pobieranie pliku zostanie skończone, przejdź do katalogu ze ściągniętym plikiem i wypakuj plik .zip do wybranego katalogu”. Listing jest zaprezentowany w następujący sposób:



${greeting}

${tagline}





Jeśli fragment listingu jest z jakiegoś powodu szczególnie istotny, jest dodatkowo wyróżniony:

DefaultServlet org.springframework.web.servlet.DispatcherServlet

Nowe terminy lub istotne fragmenty są w tekście pogrubione. Słowa widoczne na ekranie, w menu lub dialogach są oznaczone następująco: „Wyświetli się okno o nazwie Zmienne systemowe. W nim wybierz zakładkę Zaawansowane, następnie naciśnij przycisk Zmienne środowiskowe, by otworzyć okno ze zmiennymi środowiskowymi”.

12

Przedmowa

Ostrzeżenia, ważne komunikaty, porady i wskazówki pojawiają się w takich ramkach.

Kod źródłowy do pobrania Przygotowaliśmy dla Ciebie pliki zawierające kod źródłowy przykładów zaprezentowanych w tej książce. Możesz je pobrać spod adresu: ftp://ftp.helion.pl/przyklady/sprimv.zip.

13

Spring MVC. Przewodnik dla początkujących

14

1 Konfiguracja środowiska do programowania w Springu W tym rozdziale zobaczysz, jak utworzyć prostą aplikację z użyciem Springa MVC. Zanim rozpoczniesz pracę nad wykorzystującą go aplikacją, musisz zainstalować kilka niezbędnych programów oraz narzędzi. Na początek nauczysz się, jak zainstalować oprogramowanie wymagane do uruchomienia środowiska programistycznego w celu rozpoczęcia pracy nad aplikacją. Proces instalacji oprogramowania i konfigurowania środowiska został przedstawiony dla systemów operacyjnych z rodziny Windows. Na szczęście wspomniany proces nie różni się znacznie dla innych systemów operacyjnych. W razie wątpliwości na stronach dostawców narzędzi możesz zasięgnąć porady, jak zainstalować je na innych systemach operacyjnych. W tym rozdziale nauczysz się, jak zainstalować Javę, serwer aplikacji Tomcat, środowisko Spring Tool Suite, a także jak skonfigurować narzędzie Maven służące do automatycznej budowy aplikacji oraz jak utworzyć i uruchomić swój pierwszy projekt używający Springa MVC.

Instalacja Javy Oczywistym pierwszym krokiem jest instalacja Javy. Bardziej techniczna nazwa tej instalacji to Java Development Kit (JDK). JDK zawiera kompilator Javy (javac), wirtualną maszynę Javy oraz zestaw narzędzi służących do kompilacji i uruchamiania programów napisanych w Javie.

Spring MVC. Przewodnik dla początkujących

Ćwiczenie praktyczne — instalacja JDK Użyjesz Javy w wersji 7, jednak Java w wersji 6 lub dowolnej innej wyższej jest również akceptowalna. Oto, jak zainstalować JDK na systemie operacyjnym z rodziny Windows: 1. Odwiedź stronę Oracle zawierającą instalator Javy SE, wpisując w przeglądarce adres: http://www.oracle.com/technetwork/java/javase/downloads/index.html. 2. Wybierz hiperłącze umożliwiające pobranie JDK 7. Zaakceptuj umowę licencyjną widoczną na stronie, na którą zostałeś przekierowany. 3. Następnie użyj hiperłącza odpowiadającego architekturze Twojego systemu operacyjnego. Na przykład jeśli Twój komputer posiada 32-bitowy system operacyjny, wybierz hiperłącze odpowiadające systemom z rodziny Windows x86. Jeżeli system operacyjny Twojego komputera jest 64-bitowy, skorzystaj z hiperłącza odpowiadającego systemom z rodziny Windows x64. 4. Rozpocznie się pobieranie instalatora. Gdy pobieranie zostanie zakończone, udaj się do katalogu z plikiem instalatora, następnie dwukrotnie kliknij pobrany plik. Otworzy się okno instalatora. Naciskaj przycisk Next, nie zmieniając domyślnych wartości (rysunek 1.1). Po zakończeniu ostatniego kroku instalatora naciśnij przycisk Close.

Rysunek 1.1. Instalacja JDK Dodatkowy instalator umożliwia instalację środowiska uruchomieniowego Javy (JRE). Przejdź przez wszystkie kroki instalatora, aby zainstalować JRE w systemie operacyjnym.

16

Rozdział 1. • Konfiguracja środowiska do programowania w Springu

5. Zainstalowane JDK znajdziesz w domyślnej lokalizacji. W naszym przypadku domyślną lokalizacją jest C:\Program Files\Java\jdk1.7.0_71\.

Ćwiczenie praktyczne — ustawianie zmiennych środowiskowych Po zainstalowaniu JDK musisz jeszcze przeprowadzić kilka konfiguracji, aby mieć dostęp do Javy w dowolnym folderze na komputerze. Ustawiając w systemie operacyjnym Windows zmienne środowiskowe dla Javy, sprawisz, że kompilator Javy oraz inne narzędzia będą dostępne w całym systemie operacyjnym: 1. Wybierz Panel sterowania/System/Zaawansowane ustawienia systemu. 2. Pojawi się okno o nazwie Właściwości systemu. Wybierz zakładkę Zaawansowane i naciśnij przycisk Zmienne środowiskowe…, by otworzyć okno edycji zmiennych środowiskowych. 3. Naciśnij przycisk Nowa na panelu Zmienne systemowe. Utwórz zmienną o nazwie JAVA_HOME oraz wartości równej ścieżce, w której została zainstalowana JDK. W tym przypadku jest to C:\Program Files\Java\jdk1.7.0_71\. Jeżeli nie masz wystarczających uprawnień, by utworzyć zmienną w panelu Zmienne systemowe, możesz utworzyć analogiczną zmienną środowiskową w panelu Zmienne użytkownika. 4. Następnie w tym samym panelu kliknij dwukrotnie wpis Path. Zależnie od edytowanego panelu pojawi się okno o nazwie Edytowanie zmiennej systemowej lub Edytowanie zmiennej użytkownika (rysunek 1.2). 5. Edytuj wartość zmiennej Path, dodając wpis ;%JAVA_HOME%\bin do istniejącej wartości zmiennej. Zachowaj ostrożność, edytując zmienną Path. Powinieneś dodać tekst na końcu istniejącej wartości. Nie kasuj ani nie zmieniaj istniejących wartości. Upewnij się, że nie opuściłeś znaku ; (średnika). Jest on pierwszym znakiem w tekście, który dodajesz.

6. Naciśnij przycisk OK. Udało Ci się zainstalować Javę. Aby się przekonać, że instalacja przebiegła pomyślnie, otwórz okno wiersza poleceń, wpisz java -version i naciśnij Enter (listing 1.1). W oknie konsoli pojawi się wersja zainstalowanej Javy. Listing 1.1. Weryfikacja wersji zainstalowanej wirtualnej maszyny Javy C:\>java -version java version "1.7.0_71" Java(TM) SE Runtime Environment (build 1.7.0_71-b14) Java HotSpot(TM) 64-Bit Server VM (build 24.71-b01, mixed mode)

17

Spring MVC. Przewodnik dla początkujących

Rysunek 1.2. Edycja zmiennej systemowej Path

Konfiguracja narzędzia do budowy Budowanie oprogramowania zazwyczaj obejmuje następujące działania:  kompilacja całości kodu źródłowego;  generacja dokumentacji na podstawie kodu źródłowego;  pakowanie skompilowanego kodu do archiwum JAR lub WAR;  instalacja spakowanego archiwum na serwerze.

Ręczne wykonywanie wszystkich powyższych zadań jest czasochłonne i podatne na błędy. W związku z tym możesz użyć narzędzia do budowy, aby zautomatyzować wszystkie akcje powiązane z budową projektu programistycznego, od kompilacji po wdrożenie.

Ćwiczenie praktyczne — instalacja Mavena, czyli narzędzia do budowy Istnieje wiele narzędzi wspomagających budowę projektów javowych. Użyj Mavena w wersji 3.2.1. Oto, jakie kroki należy wykonać, by zainstalować Mavena: 1. Otwórz stronę umożliwiającą pobranie Mavena, wpisując w przeglądarce adres: http://maven.apache.org/download.cgi.

18

Rozdział 1. • Konfiguracja środowiska do programowania w Springu

2. Użyj hiperłącza do pliku apache-maven-3.2.3-bin.zip i rozpocznij pobieranie. 3. Po ukończeniu pobierania przejdź do folderu zawierającego pobrany plik i wypakuj archiwum ZIP do wybranego katalogu. 4. Następnie utwórz zmienną środowiskową o nazwie M2_HOME, podobnie jak utworzyłeś zmienną JAVA_HOME. Jako wartość zmiennej wprowadź ścieżkę katalogu, w którym znajduje się rozpakowane archiwum Mavena. 5. Utwórz jeszcze jedną zmienną środowiskową — o nazwie M2. Nadaj jej wartość %M2_HOME%\bin, tak jak na rysunku 1.3.

Rysunek 1.3. Ustawianie zmiennej środowiskowej M2

6. Na koniec dodaj zmienną M2 do zmiennej środowiskowej PATH, dopisując ;%M2% do wartości zmiennej środowiskowej PATH. Zainstalowałeś Mavena — narzędzie do budowy. Aby sprawdzić, czy instalacja została przeprowadzona pomyślnie, wykonaj krok podobny do weryfikacji poprawności instalacji Javy. Otwórz nowe okno wiersza poleceń, wprowadź komendę mvn -version i naciśnij Enter. Powinieneś zobaczyć informacje dotyczące Mavena, takie jak w listingu 1.2. Listing 1.2. Weryfikacja instalacji Mavena C:\>mvn -version Apache Maven 3.2.3 (33f8c3e1027c3ddde99d3cdebad2656a31e8fdf4; 2014-08-11T22:58:10+02:00) Maven home: C:\Program Files\maven\apache-maven-3.2.3 Java version: 1.7.0_71, vendor: Oracle Corporation Java home: C:\Program Files\Java\jdk1.7.0_71\jre Default locale: pl_PL, platform encoding: Cp1250 OS name: "windows 7", version: "6.1", arch: "x86", family: "windows"

Instalacja serwera WWW Do tej pory nauczyłeś się, jak zainstalować JDK oraz Mavena. Używając tych narzędzi, będziesz potrafił kompilować kod źródłowy w języku Java do plików .class oraz pakować te pliki do archiwów JAR lub WAR. Jak jednak uruchamiać owe spakowane archiwa? W tym celu skorzystaj z pomocy serwera WWW. Jego zadaniem będzie udostępnianie spakowanych archiwów jako działających aplikacji. 19

Spring MVC. Przewodnik dla początkujących

Ćwiczenie praktyczne — instalacja serwera WWW Tomcat Apache Tomcat jest popularnym serwerem WWW oraz kontenerem serwletów Javy. Skorzystaj z wersji 7.0. Oto, w jaki sposób możesz przeprowadzić instalację serwera WWW Tomcat: 1. Otwórz stronę domową projektu Apache Tomcat, korzystając z adresu: http://tomcat.apache.org/. 2. Wybierz hiperłącze prowadzące do strony umożliwiającej pobranie Tomcata w wersji 7.0. 3. Użyj hiperłącza prowadzącego do instalatora Tomcata jako serwisu dla 32-bitowych oraz 64-bitowych systemów Windows i rozpocznij pobieranie. 4. Po skończeniu pobierania przejdź do katalogu z pobranym plikiem i uruchom go. Otworzy się okno instalatora. 5. Przejdź przez kolejne kroki instalatora za pomocą przycisku Next, pozostawiając wybrane domyślne opcje. Podczas ostatniego kroku, przed naciśnięciem przycisku Finish, upewnij się, że pole o nazwie Run Apache Tomcat nie zostało zaznaczone. Instalacja serwera Apache Tomcat z domyślnymi ustawieniami powiedzie się wyłącznie, jeśli Java również została zainstalowana w domyślnej lokalizacji. Jeśli tak się nie stało, należy podczas instalacji Tomcata wskazać poprawną ścieżkę do JRE, tak jak zostało to przedstawione na rysunku 1.4.

Rysunek 1.4. Wybór JRE używanego przez serwer Tomcat

20

Rozdział 1. • Konfiguracja środowiska do programowania w Springu

Konfiguracja środowiska programistycznego Zainstalowałeś Javę i Mavena, by kompilować i pakować kod źródłowy Javy, oraz Tomcata, by instalować i uruchamiać swoją aplikację. Niemniej jednak najpierw musisz napisać kod wykorzystujący Springa MVC, by następnie skompilować, spakować i uruchomić aplikację. Możesz wykorzystać dowolny dostępny w Twoim systemie operacyjnym prosty edytor tekstu, aby pisać kod aplikacji. W takim przypadku nie będziesz miał jednak dostępu do takich udogodnień jak wykrywanie błędów składni w czasie pisania kodu, automatyczne podpowiadanie istotnych słów kluczowych, podświetlanie składni, prosta nawigacja itp. Zintegrowane środowisko programistyczne (Integrated Development Environment — IDE) przyspieszy i ułatwi Ci pisanie kodu oraz usprawni eliminację błędów. Jako IDE użyj Spring Tool Suite (STS).

Ćwiczenie praktyczne — instalacja Spring Tool Suite STS jest najlepszym środowiskiem programistycznym opartym na Eclipse, służącym do budowania aplikacji z użyciem Springa. Zobacz, w jaki sposób zainstalować STS: 1. Otwórz stronę udostępniającą pliki instalatora STS dostępną pod adresem: http://spring.io/tools/sts/all. 2. Skorzystaj z hiperłącza wskazującego instalator STS w postaci archiwum ZIP odpowiadającego architekturze Twojego systemu operacyjnego (32- lub 64-bitowy), co rozpocznie pobieranie pliku. Najnowszą stabilną wersją STS w chwili przygotowywania tego wydania było STS 3.4.1 oparte na Eclipse 4.4. 3. Gdy pobieranie zostanie ukończone, przejdź do katalogu z pobranym archiwum i wypakuj je w dowolnej lokalizacji. Pobieranie kodu źródłowego z przykładami Możesz pobrać pliki z kodem źródłowym ze strony wydawnictwa Helion pod adresem: ftp://ftp.helion.pl/

przyklady/sprimv.zip.

Instalację wszystkich narzędzi i oprogramowania niezbędnego do implementacji aplikacji korzystającej ze Springa MVC zbliża się ku końcowi. Niedługo będziesz mógł utworzyć w STS projekt oparty na Springu MVC. Zanim to się stanie, wykonaj jeszcze jedną konfigurację w STS.

21

Spring MVC. Przewodnik dla początkujących

Ćwiczenie praktyczne — konfigurowanie Tomcata w STS Jak wspomniano wcześniej, możesz używać serwera WWW Tomcat, by pracować ze swoją aplikacją. Musisz jednak wcześniej poinformować STS o lokalizacji kontenera Tomcata, by w prosty sposób uruchamiać na nim projekty utworzone w STS. Skonfiguruj Tomcata w STS: 1. Otwórz STS, używając ikony dostępnej w menu Start. 2. STS poprosi o wskazanie katalogu roboczego. Wskaż dowolny odpowiadający Ci katalog i wciśnij przycisk OK. 3. STS zaprezentuje ekran powitalny. Zamknij go, a następnie na pasku menu wybierz Window/Preferences/Server/Runtime Environments. 4. Po prawej stronie ekranu znajdziesz dostępne serwery. Pod listą dostępnych serwerów znajduje się również VMware vFabric tc Server zainstalowany domyślnie razem z STS. 5. Użyj przycisku Add, by dodać serwer WWW Tomcat. 6. Pojawi się okno kreatora. Aby wybrać serwer, w polu o nazwie Select the type of runtime environment wpisz tomcat. Z listy wybierz Tomcat v7.0, zaznacz pole Create a new local server, a następnie naciśnij przycisk Next. Okno wyboru serwera powinno wyglądać jak na rysunku 1.5.

Rysunek 1.5. Wybór serwera Tomcat podczas konfiguracji STS

22

Rozdział 1. • Konfiguracja środowiska do programowania w Springu

7. W kolejnym oknie naciśnij przycisk Browse, wskaż katalog, w którym jest zainstalowany Tomcat, i naciśnij przycisk OK. Jeżeli zainstalowałeś Tomcata w domyślnej lokalizacji, znajdziesz go w katalogu C:\Program Files\Apache Software Foundation\Tomcat 7.0. Naciśnij przycisk Finish, jak widać na rysunku 1.6.

Rysunek 1.6. Wybór lokalizacji serwera Tomcat podczas konfiguracji STS

Co się właśnie wydarzyło? W kroku 2. wskazałeś ścieżkę do katalogu roboczego STS. Gdy uruchamiasz STS po raz pierwszy, jesteś poproszony o wskazanie katalogu roboczego. Dzieje się tak, ponieważ kiedy tworzysz projekt w STS, wszystkie pliki projektu zostaną utworzone wyłącznie w tej lokalizacji. Po uruchomieniu STS powinieneś dostarczyć mu informację o lokalizacji Tomcata. Tylko wtedy STS jest w stanie wgrać projekt na serwer WWW Tomcat. Ta konfiguracja jest wykonywana jednokrotnie. Nie musisz jej powtarzać przy każdym uruchomieniu STS. Utworzyłeś ją, definiując nowe środowisko uruchomieniowe w 5. kroku konfiguracji. Mimo że STS może być dostarczony wraz z wbudowanym serwerem VMware vFabric tc Server, będziesz używać serwera WWW Tomcat jako serwerowego środowiska uruchomieniowego.

23

Spring MVC. Przewodnik dla początkujących

Ćwiczenie praktyczne — konfigurowanie Mavena w STS Nauczyłeś się, jak skonfigurować Tomcata w STS. Jeżeli chcesz, by STS wykorzystywało do budowy projektu Mavena, musisz wskazać miejsce jego instalacji. Przyjrzyj się, jak skonfigurować Mavena w STS: 1. Uruchom STS, jeśli jest wyłączone. 2. Przejdź do Window/Preferences/Maven/Installations. 3. Po prawej stronie ekranu znajdziesz przycisk Add, służący do zlokalizowania zainstalowanego Mavena. 4. Naciśnij przycisk Add, a następnie wybierz folder zawierający zainstalowanego Mavena, tak jak przedstawiono to na rysunku 1.7.

Rysunek 1.7. Wybór instalacji Mavena na ekranie konfiguracji STS

5. Naciśnij przycisk OK i zamknij okno Preferences.

24

Rozdział 1. • Konfiguracja środowiska do programowania w Springu

Tworzenie pierwszego projektu opartego na Springu MVC Do tej pory nauczyłeś się, jak zainstalować wymagane oprogramowanie i narzędzia. Pora na napisanie w STS pierwszej aplikacji używającej Springa MVC. STS dostarcza prostych w użyciu szablonów projektu. Używając ich, jesteś w stanie szybko i bezproblemowo wygenerować strukturę katalogów projektu.

Ćwiczenie praktyczne — tworzenie w STS projektu opartego na Springu MVC Utwórz w STS pierwszy projekt oparty na Springu MVC: 1. W menu STS przejdź do File/New/Project. Pojawi się okno kreatora nowego projektu. 2. Z listy dostępnych typów projektów wybierz Maven Project i naciśnij przycisk Next, tak jak zostało to przedstawione na rysunku 1.8.

Rysunek 1.8. Wybór szablonu projektu Mavena

25

Spring MVC. Przewodnik dla początkujących

3. Pojawi się okno dialogowe o nazwie New Maven Project. Zaznacz pole o nazwie Create a simple project (skip archetype selection) i naciśnij przycisk Next. 4. Kreator projektu wymaga podania kilku niezbędnych informacji na temat projektu. W polu Group Id wprowadź wartość com.packt, a w polu Artifact Id wartość webstore. Następnie w polu wyboru Packaging wybierz pakowanie typu war i naciśnij przycisk Finish, tak jak przedstawiono na rysunku 1.9.

Rysunek 1.9. Konfiguracja nowego projektu

Co się właśnie wydarzyło? Utworzyłeś podstawową strukturę projektu. Każdy projekt Javy wymaga określonej struktury katalogów w celu organizacji kodu źródłowego oraz statycznych zasobów. Zamiast ręcznie tworzyć całą hierarchę katalogów, scedowałeś to zadanie na STS. Posiadając podstawowe informacje na temat projektu (grupa, nazwa artefaktu oraz sposób pakowania projektu), STS jest w stanie za pomocą wtyczki Mavena wygenerować kompletną strukturę katalogów projektu. W zasadzie STS wewnętrznie wykorzystuje Mavena w celu utworzenia struktury projektu. Załóżmy, że chcesz, by Twój projekt mógł być uruchamiany na dowolnym kontenerze serwletów, np. Tomcacie. W związku z tym pliki tego projektu będą pakowane do archiwum WAR. Po wykonaniu kroku 4. zauważysz w Package Explorerze, że struktura Twojego projektu wygląda tak jak na rysunku 1.10.

26

Rozdział 1. • Konfiguracja środowiska do programowania w Springu

Rysunek 1.10. Struktura projektu

Zależności Springa MVC Ze względu na to, że będziesz intensywnie wykorzystywać API biblioteki Springa MVC, potrzebujesz plików JAR Springa podczas implementacji. Jak zostało wspomniane wcześniej, Maven będzie odpowiadał za zarządzanie zależnościami oraz pakowanie projektu.

Ćwiczenie praktyczne — dodawanie zależności Springa do projektu Wykonaj poniższe kroki, by za pośrednictwem konfiguracji Mavena dodać powiązane ze Springiem pliki JAR. 1. Otwórz plik pom.xml (znajdziesz go w głównym katalogu projektu). 2. Pod plikiem pom.xml znajduje się kilka zakładek. Jeśli nie są widoczne, kliknij plik pom.xml prawym przyciskiem myszy i w rozwijanym menu wybierz opcję Open With, a następnie Maven POM editor. Na zakładce Dependencies naciśnij przycisk Add. Nie pomyl go z przyciskiem Add w sekcji Dependencies Management. Powinieneś użyć przycisku dostępnego na lewym panelu. 3. Pojawi się okno o nazwie Select Dependency. Jako Group Id wpisz org.springframework, parametrowi Artifact Id nadaj wartość spring-webmvc, a Version ustaw na 4.0.3.RELEASE. Parametrowi Scope nadaj wartość compile, a następnie naciśnij przycisk OK, tak jak przedstawiono na rysunku 1.11. 4. W podobny sposób dodaj zależność Java Server Pages Standard Tag Library (JSTL), używając tego samego przycisku Add. Tym razem jako Group Id podaj javax.servlet, parametrowi Artifact Id nadaj wartość jstl, a parametr Version ustaw na 1.2. Parametrowi Scope nadaj wartość compile. 5. Dodaj zależność dla servlet-api. Tym razem jako Group Id ustaw javax.servlet, dla Artifact Id podaj wartość javax.servlet-api, a parametr Version ustaw na 3.1.0. Parametrowi Scope nadaj wartość provided. Następnie naciśnij przycisk OK. 6. Na koniec nie zapomnij zapisać pliku pom.xml. 27

Spring MVC. Przewodnik dla początkujących

Rysunek 1.11. Dodawanie zależności do projektu

Co się właśnie wydarzyło? W świecie Mavena pom.xml (Project Object Model — model projektu) jest plikiem konfiguracyjnym definiującym wymagane zależności. Podczas budowy projektu Maven odczyta zawartość pliku i spróbuje pobrać biblioteki JAR z centralnego repozytorium plików Mavena (wymagane jest połączenie z internetem). W celu zlokalizowania odpowiedniego archiwum JAR Maven używa zestawu parametrów: Group Id, Artifact Id oraz Version. Za każdym razem, gdy dodajesz zależność do projektu, odpowiedni wpis pojawia się wewnątrz znaczników oraz w pliku pom.xml. Na przykład jeśli przejdziesz do zakładki pom.xml po ukończeniu kroku 3., zauważysz wpis odnoszący się do spring-mvc pomiędzy znacznikami i (listing 1.3). Listing 1.3. Deklaracja zależności w pliku pom.xml

org.springframework spring-webmvc 4.0.3.RELEASE

W kroku 3. dodałeś zależność dla biblioteki spring-mvc, a w kroku 4. dla JSTL, która jest kolekcją użytecznych znaczników JSP ułatwiających tworzenie stron JSP. W kroku 5. dodałeś archiwum JAR biblioteki servlet-api, by używać kodu powiązanego z serwletami. Możesz zauważyć, że parametr Scope dla servlet-api jest inny niż dla pozostałych dwóch zależności. Potrzebujesz tej zależności na etapie kompilacji projektu. Nie jest ona z kolei potrzebna podczas pakowania projektu do archiwum WAR. Dzieje się tak, ponieważ serwer WWW Tomcat zapewnia jar servlet-api uruchamianym projektom. W związku z tym zakres tej zależności został ustawiony na wartość provided.

28

Rozdział 1. • Konfiguracja środowiska do programowania w Springu

Po ukończeniu kroku 6. pliki JAR z zależnościami dla Twojego projektu umieszczone w sekcji Maven Dependencies powinny wyglądać tak jak na rysunku 1.12.

Rysunek 1.12. Struktura plików z zależnościami w projekcie

Dodałeś trzy zależności, jednak jak sam zauważysz, lista zależności Mavena jest dłuższa. Czy domyślasz się, co jest tego powodem? A jeśli Twoje zależności zależą od innych jarów itd.? Na przykład jar spring-mvc zależy od jarów: spring-core, spring-context oraz spring-aop. Nie zostały one jednak dodane do Twojego pliku pom.xml. W świecie Mavena nazywane są zależnościami przechodnimi. Innymi słowy: można powiedzieć, że Twój projekt przechodnio zależy od wspomnianych jarów. Maven automatycznie pobierze wszystkie przechodnio zależne biblioteki JAR. Pięknem tego mechanizmu jest to, że musisz poinformować Mavena wyłącznie o bezpośrednio wymaganych zależnościach, a zarządzanie ich zależnościami przechodnimi zostanie obsłużone automatycznie.

Ćwiczenie praktyczne — dodawanie wersji Javy do pliku pom.xml Dodałeś do swojego projektu wszystkie niezbędne jary. Pozostała Ci do przeprowadzenia jedna mała konfiguracja w pliku pom.xml informująca Mavena, by użył Javy w wersji 7 podczas budowy projektu. Jak to zrobić? Po prostu dodaj dwa wpisy w pliku pom.xml. Do dzieła.

29

Spring MVC. Przewodnik dla początkujących

1. Otwórz plik pom.xml. Na dole okna zauważysz kilka zakładek — wybierz tę o nazwie Overview. Rozwiń sekcję Properties i naciśnij przycisk Create. 2. Pojawi się okno o nazwie Add property. W polu Name wpisz wartość maven. compiler.source, a w polu Value wprowadź 1.7, tak jak na rysunku 1.13.

Rysunek 1.13. Ustawianie wersji kompilatora Javy w pliku pom.xml

3. W podobny sposób skonfiguruj właściwość o nazwie maven.compiler.target i wartości 1.7. 4. Zapisz plik pom.xml.

Szybkie wprowadzenie do koncepcji MVC Utworzyłeś projekt i dodałeś wszystkie wymagane biblioteki. Możesz przystąpić do programowania. Rozdział po rozdziale będziesz rozwijać projekt internetowego sklepu. Na początek utworzysz stronę domową projektu. Jej zadaniem będzie wyświetlanie powitania dla klientów odwiedzających sklep. Po wpisaniu w przeglądarce adresu: http://localhost:8080/webstore/ dostępna będzie strona podobna do przedstawionej na rysunku 1.14.

Rysunek 1.14. Strona powitalna aplikacji

30

Rozdział 1. • Konfiguracja środowiska do programowania w Springu

Nie martw się, jeśli nie będziesz w stanie zrozumieć części kodu. W kolejnych rozdziałach przyjrzysz się szczegółowo wszystkim zastosowanym obecnie rozwiązaniom. W tej chwili celem jest błyskawiczna implementacja prostej strony WWW za pomocą Springa MVC.

Ćwiczenie praktyczne — dodawanie strony powitalnej Aby utworzyć stronę powitalną, wykonaj następujące kroki: 1. Utwórz podkatalog WEB-INF/jsp/ w katalogu src/main/webapp/, a następnie w katalogu src/main/webapp/WEB-INF/jsp/ utwórz plik welcome.jsp zawierający kod zaprezentowany w listingu 1.4. Następnie zapisz plik. Listing 1.4. Ekran powitalny aplikacji



Witaj



${greeting}

${tagline}





2. Utwórz klasę o nazwie HomeController w pakiecie com.packt.webstore.controller w katalogu src/main/java i umieść w niej kod źródłowy zaprezentowany w listingu 1.5. Listing 1.5. Definicja pierwszego kontrolera package com.packt.webstore.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping;

31

Spring MVC. Przewodnik dla początkujących

@Controller public class HomeController { @RequestMapping("/") public String welcome(Model model) { model.addAttribute("greeting", "Witaj w sklepie internetowym!"); model.addAttribute("tagline", "Wyjątkowym i jedynym sklepie internetowym"); return "welcome"; } }

Co się właśnie wydarzyło? W kroku 1. utworzyłeś widok w JSP. Zwróć uwagę na znaczniki oraz

. Możesz zauważyć, że oba okalają wyrażenia umieszczone wewnątrz nawiasów klamrowych poprzedzonych znakiem $ (listing 1.6). Listing 1.6. Wyświetlanie zawartości modelu w pliku JSP ${greeting}

${tagline}



Czym więc jest ${greeting}? Zapis ten oznacza, że greeting jest zmienną. Jej wartość zostanie wyświetlona w nagłówku pierwszego poziomu. Natomiast wartość zmiennej tagline zostanie wyświetlona w akapicie. Pora odpowiedzieć na pytanie, gdzie są nadawane te wartości. Odpowiedzi należy szukać w metodzie welcome kontrolera HomeController. Przyjrzyj się fragmentowi kodu zaprezentowanemu w listingu 1.7. Listing 1.7. Dodawanie zmiennych do modelu model.addAttribute("greeting", "Witaj w sklepie internetowym!"); model.addAttribute("tagline", "Wyjątkowym i jedynym sklepie internetowym");

Zauważ, że nazwy zmiennych greeting oraz tagline są pierwszymi argumentami przekazywanymi do metody addAttribute, drugim parametrem są wartości owych zmiennych. Umieściłeś łańcuchy znaków "Witaj w sklepie internetowym!" oraz "Wyjątkowym i jedynym sklepie inter netowym" w modelu, indeksując je kluczami greeting i tagline. Na razie załóż, że model jest pewnym rodzajem mapy. Osoby mające doświadczenie w pisaniu serwletów zauważą, że model.addAttribute działa dokładnie tak samo jak request.setAttribute. Niezależnie od tego, jaką wartość umieścisz w modelu, masz do niej dostęp w widoku (JSP), używając odpowiadającego jej klucza umieszczonego wewnątrz wyrażenia o notacji ${}.

32

Rozdział 1. • Konfiguracja środowiska do programowania w Springu

Serwlet przekazujący Utworzyłeś kontroler potrafiący umieścić wartości w modelu. Utworzyłeś również widok potrafiący odczytać te wartości z modelu. Model jest swego rodzaju pośrednikiem pomiędzy widokiem a kontrolerem. Zaimplementowane do tej pory elementy pozwalają na wyświetlenie strony powitalnej. Czy w takim razie możesz już uruchomić projekt? Nie. Na tym etapie, jeżeli uruchomisz projekt i wprowadzisz adres: http://localhost:8080/webstore/ w przeglądarce, serwer zwróci błąd HTTP 404. Dzieje się tak, ponieważ nie zostało utworzone żadne mapowanie serwletu. W projekcie Springa MVC musisz skonfigurować mapowanie front servletu (czasem nazywanego front controllerem). Jest to jeden ze wzorców projektowych, w którym wszystkie żądania do konkretnej aplikacji internetowej są przekazywane do jednego serwletu. Szkielet Springa zapewnia implementację frontowego kontrolera. Jest to serwlet przekazujący (org.springframework. web.servlet.DispatcherServlet). Nie skonfigurowałeś jeszcze w projekcie serwletu przekazującego, dlatego też aplikacja będzie odpowiadać błędem HTTP 404.

Ćwiczenie praktyczne — konfiguracja serwletu przekazującego Serwlet przekazujący jest mechanizmem analizującym URL przychodzący w żądaniu i uruchamiającym odpowiadającą mu metodę w kontrolerze. W aplikacji, którą właśnie napisałeś, metoda welcome, znajdująca się w klasie HomeController, ma być wywołana, jeżeli w przeglądarce zostanie wywołany adres: http://localhost:8080/webstore/. Skonfiguruj więc w swoim projekcie serwlet przekazujący: 1. W katalogu src/main/webapp/WEB-INF/ utwórz plik web.xml, wprowadź kod zaprezentowany w listingu 1.8, a następnie zapisz plik. Listing 1.8. Definicja serwletu przekazującego

DefaultServlet org.springframework.web.servlet.DispatcherServlet 

DefaultServlet /

33

Spring MVC. Przewodnik dla początkujących

2. W katalogu src/main/webapp/WEB-INF/ utwórz plik XML o nazwie DefaultServletservlet.xml, wprowadź zawartość listingu 1.9 i zapisz plik. Listing 1.9. Konfiguracja serwletu przekazującego







Co się właśnie wydarzyło? Jeżeli masz już doświadczenie w pisaniu serwletów, to powinieneś wiedzieć, czym jest plik web.xml oraz znać zagadnienie konfigurowania serwletów. W pliku web.xml zdefiniowałeś serwlet o nazwie DefaultServlet w sposób podobny do konfiguracji dowolnego innego serwletu. Jedyną różnicą jest to, że nie utworzyłeś żadnej klasy serwletu dla tej konfiguracji. W tym przypadku klasa serwletu (org.springframework.web.servlet.DispatcherServlet) jest dostarczona przez środowisko Springa MVC i użyta w pliku web.xml. Po wykonaniu tej operacji DispatcherServlet (DefaultServlet) jest gotowy do obsługi dowolnego żądania przychodzącego do aplikacji podczas jej działania i przekierowania go do wskazanej metody kontrolera. Niemniej jednak, by poprawnie przekazywać żądania do właściwych kontrolerów, Dispatcher Servlet musi być poinformowany, gdzie są zlokalizowane pliki kontrolerów oraz widoków. Informacje te dostarczyłeś w kroku 2. podczas edycji pliku DispatcherServlet-servlet.xml. Nie przejmuj się, jeśli jeszcze nie do końca rozumiesz wszystkie konfiguracje w plikach web.xml i DispatcherServlet-servlet.xml. Zostaną one dokładniej opisane w następnym rozdziale. Na tym etapie wystarczy, że zapamiętasz, że tworzysz tę konfigurację jednokrotnie i że jest ona wymagana do poprawnego uruchomienia Twojego projektu.

34

Rozdział 1. • Konfiguracja środowiska do programowania w Springu

Instalacja projektu W poprzednim kroku pomyślnie utworzyłeś projekt. Możesz być ciekaw, co się stanie, jeśli teraz go uruchomisz. Ponieważ Twój projekt jest aplikacją internetową, potrzebujesz serwera WWW, by go uruchomić.

Ćwiczenie praktyczne — uruchamianie projektu Skonfigurowałeś już serwer Tomcat w STS, użyj go więc, by zainstalować i uruchomić projekt: 1. Prawym przyciskiem myszy otwórz menu Package Explorera, a następnie wybierz Run As/Run on Server. 2. Pojawi się okno wyboru serwerów prezentujące wszystkie dostępne serwery. Wybierz skonfigurowany wcześniej serwer Tomcat v7.0. 3. Na dole okna znajdziesz pole zatytułowane Always use this server when running this project, pozwalające na wybór domyślnego serwera do uruchamiania projektu. Zaznacz je i naciśnij przycisk Finish, tak jak przedstawiono na rysunku 1.15.

Rysunek 1.15. Konfiguracja domyślnego serwera dla projektu Springa MVC

4. Powinieneś zobaczyć stronę prezentującą wiadomość powitalną, taką jak na rysunku 1.16. 35

Spring MVC. Przewodnik dla początkujących

Rysunek 1.16. Strona startowa projektu

Podsumowanie W tym rozdziale dowiedziałeś się, jakie operacje należy wykonać, by rozpocząć prace nad pierwszą aplikacją wykorzystującą Springa MVC. Operacjami tymi były m.in.: instalacja JDK, narzędzia Maven służącego do budowy projektu, kontenera serwletów o nazwie Tomcat oraz IDE STS. Nauczyłeś się, jak przeprowadzić rozmaite konfiguracje w IDE STS, Mavenie oraz Tomcacie. Utworzyłeś pierwszy projekt korzystający ze Springa MVC oraz dodałeś do niego powiązane ze Springiem zależności za pośrednictwem Mavena. Odbyłeś szybkie praktyczne ćwiczenie implementacyjne, tworząc stronę powitalną dla aplikacji sklepu internetowego. Podczas ćwiczenia nauczyłeś się, jak umieszczać w modelu zmienne i jak je z niego wydobywać. Wszystko, czego doświadczyłeś, było wyłącznie rzutem oka na Springa MVC. Pozostało jeszcze wiele do odkrycia, np. na czym polega połączenie widoku i kontrolera lub jak wygląda przepływ żądania. Poznasz te zagadnienia w następnym rozdziale. Do zobaczenia na miejscu!

36

2 Architektura Springa MVC — projektowanie Twojego sklepu internetowego Poprzedni rozdział był niczym więcej niż rzut oka na Springa MVC. Skoncentrowałeś się w nim na uruchomieniu aplikacji korzystającej ze Springa MVC. Pora na zagłębienie się w architekturę Springa MVC.

Po zapoznaniu się z treścią rozdziału będziesz w pełni rozumieć koncepcje:  serwletu przekazującego oraz mapowania żądań;  kontekstu i konfiguracji aplikacji internetowej;  przepływu żądania Springa MVC oraz Web MVC;  architektury aplikacji internetowej.

Serwlet przekazujący W pierwszym rozdziale zapoznałeś się z koncepcją serwletu przekazującego oraz nauczyłeś się, jak go zdefiniować w pliku web.xml. Dowiedziałeś się również, że każde z żądań HTTP jest przez niego przetwarzane, a następnie przekierowywane do metody kontrolera odpowiedzialnej za

Spring MVC. Przewodnik dla początkujących

przetworzenie danego żądania. W poprzednim rozdziale utworzyłeś stronę powitalną prezentowaną, gdy w przeglądarce internetowej wprowadzisz adres: http://localhost:8080/webstore/. Mapowanie URL-a do właściwej metody kontrolera jest głównym zadaniem serwletu przekazującego. Serwlet przekazujący odczytuje adres URL żądania, odnajduje odpowiednią metodę kontrolera odpowiedzialną za jego obsługę i uruchamia ją. Ten proces mapowania żądania na odpowiednią metodę kontrolera jest nazywany mapowaniem żądań. Dzieje się to za pomocą adnotacji @RequestMapping (org.springframework.web.bind.annotation.RequestMapping).

Ćwiczenie praktyczne — badanie mapowania żądań Zbadaj, co się stanie, gdy zmienisz wartość adnotacji @RequestMapping, wykonując poniższe operacje: 1. Otwórz środowisko STS i naciśnij prawym przyciskiem myszy projekt webstore. Następnie wybierz Run As/Run on Server. W przeglądarce otworzy się znana Ci strona powitalna. 2. W pasku adresu przeglądarki internetowej wpisz adres: http://localhost:8080/ webstore/welcome. 3. W przeglądarce wyświetli się strona błędu HTTP 404, jak na rysunku 2.1, a w konsoli pojawi się ostrzeżenie: WARNING: No mapping found for HTTP request with URI [/webstore/welcome] in DispatcherServlet with name ' DefaultServlet'

Rysunek 2.1. Dziennik błędów prezentujący ostrzeżenie informujące o braku mapowania

38

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego

4. Otwórz klasę HomeController i w adnotacji @RequestMapping zmień wartość jej parametru na /welcome, a następnie zapisz zmiany. Ujmując w skrócie: Twoje nowe mapowanie żądania będzie wyglądało następująco: @RequestMapping("/welcome"). 5. Ponownie uruchom aplikację i wprowadź ten sam adres URL co w kroku 2. Wyświetli się znana Ci strona powitalna, a w konsoli nie pojawi się żaden komunikat błędu. 6. Otwórz klasę HomeController i nadaj adnotacji @RequestMapping pierwotną wartość, tj. @RequestMapping("/"), a następnie zapisz zmiany.

Co się właśnie wydarzyło? Po uruchomieniu aplikacji, gdy wprowadziłeś w przeglądarce adres: http://localhost:8080/ webstore/welcome, serwlet przekazujący (org.springframework.web.servlet.DispatcherServlet) natychmiast próbuje znaleźć metodę kontrolera odpowiadającą ścieżce żądania (rysunek 2.2) o wartości /welcome.

Rysunek 2.2. Logiczne fragmenty adresu URL typowej aplikacji Springa MVC W aplikacji Springa MVC adres URL może być logicznie podzielony na pięć części (rysunek 2.2). Adnotacja @RequestMapping dopasowuje się wyłącznie do ścieżki żądania adresu URL. Protokół, nazwa hosta oraz nazwa aplikacji są ignorowane. Adnotacja @RequestMapping posiada jeszcze jeden atrybut, o nazwie method, pozwalający zawęzić mapowanie na podstawie typu żądania HTTP (GET, POST, HEAD, OPTIONS, PUT, DELETE oraz TRACE). Jeżeli nie zdefiniujesz atrybutu method w adnotacji @RequestMapping, zostanie mu nadana domyślna wartość GET. Więcej informacji na temat atrybutu method i adnotacji @RequestMapping znajdziesz w rozdziale 4., „Praca z bibliotekami znaczników Springa”, w sekcji poświęconej przetwarzaniu formularzy.

Skoro nie masz zdefiniowanego mapowania dla ścieżki /welcome, przeglądarka otrzymuje stronę błędu HTTP 404, a w konsoli pojawia się następujący komunikat: WARNING: No mapping found for HTTP request with URI [/webstore/welcome] in DispatcherServlet with name 'DefaultServlet'

Na podstawie wpisu w dzienniku błędów można było wywnioskować, że nie istnieje mapowanie żądań dla ścieżki /webstore/welcome. Zmapowałeś tę ścieżkę URL do istniejącej metody kontrolera, ustawiając w kroku 4. adnotacji @RequestMapping wartość /welcome. Dzięki temu żądanie zadziałało zgodnie z oczekiwaniami.

39

Spring MVC. Przewodnik dla początkujących

Następnie w kroku 6. przywróciłeś adnotacji @RequestMapping wartość /. W jakim celu? By strona powitalna ponownie była widoczna po wprowadzeniu adresu: http://localhost:8080/webstore/. Zauważ, że w tym konkretnym przypadku ścieżką jest ostatni znak żądania (/). Zagadnieniu mapowania żądań przyjrzysz się w kolejnych rozdziałach.

Krótki test — mapowanie żądań Pytanie 1. Zakładając, że chciałbyś stworzyć mapowanie żądania: http://localhost:8080/BookPedia/ category/fiction dla aplikacji Springa MVC o nazwie BookPedia, jak wyglądałaby adnotacja @RequestMapping kontrolera mającego obsłużyć to żądanie? 1. @RequestMapping("/fiction"). 2. @RequestMapping("category/fiction"). 3. @RequestMapping("BookPedia/category/fiction").

Kontekst aplikacji internetowej W aplikacji opartej na Springu jej obiekty funkcjonują wewnątrz kontenera obiektów. Kontener tworzy obiekty, powiązania między nimi oraz zarządza ich cyklem życia. Obiekty kontenera są nazywane zarządzanymi przez Springa beanami (lub po prostu beanami), a kontener w świecie Springa jest nazywany kontekstem aplikacji. Kontener Springa wykorzystuje wstrzykiwanie zależności (Dependency Injection — DI), by zarządzać beanami tworzącymi aplikację. Kontekst aplikacji (org.springframework.context. ApplicationContext) tworzy beany, kojarzy je ze sobą na podstawie ich konfiguracji oraz dostarcza, gdy są potrzebne. Konfiguracja beana może zostać utworzona w pliku XML za pomocą adnotacji lub z użyciem klas konfiguracyjnych w Javie. W prezentowanych przykładach beany będą konfigurowane za pomocą plików XML oraz adnotacji. Kontekst aplikacji internetowej jest rozszerzeniem kontekstu aplikacji zaprojektowanej do współpracy ze standardowym kontekstem serwletu (javax.servlet.ServletContext). Kontekst aplikacji internetowej zazwyczaj zawiera beany powiązane z interfejsem użytkownika, takie jak widoki i resolwery widoków. W pierwszym rozdziale utworzyłeś plik XML o nazwie DefaultServlet-servlet.xml, który jest niczym innym niż konfiguracją beana wchodzącego w skład kontekstu Twojej aplikacji internetowej.

40

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego

Ćwiczenie praktyczne — zrozumieć kontekst aplikacji internetowej Zapoznałeś się już ze stroną teoretyczną kontekstu aplikacji internetowej, pora na drobne eksperymenty z nazwą i lokalizacją pliku konfiguracyjnego kontekstu aplikacji (DefaultServletservlet.xml) oraz obserwację wyników. Wykonaj następujące kroki: 1. Zmień nazwę pliku z DefaultServlet-servlet.xml na DispatcherServlet-servlet.xml. Plik znajdziesz w katalogu src/main/webapp/WEB-INF/. 2. Uruchom ponownie swój sklep internetowy, wpisując w przeglądarce adres: http://localhost:8080/webstore/. Zostanie wygenerowana strona prezentująca błąd HTTP 500 (rysunek 2.3) oraz wyjątek FileNotFoundException: java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/DefaultServlet-servlet.xml]

Rysunek 2.3. Strona wyświetlająca błąd typu FileNotFoundException dla pliku DefaultServlet-servlet.xml

3. By naprawić ten błąd, w pliku web.xml zmień nazwę serwletu z DefaultServlet na DispatcherServlet. Po zmianie nazwy konfiguracja Twojego serwletu w pliku web.xml powinna wyglądać jak w listingu 2.1. Listing 2.1. Konfiguracja domyślnego serwletu

DispatcherServlet org.springframework.web.servlet.DispatcherServlet

DispatcherServlet /

4. Uruchom aplikację i ponownie wpisz w przeglądarce adres: http://localhost:8080/ webstore/. Strona powitalna jest znów dostępna.

41

Spring MVC. Przewodnik dla początkujących

5. Zmień nazwę pliku DispatcherServlet-servlet.xml na DispatcherServlet-context.xml. 6. Utwórz podkatalog spring/webcontext/ w katalogu WEB-INF i przenieś plik DispatcherServlet-context.xml do nowo utworzonego katalogu src/main/webapp/ WEB-INF/spring/webcontext/. 7. Po uruchomieniu aplikacji ponownie pojawi się strona informująca o błędzie HTTP 500 oraz prezentująca wyjątek FileNotFoundException: java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/DefaultServlet-servlet.xml]

8. By naprawić ten błąd, dodaj w pliku web.xml zaprezentowany w listingu 2.2 kod pomiędzy znacznikami i . Listing 2.2. Definicja lokalizacji pliku zawierającego kontekst konfiguracji

contextConfigLocation

/WEB-INF/spring/webcontext/DispatcherServlet-context.xml

9. Ponownie uruchom aplikację, wprowadzając w przeglądarce adres: http://localhost: 8080/webstore/. Powinna zostać wygenerowana strona powitalna.

Co się właśnie wydarzyło? Na początku zmieniłeś nazwę pliku DefaultServlet-servlet.xml na DispatcherServlet-servlet.xml, co zaowocowało w czasie pracy aplikacji następującym wyjątkiem FileNotFoundException: java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/DefaultServlet-servlet.xml]

Aby naprawić ten błąd, zmieniłeś w pliku web.xml konfigurację serwletu przekazującego na przedstawioną w listingu 2.3. Listing 2.3. Konfiguracja serwletu przekazującego

DispatcherServlet org.springframework.web.servlet.DispatcherServlet

DispatcherServlet /

42

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego

Zmieniłeś nazwę serwletu na DispatcherServlet, by dostosować ją do pliku konfiguracji kontekstu aplikacji o nazwie DispatcherServlet-servlet.xml. Za pomocą tego ćwiczenia dowiedziałeś się, że podczas uruchamiania projektu opartego na Springu MVC serwlet przekazujący szuka w katalogu WEB-INF pliku konfiguracji kontekstu aplikacji o nazwie pasującej do wzorca -servlet.xml. W związku z tym musisz pamiętać o zapewnieniu właściwej nazwy pliku konfiguracji kontekstu aplikacji oraz przechowywaniu go w katalogu WEB-INF. Pojawia się pytanie: co zrobić, jeśli chcesz, żeby plik znajdował się w innym katalogu? Jedną z ważniejszych rzeczy, na które należy zwrócić uwagę w znaczniku , jest wartość znacznika /. Przypisując / jako wzorzec URL dla serwletu przekazującego, deklarujesz, że DispatcherServlet jest domyślnym serwletem dla Twojej aplikacji. Każde żądanie trafiające do Twojej aplikacji będzie obsłużone przez DispatcherServlet.

Na przykład w krokach 5. i 6. zmieniłeś nazwę pliku konfiguracji kontekstu aplikacji, a następnie przeniosłeś go do zupełnie nowego katalogu (src/main/webapp/WEB-INF/spring/webcontext/). Jak w związku z tym został naprawiony błąd HTTP 500? Odpowiedź znajdziesz, przyglądając się parametrowi o nazwie contextConfiguration, dodanemu w kroku 8. (listing 2.4). Listing 2.4. Ustawianie parametru contextConfiguration w konfiguracji serwletu przekazującego

DispatcherServlet org.springframework.web.servlet.DispatcherServlet

contextConfigLocation

/WEB-INF/spring/webcontext/DispatcherServlet-context.xml



Twoja aplikacja może znów być bezproblemowo uruchamiana. Poeksperymentowałeś z plikiem konfiguracji kontekstu aplikacji oraz dowiedziałeś się, że serwlet przekazujący musi posiadać informację o wspomnianym pliku konfiguracyjnym podczas startu aplikacji. Pojawiają się następujące pytania: dlaczego serwlet przekazujący szuka tego pliku konfiguracyjnego oraz co jest w tym pliku zdefiniowane? Zanim poszukasz na nie odpowiedzi, spróbuj rozwiązać poniższy krótki test, aby upewnić się, że rozumiesz koncepcję konfiguracji kontekstu aplikacji internetowej.

Krótki test — kontekst aplikacji internetowej Pytanie 1. Jeżeli w serwlecie przekazującym nie została zdefiniowana wartość parametru context ConfigLocation, jaka lokalizacja będzie wybrana przez Springa MVC podczas wyszukiwania pliku konfiguracyjnego kontekstu aplikacji? 43

Spring MVC. Przewodnik dla początkujących

1. Katalog WEB-INF. 2. Katalog WEB-INF/spring. 3. Katalog WEB-INF/spring/appServlet. Pytanie 2. Jeżeli nie chcesz ustawiać parametru contextConfigLocation w przedstawionej w listingu 2.5 konfiguracji serwletu przekazującego, jak zapobiec generowaniu się błędu HTTP 500? Listing 2.5. Przykładowa konfiguracja serwletu przekazującego

FrontController org.springframework.web.servlet.DispatcherServlet

4. Tworząc plik kontekstu o nazwie FrontController-context.xml w katalogu WEB-INF. 5. Tworząc plik o nazwie DispatcherServlet-context.xml w katalogu WEB-INF. 6. Tworząc plik o nazwie FrontController-servlet.xml w katalogu WEB-INF.

Kontekst konfiguracji aplikacji internetowej Plik kontekstu konfiguracji aplikacji internetowej (DispatcherServlet-context.xml) jest niczym innym niż prostym plikiem konfiguracyjnym springowego beana. Spring utworzy beany (obiekty) dla każdej definicji beana będącej w pliku podczas uruchamiania Twojej aplikacji. Jeśli otworzysz ten plik (w lokalizacji /WEB-INF/spring/webcontext/DispatcherServlet-context.xml), znajdziesz w nim kilka definicji beanów oraz konfiguracji (listing 2.6). Listing 2.6. Definicje beanów oraz konfiguracji w pliku DispatcherServlet-Context.xml



44

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego





Pierwszym znacznikiem wewnątrz definicji jest . Za pomocą tego znacznika deklarujemy, że chcemy, by Spring MVC skonfigurował beany DefaultAnnotation HandlerMapping, AnnotationMethodHandlerAdapter oraz ExceptionHandlerExceptionResolver. Wymienione beany są niezbędne do przekazywania przez Springa MVC żądań do kontrolerów. Znacznik wykonuje w tle wiele operacji. Między innymi zapewnia dostęp do wielu przydatnych adnotacji, takich jak @NumberFormat i @DateTimeFormat użytecznych na etapie formatowania beanów pól formularza podczas wiązania wartości z tymi polami. Udostępnia też adnotację @Valid, umożliwiającą sprawdzanie poprawności parametrów metody kontrolera. Adnotacje @RequestBody oraz @ResponseBody używane w metodach oznaczonych przez @RequestMapping lub @ExceptionHandler wspierają transformację obiektów Javy na dane w formacie JSON lub XML oraz odwrócenie tego procesu podczas wiązania wartości formularza. Zastosowanie wspomnianych adnotacji zostanie zaprezentowane w kolejnych rozdziałach. Na razie zapamiętaj, że znacznik jest wymagany w celu uzyskania dostępu do takich adnotacji jak @Controller i @RequestMapping. W jakim celu został użyty znacznik ? Aby odpowiedzieć na to pytanie, należy wcześniej zapoznać się z garścią informacji. Adnotacja @Controller wskazuje, że oznaczona nią klasa pełni funkcję kontrolera. Jak już wiesz, serwlet przekazujący przeszukuje oznaczone w ten sposób klasy, by wyszukać metodę (oznaczoną adnotacją @RequestMapping) mającą obsłużyć dane żądanie. Aby kontroler mógł być wyszukiwalny, musi mieć utworzonego beana w kontekście aplikacji internetowej. Możesz utworzyć beany dla kontrolerów poprzez konfigurację bean (używając znacznika ; przykład utworzenia wspomnianego znacznika dla klasy InternalResourceViewResolver zostanie przedstawiony w dalszej części rozdziału). Możesz również zdać się na mechanizm automatycznego wykrywania kontrolerów dostępny w Springu. Aby aktywować automatyczne wykrywanie klas oznaczanych adnotacją @Controller, musisz dodać znacznik do konfiguracji. Dzięki powyższym informacjom powinieneś już rozumieć cel używania omawianego znacznika. Spring utworzy w czasie działania aplikacji beany (obiekty) dla każdej klasy oznaczonej adnotacją @Controller. Serwlet przekazujący będzie przeszukiwał wszystkie klasy posiadające adnotację @Controller, sprawdzając wartości adnotacji @RequestMapping w celu odnalezienia mapowania właściwego do obsłużenia żądania. Parametr base-package znacznika wskazuje pakiet, który powinien być przeszukiwany przez Springa podczas wyszukiwania klas kontrolerów, z których utworzone zostaną beany:

45

Spring MVC. Przewodnik dla początkujących

Powyższy kod instruuje Springa, by wyszukiwał klasy kontrolerów w pakiecie com.packt.webstore oraz jego podpakietach. Znacznik oprócz klas kontrolerów rozpoznaje również serwisy oraz klasy repozytoriów. Więcej informacji na temat serwisów oraz repozytoriów znajdziesz w dalszej części książki.

Krótki test — konfiguracja kontekstu aplikacji internetowej Pytanie 1. Co jest wymagane, by klasa była rozpoznawana przez Springa jako kontroler? 1. Powinna posiadać adnotację @Controller. 2. W pliku konfiguracji kontekstu aplikacji internetowej powinny znajdować się znaczniki oraz . 3. Powinna być umieszczona w pakiecie lub podpakiecie określonym wewnątrz znacznika . 4. Wszystko powyższe.

Resolwery widoków Wiesz już, w jakim celu w pliku konfiguracji kontekstu aplikacji internetowej umieszczono znaczniki przedstawione w listingu 2.7. Listing 2.7. Fragment konfiguracji serwletu przekazującego katalog zawierający klasy kontrolerów

Za ich pomocą Spring tworzy beany niezbędne do obsługi żądań HTTP oraz beany klas oznaczonych adnotacją @Controller. Spring wymaga jeszcze jednego beana: resolwera widoków, aby uruchomić aplikację korzystającą ze Springa MVC. Resolwer widoków wspomaga serwlet przekazujący w identyfikacji widoków, które mają być wygenerowane jako odpowiedź dla konkretnego żądania. Spring MVC dostarcza rozmaitych implementacji resolwerów widoków. Jedną z nich jest InternalResourceViewResolver. Ostatnim znacznikiem w konfiguracji kontekstu aplikacji jest definicja beana klasy InternalResourceView Resolver przedstawiona w listingu 2.8.

46

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego

Listing 2.8. Definicja beana klasy InternalResourceViewResolver



Za pomocą powyższej definicji beana w kontekście konfiguracji aplikacji internetowej poinstruowałeś Springa MVC, by utworzył beana klasy InternalResourceViewResolver (org.springframework.web.servlet.view.InternalResourceViewResolver). Więcej informacji o resolwerach widoków znajdziesz w rozdziale 5., „Praca z resolwerami widoków”.

Ćwiczenie praktyczne — zrozumieć InternalResourceViewResolver W jakim celu poinstruowałeś Springa, by utworzył beana klasy InternalResourceViewResolver? W jaki sposób ten bean będzie używany? Jaka jest jego rola w Springu MVC? Odpowiedzi na te pytania poznasz, przeprowadzając poniższe ćwiczenie: 1. Otwórz plik DispatcherServlet-context.xml (znajdziesz go w katalogu src/main/ webapp/WEB-INF/spring/webcontext/). 2. Zmień wartość parametru prefix beana InternalResourceViewResolver w następujący sposób:

3. Uruchom projekt i odwiedź adres: http://localhost:8080/webstore/. Przeglądarka wyświetli zaprezentowaną na rysunku 2.4 stronę błędu HTTP 404. 4. Zmień nazwę katalogu o nazwie jsp (znajdziesz go w /src/main/webapp/WEB-INF/jsp) na views. 5. Uruchom aplikację i wpisz adres: http://localhost:8080/webstore/. Ponownie wyświetli się strona powitalna.

Co się właśnie wydarzyło? Po zmianie wartości prefix beana InternalResourceViewResolver próba wyświetlenia strony spod adresu: http://localhost:8080/webstore/ zaowocowała błędem HTTP 404. Wspomniany błąd oznacza, że serwer nie odnalazł żądanej strony. Nasuwa się pytanie: jakiej zażądałeś strony? W rzeczywistości nie zażądałeś żadnej konkretnej strony. Strona generująca błąd została wybrana przez serwlet przekazujący. Wiesz już, że serwlet przekazujący wywołuje metodę dowolnego kontrolera będącego w stanie obsłużyć żądanie. W tym przypadku jest to metoda welcome pochodząca z klasy HomeController. Jest to jedyne mapowanie żądania posiadające ścieżkę żądania adresu: http://localhost:8080/webstore/ w adnotacji @RequestMapping. 47

Spring MVC. Przewodnik dla początkujących

Rysunek 2.4. Strona błędu prezentująca informację o nieodnalezieniu zasobu

Zauważ, że:  Wartość parametru prefix w definicji beana InternalResourceViewResolver w pliku DispatcherServlet-context.xml to /WEB-INF/views/.  Wartość zwracana przez metodę welcome pochodzącą z klasy kontrolera HomeController to welcome.  Wartość parametru suffix beana InternalResourceViewResolver to .jsp.

Jeżeli połączysz powyższe trzy wartości, otrzymasz żądanie strony znajdującej się pod ścieżką /WEB-INF/views/welcome.jsp. Przyjrzyj się wiadomości błędu HTTP 404 zaprezentowanej na poprzednim zrzucie ekranu. Na serwerze nie została odnaleziona strona w lokalizacji /WEB-INF/ views/welcome.jsp w aplikacji o nazwie webstore/. Podsumowując: InternalResourceViewResolver ustala ścieżkę do widoku poprzez konkatenację wartości parametru prefix, nazwy widoku zwykle zwracanej przez metodę kontrolera oraz wartości parametru suffix. Metoda kontrolera nie zwraca ścieżki do pliku widoku, a jedynie jego logiczną nazwę. Utworzenie adresu widoku jest zadaniem resolwera InternalResourceView Resolver. Gdzie jest używany wspomniany adres? Serwlet przekazujący wykorzystuje go w celu pobrania pliku widoku z serwera. Jeżeli adres okaże się niepoprawny, zamiast pliku serwer zwróci kod błędu HTTP 404. Zazwyczaj po wywołaniu metody kontrolera serwlet przekazujący oczekuje na wynik działania metody, będący logiczną nazwą widoku. Otrzymana nazwa jest przekazywana do resolwera widoków (InternalResourceViewResolver) w celu wygenerowania adresu URL widoku. Wyge-

48

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego

nerowany adres jest przekazywany do serwletu przekazującego. Następnie wyrenderowany plik widoku zostaje przesłany do przeglądarki WWW za pośrednictwem serwletu przekazującego. Dlaczego w kroku 3. został wygenerowany błąd? Ponieważ w kroku 2. została zmieniona wartość parametru prefix beana InternalResourceViewResolver. Zwracany przez niego adres URL miał postać niepoprawnej ścieżki /WEB-INF/views/welcome.jsp (nie istniał podkatalog views w katalogu WEB-INF). W kroku 4. zmieniłeś nazwę katalogu z jsp na views, co przywróciło działanie aplikacji.

Model – widok – kontroler Zapoznałeś się już z pojęciami serwletu przekazującego, mapowania żądań, kontrolerów oraz resolwera widoków. Warto, byś poznał ogólny obraz przepływu żądania w Springu MVC po to, byś mógł zrozumieć zadania każdego z komponentów. Przedtem jednak powinieneś zapoznać się bliżej z koncepcją model – widok – kontroler (Model-View-Controller — MVC). Warstwy prezentacji korporacyjnych aplikacji mogą być logicznie podzielone na trzy główne fragmenty:  Fragment odpowiedzialny za zarządzanie danymi (model).  Fragment generujący interfejsy użytkownika oraz ekrany (widok).  Fragment koordynujący interakcję pomiędzy użytkownikiem, interfejsem użytkownika

oraz danymi (kontroler). Diagram zaprezentowany na rysunku 2.5 pomoże Ci zrozumieć przepływ zdarzeń oraz instrukcji we wzorcu MVC.

Rysunek 2.5. Klasyczny wzorzec MVC

Zawsze gdy użytkownik wchodzi w interakcję z widokiem poprzez wybranie hiperłącza lub wciśnięcie przycisku, widok generuje zdarzenie informujące kontroler. Ten z kolei generuje rozkaz do modelu w celu aktualizacji danych. Także gdy dane w modelu zostaną zaktualizowane lub zmienione, komunikat o zmianie jest przekazywany do widoku, który wysyła do modelu

49

Spring MVC. Przewodnik dla początkujących

zapytanie o najnowsze dane. Jeżeli model i widok mogą oddziaływać na siebie bezpośrednio, masz do czynienia z klasycznym MVC. W związku z ograniczeniami w protokole HTTP Spring MVC stosuje wzorzec zwany Web MVC. Aplikacje internetowe są oparte na protokole HTTP, który jest bezstanowy i ma typ pull. Oznacza to, że jeśli nie będzie wygenerowane żądanie, nie zostanie przesłana odpowiedź. Za każdym razem gdy chcesz poznać bieżący stan aplikacji, musisz przesłać w tym celu żądanie. Wzorzec projektowy MVC wymaga protokołu typu push w celu informowania widoków o zmianach przez model. W modelu Web MVC odpowiedzialność za zmiany stanów, odpytywanie o stan oraz informowanie o zmianach spoczywa na kontrolerze.

W modelu Web MVC każda interakcja pomiędzy modelem i widokiem jest realizowana za pośrednictwem kontrolera. Jest on swego rodzaju mostem pomiędzy modelem a widokiem. W odróżnieniu od klasycznego MVC bezpośrednia komunikacja pomiędzy widokiem a modelem nie występuje.

Przegląd przepływu żądania w Springu MVC Punktem wejścia żądania HTTP w aplikacji opartej na Springu MVC jest serwlet przekazujący. Pełniąc funkcję front controllera, przekazuje przychodzące żądania do innych kontrolerów. Jego głównym zadaniem jest odnajdywanie właściwego kontrolera i przekazywanie mu żądania w celu dalszej obsługi. Diagram widoczny na rysunku 2.6 przedstawia przepływ żądania w aplikacji opartej na Springu MVC.

Rysunek 2.6. Przepływ żądania w Springu MVC

W skrócie przepływ żądania w Springu MVC wygląda następująco: 1. Po wprowadzeniu adresu URL w przeglądarce żądanie trafia do serwletu przekazującego, który jest centralnym punktem wejścia do aplikacji internetowej. 2. Serwlet przekazujący przekierowuje żądanie do kontrolera mogącego obsłużyć żądanie.

50

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego

3. Metoda kontrolera aktualizuje obiekty w modelu, a następnie zwraca logiczną nazwę widoku oraz zaktualizowany model do serwletu przekazującego. 4. Serwlet przekazujący z pomocą resolwera widoków określa widok, który ma zostać wyrenderowany, a następnie przekazuje do niego dane z modelu. 5. Widok dostarcza dynamiczne wartości z modelu do strony internetowej, renderuje finalną postać strony, a następnie zwraca ją do serwletu przekazującego. 6. Serwlet przekazujący przesyła odpowiedź z wyrenderowaną stroną do przeglądarki internetowej.

Architektura aplikacji internetowej Znasz już ogólny przepływ żądania oraz odpowiedzialności każdego z komponentów typowej aplikacji napisanej w Springu MVC. Potrzebujesz jednak więcej informacji, nim będziesz w stanie zbudować aplikację sklepu internetowego. Musisz również poznać najlepsze praktyki stosowane podczas tworzenia korporacyjnej aplikacji internetowej. Jedną z nich jest organizowanie kodu źródłowego w warstwy, co ułatwi ponowne używanie kodu oraz zmniejszy sprzężenie pomiędzy obiektami. Typowa aplikacja internetowa ma zwykle cztery warstwy: prezentacji, domeny, usług oraz danych. Wszystkie komponenty, jakie do tej pory poznałeś, tj.: serwlet przekazujący, kontrolery, resolwery widoków itd., są uznawane za część warstwy prezentacji. Pora na omówienie pozostałych warstw oraz ich komponentów.

Warstwa domeny Warstwa domeny zazwyczaj zawiera model domenowy. Czym z kolei jest model domenowy? Jest to reprezentacja typów danych wymaganych przez logikę biznesową. Opisuje rozmaite obiekty domenowe (encje), ich atrybuty, role, powiązania oraz ograniczenia nakładane przez specyfikę domeny. Zapoznaj się z zaprezentowanym na rysunku 2.7 diagramem modelu domenowego przedstawiającego obsługę zamówienia, aby przybliżyć sobie koncepcję modeli domenowych.

Rysunek 2.7. Przykładowy model domeny

51

Spring MVC. Przewodnik dla początkujących

Każdy blok na powyższym diagramie przedstawia jedną biznesową encję. Linie reprezentują powiązania pomiędzy encjami. Posiłkując się diagramem, zauważysz, że w domenie obsługi zamówień klient może mieć wiele zamówień, każde z nich może zawierać wiele elementów, a każdy z elementów reprezentuje jeden produkt. Programista zamieni model domenowy w odpowiadające mu obiekty domenowe oraz powiązania. Obiekt domenowy jest logicznym kontenerem czystych domenowych informacji. Ze względu na to, że będziesz implementować sklep internetowy, Twoim głównym obiektem domenowym może być produkt. Zacznij pracę od jego implementacji.

Ćwiczenie praktyczne — tworzenie obiektu domenowego Do tej pory Twój sklep internetowy prezentował wyłącznie stronę powitalną. Pora na zaprezentowanie Twojego pierwszego produktu na stronie internetowej. W tym celu utwórz obiekt domenowy mający prezentować informacje o produkcie: 1. Utwórz klasę o nazwie Product w pakiecie com.packt.webstore.domain w katalogu src/main/java, a następnie umieść w niej kod źródłowy zaprezentowany w listingu 2.9. Listing 2.9. Implementacja klasy Product package com.packt.webstore.domain; import java.math.BigDecimal; public class Product { private String productId; private String name; private BigDecimal unitPrice; private String description; private String manufacturer; private String category; private long unitsInStock; private long unitsInOrder; private boolean discontinued; private String condition; public Product() { super(); } public Product(String productId, String name, BigDecimal unitPrice) { this.productId = productId; this.name = name; this.unitPrice = unitPrice; }

52

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego

// W tym miejscu dodaj gettery i settery dla wszystkich pól. @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Product other = (Product) obj; if (productId == null) { if (other.productId != null) return false; } else if (!productId.equals(other.productId)) return false; return true; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((productId == null) ? 0 : productId.hashCode()); return result; } @Override public String toString() { return "Produkt [productId=" + productId + ", nazwa=" + name +"]"; } }

Dodaj gettery i settery dla wszystkich pól powyższej klasy. Zostały pominięte, by zmniejszyć objętość kodu, są jednak niezbędne dla poprawnego działania aplikacji. 2. Utwórz klasę o nazwie ProductController w pakiecie com.packt.webstore.controller w katalogu src/main/java, a następnie umieść w niej kod źródłowy z listingu 2.10. Listing 2.10. Implementacja klasy ProductController package com.packt.webstore.controller; import import import import import

java.math.BigDecimal; org.springframework.stereotype.Controller; org.springframework.ui.Model; org.springframework.web.bind.annotation.RequestMapping; com.packt.webstore.domain.Product;

53

Spring MVC. Przewodnik dla początkujących

@Controller public class ProductController { @RequestMapping("/products") public String list(Model model) { Product iphone = new Product("P1234","iPhone 5s", new BigDecimal(500)); iphone.setDescription("Apple iPhone 5s, smartfon z 4-calowym wyświetlaczem o rozdzielczości 6401136 oraz 8-megapikselowym aparatem"); iphone.setCategory("Smart Phone"); iphone.setManufacturer("Apple"); iphone.setUnitsInStock(1000); model.addAttribute("product", iphone); return "products"; } }

3. Na koniec utwórz plik widoku JSP o nazwie products.jsp w katalogu src/main/webapp/ WEB-INF/views, a następnie umieść w nim kod znajdujący się w listingu 2.11. Listing 2.11. Deklaracja widoku products.jsp



Produkty



Produkty

Wszystkie produkty dostępne w naszym sklepie







${product.name}

${product.description}

${product.unitPrice}PLN

Liczba sztuk w magazynie: ${product.unitsInStock}



54

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego





4. Uruchom aplikację i wpisz adres: http://localhost:8080/webstore/products. Zobaczysz stronę wyświetlającą informację o produkcie, tak jak na rysunku 2.8.

Rysunek 2.8. Strona wyświetlająca informacje o produkcie

Co się właśnie wydarzyło? Twoim zadaniem było utworzenie strony wyświetlającej informacje o produkcie. Potrzebowałeś obiektu domenowego do przechowywania informacji o produkcie. Dlatego w kroku 1. utworzyłeś klasę o nazwie Product (Product.java), której zadaniem jest przechowywanie informacji o produkcie, np.: nazwy, opisu, ceny. Jak już zostało wspomniane w sekcji „Przegląd przepływu żądania w Springu MVC”, aby przedstawić dynamiczne dane na stronie internetowej, należy je umieścić w modelu — tylko

55

Spring MVC. Przewodnik dla początkujących

wtedy widok ma dostęp do danych modelu i będzie mógł je wyświetlić na stronie internetowej. W tym celu w kroku 3. utworzony został kolejny kontroler — o nazwie ProductController (ProductController.java). W klasie ProductController utworzyłeś metodę list, której zadaniem jest utworzenie obiektu domenowego w celu przechowywania informacji o produkcie Apple iPhone 5s. Następnie dodaje ona ten obiekt do modelu. Na koniec zwraca nazwę widoku (products). Za wspomniane operacje odpowiadają linie kodu w metodzie list klasy ProductController przedstawione w listingu 2.12. Listing 2.12. Fragment implementacji metody list w klasie ProductController model.addAttribute("product", iphone); return "products";

W pliku konfiguracji kontekstu aplikacji internetowej skonfigurowałeś InternalResourceView Resolver jako resolwer widoków. W związku z tym podczas poszukiwania widoku o wskazanej nazwie (w tym przypadku products) resolwer widoków będzie szukał pliku products.jsp w katalogu /WEB-INF/views/. Dlatego też w kroku 4. utworzyłeś plik products.jsp. Gdybyś pominął ten krok, aplikacja w czasie pracy zwracałaby błąd HTTP 404 podczas próby wyświetlenia informacji o produkcie. W celu poprawy czytelności widok products.jsp zawiera sporo znaczników div z załączonymi stylami Bootstrapa CSS (Bootstrap jest otwartym frameworkiem CSS). Na pierwszy rzut oka struktura widoku products.jsp wydaje się skomplikowana. Faktycznie jest bardzo prosta. Znaczniki div służą wyłącznie do dekoracji. Zwróć natomiast uwagę na znajdujące się w pliku products.jsp cztery znaczniki przedstawione w listingu 2.13. Ich zadaniem jest pobieranie danych z modelu. Listing 2.13. Fragment pliku products.jsp odpowiedzialny za wyświetlanie produktu ${product.name}

${product.description}

${product.unitPrice} PLN

Liczba sztuk w magazynie: ${product.unitsInStock}



Przyjrzyj się dokładnie wyrażeniu ${product.unitPrice}. Słowo product w wyrażeniu jest nazwą klucza użytego do przechowania obiektu iphone w modelu. (Przypomnij sobie fragment kodu model.addAttribute("product", iphone); pochodzący z klasy ProductController). Tekst unitPrice jest niczym innym niż jednym z pól klasy domenowej Product (Product.java). W podobny sposób zostały zaprezentowane inne ważne pola klasy domenowej Product w pliku products.jsp. Po ukończeniu kroku 4., gdy odwiedzisz adres: http://localhost:8080/WebStore/products, zostanie wyświetlona strona z informacjami o produkcie, taka jak zaprezentowana na poprzednim zrzucie ekranu.

56

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego

Mówiąc, że price jest nazwą pola, zakładamy, że przestrzegane są konwencje nazewnicze beanów Javy dla getterów i setterów Twojej klasy domenowej. Ma to znaczenie, ponieważ Spring podczas przetwarzania wyrażenia ${product.unitPrice} próbuje wywołać metodę gettera pola, by pobrać wartość. W związku z tym będzie oczekiwać obecności metody getUnitPrice() w pliku Product.java.

Utworzyłeś klasę domenową, by przechowywać informacje o produkcie. Następnie utworzyłeś jeden obiekt produktu i dodałeś go do modelu. Na koniec wyświetliłeś informację o produkcie w widoku.

Warstwa danych Gdy miałeś do czynienia z jednym produktem, wystarczyło, że utworzyłeś go w kontrolerze i wyświetliłeś na stronie. Jednak typowy sklep internetowy zawiera tysiące produktów. Wszystkie informacje o nich są zazwyczaj przechowywane w bazach danych. W związku z tym musisz usprawnić klasę ProductController tak, by potrafiła załadować wszystkie informacje o produktach z bazy danych do modelu. Jeśli jednak całą logikę odczytu danych z bazy umieścisz w klasie ProductController, rozrośnie się ona ponad miarę. Logicznie rzecz biorąc, pobieranie danych nie jest zadaniem kontrolera, ponieważ jest on komponentem z warstwy prezentacji. Co więcej, powinieneś uporządkować kod powiązany z pobieraniem danych w odrębnej warstwie, by można używać go tak często, jak to tylko możliwe, w innych kontrolerach i warstwach. Jak pobierać informacje z bazy danych zgodnie z duchem Springa MVC? Z pomocą przychodzi koncepcja warstwy danych. Warstwa ta zwykle składa się z obiektów repozytorium służących do dostępu do obiektów domenowych. Obiekt domenowy odpytuje źródło danych o informacje, następnie mapuje otrzymane informacje na obiekt domenowy, a wszystkie zmiany wprowadzone do tego obiektu zapisuje w źródle danych. Innymi słowy: obiekt repozytorium jest zazwyczaj odpowiedzialny za operacje CRUD (Create — utwórz, Read — odczytaj, Update — aktualizuj, Delete — skasuj) na obiektach domenowych. Adnotacja @Repository (org.springframework. stereotype.Repository) oznacza klasę jako repozytorium. Wskazuje również, że wyjątki SQL zgłaszane przez metody obiektów repozytorium powinny być tłumaczone na pochodzące ze Springa wyjątki typu DataAccessException. Pora na utworzenie warstwy danych dla aplikacji.

57

Spring MVC. Przewodnik dla początkujących

Ćwiczenie praktyczne — tworzenie obiektu repozytorium Przeprowadź następujące kroki, by utworzyć klasę repozytorium w celu uzyskania dostępu do obiektów domenowych produktów: 1. Utwórz interfejs o nazwie ProductRepository w pakiecie com.packt.webstore. domain.repository w katalogu z kodem źródłowym src/main/java. Umieść w nim pojedynczą deklarację metody: List getAllProducts();

2. Utwórz klasę o nazwie InMemoryProductRepository w pakiecie com.packt.webstore. domain.repository.impl w katalogu z kodem źródłowym src/main/java. Umieść w niej kod źródłowy zaprezentowany w listingu 2.14. Listing 2.14. Implementacja klasy InMemoryProductRepository package com.packt.webstore.domain.repository.impl; import import import import import import

java.math.BigDecimal; java.util.ArrayList; java.util.List; org.springframework.stereotype.Repository; com.packt.webstore.domain.Product; com.packt.webstore.domain.repository.ProductRepository;

@Repository public class InMemoryProductRepository implements ProductRepository{ private List listOfProducts = new ArrayList(); public InMemoryProductRepository() { Product iphone = new Product("P1234","iPhone 5s", new BigDecimal(500)); iphone.setDescription("Apple iPhone 5s, smartfon z 4-calowym ekranem o rozdzielczości 6401136 i 8-megapikselowym aparatem"); iphone.setCategory("Smartfon"); iphone.setManufacturer("Apple"); iphone.setUnitsInStock(1000); Product laptop_dell = new Product("P1235","Dell Inspiron", new BigDecimal(700)); laptop_dell.setDescription("Dell Inspiron, 14-calowy laptop (czarny) z procesorem Intel Core 3. generacji"); laptop_dell.setCategory("Laptop"); laptop_dell.setManufacturer("Dell"); laptop_dell.setUnitsInStock(1000);

58

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego

Product tablet_Nexus = new Product("P1236","Nexus 7", new BigDecimal(300)); tablet_Nexus.setDescription("Google Nexus 7 jest najlżejszym 7-calowym tabletem z 4-rdzeniowym procesorem Qualcomm Snapdragon™ S4 Pro"); tablet_Nexus.setCategory("Tablet"); tablet_Nexus.setManufacturer("Google"); tablet_Nexus.setUnitsInStock(1000); listOfProducts.add(iphone); listOfProducts.add(laptop_dell); listOfProducts.add(tablet_Nexus); } public List getAllProducts() { return listOfProducts; } }

3. Otwórz klasę ProductController znajdującą się w pakiecie com.packt.webstore. controller w katalogu z kodem źródłowym src/main/java i dodaj prywatną referencję do ProductRepository, używając adnotacji @Autowired (org.springframework. beans.factory.annotation.Autowired), jak na listingu 2.15. Listing 2.15. Deklaracja referencji do obiektu typu ProductRepository @Autowired private ProductRepository productRepository;

4. Zmień ciało metody list w klasie ProductController według listingu 2.16. Listing 2.16. Implementacja metody list w klasie ProductController @RequestMapping("/products") public String list(Model model) { model.addAttribute("products", productRepository.getAllProducts()); return "products"; }

5. Otwórz plik z widokiem products.jsp znajdujący się w katalogu src/main/webapp/ WEB-INF/views/ i zastąp znajdujący się tam kod źródłowy zawartością listingu 2.17. Listing 2.17. Definicja widoku products.jsp wyświetlającego listę produktów



59

Spring MVC. Przewodnik dla początkujących

Produkty



Produkty

Wszystkie produkty dostępne w naszym sklepie







${product.name}

${product.description}

${product.unitPrice} PLN

Liczba sztuk w magazynie: ${product.unitsInStock}







6. Uruchom aplikację i wprowadź w przeglądarce adres: http://localhost:8080/webstore/ products. Powinieneś zobaczyć stronę wyświetlającą informacje o produktach, jak na rysunku 2.9.

Co się właśnie wydarzyło? Ze względu na to, że nie chcesz umieszczać całej logiki powiązanej z pobieraniem danych w klasie ProductController, oddelegowałeś to zadanie do klasy o nazwie InMemoryProductRepository. Wspomniana klasa zawiera jedną metodę, o nazwie getAllProducts(), której zadaniem jest dostarczanie obiektów domenowych produktów. Klasa InMemoryProductRepository jest atrapą repozytorium przechowującą obiekty w pamięci RAM. Nie pobiera żadnych informacji o obiektach domenowych produktów z bazy danych.

60

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego

Rysunek 2.9. Strona prezentująca listę produktów pobranych z repozytorium w pamięci operacyjnej

Zamiast tego tworzy listę obiektów domenowych produktów w konstruktorze. W kroku 2. utworzyłeś klasę InMemoryProductRepository. W niej utworzyłeś metodę getAllProducts(), a w konstruktorze utworzyłeś kilka produktów. Zapewne zastanawiasz się, co się stało w kroku 1. Utworzyłeś w nim interfejs o nazwie Product Repository, który definiuje oczekiwane zachowanie repozytorium produktów. Od teraz jedynym oczekiwanym zachowaniem wspomnianego interfejsu jest zwracanie listy obiektów domenowych produktów (getAllObjects), natomiast klasa InMemoryProductRepository jest implementacją tego interfejsu. Tematem książki nie jest tworzenie kodu do pobierania rzeczywistych danych. W związku z tym klasa InMemoryProductRepository została utworzona w celach demonstracyjnych. Niemniej jednak wspomnianą klasę można zamienić na dowolną implementację zdolną do pobierania rzeczywistych informacji z bazy danych.

61

Spring MVC. Przewodnik dla początkujących

Dlaczego zostały utworzone zarówno interfejs, jak i implementacja repozytorium produktów? Pamiętaj, że tworzysz w swojej aplikacji warstwę danych. Gdzie będzie używany obiekt repozytorium warstwy danych? Prawdopodobnie będzie używany przez obiekt kontrolera (w tym przypadku przez ProductController) pochodzący z warstwy prezentacji. Bezpośrednie odwoływanie się do obiektów innej warstwy (tutaj obiektu kontrolera z warstwy prezentacji z obiektem repozytorium z warstwy danych) nie jest dobrą praktyką. Zamiast tego możesz w przyszłości w kontrolerze odwoływać się do interfejsu. Dzięki temu w dowolnej chwili będziesz mógł łatwo zmieniać implementacje repozytorium, nie dokonując żadnych zmian w kodzie klasy kontrolera. Z tego powodu w kroku 3. w klasie ProductController została utworzona referencja do interfejsu ProductRepository, a nie do klasy InMemoryProductRepository. Przyjrzyj się fragmentowi klasy ProductController zaprezentowanemu w listingu 2.18. Listing 2.18. Zastosowanie adnotacji @Autowired @Autowired private ProductRepository productRepository;

W jakim celu zastosowano tutaj adnotację @Autowired? Jeśli przyjrzysz się dokładnie klasie ProductController, zauważysz, że dla referencji productRepository nie został utworzony żaden obiekt. Nigdzie nie widnieje linia kodu podobna do productRepository = new InMemoryProduct Repository();. Co sprawia, że zaprezentowany poniżej kod productRepository.getAllProducts() działa poprawnie, nie wywołując błędu NullPointerException w metodzie list klasy ProductController? model.addAttribute("products", productRepository.getAllProducts() );

Co sprawia, że obiekt InMemoryProductRepository zostaje przyporządkowany do referencji productRepository? Przyporządkowanie odbywa się za pośrednictwem frameworka Springa. Jak już wiesz, Spring zajmuje się tworzeniem beanów (obiektów) każdej klasy oznaczonej adnotacją @Controller oraz zarządzaniem nimi. Podobnie rzecz się ma z klasami oznaczonymi adnotacją @Repository. Gdy nad referencją ProductRepository została umieszczona adnotacja @Autowired, Spring przypisał jej utworzony wcześniej i przechowywany w swoim kontenerze (kontekście aplikacji internetowej) obiekt typu InMemoryProductRepository. Jak zapewne pamiętasz, w pliku konfiguracji kontekstu aplikacji internetowej skonfigurowałeś przeglądanie komponentów za pomocą poniższego znacznika:

Dowiedziałeś się również, że kontekst aplikacji internetowej skonfigurowany we wspomniany sposób będzie wykrywał nie tylko kontrolery (@Controller), ale również inne stereotypy, takie jak repozytoria (@Repository) i serwisy (@Service).

62

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego

Klasa InMemoryProductRepository została oznaczona adnotacją @Repository. Dzięki temu Spring jest poinformowany, że jeśli jakakolwiek referencja typu ProductRepository posiada adnotację @Autowired, powinien jej zostać przypisany obiekt typu InMemoryProductRepository. Taki proces zarządzania zależnościami między klasami jest nazywany wstrzykiwaniem zależności lub wiązaniem w świecie Springa. Aby oznaczyć klasę jako obiekt repozytorium, należy opatrzyć ją adnotacją @Repository (org.springframework.stereotype.Repository). Rozumiesz już, jak działa warstwa danych. Jak jednak wyświetlić na stronie internetowej zwróconą listę produktów? Operacja jest bardzo podobna do znanego Ci już dodawania pojedynczego produktu do modelu. Tym razem zamiast jednego obiektu dodajesz do modelu całą listę za pomocą poniższej linii kodu pochodzącej z metody list klasy ProductController: model.addAttribute("products", productRepository.getAllProducts() );

W powyższym kodzie productRepository.getAllProducts() zwraca listę obiektów domenowych produktów (List). Wspomnianą listę dodajesz bezpośrednio do modelu. W pliku widoku (products.jsp), używając znacznika , iterujesz po liście i wyświetlasz informacje o każdym z produktów wewnątrz ostylowanego znacznika div, tak jak przedstawia to listing 2.19. Listing 2.19. Iteracja po liście produktów w widoku products.jsp



${product.name}

${product.description}

${product.unitPrice}PLN

Liczba sztuk w magazynie: ${product.unitsInStock}





Zauważ, że element products w wyrażeniu ${products} jest niczym innym niż kluczem użytym podczas dodawania listy produktów do modelu w klasie ProductController. Pętla for each jest specjalnym znacznikiem JSTL służącym do iteracji. Przejrzy całą listę produktów, przyporządkowując każdy z nich do zmiennej o nazwie product (var="product") przy każdej iteracji. Ze zmiennej product pobierane są informacje o nazwie, opisie oraz cenie produktu. Informacje te są wyświetlane wewnątrz znaczników oraz

. Dzięki temu jesteś w stanie zobaczyć listę produktów na stronie internetowej poświęconej produktom.

63

Spring MVC. Przewodnik dla początkujących

Warstwa usług Do tej pory udało Ci się z powodzeniem utworzyć warstwę prezentacji zawierającą kontroler, serwlet przekazujący, resolwery widoków itp. Następnie utworzyłeś warstwę danych zawierającą pojedynczą klasę domenową, tj. Product. Na koniec utworzyłeś warstwę danych zawierającą interfejs repozytorium oraz jego implementację zapewniającą dostęp do obiektów domenowych klasy Product. Brakuje ostatniego fragmentu nazywanego warstwą usług. Do czego jest ona potrzebna? Wiesz już, że warstwa danych odpowiada za logikę powiązaną z dostępem do danych (CRUD), warstwa prezentacji obsługuje aktywności powiązane z żądaniami HTTP oraz widokami, a warstwa domeny zawiera klasy przechowujące informacje pobrane z bazy danych lub z warstwy danych. Gdzie z kolei umieścić kod implementujący operacje biznesowe? Warstwa usług eksponuje operacje biznesowe, które mogą być wyrażone za pomocą wielu operacji CRUD. Wspomniane operacje są zazwyczaj wykonywane za pośrednictwem obiektów repozytorium. Na przykład jedną z operacji biznesowych może być obsługa zamówienia. By ją przeprowadzić, należy wykonać poniższe akcje: 1. Upewnić się, że wszystkie zamówione produkty są dostępne w sklepie. 2. Sprawdzić, czy liczba produktów w sklepie jest wystarczająca. 3. Zaktualizować asortyment, zmniejszając liczebność produktów o liczbę zamówionych egzemplarzy. Obiekty usług są dobrymi kandydatami do przechowywania logiki operacji biznesowych. Operacje usług mogą również reprezentować zakresy transakcji SQL. Oznacza to, że wszystkie podstawowe operacje CRUD przeprowadzane wewnątrz operacji biznesowej powinny znajdować się wewnątrz transakcji, tj. wszystkie zmiany powinny zostać przeprowadzone razem lub, w razie wystąpienia błędu, wszystkie powinny zostać wycofane.

Ćwiczenie praktyczne — tworzenie obiektu usługi Wykonaj następujące kroki, by utworzyć obiekt usługi, który będzie przeprowadzał prostą operację biznesową podczas przetwarzania zamówienia: 1. Otwórz interfejs ProductRepository pochodzący z pakietu com.packt.webstore. domain.repository z pliku kodu źródłowego src/main/java i dodaj kolejną deklarację: Product getProductById(String productId);

2. Otwórz klasę implementującą InMemoryProductRepository i dodaj implementację zadeklarowanej wcześniej metody w sposób przedstawiony w listingu 2.20.

64

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego

Listing 2.20. Implementacja metody getProductById public Product getProductById(String productId) { Product productById = null; for(Product product : listOfProducts) { if(product!=null && product.getProductId()!=null && product.getProductId().equals(productId)){ productById = product; break; } } if(productById == null){ throw new IllegalArgumentException("Brak produktu o wskazanym id: "+ productId); } return productById; }

3. Utwórz interfejs o nazwie OrderService w pakiecie com.packt.webstore.service w katalogu z kodem źródłowym src/main/java. Następnie dodaj deklarację metody: void processOrder(String productId, int count);

4. Utwórz klasę o nazwie OrderSeriveImpl w pakiecie com.packt.webstore.service.impl w katalogu z kodem źródłowym src/main/java. Następnie umieść w niej kod źródłowy przedstawiony w listingu 2.21. Listing 2.21. Implementacja klasy OrderServiceImpl package com.packt.webstore.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.packt.webstore.domain.Product; import com.packt.webstore.domain.repository.ProductRepository; import com.packt.webstore.service.OrderService; @Service public class OrderServiceImpl implements OrderService{ @Autowired private ProductRepository productRepository; public void processOrder(String productId, int count) { Product productById = productRepository. getProductById(productId); if(productById.getUnitsInStock() < count){ throw new IllegalArgumentException("Zbyt mało towaru. Obecna liczba sztuk w magazynie: "+ productById.getUnitsInStock()); }

65

Spring MVC. Przewodnik dla początkujących

productById.setUnitsInStock(productById.getUnitsInStock() - count); } }

5. Utwórz kolejną klasę kontrolera, o nazwie OrderController, w pakiecie com.packt. webstore.controller w katalogu z kodem źródłowym src/main/java i umieść w niej kod zaprezentowany w listingu 2.22. Listing 2.22. Implementacja klasy OrderController package com.packt.webstore.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import com.packt.webstore.service.OrderService; @Controller public class OrderController { @Autowired private OrderService orderService; @RequestMapping("/order/P1234/2") public String process() { orderService.processOrder("P1234", 2); return "redirect:/products"; } }

6. Uruchom aplikację i wprowadź adres: http://localhost:8080/webstore/order/P1234/2. Wyświetli się strona prezentująca informacje o produktach, jak na rysunku 2.10 (zauważ, że liczba sztuk iPhone’a 5s w magazynie to teraz 998).

Co się właśnie wydarzyło? Zanim przeczytasz komentarz do kroków, pamiętaj o regule dotyczącej obiektów repozytorium: wszystkie akcje związane z dostępem do danych (CRUD) obiektów domenowych powinny być przeprowadzane wyłącznie za pośrednictwem obiektów repozytorium. Pamiętaj również, że obiekty usług wykonują operacje na danych za pośrednictwem obiektów repozytorium. Z tego powodu zanim powstał interfejs oraz implementacja usługi, w krokach 1. i 2 utworzony został interfejs oraz implementacja używanej przez usługę metody getProductById. Zadaniem metody getProductById zaimplementowanej w klasie InMemoryProductRepository jest zwracanie obiektu domenowego produktu na podstawie wskazanego parametru id. Metoda ta jest Ci potrzebna podczas pisania logiki metody obiektu usługi (processOrder) w klasie Order ServiceImpl. Jeśli nie zostanie odnaleziony produkt o zadanym parametrze id, klasa InMemory ProductRepository zgłosi wyjątek IllegalArgumentException.

66

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego

Rysunek 2.10. Lista produktów wyświetlająca informację o produkcie po zaktualizowaniu liczby dostępnych sztuk za pośrednictwem usługi

W krokach 3. i 4. utworzyłeś definicję oraz implementację usługi. W kroku 3. zdefiniowałeś interfejs o nazwie OrderService, by określić w nim oczekiwane zachowania usługi obsługującej zamówienia. Na razie zdefiniowałeś tylko jedno zachowanie. Jest nim przetwarzanie zamówienia za pomocą metody processOrder. Wspomniana metoda przyjmuje parametry productId oraz count. W kroku 4. w klasie OrderServiceImpl zaimplementowałeś metodę processOrder. Jej zadaniem jest zmniejszanie liczby dostępnych sztuk produktu o danym productId o wartość parametru count. W poprzednim ćwiczeniu w obrębie klasy ProductController połączyłeś kontroler z repozytorium, używając referencji do interfejsu ProductRepository. Uniknąłeś dzięki temu silnego sprzężenia wspomnianych elementów. W obecnym ćwiczeniu połączyłeś warstwę usług z warstwą danych za pomocą referencji do interfejsu ProductRepository w sposób przedstawiony w listingu 2.23. Listing 2.23. Deklaracja referencji do interfejsu typu ProductRepository @Autowired private ProductRepository productRepository;

Jak już wiesz, Spring przyporządkował obiekt klasy InMemoryProductRepository do referencji productRepository w przedstawionym powyżej listingu. Stało się tak, ponieważ referencja productRepository jest oznaczona adnotacją @Autowired, a Spring tworzy obiekty oznaczone

67

Spring MVC. Przewodnik dla początkujących

adnotacjami @Service oraz @Repository i nimi zarządza. Zauważ, że klasa OrderServiceImpl jest oznaczona adnotacją @Service (org.springframework.stereotype.service). Użyłeś referencji productRepository, by pobrać produkt o zadanym id wewnątrz metody processOrder zaimplementowanej w klasie OrderServiceImpl, jak w listingu 2.24. Listing 2.24. Implementacja metody processOrder public void processOrder(String productId, int count) { Product productById = productRepository.getProductById(productId); if(productById.getUnitsInStock() < count){ throw new IllegalArgumentException("Zbyt mało towaru. Obecna liczba sztuk w magazynie: "+ productById.getUnitsInStock()); } productById.setUnitsInStock(productById.getUnitsInStock() - count); }

Aby zapewnić transakcyjność, Spring dostarcza adnotację @Transactional (org.springframework. transaction.annotation.Transactional). Musisz oznaczyć metody usług adnotacją @Transac tional, by nadać im atrybuty transakcyjności oraz skonfigurować transakcyjne zachowanie w konfiguracji kontekstu aplikacji. Skoro używasz atrapy repozytorium udającej mechanizm dostępu do danych i przechowującej informacje w pamięci operacyjnej, oznaczanie metod usługi adnotacją @Transactional jest bezcelowe. Więcej informacji na temat zarządzania transakcjami w Springu znajdziesz pod adresem: http://docs.spring.io/spring/ docs/current/springframework-reference/html/transaction.html.

Utworzyłeś warstwę usług. Można już z niej korzystać w warstwie prezentacji. Pora połączyć warstwę usług z kontrolerem. W kroku 5. utworzyłeś kolejny kontroler, o nazwie OrderController, a w nim metodę o nazwie process, mapującą żądania w sposób przedstawiony w listingu 2.25. Listing 2.25. Implementacja metody process @RequestMapping("/order/P1234/2") public String process() { orderService.processOrder("P1234", 2); return "redirect:/products"; }

Metoda process pochodząca z klasy OrderController używa referencji orderService, by przetwarzać zamówienie dotyczące produktu o id równym P1234. Po pomyślnym wykonaniu metody process w kontrolerze OrderController liczba dostępnych sztuk produktu o id równym P1234 powinna zostać zmniejszona o 2.

68

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego

Zauważ, że metoda process mapuje ścieżkę /order/P1234/2 za pomocą adnotacji @RequestMapping. Gdy odwiedzisz adres: http://localhost:8080/webstore/order/P1234/2, zauważysz, że liczba dostępnych sztuk produktu o id równym P1234 została zmniejszona o 2.

Dla ambitnych — uzyskiwanie dostępu do domenowego obiektu produktu za pośrednictwem usługi Klasa ProductController posiada wyłącznie referencję do obiektu typu ProductRepository. Zadaniem obiektu jest zapewnianie dostępu do obiektów domenowych klasy Product. Bezpośrednia komunikacja pomiędzy ProductRepository a ProductController nie jest dobrą praktyką. Wskazane jest, by odwoływać się do warstwy danych za pomocą obiektu pochodzącego z warstwy usług. Do tej pory nie stworzyłeś żadnego obiektu usług mogącego pośredniczyć pomiędzy klasami ProductController oraz ProductRepository. Dlaczego by nie utworzyć warstwy usług w celu pośredniczenia między klasami ProductCon troller i ProductRepository? Spróbuj wykonać poniższe kroki: 1. Utwórz interfejs o nazwie ProductService z deklaracją metody List getAllProducts();. 2. Utwórz klasę ProductServiceImpl zawierającą implementację interfejsu ProductService. 3. W klasie ProductServiceImpl oznacz adnotacją @Autowired referencję do ProductRepository. 4. Zastąp referencję do ProductRepository referencją do ProductService w klasie ProductController. Analogicznie zamień metodę list w klasie ProductController. 5. Po wykonaniu powyższych kroków zachowanie aplikacji nie powinno się zmienić. Odwiedź adres: http://localhost:8080/webstore/products/, by wyświetlić listę produktów.

Rzut oka na architekturę aplikacji internetowej Do tej pory dowiedziałeś się, jak grupować kod w warstwy, by uniknąć ścisłego sprzężenia między różnymi plikami z kodem źródłowym, ułatwić ponowne użycie kodu oraz zapewnić podział odpowiedzialności. W celach demonstracyjnych utworzyłeś po jednej klasie w warstwach domeny, usług oraz danych. Typowa aplikacja MVC może posiadać wiele klas w każdej z wymienionych warstw. Warstwy są zazwyczaj ze sobą połączone za pomocą interfejsów, a dostęp do obiektów domenowych jest zapewniany za pośrednictwem interfejsów warstwy usług. Typowe korporacyjne aplikacje Springa MVC posiadają cztery warstwy: prezentacji, domeny, usług oraz danych. Warstwa domeny jest czasem nazywana warstwą modeli. Diagram zaprezentowany na rysunku 2.11 pomoże Ci przyswoić sobie tę ideę.

69

Spring MVC. Przewodnik dla początkujących

Rysunek 2.11. Warstwy aplikacji napisanej z użyciem Springa MVC

Nauczyłeś się, jak tworzyć obiekty warstwy usług oraz warstwy danych. Miej na uwadze, że był to jedynie rzut oka na wspomniane warstwy. Spring dostarcza kompleksowego wsparcia w pracy z bazami danych oraz transakcjami. Jest to jednak temat tak rozległy, że zasługuje na opisanie w odrębnej książce. W kolejnych rozdziałach nie będziesz się skupiać na zagadnieniach związanych z transakcjami i bazami danych, ale na warstwie prezentacji, w skład której wchodzi większość elementów Springa MVC.

Dla ambitnych — wyświetlenie wszystkich klientów Dobrze, że potrafisz wyświetlić listę wszystkich produktów w Twojej aplikacji internetowej, używając adresu: http://localhost:8080/webstore/products, ale to nie wystarczy, by stworzyć użyteczny sklep internetowy. Musisz również utrzymywać informacje o klientach, by wzbudzać ich zainteresowanie ofertą za pomocą zniżek utworzonych na podstawie informacji o poprzednich zakupach. Dlaczego by nie utrzymywać informacji o klientach w Twojej aplikacji? Wykonaj poniższe kroki, by rozbudować ją o utrzymywanie informacji o klientach: 1. Utwórz kolejną klasę domeny (o nazwie Customer) w pakiecie, w którym znajduje się klasa Product. 2. Dodaj do niej pola: customerId, name, address oraz noOfOrdersMade. 3. Utwórz warstwę danych zwracającą wszystkich klientów. 4. Utwórz interfejs o nazwie CustomerRepository z sygnaturą metody List getAllCustomers();. 5. Utwórz klasę InMemoryCustomerRepository implementującą interfejs Customer Repository. Utwórz w niej atrapy klientów analogicznie do atrap produktów w klasie InMemoryProductRepository. 6. Utwórz warstwę usług, by pobierać wszystkich klientów z repozytorium. 7. Utwórz interfejs o nazwie CustomerService z sygnaturą metody List getAllCustomers();. 8. Utwórz klasę CustomerServiceImpl implementującą interfejs CustomerService.

70

Rozdział 2. • Architektura Springa MVC — projektowanie sklepu internetowego

9. Utwórz klasę kontrolera o nazwie CustomerController. 10. Dodaj mapowanie żądania dla adresu URL: http://localhost:8080/webstore/customers. 11. Utwórz plik widoku o nazwie customers.jsp. Po zakończeniu ćwiczenia będziesz w stanie zobaczyć wszystkich swoich klientów na stronie dostępnej pod adresem: http://localhost:8080/webstore/customers. Klienci są wyświetlani podobnie jak produkty na stronie dostępnej pod adresem: http://localhost:8080/webstore/products.

Podsumowanie Na początku rozdziału poznałeś zadania serwletu przekazującego. Dowiedziałeś się, jak mapuje on żądania, używając adnotacji @RequestMapping. Następnie przyjrzałeś się kontekstowi aplikacji internetowej oraz dowiedziałeś się, jak skonfigurować go w swojej aplikacji internetowej. Pobieżnie zapoznałeś się z koncepcją resolwera widoków oraz zbadałeś, jak InternalResourceView Resolver wybiera plik widoku na podstawie jego logicznej nazwy. Przedstawiona została koncepcja MVC, przepływ żądania w aplikacji opartej na Springu MVC oraz architektura aplikacji internetowej. W sekcji poświęconej architekturze aplikacji internetowej nauczyłeś się, jak tworzyć i grupować kod w różnych warstwach aplikacji opartej na Springu MVC, takich jak warstwa domeny, danych i usług. Dowiedziałeś się również, jak pobierać obiekty domeny produktu z repozytorium oraz jak za pomocą kontrolera wyświetlać je na stronie internetowej. Wiesz również, gdzie umieszczać obiekty warstwy usług. Na koniec przyjrzałeś się architekturze aplikacji internetowej. Powinieneś już rozeznawać się w Springu MVC oraz komponentach związanych z implementacją aplikacji opartej na tym szkielecie. W następnym rozdziale dowiesz się więcej o kontrolerach i powiązanych z nimi zagadnieniach. Do zobaczenia na miejscu!

71

Spring MVC. Przewodnik dla początkujących

72

3 Kontroluj swój sklep za pomocą kontrolerów W rozdziale 2., „Architektura Springa MVC — projektowanie Twojego sklepu internetowego”, poznałeś ogólną architekturę aplikacji opartej na Springu MVC. Żadne z pojęć nie zostało przedstawione szczegółowo, celem było zrozumienie architektury jako całości. W bieżącym rozdziale skoncentrujesz się na kontrolerach w Springu MVC. Ich rola w tym szkielecie jest znaczna.

Rozdział porusza następujące zagadnienia:  definiowanie kontrolerów;  wzorce szablonów URI;  zmienne tablicowe;  parametry żądania.

Definiowanie kontrolera Kontrolery są komponentami warstwy prezentacji odpowiedzialnymi za reagowanie na działania użytkownika. Takim działaniem może być wprowadzenie adresu URL w przeglądarce, uruchomienie hiperłącza, przesłanie formularza itd. Każda klasa Javy może zostać zmieniona w kontroler za pomocą adnotacji @Controller (org.springframework.stereotype.Controller). Jak już wiesz, adnotacja @Controller pozwala Springowi automatycznie wykryć i zarejestrować definicję beana w kontekście aplikacji internetowej. Aby uruchomić automatyczne rejestrowanie, musisz dodać znacznik w pliku konfiguracji kontekstu aplikacji.

Spring MVC. Przewodnik dla początkujących

Nauczyłeś się, jak przeprowadzić tę operację, w rozdziale 2., „Architektura Springa MVC — projektowanie Twojego sklepu internetowego”, w podrozdziale „Kontekst konfiguracji aplikacji internetowej”. Kontroler składa się m.in. z metod mapujących żądania, zwanych również metodami obsługi zdarzenia. Są one oznaczone adnotacjami @RequestMapping (org.springframework.web.bind. annotation.RequestMapping). Wspomniana adnotacja służy do mapowania adresów URL do konkretnych metod obsługi zdarzenia. W rozdziale 2., „Architektura Springa MVC — projektowanie Twojego sklepu internetowego”, została pokrótce przedstawiona adnotacja @RequestMapping oraz jej zastosowanie na poziomie metody obsługującej zdarzenie. Wspomniana adnotacja może być również użyta na poziomie klasy kontrolera. W takim przypadku Spring MVC rozpatruje adnotację na poziomie klasy przed mapowaniem URL-a do konkretnej metody. Takie zachowanie jest nazywane relatywnym mapowaniem żądania. Pojęcia: metoda mapująca żądanie, metoda mapująca, metoda obsługi zdarzenia oraz metoda kontrolera oznaczają metody w kontrolerze oznaczone adnotacją @RequestMapping. Wszystkie te pojęcia są w tej książce używane zamiennie.

Ćwiczenie praktyczne — dodawanie mapowania żądania na poziomie klasy Pora oznaczyć klasę ProductController adnotacją @RequestMapping, by zademonstrować koncepcję relatywnego mapowania żądań. Zanim to jednak nastąpi, upewnij się, że w klasie ProductCon troller zastąpiłeś referencję do ProductRepository referencją do ProductService, tak jak zostało to przedstawione w poprzednim rozdziale w podrozdziale „Ćwiczenie praktyczne — tworzenie obiektu usługi”. Ponieważ bezpośrednia komunikacja między warstwą prezentacji a warstwą danych nie jest dobrą praktyką, dostęp do warstwy danych powinien odbywać się za pośrednictwem warstwy usług. Wykonaj następujące kroki (jeżeli już wykonałeś to ćwiczenie, przejdź od razu do kroku 5.): 1. Utwórz interfejs o nazwie ProductService w pakiecie com.packt.webstore.service w katalogu src/main/java i umieść w nim deklaracje metod przedstawione w listingu 3.1. Listing 3.1. Deklaracja metod interfejsu ProductService List getAllProducts(); Product getProductById(String productID);

2. Utwórz klasę o nazwie ProductServiceImpl w pakiecie com.packt.webstore. service.impl w katalogu src/main/java i umieść w niej kod zaprezentowany w listingu 3.2.

74

Rozdział 3. • Kontroluj swój sklep za pomocą kontrolerów

Listing 3.2. Implementacja klasy ProductServiceImpl package com.packt.webstore.service.impl; import import import import import import

java.util.List; org.springframework.beans.factory.annotation.Autowired; org.springframework.stereotype.Service; com.packt.webstore.domain.Product; com.packt.webstore.domain.repository.ProductRepository; com.packt.webstore.service.ProductService;

@Service public class ProductServiceImpl implements ProductService{ @Autowired private ProductRepository productRepository; public List getAllProducts() { return productRepository.getAllProducts(); } public Product getProductById(String productID) { return productRepository.getProductById(productID); } }

3. Otwórz klasę ProductController, usuń istniejącą referencję do ProductRepository i dodaj referencję do ProductService zaprezentowaną w listingu 3.3. Listing 3.3. Deklaracja referencji do obiektu typu ProductServiceImpl @Autowired private ProductService productService;

4. W klasie ProductController zmień metodę list tak, jak przedstawia to listing 3.4 (zauważ, że tym razem używasz referencji productService, by pobrać wszystkie produkty). Listing 3.4. Implementacja metody list w klasie ProductController @RequestMapping("/products") public String list(Model model) { model.addAttribute("products", productService.getAllProducts()); return "products"; }

5. Klasę ProductController udekoruj poniższą adnotacją: @RequestMapping("/products")

75

Spring MVC. Przewodnik dla początkujących

6. Z adnotacji @RequestMapping metody list usuń parametr. Metoda list będzie udekorowana wspomnianą adnotacją w sposób przedstawiony w listingu 3.5. Listing 3.5. Adnotacja metody list @RequestMapping public String list(Model model) {

7. Dodaj kolejną metodę obsługi zdarzenia w klasie ProductController, jak w listingu 3.6. Listing 3.6. Deklaracja metody allProducts @RequestMapping("/all") public String allProducts(Model model) { model.addAttribute("products", productService.getAllProducts()); return "products"; }

8. Uruchom ponownie aplikację i wpisz w przeglądarce adres: http://localhost:8080/ webstore/products/all, aby zobaczyć listę wszystkich produktów.

Co się właśnie wydarzyło? Została zademonstrowana koncepcja relatywnego mapowania żądań. W klasie ProductController dokonałeś trzech poniższych modyfikacji:  W kroku 5. dodałeś adnotację @RequestMapping o wartości "/products" na poziomie klasy.  Usunąłeś wartość adnotacji @RequestMapping dla metody list.  Utworzyłeś metodę obsługi zdarzenia o nazwie allProducts, która dodaje do modelu listę produktów identyczną z metodą list, jednak używa innego mapowania adresu URL — @RequestMapping("/all").

W dotychczasowych przykładach używałeś adnotacji @RequestMapping na poziomie metody kontrolera, jednak Spring MVC pozwala również na używanie ich na poziomie klasy. W takim wypadku Spring MVC mapuje ścieżkę adresu URL na poziomie metody odpowiednio do wartości adnotacji @RequestMapping na poziomie klasy. W kroku 5. dodałeś na poziomie klasy adnotację @RequestMapping z wartością mapowania adresu URL /products. W kroku 7. dodałeś nową metodę obsługi zdarzenia, o nazwie allProducts, mapującą ścieżkę /all. Ścieżka żądania obsługiwana przez metodę allProducts jest złączeniem wartości mapowań na poziomie klasy oraz metody — w tym przypadku jest to /products/all. Jeżeli zdefiniujesz mapowanie na poziomie klasy, Spring MVC przeanalizuje, je zanim dopasuje żądanie do metody.

76

Rozdział 3. • Kontroluj swój sklep za pomocą kontrolerów

Kroki 1. – 4. pokazują, jak utworzyć obiekty warstwy usług w klasie ProductController i jak ich używać. Na razie klasa ProductServiceImpl nie zawiera żadnej logiki biznesowej. Zamiast tego deleguje do warstwy danych żądania pobrania obiektów typu Product. Na razie tworzenie warstwy usług nie ma żadnego racjonalnego uzasadnienia. Niemniej jednak, jeśli w przyszłości zdecydujesz się na zmianę obiektu InMemoryProductRepository na obiekt odwołujący się do rzeczywistej bazy danych, będziesz potrzebował warstwy usług, by implementować w niej kod odpowiedzialny za zadania wymagające transakcji. Aby zachować zgodność z najlepszymi praktykami w branży, pozostałe przykłady w książce będą wykorzystywać warstwę usług.

W kroku 6. nie sprecyzowałeś ścieżki do żądania w adnotacji @RequestMapping dekorującej metodę list. Dzięki temu metoda list stała się domyślną metodą mapującą żądania w klasie kontrolera ProductController. Odtąd, jeśli początek ścieżki żądania zostanie dopasowany do mapowania na poziomie klasy, ale pozostała część nie zostanie dopasowana do żadnej z metod, dla tego żądania zostanie wywołana ta metoda. W tym przypadku adres: http://localhost:8080/webstore/products będzie zmapowany do metody list, a adres: http://localhost:8080/webstore/products/all zostanie zmapowany do metody allProducts. Jeżeli utworzysz więcej niż jedną domyślną metodę mapującą wewnątrz kontrolera, otrzymasz wyjątek IllegalStateException z komunikatem „Ambigous mapping found”. Kontroler może posiadać co najwyżej jedną domyślną metodę mapującą.

Krótki test — dodawanie mapowania żądań na poziomie klasy Pytanie 1. Zakładając, że Twoja aplikacja nazywa się library, a mapowania na poziomie klasy i metod są takie, jak zaprezentowano w listingu 3.7, jak musi być skonstruowany URL, by wywołana została metoda books? Listing 3.7. Przykładowe mapowania na poziomie klasy oraz metody @RequestMapping("/books") public class BookController { ... @RequestMapping(value = "/list") public String books(Model model) { ...

1. http://localhost:8080/library/books/list. 2. http://localhost:8080/library/list. 3. http://localhost:8080/library/list/books. 4. http://localhost:8080/library/.

77

Spring MVC. Przewodnik dla początkujących

Pytanie 2. Jeżeli do kontrolera BookController zostanie dodana przedstawiona w listingu 3.8 metoda o nazwie bookDetails, który adres URL będzie przez nią zmapowany? Listing 3.8. Przykładowe mapowanie na poziomie klasy @RequestMapping public String bookDetails(Model model) { ...

1. http://localhost:8080/library/books/details. 2. http://localhost:8080/library/books. 3. http://localhost:8080/library/details. 4. http://localhost:8080/library/.

Rola kontrolera w Springu MVC W Springu MVC metody kontrolera są miejscem, do którego docelowo trafiają żądania HTTP. Po wywołaniu metoda kontrolera rozpoczyna przetwarzanie żądania, posiłkując się warstwą usług, by wykonać wymagane operacje. Zazwyczaj warstwa usług wykonuje operacje biznesowe na obiektach domenowych i odwołuje się do warstwy danych w celu aktualizacji obiektów domenowych. Gdy obiekt warstwy usług wykona zlecone zadania, kontroler aktualizuje i uzupełnia obiekt modelu, a następnie wybiera widok, który zostanie przedstawiony użytkownikowi jako odpowiedź. Pamiętaj, że w Springu MVC kontroler nie jest świadomy, jaka technologia została użyta do utworzenia widoku. Dlatego kontroler zwraca wyłącznie logiczną nazwę widoku. Następnie serwlet przekazujący za pośrednictwem resolwera widoków ustala, który widok ma zostać wyrenderowany. Dla kontrolera model jest kolekcją obiektów, a widok jest określony wyłącznie logiczną nazwą. We wszystkich wcześniejszych ćwiczeniach kontrolery zwracały logiczną nazwę widoku i aktualizowały model, korzystając z parametru model przekazanego do metody kontrolera. Jest inna, rzadko używana metoda aktualizowania modelu i zwracania widoku przez kontroler za pomocą obiektu typu ModelAndView (org.springframework.web.servlet.ModelAndView). Przyjrzyj się przykładowi zaprezentowanemu w listingu 3.9. Listing 3.9. Implementacja metody zwracającej obiekt typu ModelAndView @RequestMapping("/all") public ModelAndView allProducts() { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("products", productService.getAllProducts());

78

Rozdział 3. • Kontroluj swój sklep za pomocą kontrolerów

modelAndView.setViewName("products"); return modelAndView; }

Fragment kodu przedstawiony w listingu 3.9 prezentuje, jak opakować model i widok, używając obiektu typu ModelAndView.

Interfejs HandlerMapping Wiesz już, że DispatcherServlet na podstawie mapowania żądań przekierowuje żądanie do obsługującej je metody. Aby zinterpretować mapowania zdefiniowane w adnotacji @RequestMapping, serwlet przekazujący potrzebuje implementacji interfejsu HandlerMapping (org.springframework. web.servlet.HandlerMapping). Serwlet odpytuje jedną lub więcej implementacji interfejsu HandlerMapping, by zbadać, który kontroler (handler) może obsłużyć żądanie. Tak więc Handler Mapping określa, który kontroler ma zostać wywołany. Interfejs HandlerMapping dostarcza definicje metody mapującej żądania do kontrolerów. Jego implementacje potrafią zbadać żądanie i wskazać odpowiedni kontroler. Spring MVC dostarcza wiele implementacji wspomnianego interfejsu. Implementacją, której używasz do wykrycia i zinterpretowania mapowania pochodzącego z adnotacji @RequestMapping, jest klasa Request MappingHandlerMapping (org.springframework.web.servlet.mvc.method.annotation.Request MappingHandlerMapping). Zacząłeś jej używać, dodając znacznik do pliku konfiguracji kontekstu aplikacji internetowej, by Spring MVC mógł utworzyć i zarejestrować beana RequestMappingHandlerMapping w kontekście konfiguracji Twojej aplikacji. Dodałeś go w rozdziale 2., „Architektura Springa MVC — projektowanie Twojego sklepu internetowego”, w podrozdziale „Kontekst konfiguracji aplikacji internetowej”.

Używanie szablonów wzorców URI W poprzednich rozdziałach nauczyłeś się, jak odwzorować konkretny URL na metodę kontrolera. Na przykład jeżeli wprowadzonym adresem był: http://localhost:8080/webstore/products, mapowałeś to żądanie na metodę list kontrolera ProductController i wyświetlałeś stronę zawierającą informacje o wszystkich produktach. A jeśli chciałbyś wyświetlić wyłącznie pewien podzbiór produktów na podstawie kategorii? Na przykład gdy użytkownik wprowadzi adres: http://localhost:8080/webstore/products/laptop, powinien zobaczyć listę laptopów. Natomiast gdy wprowadzi adres: http://localhost:8080/webstore/ products/tablet, powinien zobaczyć na stronie wynikowej wyłącznie listę tabletów.

79

Spring MVC. Przewodnik dla początkujących

Jednym ze sposobów jest utworzenie w kontrolerze odrębnych metod mapujących żądania dla każdej unikalnej kategorii. To rozwiązanie nie będzie się skalować. Jeśli będziesz miał setki kategorii, będziesz musiał utworzyć setki metod mapujących w kontrolerze. Jak to zrobić w bardziej elegancki sposób? Możesz użyć funkcjonalności Springa MVC pozwalającej na tworzenie szablonów wzorców URI. Jeśli przyjrzysz się przykładowym adresom, zauważysz, że nie różnią się niczym oprócz kategorii (laptop i tablet):  http://localhost:8080/webstore/products/laptop,  http://localhost:8080/webstore/products/tablet.

Możesz utworzyć wspólny szablon URI dla wspomnianych adresów URL, który może wyglądać w następujący sposób: http://localhost:8080/webstore/products/{category}. Spring MVC potrafi wykorzystać ten fragment szablonu ({category}), by utworzyć zmienną zwaną zmienną ścieżki w świecie Springa.

Ćwiczenie praktyczne — wyświetlanie produktów na podstawie kategorii Pora rozszerzyć stronę produktów o przeglądanie na podstawie kategorii przesłanej jako zmienna ścieżki: 1. Otwórz interfejs ProductRepository i dodaj w nim deklarację metody getProductsByCategory: List getProductsByCategory(String category);

2. Otwórz klasę InMemoryProductRepository implementującą interfejs Product Repository i dodaj przedstawioną w listingu 3.10 implementację metody zadeklarowanej w poprzednim punkcie. Listing 3.10. Implementacja metody getProductsByCategory public List getProductsByCategory(String category) { List productsByCategory = new ArrayList(); for(Product product: listOfProducts) { if(category.equalsIgnoreCase(product.getCategory())){ productsByCategory.add(product); } } return productsByCategory; }

3. Otwórz interfejs ProductService i dodaj poniższą deklarację metody getProducts ByCategory:

80

Rozdział 3. • Kontroluj swój sklep za pomocą kontrolerów

List getProductsByCategory(String category);

4. Otwórz ProductServiceImpl, klasę zawierającą implementację usługi, i dodaj do niej implementację metody przedstawioną w listingu 3.11. Listing 3.11. Implementacja metody getProductsByCategory public List getProductsByCategory(String category) { return productRepository.getProductsByCategory(category); }

5. Otwórz klasę ProductController i dodaj metodę mapującą żądanie przedstawioną w listingu 3.12. Listing 3.12. Implementacja metody mapującej ścieżkę żądania /{category} @RequestMapping("/{category}") public String getProductsByCategory(Model model, @PathVariable("category") String productCategory) { model.addAttribute("products", productService.getProductsByCategory (productCategory)); return "products"; }

6. Uruchom aplikację i wprowadź w przeglądarce adres: http://localhost:8080/webstore/ products/tablet. Powinna się wyświetlić strona taka jak na rysunku 3.1.

Co się właśnie wydarzyło? Krok 5. był najważniejszy spośród przedstawionych na powyższej liście. Wszystkie poprzedzające były wyłącznie krokami przygotowawczymi. W kroku 5. zwyczajnie dodałeś listę obiektów typu Product do modelu: model.addAttribute("products", productService.getProductsByCategory(ProductCategory));

Przyjrzyj się metodzie getProductsByCategory obiektu productService. Potrzebujesz jej, by dostać listę produktów danej kategorii. Obiekt productService nie potrafi zwrócić takiej listy bez pomocy repozytorium. Dlatego też w kroku 4. w klasie ProductServiceImpl użyłeś referencji do productRepository, by otrzymać wspomnianą listę. Przyjrzyj się fragmentowi kodu pochodzącego z klasy ProductServiceImpl: return productRepository.getProductsByCategory(category);

Kolejnym wartym uwagi fragmentem kodu dodanego w kroku 5. jest parametr ścieżki mapowanej przez adnotację @RequestMapping: @RequestMapping("/{category}")

81

Spring MVC. Przewodnik dla początkujących

Rysunek 3.1. Wyświetlanie produktów według kategorii podanej w ścieżce adresu URL

Otaczając fragment ścieżki nawiasem klamrowym, wskazujesz, że Spring MVC ma traktować ten fragment jako szablon zmiennej ścieżki. Dokumentacja Springa MVC informuje, że szablonem URI jest tekst o strukturze podobnej do URI, posiadający co najmniej jedną zmienną ścieżki. Gdy przypiszesz zmiennym wartości, tworzysz URI zgodny z szablonem. Na przykład szablon URI: http://localhost:8080/webstore/products/{category} zawiera zmienną category. Nadając tej zmiennej wartość laptop, otrzymujesz URI: http://localhost:8080/webstore/ products/laptop. W Springu MVC możesz użyć adnotacji @PathVariable (org.springframework. web.bind.annotation.PathVariable), by odczytać zmienną szablonu URI. Ponieważ na poziomie klasy kontrolera jest obecna adnotacja @RequestMapping ("/products"), żądanie obsługiwane przez metodę getProductsByCategory będzie miało postać /products/ {category}. W czasie pracy aplikacji, jeżeli zostanie przesłany URL: http://localhost:8080/ webstore/products/laptop, zmienna category będzie miała wartość laptop. Podobnie gdy adres będzie miał postać: http://localhost:8080/webstore/products/tablet, zmienna category będzie miała wartość tablet. Jak pobrać wartość przechowywaną w category, zmiennej ścieżki szablonu URI? Jak już zostało wspomniane, używa się w tym celu adnotacji @PathVariable. Wystarczy udekorować wspomnianą adnotacją parametr metody, tak jak w poniższym przykładzie:

82

Rozdział 3. • Kontroluj swój sklep za pomocą kontrolerów

public String getProductsByCategory(@PathVariable("category") String productCategory, Model model) {

Spring MVC odczyta dowolną wartość obecną w miejscu zmiennej szablonu URI o nazwie category i przypisze ją do parametru metody o nazwie productCategory. Możesz ją przypisać do zmiennej, a następnie przekazać do metody obiektu productService, by pobrać listę produktów o danej kategorii. Zwróconą listę obiektów możesz dodać do modelu i zwrócić nazwę tego samego widoku, którego używałeś do wyświetlenia wszystkich produktów. Atrybut nazwy zmiennej w adnotacji @PathVariable powinien być taki sam jak nazwa zmiennej występującej w ścieżce zdefiniowana w adnotacji @RequestMapping. Na przykład jeśli ścieżka ma postać "/products/{identity}", to aby uzyskać wartość zmiennej identity, adnotacja @Path Variable musi mieć postać @PathVariable("identity"). Jeżeli adnotacja @PathVariable została użyta bez atrybutu, spróbuje pobrać wartość zmiennej ścieżki o takiej samej nazwie jak zmienna, którą dekoruje. Na przykład jeżeli zadeklarujesz zmienną @PathVariable String productId, Spring automatycznie założy, że powinien szukać zmiennej ścieżki "{productId}" w URL-u. Mapowanie żądania może mieć dowolną liczbę adnotacji @PathVariable.

Na koniec w kroku 6., gdy wprowadziłeś adres: http://localhost:8080/webstore/products/tablet, wyświetlone zostały informacje o tablecie Google Nexus 7. Analogicznie gdy wprowadziłeś adres: http://localhost:8080/webstore/products/laptop, wyświetlone zostały informacje o laptopie Dell Inspiron.

Krótki test — zmienne ścieżki żądania Pytanie 1. Zakładając, że aplikacja internetowa nazywa się WebStore, a jej mapowania żądań na poziomie klasy i metody są takie, jak przedstawiono na listingu 3.13, który z adresów URL zostanie poprawnie obsłużony przez aplikację? Listing 3.13. Przykładowe mapowania na poziomie klasy oraz metody @RequestMapping("/items") public class ProductController { ... @RequestMapping(value = "/type/{type}", method = RequestMethod.GET) public String productDetails(@PathVariable("type") String productType, Model model) {

1. http://localhost:8080/WebStore/items/electronics. 2. http://localhost:8080/WebStore/items/type/electronics.

83

Spring MVC. Przewodnik dla początkujących

3. http://localhost:8080/WebStore/items/productType/electronics. 4. http://localhost:8080/WebStore/type/electronics. Pytanie 2. Która z sygnatur metod poprawnie pobierze zmienne ścieżki zdefiniowane w zadeklarowanej poniżej adnotacji mapującej żądanie? @RequestMapping(value="/manufacturer/{ manufacturerId}/product/{productId}")

1. public String productByManufacturer(@PathVariable String manufacturerId, @PathVariable String productId, Model model). 2. public String productByManufacturer(@PathVariable String manufacturer, @PathVariable String product, Model model). 3. public String productByManufacturer(@PathVariable("manufacturer") String manufacturerId, @PathVariable("product") String productId, Model model). 4. public String productByManufacturer(@PathVariable("manufacturerId") String manufacturer, @PathVariable("productId") String product, Model model).

Używanie zmiennych tablicowych Oprócz poznanej w poprzednim podrozdziale metody deklarowania zmiennych w szablonach URI istnieje alternatywna metoda deklarowania zmiennych w adresie żądania. Można je deklarować za pomocą par klucz-wartość. Zmienne zadeklarowane w ten sposób są w Springu MVC nazywane zmiennymi tablicowymi. Przyjrzyj się poniższemu adresowi URL: http://localhost:8080/webstore/products/filter/price;low=500;high=1000

W powyższym adresie ścieżką jest wyłącznie część: http://localhost:8080/webstore/products/ filter/price. Następujący po niej fragment low=500;high=1000 to zmienne tablicowe. Wyjątkowymi czyni je fakt, że jednej zmiennej można przyporządkować kilka wartości. To znaczy, że możesz zmiennej w URI przyporządkować listę wartości. Przyjrzyj się poniższemu adresowi URL: http://localhost:8080/webstore/products/filter/ByCriteria;brand=google,dell; category=tablet,laptop

W powyższym adresie zadeklarowane zostały dwie zmienne: brand oraz category. Obu zostały przyporządkowane wielokrotne wartości: brand=google,dell oraz category=tablet,laptop. Jak odczytać mapowanie wartości zmiennych tak zadeklarowanych w żądaniu URL? Należy użyć specjalnej adnotacji @MatrixVariable (org.springframework.web.bind.annotation. MatrixVariable). Przydatnym udogodnieniem zapewnianym przez adnotację @MatrixVariable jest możliwość odczytu zmiennych tablicowych jako mapy kolekcji (Map). Udogodnienie to jest potrzebne podczas pracy ze złożonymi żądaniami.

84

Rozdział 3. • Kontroluj swój sklep za pomocą kontrolerów

Ćwiczenie praktyczne — filtrowanie wyświetlanych produktów Wyobraź sobie wymaganie, by filtrować listę produktów na podstawie zmiennych brand oraz category. Na przykład chcesz wyświetlić wszystkie produkty należące do kategorii laptop lub tablet, a ich producentami są Google lub Dell. Za pomocą zmiennych tablicowych możesz w zaprezentowany poniżej sposób utworzyć adres URL posiadający wymagane wartości zmiennych brand i category: http://localhost:8080/webstore/products/filter/ByCriteria;brand=google,dell; category=tablet,laptop

Odwzoruj powyższy URL w metodzie obsługi zdarzenia, używając adnotacji @MatrixVariable: 1. Otwórz interfejs ProductRepository i dodaj w nim deklarację metody getProductsByFilter. Set getProductsByFilter(Map filterParams);

2. Otwórz klasę implementującą InMemoryProductRepository i umieść w niej implementację metody getProductsByFilter przedstawioną w listingu 3.14. Listing 3.14. Implementacja metody getProductsByFilter w klasie InMemoryProductRepository public Set getProductsByFilter(Map filterParams) { Set productsByBrand = new HashSet(); Set productsByCategory = new HashSet(); Set criterias = filterParams.keySet(); if(criterias.contains("brand")) { for(String brandName: filterParams.get("brand")) { for(Product product: listOfProducts) { if(brandName.equalsIgnoreCase(product.getManufacturer())){ productsByBrand.add(product); } } } } if(criterias.contains("category")) { for(String category: filterParams.get("category")) { productsByCategory.addAll(this.getProductsByCategory(category)); } } productsByCategory.retainAll(productsByBrand); return productsByCategory; }

3. W interfejsie ProductService umieść poniższą deklarację metody getProductsByFilter: Set getProductsByFilter(Map filterParams);

85

Spring MVC. Przewodnik dla początkujących

4. Otwórz klasę ProductServiceImpl implementującą serwis wymieniony w punkcie 3. i umieść w niej implementację metody getProductsByFilter przedstawioną w listingu 3.15. Listing 3.15. Implementacja metody getProductsByFilter w klasie ProductServiceImpl public Set getProductsByFilter(Map filterParams) { return productRepository.getProductsByFilter(filterParams); }

5. Dodaj zaprezentowaną w listingu 3.16 implementację metody kontrolera w klasie ProductController. Listing 3.16. Implementacja metody getProductsByFilter w klasie ProductController @RequestMapping("/filter/{ByCriteria}") public String getProductsByFilter(@MatrixVariable(pathVar="ByCriteria") Map filterParams, Model model) { model.addAttribute("products", productService.getProductsByFilter (filterParams)); return "products"; }

6. Przejdź do katalogu src/main/webapp/WEB-INF/spring/webcontext/, otwórz plik konfiguracji kontekstu aplikacji (DispatcherServlet-context.xml) i aktywuj wsparcie zmiennych tablicowych, ustawiając parametrowi enable-matrix-variables w znaczniku wartość true:

7. Uruchom aplikację i w przeglądarce wpisz adres: http://localhost:8080/webstore/ products/filter/ByCriteria;brand=google,dell;category=tablet,laptop;. Wyświetli się strona prezentująca produkty, przedstawiona na rysunku 3.2.

Co się właśnie wydarzyło? Celem ćwiczenia było wydobycie zmiennej tablicowej z adresu URL i wykonanie z jej użyciem pożytecznej operacji. W tym przypadku adresem, który próbowałeś odwzorować, był: http:// localhost:8080/webstore/products/filter/ByCriteria;brand=google,dell;category=tablet,laptop, zawierający interesujące Cię zmienne tablicowe brand oraz category. Wartości tych zmiennych to odpowiednio google,dell oraz tablet,laptop. Ścieżka powyższego zapytania to: http:// localhost:8080/webstore/products/filter/ByCriteria, dlatego też w kroku 5. metoda mapująca żądanie została udekorowana w następujący sposób: @RequestMapping("/filter/{ByCriteria}")

86

Rozdział 3. • Kontroluj swój sklep za pomocą kontrolerów

Rysunek 3.2. Zastosowanie zmiennych tablicowych do wyświetlania listy produktów na podstawie kryteriów

Możesz się zastanawiać, dlaczego w adnotacji @RequestMapping pojawił się szablon URI, przypominający mapowanie do zmiennej ścieżki. Dzieje się tak dlatego, że adres żądania zawiera zmienną tablicową, a w adnotacji @RequestMapping musisz zdefiniować początkowy segment tej zmiennej w adresie URL. Dlatego element ByCriteria został zdefiniowany jako szablon URI w adnotacji mapującej żądanie (@RequestMapping("/filter/{ByCriteria}")). Adres URL może zawierać wiele zmiennych tablicowych. Każda z nich musi być zakończona średnikiem. Aby przyporządkować wiele wartości do jednej zmiennej, każda wartość musi być oddzielona przecinkiem. Alternatywnie można kilkakrotnie użyć tej samej nazwy zmiennej, np. zaprezentowany poniżej adres URL zawiera powtórzone zmienne brand oraz category: http://localhost:8080/webstore/products/filter/ByCategory;brand=google; brand=dell;category=tablet;category=laptop

Zmapowałeś żądanie do metody getProductsByFilter, ale jak pobrać wartości zmiennych tablicowych? Należy użyć adnotacji @MatrixVariable. Jest ona bardzo podobna do adnotacji @Path Variable. Jeżeli przyjrzysz się sygnaturze metody getProductsByFilter, zdefiniowanej w punkcie 5., zauważysz, że adnotacja @MatrixParam udekorowała zmienną filterParams w następujący sposób:

87

Spring MVC. Przewodnik dla początkujących

public String getProductsByFilter(@MatrixVariable(pathVar="ByCriteria") Map filterParams, Model model)

Spring MVC odczytał zmienne tablicowe odnalezione w adresie URL za szablonem URI {ByCri teria} i umieścił je w mapie parametrów metody o nazwie filterParameters. Nazwy zmiennych są odzwierciedlone jako klucze, a lista wartości tych zmiennych — jako wartości odpowiadające tym kluczom w mapie filterParameters. Atrybut pathVar adnotacji @MatrixVariable jest używany w celu identyfikacji segmentu zmiennych matrycowych w adresie URL. Dlatego też jego wartość to ByCriteria, która jest niczym innym niż wartością szablonu URI użytego podczas mapowania adresu żądania. Adres URL może zawierać wiele zmiennych tablicowych. Przyjrzyj się poniższemu URL-owi: http://localhost:8080/webstore/products/filter/ByCriteria;brand=google,dell; category=tablet,laptop/BySpecification;dimention=10,20,15;color=red,green,blue

Powyższy adres zawiera dwa segmenty zmiennych tablicowych, identyfikowanych odpowiednio przez segmenty ByCriteria oraz BySpecification. Aby umieścić każdą ze zmiennych tablicowych w mapie, musisz utworzyć sygnaturę metody przedstawioną w listingu 3.17. Listing 3.17. Implementacja metody filter wykorzystującej zmienne tablicowe @RequestMapping("/filter/{ByCriteria}/{BySpecification}") public String filter(@MatrixVariable(pathVar="ByCriteria") Map criteriaFilter, @MatrixVariable(pathVar="BySpecification") Map specFilter, Model model) {

Przekazałeś wartość tablicy parametrów do parametru metody typu Map o nazwie filterParams, co się jednak dalej z nim działo? Został przekazany jako parametr do metody usługi, by pobrać produkty filtrowane na podstawie kryteriów, jak na poniższym przykładzie: productService.getProductsByFilter(filterParams)

Usługa przekazuje mapę do repozytorium, by pobrać listę produktów na podstawie kryteriów. Otrzymana lista zostaje dodana do modelu. Jako wynik metody zostaje zwrócona nazwa widoku użytego wcześniej do zaprezentowania wszystkich produktów. Aby umożliwić korzystanie ze zmiennych tablicowych w Springu MVC, w kroku 6. ustawiłeś atrybut enable-matrix-variables z wartością true w znaczniku . Na koniec w kroku 7. byłeś w stanie obejrzeć listę produktów utworzoną na podstawie zadanych kryteriów.

88

Rozdział 3. • Kontroluj swój sklep za pomocą kontrolerów

Zrozumieć parametry żądania Zmienne tablicowe oraz zmienne ścieżki doskonale sprawdzają się w pobieraniu wartości ze ścieżki adresu URL żądania. Jest jednak jeszcze jeden sposób umieszczania zmiennych w żądaniu HTTP. Mogą one być umieszczane w ciele żądania — są to tzw. parametry HTTP. Mogłeś już słyszeć o parametrach GET oraz POST. Parametry GET są używane od lat w celu umieszczania wartości w URL-u, natomiast parametry POST są używane w celu umieszczania wartości w ciele żądania. Więcej o parametrach POST przeczytasz w następnym rozdziale, podczas zajmowania się przesyłaniem formularzy. Pora nauczyć się, jak pobrać parametry GET z żądania w podejściu Springa MVC. Aby zademonstrować użycie parametru żądania, wzbogacisz aplikację o stronę wyświetlającą szczegółowe informacje o produkcie.

Ćwiczenie praktyczne — dodawanie strony ze szczegółowymi danymi produktu Do tej pory strona z informacjami o produktach przedstawiała wyłącznie podstawowe informacje, takie jak nazwa produktu, opis, cena oraz liczba dostępnych sztuk. Nie były wyświetlane takie informacje, jak nazwa producenta, kategoria, identyfikator produktu itd. Pora utworzyć stronę przedstawiającą szczegółowe informacje o produkcie: 1. Otwórz klasę ProductController i dodaj metodę mapującą żądanie przedstawioną w listingu 3.18. Listing 3.18. Implementacja metody getProductById w klasie ProductController @RequestMapping("/product") public String getProductById(@RequestParam("id") String productId, Model model) { model.addAttribute("product", productService.getProductById(productId)); return "product"; }

2. Utwórz plik o nazwie product.jsp w katalogu src/main/webapp/WEB-INF/views, umieść w nim kod źródłowy przedstawiony w listingu 3.19, a następnie zapisz go. Listing 3.19. Zawartość pliku product.jsp



89

Spring MVC. Przewodnik dla początkujących

Produkt



Produkt



${product.name}

${product.description}

Kod produktu: ${product.productId}

Producent: ${product.manufacturer}

Kategoria: ${product.category}

Dostępna liczba sztuk :${product.unitsInStock}

${product.unitPrice}PLN



Zamów teraz





Uruchom aplikację i wprowadź adres: http://localhost:8080/webstore/products/product?id=P1234;. Zobaczysz stronę prezentującą informacje o produkcie, przedstawioną na rysunku 3.3.

90

Rozdział 3. • Kontroluj swój sklep za pomocą kontrolerów

Rysunek 3.3. Użycie parametru żądania do wyświetlania strony z informacjami o produkcie

Co się właśnie wydarzyło? W kroku 1. wykonałeś operację podobną do przeprowadzonej w metodzie getProductsByCate gory kontrolera ProductController. Dodałeś do modelu obiekt produktu zwracany przez obiekt usługi: model.addAttribute("product", productService.getProductById(productId));

Nasuwa się istotne pytanie: skąd jest dostarczany parametr productId? Odpowiedź jest prosta. Jak być może się domyślasz, skoro parametr productId jest udekorowany adnotacją @Request Param("id") (org.springframework.web.bind.annotation.RequestParam), Spring MVC spróbuje odczytać z żądania parametr GET o nazwie id i przekazać go do metody getProductById jako parametr o nazwie productId. Adnotacja @RequestParam zachowuje taką samą konwencję jak inne adnotacje. Jeżeli nazwa parametru GET oraz nazwa oznaczonej metody są takie same, nie trzeba określać nazwy atrybutu w adnotacji @RequestParam. Na koniec w kroku 6. dodałeś plik widoku o nazwie product.jsp, ponieważ Twoją intencją było wyświetlenie wszystkich informacji o produkcie. Widok product.jsp nie używa żadnych wysublimowanych technik. Jak zwykle z modelu zostają pobrane wartości i wyświetlone wewnątrz znaczników HTML za pomocą typowej notacji języka wyrażeń JSTL ${}, jak w listingu 3.20.

91

Spring MVC. Przewodnik dla początkujących

Listing 3.20. Przykład zastosowania notacji JSTL ${product.name}

${product.description}

...

Wiesz już, jak pobrać parametr GET z adresu URL, ale jak przekazać więcej niż jeden parametr GET w adresie URL? Należy każdą parę klucz-wartość oddzielić symbolem &. Na przykład jeżeli chcesz przesłać category oraz price jako parametry GET żądania, musisz utworzyć poniższy adres URL: http://localhost:8080/WebStore/products/product?category=laptop&price=700

Podobnie aby odwzorować powyższy URL w metodzie mapującej żądanie, musi ona mieć co najmniej dwa parametry oznaczone adnotacją @RequestParam: public String getProducts(@RequestParam String category, @RequestParam String price) {

Krótki test — parametry żądania Pytanie 1. Który adres żądania jest poprawny w kontekście sygnatury metody mapującej żądania przedstawionej w listingu 3.21? Listing 3.21. Przykładowe mapowanie na poziomie metody @RequestMapping(value = "/products", method = RequestMethod.GET) public String productDetails(@RequestParam String rate, Model model)

1. http://localhost:8080/webstore/products/rate=400. 2. http://localhost:8080/webstore/products?rate=400. 3. http://localhost:8080/webstore/products?rate/400. 4. http://localhost:8080/webstore/products/rate=400.

Ćwiczenie praktyczne — implementacja widoku strony typu master-detail Strona master-detail to strona zawierająca informacje o produktach oraz odnośniki do stron zawierających szczegółowe informacje o każdym z nich. Po wybraniu dowolnego produktu na stronie głównej użytkownik zostaje przekierowany do strony produktu, zawierającej wszystkie informacje o nim. Pora zbudować widok master-detail szczegółowych informacji, by wybierając dowolny produkt, zobaczyć stronę prezentującą wszystkie informacje na jego temat.

92

Rozdział 3. • Kontroluj swój sklep za pomocą kontrolerów

Zdążyłeś już zaimplementować stronę prezentującą listę produktów (http://localhost:8080/webstore/ products) oraz stronę z informacjami o produkcie (http://localhost:8080/webstore/products/ product?id=1234). Jedyne, co zostało do zrobienia, to połączenie obu widoków. Wykonaj poniższe kroki: 1. Otwórz plik products.jsp. Znajdziesz go w katalogu src/main/webapp/WEB-INF/ views/ Twojego projektu. W pierwszej linii dodaj referencję do biblioteki znaczników Springa:

2. Dodaj fragment kodu zaprezentowany w listingu 3.22 za znacznikiem paragrafu informującego o liczbie dostępnych sztuk produktu w pliku products.jsp. Listing 3.22. Rozszerzenie pliku products.jsp o wyświetlanie liczby dostępnych sztuk produktu



Szczegóły



3. Otwórz plik product.jsp. Znajdziesz go w katalogu src/main/webapp/WEB-INF/views/ Twojego projektu. Dodaj w pierwszej linii referencję do biblioteki znaczników Springa:

Następnie dodaj zaprezentowany w listingu 3.23 fragment kodu przed odnośnikiem Kup teraz w pliku product.jsp. Listing 3.23. Implementacja przycisku Wstecz w widoku product.jsp

Wstecz

4. Uruchom aplikację i wprowadź adres: http://localhost:8080/webstore/products. Zostanie Ci zaprezentowana strona z listą produktów posiadających przyciski Szczegóły, tak jak na rysunku 3.4. 5. Naciśnij dowolny przycisk o nazwie Szczegóły. Zostanie Ci zaprezentowana strona ze szczegółowymi informacjami na temat wybranego produktu oraz przyciskiem Wstecz odsyłającym do strony prezentującej listę produktów.

Co się właśnie wydarzyło? Wykonane zostały proste operacje. W kroku 2. w pliku products.jsp za pomocą znacznika zaprezentowanego w listingu 3.24 zostało dodane hiperłącze.

93

Spring MVC. Przewodnik dla początkujących

Rysunek 3.4. Główny widok listy produktów Listing 3.24. Implementacja hiperłącza do strony prezentującej informacje o produkcie o wskazanym identyfikatorze " class="btn btn-primary"> Szczegóły

Przyjrzyj się poniższemu atrybutowi href znacznika , którego wartością jest znacznik :

Znacznik został użyty w celu skonstruowania poprawnego adresu URL w standardzie Springa. Wspomniany znacznik miał być użyty w kroku 2., w związku z tym w kroku 1. została dodana referencja do biblioteki znaczników Springa. Zwróć uwagę na wartość atrybutu

94

Rozdział 3. • Kontroluj swój sklep za pomocą kontrolerów

znacznika . Parametrowi id zostało przyporządkowane wyrażenie ${product. productId}. Podczas generacji hiperłącza Spring MVC za pomocą wspomnianego wyrażenia ustawi odpowiedni identyfikator produktu. Na przykład podczas generacji hiperłącza dla pierwszego produktu Spring MVC ustawi identyfikator produktu o wartości P1234. Końcowa wartość adresu URL wewnątrz znacznika będzie miała postać /products/product?id=P1234. Wygenerowany adres jest mapowany przez stronę informacji o produkcie. Używając tego hiperłącza, przeniesiesz się na stronę z informacjami o tym produkcie. Analogicznie potrzebujesz hiperłącza kierującego ze strony prezentującej szczegółowe informacje o produkcie do strony z listą produktów. Dlatego też w kroku 4. dodałeś do pliku product.jsp znacznik przedstawiony w listingu 3.25. Listing 3.25. Implementacja hiperłącza do strony prezentującej informacje o produktach

Powrót

Znacznik span jest w powyższym przykładzie nieistotny, służy wyłącznie do udekorowania przycisku ikoną. Jedynym interesującym elementem jest atrybut href znacznika , którego wartością jest znacznik z atrybutem /products.

Dla ambitnych — dodawanie wielu filtrów do listy produktów Nauczyłeś się rozmaitych technik umieszczania parametrów w adresach URL, takich jak zmienne ścieżki, zmienne tablicowe oraz parametry GET. Wiesz, jak pobrać produkty z zadanej kategorii, używając zmiennych ścieżki, jak pobrać produkty z określonego przedziału cenowego oraz jak pobrać produkt na podstawie jego pola id. Wyobraź sobie, że chcesz użyć wielu kryteriów, by wyświetlić pożądany produkt, np. produkt z kategorii tablet o cenie od 200 PLN do 400 PLN, którego producentem jest Google. Możesz wygenerować poniższy URL, aby pobrać produkt, który spełni wszystkie powyższe kryteria: http://localhost:8080/webstore/products/tablet/price; low=200;high=400?manufacturer=Google

Spróbuj napisać metodę kontrolera obsługującą powyższy adres URL. Poniżej znajdziesz kilka podpowiedzi, jak osiągnąć ten cel:  Utwórz metodę warstwy danych zwracającą wszystkie produkty danego producenta. W tym celu dodaj poniższą deklarację metody w interfejsie ProductRepository: List getProductsByManufacturer(String manufacturer);

95

Spring MVC. Przewodnik dla początkujących

Dodaj implementację metody getProductsByManufacturer() w klasie InMemory ProductRepository. Przypomina ona metodę getProductsByCategory(). Jedyną różnicą jest to, że produkty są pobierane na podstawie nazwy producenta, a nie kategorii.  Rozszerz interfejs ProductService o metodę getProductsByManufacturer() i zaimplementuj ją w klasie ProductServiceImpl.  W klasie ProductController utwórz metodę kontrolera o nazwie filterProducts

mapującą poniższy adres URL: http://localhost:8080/webstore/products/tablet/price; low=200;high=400?manufacturer=Google

Pamiętaj, że w powyższym URL-u znajdują się zmienne tablicowe low oraz high reprezentujące przedział cen, parametr GET o nazwie manufacturer identyfikujący producenta oraz zmienna ścieżki szablonu URI o wartości tablet reprezentująca kategorię.  Użyj istniejącego widoku products.jsp do wyświetlania odfiltrowanych produktów.

Pamiętaj, że metoda getProductsByCategory wywołana na obiekcie productService zwraca listę produktów na podstawie kategorii. Metoda getProductsByPriceFilter zwraca produkty z zadanego zakresu cenowego, a nowo utworzona metoda getProductsByManufacturer zwraca produkty wytworzone przez konkretnego producenta. Musisz przetworzyć wyniki tych trzech metod, zanim dodasz do modelu listę produktów w metodzie kontrolera o nazwie filterProducts. Prawdopodobnie możesz użyć java.util.Set, by połączyć wyniki wszystkich trzech metod i jednocześnie uniknąć duplikacji. Powodzenia!

Podsumowanie W tym rozdziale nauczyłeś się, jak zdefiniować kontroler i jak używać adnotacji @Controller. Następnie poznałeś koncepcję relatywnego mapowania żądań oraz dowiedziałeś się, jak definiować mapowanie żądań na poziomie kontrolera. Wiesz już również, jak Spring relatywnie odwzorowuje żądanie na metody mapujące kontrolera. Poznałeś rolę kontrolera w Springu MVC. Dowiedziałeś się również, że serwlet przekazujący używa implementacji interfejsu Handler Mapping do wyszukiwania metod obsługujących zdarzenia. Poznałeś rozmaite metody ustawiania parametrów żądania, takie jak wzorce szablonów URI, zmienne tablicowe oraz parametry GET żądań HTTP. Na koniec dowiedziałeś się, jak implementować widok typu master-detail. W następnym rozdziale poznasz rozmaite znaczniki Springa dostępne w bibliotece znaczników. Dowiesz się również więcej o przetwarzaniu formularzy oraz wypełnianiu wartości pól formularza parametrami HTTP POST. Przygotuj się na następny rozdział!

96

4 Praca z bibliotekami znaczników Springa W poprzednich rozdziałach nauczyłeś się, jak umieszczać dane w modelu za pośrednictwem kontrolera. Nie wiesz jeszcze, jak odwrócić ten proces, tj. jak przemieszczać dane z widoku do modelu. W Springu MVC proces umieszczania danych z formularza HTML w zmiennych modelu jest nazywany wiązaniem formularza. Spring MVC dostarcza kilka bibliotek znaczników JSP w celu ułatwienia wiązania elementów formularza w modelu danych. Biblioteki znaczników Springa dostarczają również wielu innych funkcjonalności, takich jak np. wyodrębnianie komunikatów oraz obsługa błędów. W tym rozdziale dowiesz się więcej o używaniu predefiniowanych bibliotek znaczników Springa.

Po zapoznaniu się z zawartością rozdziału będziesz potrafił:  prezentować i przetwarzać formularze;  wypełniać formularze oraz korzystać z mechanizmu białych list;  używać bibliotek znaczników Springa.

Prezentowanie i przetwarzanie formularzy Spring wspiera wiele sposobów implementacji widoków. Jeżeli implementujesz widoki z użyciem JSP, możesz posłużyć się w nich znacznikami pochodzącymi z biblioteki znaczników JSP. Dostarcza ona wielu użytecznych powszechnych funkcjonalności, takich jak wiązanie formularzy, wyświetlanie zinternacjonalizowanych komunikatów o błędach itd. By korzystać ze wspomnianych znaczników, musisz dodać referencję do bliblioteki znaczników Springa w Twoim pliku JSP w sposób przedstawiony w listingu 4.1.

Spring MVC. Przewodnik dla początkujących

Listing 4.1. Dodawanie w pliku JSP referencji do biblioteki znaczników Springa

W przykładach zamieszczonych w poprzednich rozdziałach miałeś okazję zaobserwować proces przesyłu danych z modelu do widoku za pomocą kontrolera w następujący sposób: model.addAttribute(greeting,"Witaj")

Kolejny, przedstawiony poniżej fragment kodu pokazuje, w jaki sposób możesz wyświetlić te dane w widoku, używając JSTL:

${greeting}

JavaServer Pages Standard Tag Library (JSTL) jest biblioteką znaczników udostępnianą przez Oracle. Dostarcza znaczniki JSP realizujące wiele podstawowych funkcjonalności powszechnie używanych podczas budowy widoków. Umieszczając w pliku JSP linię: , dodasz referencję do biblioteki znaczników JSTL w swoim pliku widoku.

Co jednak, jeśli chcesz umieścić w modelu dane pochodzące z widoku? Jak pobrać je z kontrolera? Na przykład rozważ scenariusz, w którym administrator Twojego sklepu internetowego chce dodać nową informację o produkcie, wypełniając i wysyłając formularz HTML. Jak zebrać dane wprowadzone w formularzu, a następnie przetworzyć je w kontrolerze? Tu z pomocą przychodzi biblioteka znaczników Springa, wiążąc wartości znaczników HTML z beanem formularza w modelu. Następnie kontroler może pobrać bean formularza, wykorzystując adnotację @ModelAttribute (org.springframework.web.bind.annotation.ModelAttribute). Beany formularza (nazywane również beanami wspomagającymi formularze) są używane do przechowywania danych formularza. Możesz również używać obiektów domenowych jako beanów formularza. To podejście się sprawdza, gdy pola formularza są zbieżne z polami obiektu domenowego. Innym podejściem jest tworzenie osobnych klas dla beanów formularza, zwanych czasem obiektami transferu danych (data transfer object — DTO).

Ćwiczenie praktyczne — prezentowanie i przetwarzanie formularzy Biblioteka znaczników Springa dostarcza specjalne znaczniki oraz , podobne do odpowiedników z języka HTML, posiadające atrybuty pozwalające na wiązanie danych w elementach formularza z beanem formularza. Pora utworzyć w aplikacji internetowy formularz Springa, umożliwiający dodawanie nowych produktów do listy produktów. Wykonaj następujące kroki:

98

Rozdział 4. • Praca z bibliotekami znaczników Springa

1. Otwórz interfejs ProductRepository i dodaj poniższą deklarację metody: void addProduct(Product product);

2. Umieść w klasie InMemoryProductRepository implementację metody przedstawioną w listingu 4.2. Listing 4.2. Implementacja metody dodającej nowy produkt do repozytorium public void addProduct(Product product) { listOfProducts.add(product); }

3. Otwórz interfejs ProductService i dodaj w nim poniższą metodę: void addProduct(Product product);

4. W pliku ProductServiceImpl dodaj metodę zaprezentowaną w listingu 4.3. Listing 4.3. Implementacja metody addProduct public void addProduct(Product product) { productRepository.addProduct(product); }

5. Otwórz klasę ProductController i dodaj w niej dwie metody mapujące żądania, zaprezentowane w listingu 4.4. Listing 4.4. Implementacja mapowania ścieżki /add dla żądań typu GET oraz POST @RequestMapping(value = "/add", method = RequestMethod.GET) public String getAddNewProductForm(Model model) { Product newProduct = new Product(); model.addAttribute("newProduct", newProduct); return "addProduct"; } @RequestMapping(value = "/add", method = RequestMethod.POST) public String processAddNewProductForm(@ModelAttribute("newProduct") Product newProduct) { productService.addProduct(newProduct); return "redirect:/products"; }

6. Na koniec utwórz plik JSP o nazwie addProduct.jsp w katalogu src/main/webapp/ WEB-INF/views/. Umieść na jego początku zawartość listingu 4.5. 7. Umieść w pliku addProduct.jsp zawartość listingu 4.6, a następnie zapisz go. Zauważ, że w listingu zostały pominięte znaczniki dla niektórych pól. Wskazane jest, byś dodał je podczas powtarzania tego ćwiczenia.

99

Spring MVC. Przewodnik dla początkujących

Listing 4.5. Definicja bibliotek znaczników użytych w pliku addProduct.jsp

Listing 4.6. Formularz dodawania nowego produktu do sklepu



Produkty



Produkty

Dodaj produkty





Dodaj nowy produkt

Id produktu





Anuluj





2. Następnie utwórz w tym samym katalogu plik widoku JSP o nazwie collectShippingDetail.jsp i umieść w nim zawartość listingu 9.26. Zauważ, że nie zostały w nim umieszczone znaczniki dla większości pól obiektu domenowego typu Address o nazwie shippingAddress. Znajdziesz je w pełnej wersji kodu źródłowego, który możesz pobrać ze strony: www.helion.pl/ksiazki/ sprimv.html. Listing 9.26. Implementacja widoku collectShippingDetails



229

Spring MVC. Przewodnik dla początkujących