#09 – Spring MVC

By 16 października 2014Kurs Javy
unnamed (3)

Lekcja

Dzisiejsza lekcja zapewne będzie najciekawszą z dotychczasowych – nasza aplikacja będzie dostępna przez przeglądarkę i otrzyma pierwsze funkcjonalności!

Będzie ona zarazem jedną z najnudniejszych – poznamy w niej nieprzyjemną zasadę Napisz – Uruchom – Poczekaj, czyli w jaki sposób sprawdzamy czy aplikacja działa.

W każdej z poprzednich lekcji mogliśmy uruchomić naszą aplikację i po ułamku sekundy wiedzieliśmy, czy działa czy nie. W przypadku aplikacji webowych uruchomienie trwa dłużej a też ‚wyklikanie’ czy działa czy nie zajmuje więcej czasu. Dlatego od tego momentu należy się uzbroić w cierpliwość, ponieważ konieczność ponownego uruchamiania aplikacji po każdej drobnej zmianie może być, i będzie, frustrująca. Na pocieszenie mogę dodać jedynie, że wraz z nabieraniem wprawy takie sprawdzanie będzie coraz mniej częste, a w kolejnych lekcjach poznamy lepsze sposoby na testowanie poprawnego działania aplikacji.

Spring MVC – dodawanie do projektu

W zadaniu do poprzedniej lekcji dodaliśmy już zależności do biblioteki Spring, ale nie uaktywniliśmy jej jeszcze w naszym projekcie. Tym zajmiemy się na początek.

Maven – zależności

Jesli nie miałaś kiedy zając się ostatnim zadaniem, to poniżej rozwiązanie ;)

Dodajemy do pliku pom.xml w module koty-webapp poniższe zależności (w najnowszych wersjach):

oraz do modułu koty-application ponownie:

Jeśli potrzebujesz przypomnienia jak zarządzać zależnościami w Mavenie, zapraszam Cię do ponownego przeczytania poprzedniej lekcji numer 7 :)

Konfiguracja

Na początku parę słów o tym jak Spring MVC działa w projekcie. Spring w projektach webowych najczęściej jest uruchamiany z użyciem wzorca Front Controller. Więcej o wzorcach z przykładami będziemy rozmawiali w końcowych lekcjach kursu, ale w uproszczeniu ten wzorzec zakłada posiadanie ‚nadzorcy’, który zarządza wszystkimi zapytaniami HTTP – tzn. każde zapytanie trafia do tego zarządcy, a następnie on decyduje, jak go obsłużyć (np. do jakiego kontrolera je przekierować). W Springu tą rolę pełni tzw. DispatcherServlet. (Servlet to podstawowy element budulcowy dla aplikacji webowych w specyfikacji Java EE, nie będziemy go poznawać dokładnie w ramach głównego wątku kursu). Servlet ten konfigurujemy standardowo w pliku web.xml (o tym za chwilę) wskazując konfigurację Springową – plik XML który określa, jak ma się zachowywać Spring (o nim też w dalszej części).

Te dwa pliki to właściwie cała konfiguracja której potrzebujemy. Spring wyznaje zasadę Convention over Configuration, czyli nie musimy konfigurować elementów i wtedy użyte zostaną sensowne wartości domyślne. Najczęściej to wystarcza. Jednocześnie możemy zmienić naprawdę najbardziej szczegółowe ustawienia, jeśli tylko chcemy. W tym kursie pójdziemy tą pierwszą drogą ;)

Plik web.xml

Ten plik jest częścią standardu JavaEE i pozwala nam określać m.in. Servlety, Filtry, Listenery itp. My wykorzystamy go jednie do tego, aby ‚oddać’ pełną kontrolę Springowi – dodamy omawiany wcześniej DispatcherServlet i nie będziemy więcej modyfikować tego pliku.

Zainteresowanych zapraszam do szerszego zapoznania się ze specyfikacją lub innymi materiałami w tym temacie (w sekcji linki).

Plik ten o poniższej treści, należy umieścić w katalogu /src/main/webapp/WEB-INF (jesli katalog WEB-INF nie istnieje, należy go utworzyć).

Pełny listing 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ściwie dowolną nazwę, ale albo musi być ona podana w pliku web.xml (tak jak my to zrobiliśmy) albo musi mieć nazwę taką jak servlet. Ja z przyzwyczajenia używam takiej właśnie nazwy i tak będę się odnosił do tego pliku w dalszej części lekcji.

Plik ten jest pewnego rodzaju instrukcją, jak ma się zachowywać Spring, co ma udostępniać a czego nie i jak ma wiązać elementy ze sobą. My chcemy osiągnąć na tą chwilę jeden cel: pisząc jak najmniej chcemy żeby Spring robił jak najwięcej za nas ;) Póki co oznacza to, że włączymy konfigurację za pomocą adnotacji oraz tzw. package-scan.

Konfiguracja za pomocą adnotacji – dzięki włączeniu tego elementu klasy które zdefiniujemy w pliku XML (a nie za pomocą adnotacji) także będą sprawdzane pod kątem posiadanych adnotacji i w razie potrzeby podjęte zostaną odpowiednie kroki. Chwilowo nie będziemy wykorzystywać tej funkcjonalności, ale warto to mieć w konfiguracji, żeby nie zapomnieć w przyszłości.

Component scan – dzięki temu Spring sam ‚przejrzy’ wszystkie dostępne klasy (lub np. zawężone tylko do wybranego pakietu) i te, które są oznaczone za pomocą stereotypów, utworzy w postaci beanów. Dzięki temu nowe np. kontrolery wystarczy napisać i nie musimy się martwić o to, czy pamiętaliśmy o ich konfiguracji i podpięciu.

Poniżej nasz plik XML z komentarzami:

<?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

Podobnie jak powyższy, plik ten może mieć właściwie dowolną nazwę, ale albo musi być ona podana w pliku web.xml (tak jak my to zrobiliśmy). Różnica pomiędzy tym plikiem, a applicationContext.xml jest taka, że teoretycznie możemy mieć kilka aplikacji Springa działających w ramach jednej aplikacji webowej (definiując kilka różnych Servletów w pliku web.xml) – ta konfiguracja (uruchamiana przez ContextLoaderListener, zdefiniowany zaraz poniżej w pliku web.xml) pozwala na współdzielenie pewnych elementów. Będzie to istotne np. w przypadku Spring Security, czyli zabezpieczeń projektu, ponieważ działają one ‚przed’ samą aplikacją, nie mogą być więc jej częścią. Póki co jednak pozostawmy tą konfigurację 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ątpliwie ważnym elementem aplikacji są widoki – czyli to, co widzi użytkownik, z czym prowadzi interakcje i na czym pracuje. W Springu widoki działają w taki sposób, że metoda która chce wyświetlić widok zwraca jego nazwę, która jest dowolnym ciągiem znaków. Następnie ta nazwa trafia do tzw. view resolvera, który na podstawie nazwy widoku zwraca widok, który następnie otrzymuje model i na tej podstawie generuje kompletny widok dla użytkownika.

