#09 — Spring MVC

By 16 October 2014Kurs Javy

Lekcja

Dzisiejsza lekc­ja zapewne będzie naj­ciekawszą z doty­chcza­sowych — nasza aplikac­ja będzie dostęp­na przez przeglą­darkę i otrzy­ma pier­wsze funkcjon­al­noś­ci!

Będzie ona zarazem jed­ną z najnud­niejszych — poz­namy w niej nieprzy­jem­ną zasadę Napisz — Uru­chom — Poczekaj, czyli w jaki sposób sprawdza­my czy aplikac­ja dzi­ała.

W każdej z poprzed­nich lekcji mogliśmy uru­chomić naszą aplikację i po ułamku sekundy wiedzieliśmy, czy dzi­ała czy nie. W przy­pad­ku aplikacji webowych uru­chomie­nie trwa dłużej a też ‘wyk­likanie’ czy dzi­ała czy nie zaj­mu­je więcej cza­su. Dlat­ego od tego momen­tu należy się uzbroić w cier­pli­wość, ponieważ konieczność ponownego uruchami­a­nia aplikacji po każdej drob­nej zmi­an­ie może być, i będzie, frus­tru­ją­ca. Na pociesze­nie mogę dodać jedynie, że wraz z nabieraniem wprawy takie sprawdzanie będzie coraz mniej częste, a w kole­jnych lekc­jach poz­namy lep­sze sposo­by na testowanie poprawnego dzi­ała­nia aplikacji.

Spring MVC — dodawanie do projektu

W zada­niu do poprzed­niej lekcji dodal­iśmy już zależnoś­ci do bib­liote­ki Spring, ale nie uak­ty­wnil­iśmy jej jeszcze w naszym pro­jek­cie. Tym zajmiemy się na początek.

Maven — zależności

Jes­li nie miałaś kiedy zając się ostat­nim zadaniem, to poniżej rozwiązanie ;)

Doda­je­my do pliku pom.xml w mod­ule koty-webapp poniższe zależnoś­ci (w najnowszych wer­s­jach):

oraz do mod­ułu koty-appli­ca­tion ponown­ie:

Jeśli potrze­bu­jesz przy­pom­nienia jak zarządzać zależnoś­ci­a­mi w Mave­nie, zapraszam Cię do ponownego przeczy­ta­nia poprzed­niej lekcji numer 7 :)

Konfiguracja

Na początku parę słów o tym jak Spring MVC dzi­ała w pro­jek­cie. Spring w pro­jek­tach webowych najczęś­ciej jest uruchami­any z uży­ciem wzor­ca Front Con­troller. Więcej o wzor­cach z przykłada­mi będziemy roz­maw­iali w koń­cowych lekc­jach kur­su, ale w uproszcze­niu ten wzorzec zakła­da posi­adanie ‘nad­zor­cy’, który zarządza wszys­tki­mi zapy­ta­ni­a­mi HTTP — tzn. każde zapy­tanie trafia do tego zarząd­cy, a następ­nie on decy­du­je, jak go obsłużyć (np. do jakiego kon­trol­era je przekierować). W Springu tą rolę pełni tzw. Dis­patch­erServlet. (Servlet to pod­sta­wowy ele­ment budul­cowy dla aplikacji webowych w specy­fikacji Java EE, nie będziemy go poz­nawać dokład­nie w ramach głównego wątku kur­su). Servlet ten kon­fig­u­ru­je­my stan­dar­d­owo w pliku web.xml (o tym za chwilę) wskazu­jąc kon­fig­u­rację Springową — plik XML który określa, jak ma się zachowywać Spring (o nim też w dal­szej częś­ci).

Te dwa pli­ki to właś­ci­wie cała kon­fig­u­rac­ja której potrze­bu­je­my. Spring wyz­na­je zasadę Con­ven­tion over Con­fig­u­ra­tion, czyli nie musimy kon­fig­urować ele­men­tów i wtedy użyte zostaną sen­sowne wartoś­ci domyślne. Najczęś­ciej to wystar­cza. Jed­nocześnie może­my zmienić naprawdę najbardziej szczegółowe ustaw­ienia, jeśli tylko chce­my. W tym kur­sie pójdziemy tą pier­wszą drogą ;)

