Inspiracja , wiedza , realizacja
Jsystems

W przebudowie

Login



Java

Oracle

Linux

Android

PostgreSQL

Microsoft SQL Server

Podstawy Spring WebFlow

Data dodania: Jun 27, 2016
Data aktualizacji: Jun 27, 2016

Proste formularze z przejściami


Kod źródłowy z przykładami do tego rozdziału możesz pobrać pod adresem:

http://jsystems.pl/storage/spring/springflow1.zip


Do czego może nam się przydać Spring WebFlow? Ten moduł umożliwia robienie wieloetapowych przepływów – coś na kształt wizardów instalacyjnych. Możemy dzięki niemu w stosunkowo prosty sposób stworzyć np. kilkustronnicowy formularz rejestracyjny.

W tym rozdziale rozpoczniemy od dodania dwóch bardzo prostych funkcjonalności na potrzeby sklepu internetowego. Jedna będzie pozwalała zapisać się do newslettera, druga założyć konto w sklepie. Zaczynamy od stworzenia zwyczajnej aplikacji opartej o Spring MVC. Nie pojawia się tutaj nic nowego ani nadzwyczajnego, niemniej zaznaczę kilka kluczowych elementów:

Wpis w web.xml:



Wszystkie podstrony tej aplikacji z rozszerzeniem .do będą obsługiwane przez Springa.


Do pliku *****-servlet.xml trzeba będzie dodać przestrzenie nazw związane ze Spring WebFlow, więc zrobimy to od razu:




a konkretniej chodzi mi o dodanie przestrzeni nazw:


xmlns:flow="http://www.springframework.org/schema/webflow-config"


oraz jej położenia:


http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.3.xsd


WebFlow wymaga dodatkowych bibliotek, więc poza tym co zwykle dodać trzeba jeszcze będzie biblioteki spring-webflow,spring-binding i springframework.js:




Wszystkie trzy pliki jar znajdują się z katalogu libs projektu dołączonego do niniejszego artykułu.

Dodaję też najzwyklejszy w świecie kontroler z jedną metodą mapującą adres „/start.do” :




Do pliku JSP obsługującego to żądanie dodałem dwa linki:



I teraz się zacznie... W pliku ****-servlet.xml musimy dodać kilka niezbędnych elementów. Na potrzeby choćby jednego przepływu musimy mieć całą taką konfigurację jaką wprowadziłem w swoim pliku w liniach 29-63... Omówimy elementy zmienne, które będą się różnić w różnych aplikacjach.

Dodamy na początek przepływ który będzie się sprowadzał do dodania swojego adresu email, do newslettera. Nieco później rozbudujemy nasz projekt.

Każdy przepływ będzie miał osobny plik konfiguracyjny w którym opiszemy co po czym ma się uruchamiać i przy spełnieniu jakich warunków. Rejestrujemy te pliki konfiguracyjne w sekcji flow-registry (linie 51-53). Bardzo ważny jest tutaj parametr ID, ponieważ jest on związany z tak zwanym adresem wyzwalającym przepływ. Jeśli adres naszego formularza jest /newsletter.do, to WebFlow będzie poszukiwał w rejestrze wpisu o id newsletter, jeśli adres rejestracji konta w sklepie będzie miał postać /rejestracja.do to będzie szukał wpisu o id rejestracja. Liczy się tylko „końcówka”, same adresy wyzwalające mogą mieć postać np. /modul1/newsletter.do.

Ten adres wyzwalający oznacza rozpoczęcie przepływu – w tym przypadku rejestracji do newslettera.



Jak widać w rejestrze przepływów dla przepływu o id „newsletter” wskazałem plik newsletter-przeplyw.xml znajdujący się w katalogu /WEB-INF/flows. Tworzymy więc podkatalog flows w WEB-INF i umieszczamy w nim na razie pusty plik o nazwie newsletter-przeplyw.xml


Wewnątrz tego pliku umieszczamy taką konfigurację:



Każdy element zamieszczony tutaj to jeden „krok” w naszym przepływie. Takim krokiem może być wyświetlenie strony, wykonanie jakiejś akcji, podjęcie decyzji warunkowej. Każdy przepływ musi mieć dokładnie jeden stan początkowy i przynajmniej jeden końcowy. Oznaczają one pierwszy i ostatni krok w przepływie. Pomiędzy tymi krokami możemy dodać kolejne, nic nie stoi na przeszkodzie. W naszym mini projekcie w tym przepływie będą tylko dwa kroki. Jeden to wyświetlenie formularza do którego wprowadzimy nasz adres email, drugi to stan końcowy który po prostu przekieruje nas do strony początkowej. Nieco później zadbamy o to, by w zależności od tego czy zatwierdzimy czy anulujemy formularz wyświetlił się stosowny komunikat.