Dzięki temu, że view resolver ma określone zachowanie i sposób działania (implementuje określony interfejs, dokładniej jest to org.springframework.web.servlet.ViewResolver, posiadający jedną metodę o sygnaturze View resolveViewName(String viewName, Locale locale) ), możemy używać różnych resolverów nie zmieniając pozostałej części aplikacji. Daje nam to oczywiście bardzo dużą elastyczność :) W naszych przykładach wykorzystamy najprostszy (org.springframework.web.servlet.view.InternalResourceViewResolver), ale polecam zapoznać się z poniższymi opcjami:

Osobiście korzystam najczęściej z Apache Tiles – ale jest to naprawdę kwestia osobistych preferencji, mnie spodobał się sposób organizacji (w uproszczeniu, stronę dzielimy na fragmenty/kafelki – rzeczone tiles – i widoki współdzielą część elementów, dzięki czemu piszemy mniej HTMLa).

Wspomniana implementacja po prostu dodaje prefix (który konfigurujemy sami) oraz sufix (który podobnie sami konfigurujemy) tworząc ścieżkę, którą następnie używa jako nazwę pliku. To najszybsza na początek opcja, choć nie do końca zalecana jeśli chodzi o komercyjne projekty.

Konfigurujemy viewResolver

Aby skonfigurować viewResolver w naszym projekcie, wracamy do naszego pliku applicationContext.xml i dodajemy poniższy fragment (oczywiś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 fragmencie, to po kolei:

  • Tworzymy nowego beana który jest typu InternalResourceViewResolver
  • Konfigurujemy prefix ustawiając go na katalog /WEB-INF/views (jeśli nie pamiętasz, dlaczego katalog ten był specjalny, było to opisane w poprzedniej lekcji)
  • Konfigurujemy sufiks – w tym przypadku dodajemy kropkę oraz rozszerzenie pliku

Czyli jeśli nasz kontroler ‚poprosi’ o widok o nazwie ‚glowny’, to Spring spróbuje wyświetlić stronę z pliku /WEB-INF/views/glowny.jsp .

Tworzymy statyczny widok

Następny krok to stworzenie statycznej strony HTML i zapisanie jej jako pliku z rozszerzeniem JSP. Jeśli nie wiesz, jak zacząć, zerknij na nasz krótki wpis na ten temat. Będzie to stanowiło podstawę do wprowadzenia dynamicznych elementów i wyświetlenia właściwych informacji.

Utwórz plik HTML, w którym będzie póki co wiadomość o treści „Witaj, jesteś na stronie bazy danych kotów” i zapisz go we wspomnianym katalogu pod nazwą ‚glowny.jsp’.

Pierwszy kontroler

Kolejnym krokiem jest stworzenie pierwszego kontrolera – czyli elementu, który pozwoli nam wyświetlać treści (strony) pod konkretnym adresem URL. Będziemy potrzebowali 2 rzeczy – kontrolera i widoku. Połączymy je modelem, który już istnieje (odpowiednia klasa jest częścią frameworka Spring) i musimy jedynie wypełnić go danymi.

@Component / @Service / @Controller / @Repository – stereotypy

W Springu funkcjonuje coś, co nazywamy stereotypami. Stereotypy to adnotacje, które dodajemy przed klasę (adnotujemy nimi klasę). Są one swego rodzaju znacznikami, które mówią Springowi, że ta klasa ma pewną specjalną funkcję. Są cztery podstawowe stereotypy:

  • @Component – bazowy stereotyp, oznacza, że na podstawie tej klasy będzie utworzony bean Springa (innymi słowy: klasa ta jest zarządzana przez Spring’a, lub też cykl życia tej klasy będzie zarządzany przez Springa). Tego stereotypu używamy najczęściej do klas, które są pomocnicze i nie oferują elementów logiki biznesowej, a jedynie pomocnicze funkcje (np. konwersja między typami, jakieś wspólne elementy)
  • @Service – stereotyp który wskazuje, że ta klasa jest serwisem, tzn. oferuje pewną logikę biznesową którą będziemy wykorzystywać w innych miejscach (np. kontrolerach; ogólnie w wyższych warstwach – o warstwach opowiemy sobie szerzej w przyszłości)
  • @Repository – wskazuje że klasa pozwala na dostęp do danych, np. wspiera obsługę bazy danych. Adnotacje tą stosujemy np. w obiektach typu DAO, za 3 lekcje zobaczymy w jaki sposób uprości nam ona obsługę bazy danych.
  • @Controller – oznaczamy nią kontrolery, tj. klasy, które będą obsługiwały zapytania wysyłane poprzez przeglądarkę od użytkowników.

Utwórzmy nową klasę o nazwie KotyController i dodajmy do niej adnotację @Controller. Nasza klasa powinna wyglądać następująco:

package pl.kobietydokodu.koty;

import org.springframework.stereotype.Controller;

@Controller
public class KotyController {

}

 

@Autowired – łączenie komponentów

W przypadku obiektów zarządzanych przez Springa (tzw. beanów – , jeśli zapomniałaś czym są beany, możesz zawsze wrócić do poprzedniej lekcji :) ) możemy wykorzystać tzw. wstrzykiwanie zależności (ang. Dependency Injection, DI, czasem określane mianem IoC –  Inversion of Control – lub kontenera IoC; IoC to szersze pojęcie, ale można się spotkać z takimi sformułowaniami – więcej o tym, czym jest IoC dowiesz się z lekcji o wzorcach projektowych).

Wstrzykiwanie zależności polega na tym, że ‚mówimy’ Springowi o tym, jakich zależności nasz bean (to ważne, ponieważ wstrzykiwania zależności możemy dokonywać tylko i wyłącznie w obiektach, którymi zarządza Spring) potrzebuje, a Spring sam zadba, aby te zależności rozwiązać (tzw. dependency resolution – po prostu wstawienie odpowiedniego innego beana (to także bardzo ważne – wstrzykiwane mogą być tylko zależności zarządzane przez Springa) w to pole klasy). Mechanizm ten jest bardzo elastyczny (możemy np. za każdym razem wstawiać nowy obiekt danego typu albo wszędzie używać jednej instancji tego samego obiektu – to jest domyślne zachowanie; my poznamy tylko podstawową funkcjonalność) i obecnie powszechnie stosowany.

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

private KotDAO kotDao = new KotDAO();

Czyli nie tylko mówiliśmy że będziemy używać obiektu o typie KotDAO i odwoływać się do niego pod nazwą kotDao, ale też od razu tworzyliśmy ten obiekt. To raczej nie jest wskazane, i w normalnej aplikacji prawie na pewno niewskazane. Zamiast tego po prostu powiemy Springowi że chcemy mieć obiekt typu KotDAO i chcemy go pod nazwą kotDao. Powiemy też, że KotDAO jest zarządzany przez Spring’a.

Dodajemy najpierw nowe pole do naszego kontrolera:

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ępnie ‚informujemy’ Springa, że ma zarządzać także klasą KotDAO:

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 pierwszą interakcję z użytkownikiem – wyświetlimy mu naszą stworzoną wcześniej stronę (glowny.jsp). Wcześniej kilka słów teorii Internetu :)

Każdy z nas korzysta z przeglądarki internetowej, o czym zapewne wiesz. To, czego możesz nie wiedzieć to to, że przeglądarki korzystają najczęściej z protokołu HTTP – pozwala on na pobranie zasobów (np. strony internetowej czy grafiki) na podstawie adresu URL. Przykładowo ten blog ma adres URL http://kobietydokodu.pl – pierwsza część określa właśnie protokół http który jest używany żeby pobrać te zasoby (nie będziemy tutaj omawiać innych protokołów, zainteresowanych odsyłam do samodzielnego zgłębiania Google pod hasłem „web protocols”).