Plik web.xml

Ten plik jest częś­cią stan­dar­du JavaEE i pozwala nam określać m.in. Servle­ty, Fil­try, Lis­ten­ery itp. My wyko­rzys­tamy go jed­nie do tego, aby ‘odd­ać’ pełną kon­trolę Springowi — dodamy omaw­iany wcześniej Dis­patch­erServlet i nie będziemy więcej mody­fikować tego pliku.

Zain­tere­sowanych zapraszam do szer­szego zapoz­na­nia się ze specy­fikacją lub inny­mi mate­ri­ała­mi w tym tema­cie (w sekcji lin­ki).

Plik ten o poniższej treś­ci, należy umieś­cić w kat­a­logu /sr­c/­main/we­bap­p/WEB-INF (jes­li kat­a­log WEB-INF nie ist­nieje, należy go utworzyć).

Pełny list­ing pliku web.xml poniżej:

<web-app id="WebApp_ID" version="2.4"
 xmlns="http://java.sun.com/xml/ns/j2ee" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>Baza danych kotow</display-name>

    <servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/applicationContext.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/rootApplicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

</web-app>
Plik applicationContext.xml

Plik ten może mieć właś­ci­wie dowol­ną nazwę, ale albo musi być ona podana w pliku web.xml (tak jak my to zro­bil­iśmy) albo musi mieć nazwę taką jak servlet. Ja z przyzwycza­je­nia uży­wam takiej właśnie nazwy i tak będę się odnosił do tego pliku w dal­szej częś­ci lekcji.

Plik ten jest pewnego rodza­ju instrukcją, jak ma się zachowywać Spring, co ma udostęp­ni­ać a czego nie i jak ma wiązać ele­men­ty ze sobą. My chce­my osiągnąć na tą chwilę jeden cel: pisząc jak najm­niej chce­my żeby Spring robił jak najwięcej za nas ;) Póki co oznacza to, że włączymy kon­fig­u­rację za pomocą adno­tacji oraz tzw. pack­age-scan.

Kon­fig­u­rac­ja za pomocą adno­tacji — dzię­ki włącze­niu tego ele­men­tu klasy które zdefini­u­je­my w pliku XML (a nie za pomocą adno­tacji) także będą sprawdzane pod kątem posi­adanych adno­tacji i w razie potrze­by pod­jęte zostaną odpowied­nie kro­ki. Chwilowo nie będziemy wyko­rzysty­wać tej funkcjon­al­noś­ci, ale warto to mieć w kon­fig­u­racji, żeby nie zapom­nieć w przyszłoś­ci.

Com­po­nent scan — dzię­ki temu Spring sam ‘prze­jrzy’ wszys­tkie dostęp­ne klasy (lub np. zawężone tylko do wybranego paki­etu) i te, które są oznac­zone za pomocą stereo­typów, utworzy w postaci beanów. Dzię­ki temu nowe np. kon­trol­ery wystar­czy napisać i nie musimy się martwić o to, czy pamię­tal­iśmy o ich kon­fig­u­racji i pod­pię­ciu.

Poniżej nasz plik XML z komen­tarza­mi:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="
 http://www.springframework.org/schema/mvc 
 http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
 http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
 http://www.springframework.org/schema/context 
 http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <context:component-scan base-package="pl.kobietydokodu" /> <!-- Chcemy automatycznie obsługiwać wszystkie klasy z adnotacjami w tym pakiecie -->
    <context:annotation-config /> <!-- To na przyszłość, pozwoli nam korzystać z adnotacji także w klasach, które byśmy skonfigurowali ręcznie -->

</beans>
Plik rootApplicationContext.xml

