niedziela, 2 września 2012

Spring MVC prosty start

Spring MVC - temat obszerny i w wielu miejscach opisywany. Oto moje podejście. Zacznijmy od bardzo prostej aplikacji pokazującej serce tego modułu.
1. Zależności:
       
    <!-- Spring 3 dependencies -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>


        <!-- Servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>

        <!-- core taglib -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency> 


2. web.xml :
<!--

tutaj definiujemy kontekst aplikacji. 
Możemy podać po przecinku wiele konfigów, wszystkie zostaną załadowane jako jeden kontekst.

-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:/META-INF/root-context.xml</param-value>
    </context-param>

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


<!--

tutaj konfigurujemy servlet springowy z własnym konfigem springa.

-->

    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/mvc-dispatcher-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
Zwróćmy uwagę na bardzo ważną rzecz: skonfigurowaliśmy 2 konteksty springa. Roboczo ten skonfigurowany przez ContextLoaderListener nazwijmy głównym kontekstem, a ten drugi webowym. Są to dwa niezależne konteksty springa, przy czym kontekst webowy widzi główny kontekst jako parent. Czyli beany z kontekstu webowego mogą wstrzykiwać sobie beany z konteksu głównego. Należy dodać, że możemy mieć wiele DispatcherServlet-ów, każdy będzie widział główny kontekst, ale webowe konteksty nie będą widzieć siebie wzajemnie.

Warto zwrócić uwagę na to, że jeśli zdefiniujemy jakiś BeanFactoryPostProcessor albo BeanPostProcessor w głównym kontekście to NIE ZADZIAŁA dla beanów z webowego kontekstu. W dokumentacji interfejsu BeanPostProcessor czytamy :
"Plain bean factories allow for programmatic registration of post-processors, applying to all beans created through this factory."
Podobnie jest dla BeanFactoryPostProcessor. Zatem jasne.

Jedna jeszcze istotna uwaga: otóż główny kontekst jest załadowany zanim zostaną zainicjowane serwlety.

3. Zajrzyjmy do wnętrza podanych konfigów springa
- root-context.xml mam pusty - aplikacja narazie nie ma żadnej logiki :-)
- mvc-dispatcher-servlet.xml:
<context:component-scan 
    base-package="pl.turo.spring.mvc.controller" />    <!-- 1 --> 

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  <!-- 2 -->
    <property name="prefix">
       <value>/WEB-INF/pages/</value>       <!-- 3 -->
    </property>
    <property name="suffix">                <!-- 4 -->
        <value>.jsp</value>
    </property>
</bean>
W tym miejscu należy omówić nieco jak w teorii działa i co robi Spring MVC.
Każde żądanie które będzie obsługiwane przez DispatcherServlet przechodzi cykl:
- na podstawie żadania http należy uruchomić jakąś metodę biznesową (oczywiście przy odpowiednim wypakowaniu danych z żądania),
- metoda może coś zwrócić,
- trzeba klientowi zwrócić wynik w takiej postaci jakiej oczekuje.

Dokładnie to robi dla nas DispatcherServlet
- odbiera żadanie,
- wywołuje "odpowiednią" metodę biznesową na kontrolerze. Kontroler to nic innego jak zwykły bean. W jaki sposób serwlet szuka owej "odpowiedniej" metody pokażę niżej (konfiguracja opatrzona komentarzem "1"),
- odbiera wynik tego wywołania,
- przekierowuje renderowanie odpowiedzi do widoku. Widokiem jest klasa implementująca interfejs View. Spring dostarcza pokaźną ich liczbę i ten temat zasługuje na osobny post. DispatcherServlet odwołuje się do ViewResolver-a aby wybrać odpowiedni widok i renderować odpowiedź. Tutaj posłużyłem się klasą InternalResourceViewResolver (komentarz "2") która oczekuje że kontroler zwróci nazwę widoku. Nazwa ta jest nazwą strony jsp (komentarz "4"); szukana jest w katalogu /WEB-INF/pages (komentarz "3").

Przykład:
Jeśli kontroler zwraca "hello", to  InternalResourceViewResolver oczekuje istnienia strony /WEB-INF/pages/hello.jsp i ona zostanie odesłana jako odpowiedź.

Wiele rzeczy może tu być nadal niejasnych (np jak kontroler 'rozpakowuje' parametry żądania, jak zapisuje do odpowiedzi itd...). Na ten moment nie przejmujmy się tym i idźmy dalej.


4. Prosty kontroler:
package pl.turo.spring.mvc.controller;

@Controller
public class HomeController {

    @RequestMapping("/")
    public String index() {
        return "index";
    }

    @RequestMapping("/hello")
    public void sayHello(Model model) {
        String name = "Sławek";
        model.addAttribute("imie", name);
    }
}
Widzimy dwie adnotacje:
- @Controller jest adnotacją markerową i jest podtypem @Component. Sama z siebie nic nie dodaje,
- @RequestMapping - owa adnotacja mówi dispatcher-owi że należy ją wywołać jak przyjdzie żądanie pasujące do "/hello" (przy mojej konfiguracji mapowania dispatcher-a w web.xml będzie to http://serwer:port/nazwa_aplikacji/hello). Adnotacji tej poświęcę również osobny post. Na razie niech nam wystarczy to co już wiemy.
Zapewne zauważyliście, że metoda sayHello otrzymuje w parametrze Model - jest to dekorator mapy do której możemy wkładać dane. W moim przykładzie pod kluczem "imie" trzymam zmienną name, do której odwołuję się na stronie hello.jsp:
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 <title>Spring MVC by turo</title>
</head>

<body>Hello ${imie}</body>
</html>
Uruchamiam serwer, i pod adresem http://localhost:8080/mvc/hello widzę moją stronę :-). To tyle jeśli chodzi o wprowadzenie do Spring MVC. Jak zwykle udostępniam projekt; będąc bardziej na czasie niech to będzie git:
git://github.com/slturo/mvc-start.git

Brak komentarzy:

Prześlij komentarz