Protokół ten poza adresem URL określa także tzw. metodę – wszystkich metod mamy 8, ale na tą chwilę interesują nas dwie: POST i GET .

Metoda GET to ta, której używa przeglądarka gdy wpiszesz adres w pasku przeglądania. Gdy wpiszesz np. adres www.example.com/cos/innego, Twoja przeglądarka wyśle do serwera coś podobnego do poniższej wiadomości:

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

Mamy tam metodę, adres serwera (www.example.com) oraz tzw. ścieżkę ( /cos/innego ). Podobnie wygląda zapytanie metody POST, która jest używana do przesyłania danych do serwera (np. wypełnionego formularza czy załączonego co formularza pliku). To, co nas będzie interesowało najbardziej to właśnie ścieżka. W tym przypadku /cos byłoby nazwą kontekstu (o tym, czym jest kontekst możesz przeczytać w poprzedniej lekcji), a więc do naszej aplikacji trafi tylko fragment ścieżki – /innego .

Adnotacja @RequestMapping jest o tyle unikalna w stosunku do poprzednich, że możemy ją stosować w 2 miejscach:

  • nad klasą – wszystkie metody tej klasy, które mają także adnotacje @RequestMapping, są tak jakby prefiksowane – za chwilę zobaczymy to na przykładzie, będzie to jaśniejsze :)
  • nad metodą – wskazujemy, że ta metoda będzie uruchomiona, jeśli użytkownik wpisze konkretny adres URL

Porównajmy dwa przykłady (importy, pakiety itp pominięto dla czytelności), zakładamy że kontekst to /aplikacja, serwer 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 wypadku metoda ta będzie uruchomiona po wejściu na adres http://example.com:8080/aplikacja/kontroler/metoda  W tym wypadku metoda ta będzie uruchomiona po wejściu na adres http://example.com:8080/aplikacja/metoda

Jak widzimy powyżej, adnotacje @RequestMapping(„sciezka”) pozwalają nam wywołać określoną metodę w zależności od adresu, jaki wpisze użytkownik (ale ‚dopasowanie’ dotyczy tylko tej części ścieżki, która jest specyficzna dla aplikacji). Jeśli adnotację dodamy też do całej klasy kontrolera, to wartość którą podamy dodaje się do każdego z mapowań zdefiniowanych nad metodami tej klasy.

Model – przekazywanie danych do widoku

Zanim nauczymy się odbierać dane od użytkownika naszej aplikacji, zobaczmy jak możemy przekazywać je do widoku z kontrolera. Jeśli wrócimy do diagramu ilustrującego wzorzec MVC możemy zauważyć że służy do tego Model. W Springu mamy dwie możliwości korzystania z modelu: zamiast stringa zwrócić ModelAndView (który w zasadzie łączy model i nazwę widoku w jeden obiekt) lub ‚pobrać’ Model jako argument metody. Poniżej skorzystamy z tej drugiej metody, wydaje mi się że jest to czytelniejsze , jest to także ‚nowsza’ praktyka (tutaj możesz zapoznać się z ciekawą dyskusją na ten temat i argumentami za obiema opcjami). klasa która nas interesuje to org.springframework.ui.Model, a najbardziej jej metoda addAttribute(String, Object), która pozwala nam przekazać dowolny 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";
    }
}

Przekazuje on do widoku nasz komunikat za pomoca klucza (nazwy atrybutu) ‚message’. W naszym widoku wystarczy użyć zapisu np. :

<strong>${message}</strong>

aby wyświetlić przekazaną przez model informację. Składnia ${…} jest zdefiniowana w standardzie ExpressionLanguage . Jest to bardzo ciekawe narzędzie, które z jednej strony ma bardzo duże możliwości, a z drugiej jest proste w użyciu. Polecam zapoznać się z dokumentacją , do której link znajdziesz na końcu strony :)

Odczytywanie informacji od użytkownika

To, co omówiliśmy do tej pory pozwala nam na stworzenie aplikacji webowej, która wyświetla mniej więcej statyczne strony (wiemy już, jak dodawać elementy dynamiczne, ale nie są one póki co zależne od tego, co zrobi użytkownik). Czas najwyższy to zmienić i zająć się interakcją z użytkownikiem oraz odczytywaniem przesyłanych informacji

Dane z adresu URL

Pierwszy sposób na przekazywanie informacji do aplikacji webowej to za pomocą adresu URL. Są tutaj dwie możliwości:

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

W pierwszym przypadku dane przekazujemy za pomocą tzw. parametrów i ich odczytywanie nie różni się niczym od odczytywania danych z formularza drugą metodą (w istocie jest to formularz wysłany z użyciem metody GET) i dlatego chwilowo nie będziemy się tym zajmować.

Sposób pokazany w drugim punkcie jest preferowany jeśli mamy małą ilość informacji z kilku powodów:

  • jest przyjazny dla wyszukiwarek internetowych
  • jest estetyczny
  • łatwiej go zrozumieć i zinterpretować po prostu patrząc na niego

Spring pozwala nam w mapowaniu wykorzystać tzw. path variables (zmienne ścieżki? Nie spotkałem się prawdę mówiąc z ogólnieprzyjętym tłumaczeniem). W uproszczeniu wygląda to w ten sposób, że wskazujemy że w pewnym miejscu adresu URL będzie informacja którą chcemy uzyskać (np w powyższym przypadku imię kotka). W pewnym sensie działa to podobnie do wyrażeń regularnych, które poznaliśmy kilka lekcji wcześniej (tak naprawdę możemy używać wyrażeń regularnych żeby doprecyzować jak ma wyglądać informacja którą chcemy, ale o tym możesz poczytać samodzielnie). Łatwiej będzie nam zobaczyć to na przykładach. Dodajmy do naszego kontrolera następującą metodę:

@Controller
public class ExampleController {

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

}

Jak widzisz dodaliśmy do tej metody argument typu String i nazwie wiadomość oraz przy tym argumencie (przed typem argumentu!) dodaliśmy adnotacje @PathVariable(„nazwa”). W ścieżce którą zapisaliśmy w adnotacji @RequestMapping mamy z kolei zapis {nazwa} – tzw. placeholder. Taka budowa mówi nam mniej więcej: „To, co w adresie będzie w miejscu {xyz}, zapamiętaj do użytku pod kluczem xyz, i wstaw to do argumentu metody, który ma adnotację @PathVariable(„xyz„) „.

Dane z formularzy – metoda ‚szybka’

W podtytule specjalnie użyłem apostrofów, ponieważ opisana tutaj metoda jest ‚szybka’ do zaimplementowania tylko z pozoru – faktycznie nie wymaga ona tworzenia dodatkowych obiektów i oszczędza trochę czasu, ale znacznie utrudnia pracę z formularzami i prowadzi do dziwnych konstrukcji związanych z walidacją. Przedstawiamy ją tutaj tylko dlatego, że można ją często spotkać w istniejących projektach. Pisząc własny kod, o ile nie jesteś pewna, że to dokładnie tej metody potrzebujesz, korzystaj z metody opisanej w lekcji 10!