Podob­nie jak powyższy, plik ten może mieć właś­ci­wie dowol­ną nazwę, ale albo musi być ona podana w pliku web.xml (tak jak my to zro­bil­iśmy). Różni­ca pomiędzy tym plikiem, a applicationContext.xml jest taka, że teo­re­ty­cznie może­my mieć kil­ka aplikacji Springa dzi­ała­ją­cych w ramach jed­nej aplikacji webowej (defini­u­jąc kil­ka różnych Servletów w pliku web.xml) — ta kon­fig­u­rac­ja (uruchami­ana przez Con­text­Load­erLis­ten­er, zdefin­iowany zaraz poniżej w pliku web.xml) pozwala na współdzie­le­nie pewnych ele­men­tów. Będzie to istotne np. w przy­pad­ku Spring Secu­ri­ty, czyli zabez­pieczeń pro­jek­tu, ponieważ dzi­ała­ją one ‘przed’ samą aplikacją, nie mogą być więc jej częś­cią. Póki co jed­nak pozostawmy tą kon­fig­u­rację pustą:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="
 http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

</beans>

Widoki

Niewąt­pli­wie ważnym ele­mentem aplikacji są wido­ki — czyli to, co widzi użytkown­ik, z czym prowadzi inter­akc­je i na czym pracu­je. W Springu wido­ki dzi­ała­ją w taki sposób, że meto­da która chce wyświ­etlić widok zwraca jego nazwę, która jest dowol­nym ciągiem znaków. Następ­nie ta nazwa trafia do tzw. view resolvera, który na pod­staw­ie nazwy widoku zwraca widok, który następ­nie otrzy­mu­je mod­el i na tej pod­staw­ie generu­je kom­plet­ny widok dla użytkown­i­ka.

Dzię­ki temu, że view resolver ma określone zachowanie i sposób dzi­ała­nia (imple­men­tu­je określony inter­fe­js, dokład­niej jest to org.springframework.web.servlet.ViewResolver, posi­ada­ją­cy jed­ną metodę o syg­naturze View resolveViewName(String view­Name, Locale locale) ), może­my uży­wać różnych resolverów nie zmieni­a­jąc pozostałej częś­ci aplikacji. Daje nam to oczy­wiś­cie bard­zo dużą elasty­czność :) W naszych przykładach wyko­rzys­tamy najprost­szy (org.springframework.web.servlet.view.InternalResourceViewResolver), ale pole­cam zapoz­nać się z poniższy­mi opc­ja­mi:

Oso­biś­cie korzys­tam najczęś­ciej z Apache Tiles — ale jest to naprawdę kwes­t­ia oso­bistych pref­er­encji, mnie spodobał się sposób orga­ni­za­cji (w uproszcze­niu, stronę dzie­limy na fragmenty/kafelki — rzec­zone tiles — i wido­ki współdzielą część ele­men­tów, dzię­ki czemu pisze­my mniej HTM­La).

Wspom­ni­ana imple­men­tac­ja po pros­tu doda­je pre­fix (który kon­fig­u­ru­je­my sami) oraz sufix (który podob­nie sami kon­fig­u­ru­je­my) tworząc ścieżkę, którą następ­nie uży­wa jako nazwę pliku. To najszyb­sza na początek opc­ja, choć nie do koń­ca zale­cana jeśli chodzi o komer­cyjne pro­jek­ty.

Konfigurujemy viewResolver

Aby skon­fig­urować viewRe­solver w naszym pro­jek­cie, wracamy do naszego pliku applicationContext.xml i doda­je­my poniższy frag­ment (oczy­wiś­cie przed tagiem </beans> kończą­cym plik):

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix">
        <value>/WEB-INF/views/</value>
    </property>
    <property name="suffix">
        <value>.jsp</value>
    </property>
</bean>

To co się dzieje w powyższym frag­men­cie, to po kolei:

  • Tworzymy nowego beana który jest typu Inter­nal­Re­source­ViewRe­solver
  • Kon­fig­u­ru­je­my pre­fix ustaw­ia­jąc go na kat­a­log /WEB-INF/views (jeśli nie pamię­tasz, dlaczego kat­a­log ten był spec­jal­ny, było to opisane w poprzed­niej lekcji)
  • Kon­fig­u­ru­je­my sufiks — w tym przy­pad­ku doda­je­my krop­kę oraz rozsz­erze­nie pliku

Czyli jeśli nasz kon­trol­er ‘poprosi’ o widok o nazwie ‘glowny’, to Spring spróbu­je wyświ­etlić stronę z pliku /WEB-INF/views/glowny.jsp .

Tworzymy statyczny widok