Przyjrzyjmy sięteraz elementowi w liniach 11-14. Oznacza on wyświetlenie strony jsp o nazwie newsletterForm (która powinna znaleźć się w WEB-INF/jsp – jak mamy skonfigurowane w beanie o ID viewResolver w pliku ****-servlet.xml). Zauważ, że element ten ma również id „newsletter”. To jest stan początkowy, co oznacza że jako pierwszy krok w tym przepływie zostanie wyświetlona strona newsletterForm. Pojawiają nam się tutaj też znaczniki <transition on =”x” to=”y”/> Co to oznacza? Jeśli pojawi się (na razie bliżej nie określone) „COŚ” o nazwie „zatwierdz”, sterowanie ma zostać przekierowane do elementu „dodano”. Jeśli pojawi się takie samo „COŚ” o nazwie „anuluj”, sterowanie ma zostać przekierowane do elementu o nazwie „anulowano”.


To „COŚ” to są komunikaty wysyłane z poziomu widoku i za chwilę się tym zajmiemy. W tej chwili przyjrzyjmy się jeszcze elementom z linii 18-21. Są to dwa możliwe stany końcowe. Oba w tej chwili robią to samo – przekierowują do strony początkowej. Który ze stanów końcowych nastąpi zależy od tego, czy z widoku zostanie przesłany komunikat „zatwiedz” czy „anuluj”.


Przejdźmy teraz do naszego pliku widoku „newsletterForm” który jest wyświetlany w pierwszym kroku naszego przepływu. Niedługo pojawią się tutaj też pola do których wprowadzać będziemy dane, teraz interesuje nas tylko przepływ jako taki.



Może zastanawiać dziwny adres linka który podałem. Zacznijmy od lewej. Pojawia się tutaj

${flowExecutionUrl} - dzięki temu fragmentowi powracamy do przepływu w którego trakcie jesteśmy. Ten znacznik widoczny jest zresztą w pasku adresu:


Dalej mamy tajemniczy ciąg zawierający _eventId=zatwierdz , oraz drugi podobny _eventId=anuluj . Myślę że kiedy przyjrzysz się zaznaczonemu poniżej fragmentowi pliku konfiguracyjnego tego przepływu to wszystko stanie się jasne:



Gdybyś jednak nie wypił porannej mocnej kawy to wyjaśniam – to jest to nasze „COŚ” które wywołane powoduje przejście do kolejnego stanu. Ale jakiego stanu? W pierwszym przypadku do stanu o nazwie „dodano”, w drugim do stanu o nazwie „anulowano”. A teraz przyjrzyj się znajdującemu się nieco niżej fragmentowi pliku newsletter-przeplyw.xml:



Kliknięcie tych linków spowoduje przejście do stanów końcowych przepływu. Oczywiście można by tutaj było dać jakiś inny stan – choćby kolejny widok. Wszystko fajno, ale trochę słabe takie zapisywanie się do newslettera, jeśli nigdzie nie podajemy swojego adresu email ;)


Wzbogaćmy teraz naszą aplikację o obiekt w którym będziemy gromadzili dane na temat użytkownika. Uzyszkodnik będzie obiektem klasy którą musimy stworzyć:




Moja klasa zawiera cztery proste pola . Nie są ujęte na screenie, ale klasa posiada też gettery i settery do tych pól. Klasa musi być serializowalna.

Dodaję też jakieś fake'owe dao do obiektów klasy Uzyszkodnik. Metoda save ma przyjmować obiekt użytkownika po wypełnieniu formularza i zwracać ID pod jakim obiekt ten wylądował w bazie. Tutaj jest to jakaś wartość „na sztywno”, abyśmy teraz nie komplikowali sobie życia sprawami związanymi z bazami danych. Jest też metoda print która przyjmuje przez parametr obiekt użytkownika i wypisuje jego zawartość na konsolę. Oczywiście w żadnym normalnym DAO taka metoda by się nie pojawiała, tutaj jest jedynie do celów prezentacyjnych działania przepływów.



Ponieważ do obiektu klasy UzyszkodnikDao zamierzam się odwoływać w ramach mojego przepływu na zasadzie „uzyszkodnikDao.jakasMetoda()” w pliku konfiguracji przepływu, Spring musi wiedzieć co to takiego jest ten uzyszkodnikDao. W związku z tym deklarujemy tę klasę jako bean w pliku ***-servlet.xml:



Przejdźmy teraz do pliku konfiguracji przepływu. Pojawiło się tutaj kilka nowych rzeczy:



Element oznaczony jako nr 1 to zmienna która widoczna będzie w całym przepływie. Spring stworzy obiekt o nazwie podanej w parametrze „name” klasy podanej w parametrze „class” i umieści ją w przepływie. Nasz widoczek newsletterForm zyskał nowego kolegę pod postacią parametru model. Zauważ że jest tutaj podana nazwa naszej nowej zmiennej – uzyszkodnik. W formularzu będziemy uzupełniać pola obiektu – więc wypadałoby wskazać jakiego :). Bez tego dodatku w prawdzie przepływ będzie działał (w sensie będą działały przejścia i nie będzie sypało żadnymi błędami), ale wypełniane pola pójdą „w kosmos” a obiekt na koniec przepływu pozostanie pusty (sprawdzone organoleptycznie). W linii 13 też jest mała zmiana – przejście następuje nie do końcowego stanu a do stanu „zapiszUzytkownika” który to się właśnie pojawił. Element action-state oznacza wykonanie jakiejś akcji – wywołania metody , a nie jak view-state wyświetlenia widoku. Przyjrzyjmy się więc teraz temu elementowi. „Evaluate expression” wskazuje metodę która ma zostać wykonana. Jest to metoda „save” z obiektu klasy uzytkownikDao któgo bean przed momentem dodawaliśmy do pliku ****-servlet.xml . Do tej metody jako parametr przekazuje obiekt uzyszkodnik który.... też przed momentem definiowaliśmy ;). Chodzi więc o obiekt który sobie w ramach naszego przepływu uzupełniamy. W naszym formularzu który jest uzupełniany stan temu uzupełnialiśmy pola właśnie tego obiektu. W tym kroku przekazujemy go do metody by zapisać go w bazie. Ten obiekt jest już uzupełniony wartościami wprowadzonymi przez formularz. Pojawia się tutaj tez parametr result. Obiekt który zapisujemy wyląduje w bazie pod jakimś identyfikatorem, a identyfikator ten zwracany jest przez metodę save. Przyda się on do późniejszego wyświetlenia, lub np. podpinania kolejnych obiektów pod ten. Result spowoduje podstawienie pod pole id naszego przepływowego obiektu wartości zwracanej przez metodę wywoływaną w evaluate expression. Action-state po wykonaniu oczekiwanych operacji przekazuje sterowanie do stanu „wydrukujUzytkownika”. Stan wydrukujUzytkownika to stan akcji który wywołuje stworzoną w dao metodę print i przekazuje jej uzupełniony obiekt do wyswietlenia. Dalej przepływ przekazywany jest do stanu końcowego dodano.

Podsumowując – pierwszy stan to stan widoku wyświetlający formularz z pliku newsletterForm.jsp, przy użyciu którego uzupełniamy obiekt uzyszkodnik który jest widoczny w całym przepływie. Po zatwierdzeniu formularza następuje stan akcji „zapiszUzytkownika” który zapisuje obiekt „uzyszkodnik” do bazy i uzupełnia w nim pole id identyfikatorem pod jakim wylądował w bazie. Sterowanie jest przekazywane do stanu „wydrukujUzytkownika” który na konsoli drukuje zawartość obiektu i przekazuje sterowanie do stanu końcowego, który przekierowuje do strony startowej.


Troszkę zmian nastąpiło też w samym formularzu do wypełniania danych.



W liniach 2-5 pojawiły się importy do bibliotek tagów których używamy. Musisz je mieć, inaczej formularz nie zadziała. Elementy z linii 21 i 22 już znasz, nie będą nam więcej potrzebne ale zostawiłem je do porównania z elementami z linii 17 i 18 które je zastąpią. Zacznijmy jednak od początku – linia 12. <form:form.... z zasady oznacza początek formularza, </form:form> jego koniec. Wszystkie pola służące do wprowadzania danych muszą się znajdować pomiędzy nimi.

W tagu otwierającym formularz pojawia się parametr commandName=”uzyszkodnik”. Pewne podejrzenie odnośnie tego co to takiego, powinno już Ci się nasunąć samo. To jest wskazanie obiektu który będziemy uzupełniać, a ściślej obiektu klasy Uzytkownik który zdeklarowaliśmy w pliku konfiguracyjnym przepływu. Możemy tam przecież mieć kilka deklaracji <var..../>

<form:input path=”nazwaPola”/> odnosi się do pól tego obiektu. Krótko mówiąc są to pola tekstowe do uzupełnienia pól imię, nazwisko, email w obiekcie uzyszkodnik.

Linie 17 i 18 to przyciski. Jeden będzie zatwierdzał formularz, drugi anulował. Ściślej – oba będą wywoływały akcję którą zdeklarowaliśmy w pliku konfiguracji przepływu. Zwróć uwagę na ich nazwy. Aby spełniały założoną rolę, muszą składać się z elementów _eventId_ oraz naszego uruchamiacza przejścia do następnego stanu.


Jeśli przyjrzysz się teraz elementom „transition on …. to …” to myślę że nie trzeba będzie nic więcej wyjaśniać. Linki zrobione wcześniej (linia 21 i 22) można już usunąć.

Skoro już wszystkie elementy mamy, zobaczmy jak to działa. Rozpoczynamy przepływ wchodząc na stronę newsletter.do:



Jesteśmy na pierwszym stanie. Jest to stan widoku „newsletter”. Uzupełniam wszystkie pola:


Po zatwierdzeniu zostaje wywołane zdarzenie „zatwierdź” które powoduje przejście do stanu akcji „zapiszUzytkownika”. Zajrzyjmy teraz do konsoli:



Pierwsza linia pochodzi z dao wywołanego w stanie akcji „zapiszUzytkownika”. Jak widać wyświetlają się dane wprowadzone przez formularz, ale pole id pozostaje puste. Jest ono uzupełniane w stanie akcji „zapiszUzytkownika” . Następnie do akcji wkracza „wydrukujUzytkownika” który prezentuje nam na konsoli już uzupełnione razem z ID dane. Ostatnia linia pochodzi z naszego kontrollera od strony głównej gdzie przepływ został przekierowany przez stan końcowy.