Użycie tej metody jest podobne do opisanej powyżej – wystarczy dodać adnotację do atrybutu metody, nie musimy jednak modyfikować 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ą argumentu danaA będzie „1”, a wartością danaB będzie liczba 2 (to też świetny przykład jak Spring sam dba o konwersje typów ‚w tle’ – nie musimy mu mówić dokładnie co ma zrobić, a jedynie jakich danych oczekujemy, a on sam zajmie się resztą (o ile będzie ‚wiedział’ jak to robić).

Jeśli z kolei wpiszemy adres /aplikacja/metoda?a=1, otrzymamy błąd (400: BadRequest) ponieważ brakuje jednego z argumentów. Żeby uniknąć tego rodzaju błędów i oznaczyć któryś z parametrów jako opcjonalny, możemy dodać argument do adnotacji wskazujący, że nie jest to wymagane 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";
    }

}

Metoda ta działa zarówno dla zapytań i danych przekazywanych za pomocą metod GET jak i POST.

Problem pojawia się np. w sytuacji kiedy odbieramy dane z formularza, następnie sprawdzamy je, po czym w razie wystąpienia problemów chcemy wyświetlić użytkownikowi informacje o problemie i ponownie wyświetlić formularz. Tym sposobem sami musimy zadbać o zapisanie tych danych, ich uzupełnienie oraz wypisanie odpowiednich komunikatów. To sporo niepotrzebnego kodu, a co więcej, Spring może to zrobić za nas.

Szczegółowo opiszemy to w kolejnej lekcji, gdzie nauczymy się także dodawać walidację do poszczególnych pól i reagować na błędne dane

Zadanie

W tej lekcji zadaniem jest stworzenie podstaw do naszej aplikacji:

  • Dodaj kontroler o nazwie KotyKontroler
  • Podłacz do kontrolera naszą klasę KotDAO
  • Utwórz metody do obsługi każdej z operacji na kocie którą mamy w systemie (dodaj, wypisz wszystkie, pokaż szczegóły)
  • Utwórz i podepnij statyczne strony HTML do każdej z tych metod (na stronie z listą powinna być metoda, która pokaże listę kotów, po kliknięciu na każdego z nich powinniśmy przejść do ekranu szczegółów kota; na stronie z listą powinien być też przycisk z odnośnikiem do dodawania)
zip Pobierz rozwiązanie tego zadania

Licencja Creative Commons