Następ­ny krok to stworze­nie staty­cznej strony HTML i zapisanie jej jako pliku z rozsz­erze­niem JSP. Jeśli nie wiesz, jak zacząć, zerknij na nasz krót­ki wpis na ten tem­at. Będzie to stanow­iło pod­stawę do wprowadzenia dynam­icznych ele­men­tów i wyświ­etle­nia właś­ci­wych infor­ma­cji.

Utwórz plik HTML, w którym będzie póki co wiado­mość o treś­ci “Witaj, jesteś na stron­ie bazy danych kotów” i zapisz go we wspom­ni­anym kat­a­logu pod nazwą ‘glowny.jsp’.

Pierwszy kontroler

Kole­jnym krok­iem jest stworze­nie pier­wszego kon­trol­era — czyli ele­men­tu, który poz­woli nam wyświ­et­lać treś­ci (strony) pod konkret­nym adresem URL. Będziemy potrze­bowali 2 rzeczy — kon­trol­era i widoku. Połączymy je mod­elem, który już ist­nieje (odpowied­nia klasa jest częś­cią frame­wor­ka Spring) i musimy jedynie wypełnić go dany­mi.

@Component / @Service / @Controller / @Repository — stereotypy

W Springu funkcjonu­je coś, co nazy­wamy stereo­ty­pa­mi. Stereo­typy to adno­tac­je, które doda­je­my przed klasę (adno­tu­je­my nimi klasę). Są one swego rodza­ju znacznika­mi, które mówią Springowi, że ta klasa ma pewną spec­jal­ną funkcję. Są cztery pod­sta­wowe stereo­typy:

  • @Component — bazowy stereo­typ, oznacza, że na pod­staw­ie tej klasy będzie utwor­zony bean Springa (inny­mi słowy: klasa ta jest zarządzana przez Spring’a, lub też cykl życia tej klasy będzie zarządzany przez Springa). Tego stereo­ty­pu uży­wamy najczęś­ciej do klas, które są pomoc­nicze i nie ofer­u­ją ele­men­tów logi­ki biz­ne­sowej, a jedynie pomoc­nicze funkc­je (np. kon­wer­s­ja między typa­mi, jakieś wspólne ele­men­ty)
  • @Service — stereo­typ który wskazu­je, że ta klasa jest ser­wisem, tzn. ofer­u­je pewną logikę biz­ne­sową którą będziemy wyko­rzysty­wać w innych miejs­cach (np. kon­trol­er­ach; ogól­nie w wyższych warst­wach — o warst­wach opowiemy sobie szerzej w przyszłoś­ci)
  • @Repository — wskazu­je że klasa pozwala na dostęp do danych, np. wspiera obsługę bazy danych. Adno­tac­je tą sto­su­je­my np. w obiek­tach typu DAO, za 3 lekc­je zobaczymy w jaki sposób uproś­ci nam ona obsługę bazy danych.
  • @Controller — oznacza­my nią kon­trol­ery, tj. klasy, które będą obsługi­wały zapy­ta­nia wysyłane poprzez przeglą­darkę od użytkown­ików.

Utwórzmy nową klasę o nazwie Koty­Con­troller i doda­jmy do niej adno­tację @Controller. Nasza klasa powin­na wyglą­dać następu­ją­co:

package pl.kobietydokodu.koty;

import org.springframework.stereotype.Controller;

@Controller
public class KotyController {

}

 

@Autowired — łączenie komponentów

W przy­pad­ku obiek­tów zarządzanych przez Springa (tzw. beanów — , jeśli zapom­ni­ałaś czym są beany, możesz zawsze wró­cić do poprzed­niej lekcji :) ) może­my wyko­rzys­tać tzw. wstrzyki­wanie zależnoś­ci (ang. Depen­den­cy Injec­tion, DI, cza­sem określane mianem IoC —  Inver­sion of Con­trol — lub kon­tenera IoC; IoC to szer­sze poję­cie, ale moż­na się spotkać z taki­mi sfor­mułowa­ni­a­mi — więcej o tym, czym jest IoC dowiesz się z lekcji o wzor­cach pro­jek­towych).

Wstrzyki­wanie zależnoś­ci pole­ga na tym, że ‘mówimy’ Springowi o tym, jakich zależnoś­ci nasz bean (to ważne, ponieważ wstrzyki­wa­nia zależnoś­ci może­my dokony­wać tylko i wyłącznie w obiek­tach, który­mi zarządza Spring) potrze­bu­je, a Spring sam zad­ba, aby te zależnoś­ci rozwiązać (tzw. depen­den­cy res­o­lu­tion — po pros­tu wstaw­ie­nie odpowied­niego innego beana (to także bard­zo ważne — wstrzyki­wane mogą być tylko zależnoś­ci zarządzane przez Springa) w to pole klasy). Mech­a­nizm ten jest bard­zo elasty­czny (może­my np. za każdym razem wstaw­iać nowy obiekt danego typu albo wszędzie uży­wać jed­nej instancji tego samego obiek­tu — to jest domyślne zachowanie; my poz­namy tylko pod­sta­wową funkcjon­al­ność) i obec­nie powszech­nie stosowany.

Jeśli pamię­tasz (lub zajrzysz do kodu ;) ) do tej pory w naszej aplikacji stosowal­iśmy np taki zapis:

private KotDAO kotDao = new KotDAO();

Czyli nie tylko mówiliśmy że będziemy uży­wać obiek­tu o typ­ie Kot­DAO i odwoły­wać się do niego pod nazwą kot­Dao, ale też od razu tworzyliśmy ten obiekt. To raczej nie jest wskazane, i w nor­mal­nej aplikacji praw­ie na pewno niewskazane. Zami­ast tego po pros­tu powiemy Springowi że chce­my mieć obiekt typu Kot­DAO i chce­my go pod nazwą kot­Dao. Powiemy też, że Kot­DAO jest zarządzany przez Spring’a.

Doda­je­my najpierw nowe pole do naszego kon­trol­era:

package pl.kobietydokodu.koty;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class KotyController {

    @Autowired
    private KotDAO kotDao;

}

A następ­nie ‘infor­mu­je­my’ Springa, że ma zarządzać także klasą Kot­DAO:

package pl.kobietydokodu.koty;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Repository;

@Repository
public class KotDAO {
    ... 

@RequestMapping — funkcjonalność widoczna dla użytkowników

Czas na pier­wszą inter­akcję z użytkown­ikiem — wyświ­etlimy mu naszą stwor­zoną wcześniej stronę (glowny.jsp). Wcześniej kil­ka słów teorii Inter­ne­tu :)

Każdy z nas korzys­ta z przeglą­dar­ki inter­ne­towej, o czym zapewne wiesz. To, czego możesz nie wiedzieć to to, że przeglą­dar­ki korzys­ta­ją najczęś­ciej z pro­tokołu HTTP — pozwala on na pobranie zasobów (np. strony inter­ne­towej czy grafi­ki) na pod­staw­ie adresu URL. Przykład­owo ten blog ma adres URL http://kobietydokodu.pl — pier­wsza część określa właśnie pro­tokół http który jest uży­wany żeby pobrać te zaso­by (nie będziemy tutaj omaw­iać innych pro­tokołów, zain­tere­sowanych odsyłam do samodziel­nego zgłębia­nia Google pod hasłem “web pro­to­cols”).

Pro­tokół ten poza adresem URL określa także tzw. metodę — wszys­t­kich metod mamy 8, ale na tą chwilę intere­su­ją nas dwie: POST i GET .

Meto­da GET to ta, której uży­wa przeglą­dar­ka gdy wpiszesz adres w pasku przeglą­da­nia. Gdy wpiszesz np. adres www.example.com/cos/innego, Two­ja przeglą­dar­ka wyśle do ser­w­era coś podob­ne­go do poniższej wiado­moś­ci:

GET /cos/innego HTTP/1.1
Host: www.example.com