Jeśli uważasz powyższą lekcję za przydatną, mamy małą prośbę: polub nasz fanpage. Dzięki temu będziesz zawsze na bieżąco z nowymi treściami na blogu ( i oczywiście, z nowymi częściami kursu Javy). Dzięki!

  •  
  •  
  •  
  •  
  •  
  • Arek

    Zdaje mi się że macie delikatny błąd w kursie. (chyba że się mylę ;) )
    Przy tak podanym web.xml będziesz szukał pliku mvc-dispatcher-servlet.xml a nie applicationContext.xml ?

    • Faktycznie, dziękuję za zwrócenie uwagi, przeoczyłem to wcześniej :) Już przygotowujemy aktualizację! :)

  • Gosc

    Najlepszy kurs w sieci, fajnie że po polsku to można poznać podstawy dosyć płynnie :)

    • Cieszymy się! Bardzo nam zależy, by nasze lekcje były zrozumiałe i praktyczne, bo wychodzimy z założenia, że samą dokumentacje każdy sobie może doczytać :)

      • Michał

        Witam. Czy jest w Eclipse jakiś katalog, do którego mógłbym dodać pobrany plik org.springframework:spring-core, bo podczas tworzenia zależności w projekcie Maven’a nie mogę tego odszukać? Dostępny jest natomiast np. org.springframework.osgi.

        • dodawanie zależności w eclipsie poprzez ‚formularz’ nie zawsze znajduje wszystkie możliwe artefakty – najłatwiej jest po prostu wpisać groupId, artefactId oraz version ręcznie do formularza :)
          Niestety nie znam ‚pewnego’ sposobu na to, aby w eclipsie dana zależność stała się widoczna. Sprawdź sposób opisany tutaj: http://stackoverflow.com/a/14063863 – na 90% powinien rozwiązać Twój problem

  • Adrian

    Bardzo dobry kurs, ale mam problem z plikiem web.xml w moim projekcie go nie ma a roiłem wszystko zgodnie z instrukcjami w kursie (przynajmniej tak mi się wydaje). Co mogłem zrobić źle ?? Możecie wrzucić rozwiązanie dla tej lekcji ?

    • Cześć,
      przepraszamy za zwłokę w odpowiedzi. Plik web.xml trzeba umieścić w Twoim projekcie w katalogu /main/webapp/WEB-INF – faktycznie nie jest to opisane w lekcji (już poprawiamy). Rozwiązanie jest już w przygotowaniu i pojawi się dzisiaj albo jutro :)
      Pozdrawiamy!

      • kux

        Kon­fig­u­ru­jemy 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­jalny, było to
        opisane w poprzed­niej lekcji) – nie widzę tego w poprzedniej lekcji :/ Było o WEB-INF ale nic o views

        PS: nieładnie usuwać posty ;)

        • Chodziło o to, że katalog /WEB-INF jest katalogiem, który JavaEE traktuje specjalnie. Katalog, który w nim tworzymy (views) nie ma w sobie nic specjalnego – możemy go nazwać dowolnie, może być ‚widoki’ czy ‚jsp’ – we wpisie chodziło o wyjątkowość folderu WEB-INF, a nie WEB-INF/views

          • 3qn

            JavaEE na Tomca-cie ? chyba TomEE ;)

          • Tomcat obsługuje wszystko, co potrzebujemy do tej pory – Servlet API czy JSTL jest częścią bazowego Tomcat’a. TomEE faktycznie jest certyfikowany do JavyEE, ale funkcje, które znajdziemy w nim dodatkowo do Tomcata na tą chwilę nie wykorzystujemy (CDI, EJB, JSF, JPA – używamy Hibernate – czy JTA; http://tomee.apache.org/apache-tomee.html). Dlatego spokojnie możemy używać ‚lżejszej’ wersji czystego Tomcata. Drugą kwestią jest to, że Tomcata (a nie TomEE) znajdziemy na większości platform IaaS, sensowniej jest więc dodatkowe funkcje, jeśli potrzebujemy, pobrać w postaci dependency.

  • Paweł

    Może trochę głupie pytanie ale mam wątpliwość :)

    Na samym początku lekcji mamy dodać zależności do modułu koty-services. Z tego co się orientuję, to wcześniej nie mieliśmy takiego modułu (był tylko koty-application, koty-domain, koty-webapp).
    Czy to oznacza że trzeba stworzyć nowy moduł o takiej nazwie?
    Proszę o wyjaśnienie łopatologiczne :)

    • Cześć,
      w treści lekcji zakradł się mały błąd, chodziło oczywiście o moduł koty-application. Dziękujemy za zwrócenie uwagi! Do tego modułu dodaj odpowiednie zależności :)
      Pozdrawiamy

      • Paweł

        Cześć, dzięki za odpowiedź. Jest jeszcze jedna wątpliwość.
        Zmieniłeś powyżej koty-webapp –> koty-application
        a z tego co rozumiem powinieneś zmienić koty-services –> koty-application??

        • Faktycznie, późne pory nie sprzyjają poprawianiu ;) Dzięki bardzo za spostrzegawczość!

  • kux

    Utwórzmy nową klasę o nazwie Koty­Con­troller i doda­jmy do niej adno­tację @Controller. – tylko pytanie gdzie to należy utworzyć? Potem na listingu tej klasy mamy:

    package pl.kobietydokodu.koty;

    Czyli tworzymy nowy package? Czyli obok koty-applicaction, koty-domain, koty-webapp mamy też folder koty? Proszę mi wybaczyć ale chcę robić krok po kroku i próbuję rozwiąć stające co chwila przede mną wątpliwości.

    • Cześć,
      tworząc nową klasę w Eclipse możesz podać pakiet ręcznie – Eclipse sam utworzy odpowiednie katalogi i nie będziesz musiał tworzyć ich ręcznie.
      Pakiety to jednak co innego niż moduły mavena – nie mylmy tych pojęć, moduły mavena to sposób organizacji systemu, podzielenia go na mniejsze częśći. Pakiety to sposób na organizację kodu, podzielenie go na grupy. Pakiety to cecha języka Java, z kolei moduły Maven są związane tylko i wyłącznie z narzędziem Maven.

      • kux

        Cześć, dzięki za odp!
        Nie rozwiewa to jednak moich wątpliwości gdzie ta klasa powinna być umieszczona? Czy mamy utworzyć mony moduł koty czy nie ma to w zasadzie znaczenia?

        PS. Nie chciało mi załapać adnotacji @Controller i dopiero po dodaniu w głównym pom poniższego zadziałało:

        org.springframework
        spring-web
        4.1.6.RELEASE

        • Cześć,
          nie trzeba tworzyć nowego modułu – te, które są obecnie, są wystarczające. Ta zależność powinna trafić do modułu -webapp, tam też powinna być klasa kontrolera. Być może klasę kontrolera umieściłeś w innym module ? Ciężko mi powiedzieć nie widząc kodu, ale dodanie wspomnianej zależności nie powinno być konieczne.

          • kux

            Poniżej moja struktura. Usunąłem z głównego pom.xml zależność o której mówilem wcześniej (spring-web). I teraz: jeśli KotyController leży w koty-webapp to jest okej, ale jeśli wg tego co piszesz dam ten plik do webapp to jest problem i KotyController kazda linijka na czerwono z błędami. Czy to znaczy, że zarówno web.xml jak i applicationContext.xml powinny trafić nie do webapp tylko do koty-webapp (i tam dalej utworzyć WEB-INF?)?

          • Dokładnie tak, cąły katalog WEB-INF musi trafić do modułu koty-webapp. W nadrzędnym module (tym, który ma packagingType ustawiony jako pom) nie będziemy mieli żadnych plików Javy any konfiguracyjnych (poza samym pom.xml oczywiście)

            Pozdrawiamy

          • kux

            Jeszcze mam pytanie, bo chyba przegapiłem: opisujecie jak ten projekt mavena zdeployować na Tomcacie?

          • Cześć,
            w lekcji 1.1 było o tym, jak uruchomić aplikację pod Eclipse ogólnie (http://kobietydokodu.pl/01-1-uruchamianie-aplikacji-w-eclipse/), w przypadku aplikacji webowych można zrobić podobnie – wybierając w przeglądarce projektów cały projekt a nie pojedynczą klasę, w opcjach ‚run’ będziemy mieli dostępne ‚Run on server’. Tą samą opcję zobaczymy klikając prawym przyciskiem myszy na projekt i wybierając opcję ‚Run as’.

            Jeśli chcesz uruchomić na zewnętrznym serwerze tomcat poza eclipse , w 25 lekcji (http://kobietydokodu.pl/25-publikujemy-aplikacje-w-internecie/) pisaliśmy o tym, jak wygenerować plik WAR, który możesz użyć z dowolnym serwerem aplikacji.

            Ale dziękujemy za zwrócenie uwagi, faktycznie nie jest to opisane za bardzo, postaramy się uzupełnić!

            Pozdrawiamy

          • kux

            Ok, to normalną aplikację jest jasne, z tym, że jak daję prawym to nie mam Run on server. Serwer Tomcata jest skonfigurowany w Eclipse, wszystko wydaje się być popodpinane. Jak dam Run configurations to mam co na screenie poniżej:

          • Cześć,
            zaczęliśmy pracę nad uzupełnieniem lekcji o odpalaniu pod Eclipse na serwerze aplikacji – opublikujemy ją dzisiaj albo jutro :)
            Pozdrawiamy

          • kux

            Super, czekam! :)

          • Z niezłym opóźnieniem, ale jest już na blogu:http://kobietydokodu.pl/08-1-uruchamiamy-aplikacje-webowe-w-eclipse/

          • Aga

            Cześć, gdzie można znaleźć tę uzupełnioną lekcję? :)

          • Justyna

            czy można już gdzieś znaleźć uzupełnioną lekcję?

  • Ola

    Cześć,
    czy można liczyć na rozwiązanie zadania? :)

    • Cześć!
      Staramy się nadrabiać zaległości i co jakiś czas dorzucić parę rozwiązań. Znacznie szybciej przychodzi nam odpowiadanie na konkretne pytania, więc może jesteśmy w stanie pomóc już teraz?
      Jeśli nie prosimy o wyrozumiałe czekanie, postaramy się je dodać jak najszybciej.

      Pozdrawiamy,
      Ania i Kuba

    • Ola

      To się cieszę:)
      Niestety mam problemy z eclipsem nie chce mi zainstalować serweru (Glassfish/Tomcat – ściągnęłam z marketpalce potrzebne wtyczki), więc chyba w celu rozwiązania zadania użyję NetBeansa.
      Miło, że szybko odpowiadacie na komentarze:)

      Również pozdrawiam :)

  • Ala

    Cześć ;D Wasz kurs jest naprawde super, po polsku najlepszy jaki do tej pory widzialam . Jest tylko jeden problem, zaczyna mi być ciężko zrozumieć treśc zadań i całej lekcji, bo tu i tam wkradają wam sie pewne nieścisłości, a nie można wspomóc sie gotowym rozwiązaniem.
    Pogubilam sie już zupełnie z metodami w kontrolerze, nie jestem w stanie zrozumiec którego z opisanych sposobow mam używac.
    Mam nadzieje ze pododajecie troche wyjasnien albo gotowe rozwiązania, bo kurs naprawdę jest mega.

    • Agnieszka

      Dołączam się do wątpliwości Ali. Chodzi mi głównie o ten fragment „Przed­staw­iamy ją tutaj tylko dlat­ego, że można ją często spotkać w ist­nieją­cych pro­jek­tach. Pisząc własny kod, o ile nie jesteś pewna, że to dokład­nie tej metody potrze­bu­jesz, korzys­taj z metody opisanej poniżej!”
      Drugie zdanie jest tak jakby zaprzeczeniem pierwszego, chyba że się mylę i czegoś nie zrozumiałam.

      Ps. Kurs jest świetny!

      • Agnieszka

        I jeszcze jedno, u mnie podany w tekście plik web.xml nie działał. Zadziałała natomiast poniższa wersja. Nie wiem, czym to jest spowodowane i czy zaproponowana przeze mnie wersja jest poprawna, niemniej jednak działa. Gdyby ktoś napotkał na podobny problem to może się przyda :)

        • Dziękujemy za zwrócenie uwagi na błąd! Faktycznie kod, który był wcześniej nie działał. To dlatego, że Spring szukał pliku mvc-dispatcher-servlet.xml , który nie istniał. Kod który wkleiłaś nie jest idealny (ładujemy ten sam plik dwukrotnie) ale jak najbardziej poprawny

      • Cześć,

        w tym fragmencie chodziło nam o metodę opisaną w lekcji 10 (http://kobietydokodu.pl/10-spring-mvc-formularze-i-widoki/ ) – faktycznie sformułowanie jest dziwne, już poprawiliśmy.

        Dziękuemy za czujność, zaczynamy weryfikować lekcje po kolei i wracać do nich, żeby mieć pewność, że jak najmniej tego typu problemów pozostanie :)

    • Przepraszamy za niedogodności i problemy, staramy się na bieżąco poprawiać lekcje i usuwać dziwne sformułowania. Niestety jest nas tylko dwoje i bloga prowadzimy po godzinach – nie mamy na to tyle czasu ile byśmy chcieli. Ale dzięki takim uwagom wyłapujemy więcej problemów i możemy go czynić jeszcze lepszym. Dziękujemy!

  • kux

    Well formed XML document should have only one root element – czy w takim razie blok:

    /WEB-INF/views

    .jsp

    nie powinien się znaleźć przed ? A nie jak stoi w tekście na końcu pliku?

    • oczywiście powinien on się znaleźć przed , o to nam chodziło pisząc ‚dodajemy do pliku…’ :) Już poprawiamy, żeby było czytelniej.

  • 7CB

    Dobry :)
    Fajny kurs, jeden z najlepszych dotyczących programowania, które w sieci widziałem :). Jak to jednak bywa podczas kursów, mam pewien problem:
    Zaimportowałem do poma zależność od spring-context w wersji 4.2.1 i nie mogłem zaimportować org.springframework.stereotype.Controller (w kontrolerze KotyController), wpadłem na pomysł, żeby dorzucić zależność do starszej wersji spring-context (wybrałem 4.1.6, bo była to najczęściej pobierana wersja spring-context w ostatnim czasie) i zadziałało. Miałem podobnie w innej sytuacji (niestety nie pamiętam w jakiej :p), wszystko działa, ale zastanawiam się, dlaczego tak jest? W nowszych wersjach biblioteki Springa inaczej się nazywają, czy ich nie ma? A może dzieje się tak z innego powodu, którego się nie domyśliłem?

    • Cześć,
      lokalizacja klas nie powinna się zmienić i w przypadku adnotacji @Controller szukałbym jej raczej w artefakcie webmvc. Możliwe, że nowsza wersja powodowała jakiś problem z zależnościami, przez co Maven nie pokazywał tej klasy. Ciężko mi powiedzieć na pewno, ale możesz się upewnić przeglądając pliki jar w przeglądarce pakietów w eclipse (Package explorer). Ogólnie w bibliotekach pokroju Springa pliki raczej zostają tam, gdzie były :)

  • Tomasz

    literówka w drugiej linijce podtematu „Widoki” – > „któa”

  • Hubert

    Witam, próbuję uruchomić pobrane rozwiązanie w Eclipse na serwerze Glassfish. Niestety od razu po zaimportowaniu projektu pojawiają się błędy (czerwone krzyżyki) przy wszystkich trzech plikach .jsp. Komunikat jest następujący: The superclass „javax.servlet.http.HttpServlet” was not found on the Java Build Path. Jestem początkujący jeśli chodzi o Springa, ale czy w tym projekcie nie brakuje pliku xml o nazwie mvc-dispatcher-servlet.xml?

  • Paweł Kalbarczyk

    Mam pytanie, próbuje odpalić tą aplikację webową, ale napotkałem na problem. Zarówno w moim projekcie, jak i w projekcie zaimportowanym z rozwiązania pojawia mi się błąd
    WARNING: No mapping found for HTTP request with URI [/koty-webapp/] in DispatcherServlet with name ‚mvc-dispatcher’
    Jakieś pomysły jak można to rozwiązać?

    • Pod głównym adresem nie dodaliśmy żadnego mapowania – spróbuj /koty-webapp/lista, gdzie powinna pokazać się lista kotów :)

      • Paweł Kalbarczyk

        WARNING: No mapping found for HTTP request with URI [/koty-webapp/lista] in DispatcherServlet with name ‚mvc-dispatcher’

        :-(

        • hmm, a możesz wkleić logi serwera na pastebin ? Oraz zrobić screenshot z ustawień serwera w eclipse, z zakładki ‚modules’ ? Ew. jeśli konfigurowałeś jako główny moduł serwera, spróbuj, czy pod po prostu /lista przypadkiem się nie wyświetla ;)

  • Hubert

    Zmieniłem serwer na Tomcata, żeby było dokładnie tak jak w instrukcji. Wszystko robię dokładnie tak samo, ale przy próbie uruchomienia aplikacji oczywiście błąd 404. Co może być przyczyną? Aplikacja jest dodana do serwera, po kliknięciu jego nazwy pojawia się, niby wszystko jest ok a nie działa. Pomocy.

  • Hubert

    Pobrane rozwiązanie nie uruchamia się. A wszystko zrobiłem tak jak w instrukcji uruchamiania aplikacji webowych. Na console widoczne są następujące warningi:
    lis 23, 2015 9:16:49 PM org.springframework.web.servlet.PageNotFound noHandlerFound
    WARNING: No mapping found for HTTP request with URI [/koty-webapp/lista] in DispatcherServlet with name ‚mvc-dispatcher’
    lis 23, 2015 9:18:03 PM org.springframework.web.servlet.PageNotFound noHandlerFound
    WARNING: No mapping found for HTTP request with URI [/koty-webapp/] in DispatcherServlet with name ‚mvc-dispatcher’

    Pomoże ktoś?

    • Cześć,
      podeślij proszę logi wrzucone na pastebin. U nas pobrane rozwiązanie uruchamia się bez problemu, staramy się znaleźć przyczynę dla której u niektórych to nie działa :( Daj znać też na której wersji Eclipse oraz Apach Tomcat pracujesz.

      Pozdrawiamy

      • Hubert

        Już znalazłem przyczynę. Usunąłem z pliku zawierającego kontekst aplikacji fragment . Teraz wszystko działa.

        • Świetnie, że Ci działa, ale ten fragment konfiguracji jest odpowiedzialny za obsługę adnotacji springowych i najprawdopodobniej nie on był przyczyną problemu.

  • Z tego co widzę, to aplikacja nie odpala kontrolerów z rozwiązania. Czy na zakładce Servers przy Tomcat widnieje opis Synchronized w nawiasach kwadratowych? Ew spróbuj kliknąć na niego prawym przyciskiem myszy i wybrać opcję ‚Clean’, być może to pomoże.

    • Paweł Kalbarczyk

      Started, synchronized tak podaje Tomcat w zakładce servers. Clean spróbowałem, ale nic to nie zmieniło. Eh, utknąłem. Nie wiem, może spróbuje na jakimś innym serwerze?

      • Hmm, nie udaje nam się odtworzyć takiego problemu. Mam jeszcze prośbę: spróbuj pobrać standardowego Eclipse (https://wiki.eclipse.org/Eclipse_Installer) i uruchomić za jego pomocą – nie powinno to nic zmienić, ale być może akurat w tym problem?

        • Paweł Kalbarczyk

          Spróbowałem jeszcze raz i znalazłem przyczynę problemu. Dość prozaiczna, w applicationContext.xml jest ustawione skanowanie adnotacji w danej paczce (u mnie pl.kalbarczyk), a po zmianie koty-webapp na packacking war Eclipse żąda, żeby zrobić update projektu Maven, przy czym zmienia nazwę paczki na org.koty.webapp. Przez tą zmianę klasa KotyController nie była w ogóle znajdowana. Tak więc… Działa:-)

          • Super, że udało Ci się rozwiązać problem! Eclipse czasem pyta o takie zmiany w mało widoczny sposób, można to przeoczyć :) Trzymamy kciuki za dalszą naukę i tylko tak dalej!

      • Hubert

        Spróbuj usunąć z pliku zawierającego kontekst aplikacji linię dotyczącą konfiguracji opartej na adnotacjach: .

  • MILO

    Męczę się z tym już drugi dzień… Próbuję odpalić rozwiązanie lekcji oraz swoje, ale niestety za każdym razem gdy uruchamiam adres strony dostaje 404 requested resource is not available. Swoją drogą bardzo fajny kurs :D Jest jeden błąd na stronie, jeśli użyjemy funkcji wyszukiwania ctr-f lub innego skrótu wysiada przewijanie scrollem w myszce. Trzeba odświeżyć żeby zadziałało znowu, przeglądarka chrome :)

  • Jakub

    Cześć, wydaje mi się, że wszystko zrobiłem dobrze, jednak cały czas przy uruchamianiu dostaję błąd 404. Pliki web.xml, applicationContext.xml oraz controller są identycznie jak u was, wszystko wydaje się być dobrze skonfigurowane, jednak nie działa. Jakie mogą być przyczyny 404?

    • Przy uruchamianiu aplikacji powinieneś dostać w logach wszystkie mapowania, które w niej są – sprawdź, czy wyświetlają się te informacje i jest tam ten adres pod których chciałeś wejść. Tak jak pisaliśmy innym – u nas działa, czekamy na informacje, które pomogą nam odtworzyć Wasze problemy.

      • Jakub

        U mnie w ogóle nie było żadnych mapowań wyświetlanych, ale usunąłem Tomcata i dodałem jeszcze raz i działa :) także nie wiem, co tam się działo ;)
        Btw. dzięki za duzo pracy wlozonej w bloga, na prawde jest pomocny :)

        • Ok, no to być może coś się źle skonfigurowało, grunt, że udało Ci się dodać go poprawnie! Powodzenia z dalszą nauką.

  • wannaBeJunior

    Mam problem, postępuje według tego tutoriala na swoim własnym projekcie, jednak nie udaje mi się go uruchomić. Jest szansa by ktokolwiek mógł spojrzeć na mój kod oraz podpowiedzieć co może być zle ? Z tego co widze to w ogole nie mapuje homecontrollera. Mam również problem z widokami a dokładnie plikiem home.jsp,wyskakuje blad w 1 linijce

    The superclass „javax.servlet.http.HttpServlet” was not found on the Java Build Path

    , probowalem roznych sposobow z internetu, gdy jednak dodaje dodatkowe dependencies, lub grzebie w properties projektu to blad z .jsp znika, lecz wyskakuje znaczek bledu przy projekcie mavena.

    Przesylam caly projekt na maila blog@kobietydokodu.pl oraz logi z kompilacji mavena oraz tomacata. Bardzo bym prosil o pomoc! Z gory dziekuje :)

    http://tnij.org/tomcat222

    http://tnij.org/maven222

    • Grube Lolo

      Rozwiązanie problemu „The superclass „javax.servlet.http.HttpServlet” was not found on the Java Build Path” to dodanie z repozytorium Maven zależności „javax.servlet”.

      • Daj znać proszę, czy ta odpowiedź pomogła rozwiązać twój problem :)

  • Damian

    Hej,
    Mam problem ze swoją aplikacją w springu. Przy generowaniu pliku konfiguracyjnego korzystam z Waszego kursy ale niestety aplikacja nie otwiera się, błąd 404 :(
    Czy możecie mi podpowiedzieć co robię źle?

    Dodam tylko, że mam dwa projekty w STS, które mają taką samą nazwę pliku konfiguracyjnego

    • Cześć,
      możesz podesłać swoją aplikację zapaczkowaną albo np. repozyorium na Git? Będziemy wtedy w stanie więcej powiedzieć :)

      • Damian

        Kuba,
        Dlaczego jak odpowiadam pod Twoim komentarzem to one się nie zapisują? Już z 5 pisze :)

        • Damian

          mój problematyczny projekt github/Damiank91/eLibrary
          Chyba jak podaję link to nie zapisuje się

        • Jak wklejasz linka, to komentarz czeka do akceptacji, żeby nikt nam nie spamował ;) linki widzimy, wszystko ok, zaakceptowałam jeden z komentarzy ;)

          • Damian

            Uff… Już myślałem, że dostałem bana :)

      • Damian

        Kuba,
        Czy jest szansa na pomoc? :)

        • Na pewno odpiszemy, tylko potrzebujemy wolnego czasu by spojrzeć w Twój kod, a z nim ostatnio dość ciężko. Powinniśmy dać radę odpisać w tym tygodniu ;)

  • jumpinJackFlash

    Mam problem z tą częścią kodu

    package com.jderda.dzona;

    import java.util.ArrayList;
    import java.util.List;
    import org.springframework.stereotype.Repository;

    @Repository
    public class KotDAO {

    Nie mam takiego pakietu jak com.jderda.dzona. Klasa KotDAO u mnie znajduje się w pl.kobietydokodu.koty. Stąd moje pytanie, czy mam utworzyć nowy pakiet i klasę KotDAO czy jak?

    • Cześć,
      to nasz błąd, przepraszamy – faktycznie powinien być to pakiet pl.kobietydokodu.koty ;)
      Dzięki za zwrócenie uwagi!

      • jumpinJackFlash

        Ok, dzięki za szybką odpowiedź :) No i od razu posłodzę, że naprawdę fajny kurs! ^.-

  • Monika Senderecka

    Bardzo prosiła bym tylko o wskazówkę, czy kontroler powinien znaleźć się w module web-app czy w application?

  • Matesz

    Witam :)
    Mam pytanie dotyczące danych z adresu URL. Odwołujac sie do przykładu z Waszego kursu:

    @Controller
    public class ExampleController {
    @RequestMapping(„/kot/{imie}”)
    public String szczegolyKota(@PathVariable(„imie”) String imieKota) {
    return „glowny”;
    }
    }

    Wpisujac w przegladarkę odpowiednio:
    http://localhost:8080/nazwa_projektu/kot/filemon

    następuje odwołanie:
    /nazwa_projektu/kot/WEB-INF/views/glowny.jsp

    przy czym oczywiście pod taką ścieżką nie ma nic. Proszę o wksazówkę, gdzie zrobiłem błąd?

    Pozdrawiam
    Mateusz

    • Cześć, metoda ta zwraca String ‚glowny’, który jest przekazywany do tzw. ViewResolvera. Być może masz źle skonfigurowane wyświetlanie widoków (np. używasz prefiksu WEB-INF/… zamiast /WEB-INF/… ). To ważne, bo wyświetlenie najprostszego widoku (JSP) w Springu polega na ‚przekierowaniu’ zapytania właśnie do tej strony JSP, wewnętrznie w aplikacji. Jeśli taki plik JSP nie istnieje lub coś jest zepsute z konfiguracją – wtedy może pojawić się tego typu błąd :)
      Sprawdź konfigurację ViewResolvera – myślę, że tam będzie skrywał się problem.

      • Matesz

        Dziękuję :)
        W konfiguracji ViewResolvera był błąd. Użyłem prefiksu WEB-INF/.. zamiast /WEB-INF/…
        Teraz wszystko jest w porządku :)

  • pawlosziom

    Witam,

    Mam mały problem, proszę o pomoc. Uczę się krok po kroku z tutoriala, i jestem na tej lekci podczas próbowania wyświetlania widoku statycznego. Nie wiem jaki adres mam wpisać w wyszukiwarkę. Serwer uruchamia się bez problemu nie wykrywa żadnych błędów. Jak rozumiem, kontroler ma przekazać zwracaną zmienną do widoku, co jest determinowane ścieżką opisaną w applicationContent.xml. Moje dane: grupa mavenowa oskary, /oskary.koty-webapp – > ścieżka projektu, reszta modułów nazwana tak samo, tylko z tą różnicą że moja grupa to oskary. Proszę o pomoc, bo wpisuje wszystko i nie mogę znaleźć tego , a jest to niewątpliwie podstawa, bez której trudno o dalszy rozwój.

    Z góry dziękuję.

    • pawlosziom

      Przepraszam za sianie fermentu, już sobie poradziłem z sytuacją.

  • mikigal

    Dlaczego cały czas mam 404 Error? Wszyrkie xmle są ok.

  • Dawid

    Witam

    Zastanawia mnie 1 szczegół w konfiguracji pliku web.xml: otóż do tej pory przy budowaniu aplikacji spotykałem się z takim nazewnictwem servletów: nazwa-servlet.xml który potem
    się deklaruje w pliku web.xml:
    contextConfigLocation

    /WEB-INF/spring/mvc/spring-mvc-servlet.xml

    —————————————————————————————————————————————————->
    Czy jest dobrą praktyką stosowanie nazwy typu:
    context-param>
    contextConfigLocation
    /WEB-INF/rootApplicationContext.xml

    Prosił bym o wyjaśnienie ponieważ mam mały mętlik :)

    • Nie ma w tym wypadku jednego podejścia, które byłoby ‚właściwe’, ani żadnej konwencji. Nazwy typu ‚spring-mvc-servlet’ są podobne do domyślnej nazwy (jeśli nie podamy wartości tego parametru, to Spring domyślnie będzie szukał pliku o nazwie -servlet.xml ). Nie jest to jednak ani oficjalne zalecenie, ani konwencja, o ile mi wiadomo.

      W nazewnictwie plików najważniejsze jest to, aby było ono spójne i czytelne – nie nazywaj pliku a.xml i b.xml, ale zastanawiając się pomiędzy webApplicationContext.xml a mvc-servlet.xml wybierz tą nazwę, która jest bardziej wymowna dla Ciebie

  • Piotrek

    Mam pytanie odnośnie nazwy parametru context i init contextConfigLocation. W przypadku initial parameter i context parameter. Oba parametry mają taką samą nazwę. Nie powinny się różnić nazwy? Przy przekazaniu do metody zostaną załadowane obie wartośći? Czy przekazanie do servletu parametru context a initial się nie różnią ?

    • Różnica polega na tym, że ustawiamy różne rzeczy – konfigurację dla servletu oraz konfigurację dla kontekstu głównego (inicjowanego przez listener, zadeklarowany jako ostatni). Parametry mają faktycznie identyczną nazwę, ale nie nadpisują się

  • Piotrek

    Hej. Mam pytanie odnośnie package pl.kobietydokodu. W, której lekcji on był tworzony, przerabiałem wszystkie pokolei i nie było nic o tym

  • Cześć,
    powyższy błąd nie powinien być problemem. Aplikacja powinna być dostepna pod adresem localhost:8080/koty-webapp/(ścieżka). Problemu szukałbym bardziej w samej aplikacji – w logu nie widać mapowania servletów, które przeważnie można tam znaleźć. Podeślij link do całego kodu, być może uda nam się znaleźć problem :)

  • Dobry

    P.S. jeszcze mam problem z MAVEN -> UPDATE PROJECT, kiedy to robię wysypują się 2 błędy

    https://uploads.disquscdn.com/images/fbed558d2cb1d2f4a4e55b69d71fbd5237bc481edbcc88f4b7477cf34a2f983d.png

  • Dobry

    Czy mógłbym prosić o dokładniejsze wyjaśnienie co konkretnie się dzieje w tych liniach?
    https://uploads.disquscdn.com/images/d47e3df12163d09ea3ee7fe1aa6dea7792e6086b929ddf03e21ef6821c64ed2c.png

    Z góry dziękuję za odpowiedź :)

    • Pierwsza linia to deklaracja metody kontrolera – zadeklarowaliśmy parametry, które Spring samodzielnie uzupełni (więcej o tym, jak to się dzieje znajdziesz np tutaj: http://sdqali.in/blog/2016/01/29/using-custom-arguments-in-spring-mvc-controllers/ – Spring domyślnie ma kilka takich wbudowanych mechanizmów) – w tym wypadku prosimy Springa o wstrzyknięcie modelu, czyli obiektu, który pozwoli nam przekazać dane z kontrolera do widoku, ale także pozwoli nam ‚odebrać’ dane od Interceptorów i metod kontrolera z adnotacją @ModelAttribute.

      Druga linia to po prostu dodanie atrybutu do modelu (model działa trochę jak mapa, w której kluczem jest String – nazwa, a wartością dowolny obiekt). Dzięki temu, że dodaliśmy taką rzecz do modelu, będziemy mogli odczytać tą informację na widoku (korzystając z ELa np. za pomocą składni ${message} )

      Mam nadzieję, że pomogliśmy zrozumieć co Spring tam robi :)

      • Dobry

        Dzięki :) Jest już znacznie jaśniej. Jutro spróbuję to jeszcze raz przetrawić

  • Krystian

    Hej, brakuje słówka, gdzie i do jakich projektów dodawać elementy o których piszecie i jaki ma być projekt mavena (bo rozumiem że te 2 opisane tutaj to moduły) i jaki packaging ;)

  • Darek

    Cześć,
    Mam problem z plikami jsp, IDE wyświetla błędy dla każdego z plików:
    [Description] [Resource] [Path] [Location] [Type]
    The superclass „javax.servlet.http.HttpServlet” was not found on the Java Build Path dodaj.jsp /koty-webapp/src/main/webapp/WEB-INF/views line 1 JSP Problem

    Próbowałem znaleźć rozwiązanie w sieci, ale nic nie znalazłem. Wie ktoś w czym tkwi problem. Dodam, że aplikacja pomimo błędów działa :)