Mamy tam metodę, adres ser­w­era (www.example.com) oraz tzw. ścieżkę ( /cos/innego ). Podob­nie wyglą­da zapy­tanie metody POST, która jest uży­wana do przesyła­nia danych do ser­w­era (np. wypełnionego for­mu­la­rza czy załąc­zonego co for­mu­la­rza pliku). To, co nas będzie intere­sowało najbardziej to właśnie ścież­ka. W tym przy­pad­ku /cos było­by nazwą kon­tek­stu (o tym, czym jest kon­tekst możesz przeczy­tać w poprzed­niej lekcji), a więc do naszej aplikacji trafi tylko frag­ment ścież­ki — /innego .

Adno­tac­ja @RequestMapping jest o tyle unikalna w sto­sunku do poprzed­nich, że może­my ją stosować w 2 miejs­cach:

  • nad klasą — wszys­tkie metody tej klasy, które mają także adno­tac­je @RequestMapping, są tak jak­by pre­fik­sowane — za chwilę zobaczymy to na przykładzie, będzie to jaśniejsze :)
  • nad metodą — wskazu­je­my, że ta meto­da będzie uru­chomiona, jeśli użytkown­ik wpisze konkret­ny adres URL

Porów­na­jmy dwa przykłady (importy, paki­ety itp pominię­to dla czytel­noś­ci), zakładamy że kon­tekst to /aplikacja, ser­w­er www.example.com:

@Controller
@RequestMapping("/kontroler")
public class KotyController {

    @Autowired
    private KotDAO kotDao;

    @RequestMapping("/metoda")
    public String metoda() {
        return "glowny";
    }
}
@Controller
public class KotyController {

    @Autowired
    private KotDAO kotDao;

    @RequestMapping("/metoda")
    public String metoda() {
        return "glowny";
    }
}
 W tym wypad­ku meto­da ta będzie uru­chomiona po wejś­ciu na adres http://example.com:8080/aplikacja/kontroler/metoda  W tym wypad­ku meto­da ta będzie uru­chomiona po wejś­ciu na adres http://example.com:8080/aplikacja/metoda

Jak widz­imy powyżej, adno­tac­je @RequestMapping(“sciezka”) pozwala­ją nam wywołać określoną metodę w zależnoś­ci od adresu, jaki wpisze użytkown­ik (ale ‘dopa­sowanie’ doty­czy tylko tej częś­ci ścież­ki, która jest specy­ficz­na dla aplikacji). Jeśli adno­tację dodamy też do całej klasy kon­trol­era, to wartość którą podamy doda­je się do każdego z mapowań zdefin­iowanych nad meto­da­mi tej klasy.

Model — przekazywanie danych do widoku

Zan­im nauczymy się odbier­ać dane od użytkown­i­ka naszej aplikacji, zobaczmy jak może­my przekazy­wać je do widoku z kon­trol­era. Jeśli wrócimy do dia­gra­mu ilus­tru­jącego wzorzec MVC może­my zauważyć że służy do tego Mod­el. W Springu mamy dwie możli­woś­ci korzys­ta­nia z mod­elu: zami­ast stringa zwró­cić Mod­e­lAnd­View (który w zasadzie łączy mod­el i nazwę widoku w jeden obiekt) lub ‘pobrać’ Mod­el jako argu­ment metody. Poniżej sko­rzys­tamy z tej drugiej metody, wyda­je mi się że jest to czytel­niejsze , jest to także ‘nowsza’ prak­ty­ka (tutaj możesz zapoz­nać się z ciekawą dyskusją na ten tem­at i argu­men­ta­mi za obiema opc­ja­mi). klasa która nas intere­su­je to org.springframework.ui.Model, a najbardziej jej meto­da addAttribute(String, Object), która pozwala nam przekazać dowol­ny obiekt do widoku. Zobaczmy poniższy kod:

@Controller
public class ExampleController {

    @RequestMapping("/przyklad/model")
    public String przykladModelu(Model model) {
        model.addAttribute("message", "To jest jakaś super informacja");
        return "glowny";
    }
}

Przekazu­je on do widoku nasz komu­nikat za pomo­ca klucza (nazwy atry­bu­tu) ‘mes­sage’. W naszym widoku wystar­czy użyć zapisu np. :

<strong>${message}</strong>

aby wyświ­etlić przekazaną przez mod­el infor­ma­cję. Skład­nia ${…} jest zdefin­iowana w stan­dard­zie Expres­sion­Lan­guage . Jest to bard­zo ciekawe narzędzie, które z jed­nej strony ma bard­zo duże możli­woś­ci, a z drugiej jest proste w uży­ciu. Pole­cam zapoz­nać się z doku­men­tacją , do której link zna­jdziesz na końcu strony :)

Odczytywanie informacji od użytkownika

To, co omówiliśmy do tej pory pozwala nam na stworze­nie aplikacji webowej, która wyświ­et­la mniej więcej staty­czne strony (wiemy już, jak dodawać ele­men­ty dynam­iczne, ale nie są one póki co zależne od tego, co zro­bi użytkown­ik). Czas najwyższy to zmienić i zająć się inter­akcją z użytkown­ikiem oraz odczy­ty­waniem przesyłanych infor­ma­cji

Dane z adresu URL

Pier­wszy sposób na przekazy­wanie infor­ma­cji do aplikacji webowej to za pomocą adresu URL. Są tutaj dwie możli­woś­ci:

  • http://localhost:8080/koty/pokaz?imie=mruczek
  • http://localhost:8080/koty/pokaz/mruczek

W pier­wszym przy­pad­ku dane przekazu­je­my za pomocą tzw. para­metrów i ich odczy­ty­wanie nie różni się niczym od odczy­ty­wa­nia danych z for­mu­la­rza drugą metodą (w isto­cie jest to for­mu­la­rz wysłany z uży­ciem metody GET) i dlat­ego chwilowo nie będziemy się tym zaj­mować.

Sposób pokazany w drugim punkcie jest prefer­owany jeśli mamy małą ilość infor­ma­cji z kilku powodów:

  • jest przy­jazny dla wyszuki­warek inter­ne­towych
  • jest este­ty­czny
  • łatwiej go zrozu­mieć i zin­ter­pre­tować po pros­tu patrząc na niego

Spring pozwala nam w mapowa­niu wyko­rzys­tać tzw. path vari­ables (zmi­enne ścież­ki? Nie spotkałem się prawdę mówiąc z ogól­nieprzyję­tym tłu­macze­niem). W uproszcze­niu wyglą­da to w ten sposób, że wskazu­je­my że w pewnym miejs­cu adresu URL będzie infor­ma­c­ja którą chce­my uzyskać (np w powyższym przy­pad­ku imię kot­ka). W pewnym sen­sie dzi­ała to podob­nie do wyrażeń reg­u­larnych, które poz­nal­iśmy kil­ka lekcji wcześniej (tak naprawdę może­my uży­wać wyrażeń reg­u­larnych żeby dopre­cy­zować jak ma wyglą­dać infor­ma­c­ja którą chce­my, ale o tym możesz poczy­tać samodziel­nie). Łatwiej będzie nam zobaczyć to na przykładach. Doda­jmy do naszego kon­trol­era następu­jącą metodę:

@Controller
public class ExampleController {

    @RequestMapping("/kot/{imie}")
    public String szczegolyKota(@PathVariable("imie") String imieKota) {
        return "glowny";
    }

}

Jak widzisz dodal­iśmy do tej metody argu­ment typu String i nazwie wiado­mość oraz przy tym argu­men­cie (przed typem argu­men­tu!) dodal­iśmy adno­tac­je @PathVariable(“nazwa”). W ścieżce którą zapisal­iśmy w adno­tacji @RequestMapping mamy z kolei zapis {nazwa} — tzw. place­hold­er. Taka budowa mówi nam mniej więcej: “To, co w adresie będzie w miejs­cu {xyz}, zapamię­taj do użytku pod kluczem xyz, i wstaw to do argu­men­tu metody, który ma adno­tację @PathVariable(“xyz”) “.

Dane z formularzy — metoda ‘szybka’

W pod­ty­tule spec­jal­nie użyłem apos­trofów, ponieważ opisana tutaj meto­da jest ‘szy­b­ka’ do zaim­ple­men­towa­nia tylko z pozoru — fak­ty­cznie nie wyma­ga ona tworzenia dodatkowych obiek­tów i oszczędza trochę cza­su, ale znacznie utrud­nia pracę z for­mu­la­rza­mi i prowadzi do dzi­wnych kon­strukcji związanych z wal­i­dacją. Przed­staw­iamy ją tutaj tylko dlat­ego, że moż­na ją częs­to spotkać w ist­nieją­cych pro­jek­tach. Pisząc włas­ny kod, o ile nie jesteś pew­na, że to dokład­nie tej metody potrze­bu­jesz, korzys­taj z metody opisanej w lekcji 10!

Uży­cie tej metody jest podob­ne do opisanej powyżej — wystar­czy dodać adno­tację do atry­bu­tu metody, nie musimy jed­nak mody­fikować naszej ścież­ki.

Mając np. kod:

@Controller
public class ExampleController {

    @RequestMapping("/metoda")
    public String widok(@RequestParam("a") String danaA, @RequestParam("b") Integer danaB) {
     return "glowny";
    }

}

Pod adresem /aplikacja/metoda?a=1&b=2, wartoś­cią argu­men­tu danaA będzie “1”, a wartoś­cią danaB będzie licz­ba 2 (to też świet­ny przykład jak Spring sam dba o kon­wer­sje typów ‘w tle’ — nie musimy mu mówić dokład­nie co ma zro­bić, a jedynie jakich danych oczeku­je­my, a on sam zajmie się resztą (o ile będzie ‘wiedzi­ał’ jak to robić).

Jeśli z kolei wpisze­my adres /aplikacja/metoda?a=1, otrzy­mamy błąd (400: BadRequest) ponieważ braku­je jed­nego z argu­men­tów. Żeby uniknąć tego rodza­ju błędów i oznaczyć któryś z para­metrów jako opcjon­al­ny, może­my dodać argu­ment do adno­tacji wskazu­ją­cy, że nie jest to wyma­gane pole. Przykład poniżej:

@Controller
public class ExampleController {

    @RequestMapping("/metoda")
    public String widok(@RequestParam("a") String danaA, @RequestParam(value = "b", required = false, defaultValue = "0") Integer danaB) {
        return "glowny";
    }

}

Meto­da ta dzi­ała zarówno dla zapy­tań i danych przekazy­wanych za pomocą metod GET jak i POST.

Prob­lem pojaw­ia się np. w sytu­acji kiedy odbier­amy dane z for­mu­la­rza, następ­nie sprawdza­my je, po czym w razie wys­tąpi­enia prob­lemów chce­my wyświ­etlić użytkown­ikowi infor­ma­c­je o prob­lemie i ponown­ie wyświ­etlić for­mu­la­rz. Tym sposobem sami musimy zad­bać o zapisanie tych danych, ich uzu­pełnie­nie oraz wyp­isanie odpowied­nich komu­nikatów. To sporo niepotrzeb­ne­go kodu, a co więcej, Spring może to zro­bić za nas.

Szczegółowo opisze­my to w kole­jnej lekcji, gdzie nauczymy się także dodawać wal­i­dację do poszczegól­nych pól i reagować na błędne dane

Zadanie

W tej lekcji zadaniem jest stworze­nie pod­staw do naszej aplikacji:

  • Dodaj kon­trol­er o nazwie KotyKon­trol­er
  • Podłacz do kon­trol­era naszą klasę Kot­DAO
  • Utwórz metody do obsłu­gi każdej z oper­acji na kocie którą mamy w sys­temie (dodaj, wyp­isz wszys­tkie, pokaż szczegóły)
  • Utwórz i podep­nij staty­czne strony HTML do każdej z tych metod (na stron­ie z listą powin­na być meto­da, która pokaże listę kotów, po kliknię­ciu na każdego z nich powin­niśmy prze­jść do ekranu szczegółów kota; na stron­ie z listą powinien być też przy­cisk z odnośnikiem do dodawa­nia)
zip Pobierz rozwiązanie tego zada­nia

Licencja Creative Commons

Jeśli uważasz powyższą lekcję za przy­dat­ną, mamy małą prośbę: pol­ub nasz fan­page. Dzię­ki temu będziesz zawsze na bieżą­co z nowy­mi treś­ci­a­mi na blogu ( i oczy­wiś­cie, z nowy­mi częś­ci­a­mi kur­su Javy). Dzię­ki!

  • 3
  •  
  •  
  •  
  •