2. Pierwszy przykład
Większość naszych przykładów będzie ograniczać się wyłącznie do warstwy internetowej zaimplementowanej przy użyciu Struts 2:
![]() |
Gdy opanujemy podstawy, przeanalizujemy bardziej złożony przykład z architekturą wielowarstwową.
2.1. Tworzenie przykładu
Tworzymy naszą pierwszą aplikację.
![]() |
- w [1] tworzymy nowy projekt
- w [2] wybieramy typ Java Web / Web Application
- w [3] nadajemy nazwę projektowi
- w [4] wskazujemy lokalizację projektu.
- w [5] ustalamy nowy projekt jako projekt główny.
![]() |
- w [6] wybieramy serwer Tomcat. Podczas instalacji NetBeans 7.01 zainstalowano dwa serwery: Apache Tomcat i GlassFish 3.1.
- W oknie [7] wskazujemy, że będziemy pracować z frameworkiem Struts 2. To właśnie instalacja wtyczki dla Struts 2 daje nam tę możliwość. Bez tej wtyczki framework Struts 2 nie jest nam proponowany.
- W oknie [8] należy poprosić o utworzenie przykładowego projektu, który będziemy analizować.
- W pliku [9] można sprawdzić, które biblioteki Struts 2 będą wykorzystywane.
![]() |
- w [10] – wygenerowany projekt. Wrócimy do niego później.
- W pliku [11] znajdują się biblioteki projektu. Zostały one zintegrowane przez wtyczkę Struts 2. Jeśli nie dysponujemy tą wtyczką, biblioteki te można znaleźć w folderze [lib] pobranej dystrybucji Struts 2. Następnie należy wykonać kroki 12 i 13.
2.2. Projekt wygenerowany w systemie plików
![]() |
- w [1], zakładka [Projects] przedstawia widok „programisty” projektu
- w [2], zakładka [Files] przedstawia folder projektu w systemie plików
- w [2A] gałąź [Web Pages] jest reprezentowana w [2] przez folder [web] [2B]
- w [3A] gałąź [Source Packages] jest reprezentowana w [2] przez folder [java] [3B]
2.3. Plik konfiguracyjny [META-INF/context.xml]
![]() |
Plik ten ma następującą treść:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/exemple-01"/>
Wiersz 2 wskazuje, że kontekstem aplikacji internetowej jest /exemple-01. Wszystkie pliki typu [http://machine:port/exemple-01/...] o numerze URL będą przetwarzane przez tę aplikację. Kontekst ten można znaleźć we właściwościach projektu [2]: kliknij prawym przyciskiem myszy na projekcie / Properties / Run.
2.4. Plik konfiguracyjny [WEB-INF/web.xml]
![]() |
Każda aplikacja internetowa jest konfigurowana za pomocą pliku [web.xml] znajdującego się w folderze [WEB-INF] aplikacji. Wygenerowany plik ma następującą treść:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<URL-pattern>/*</URL-pattern>
</filter-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>example/HelloWorld.JSP</welcome-file>
</welcome-file-list>
</web-app>
- Wiersze 3–6 definiują filtr zaimplementowany przez klasę [org.apache.struts2.dispatcher.FilterDispatcher] w Struts 2. To właśnie ta klasa będzie pełnić rolę kontrolera C dla modelu MVC.
- Wiersze 7–10: definiują powiązanie między modelem URL a filtrem, który ma przetwarzać elementy URL zgodne z tym modelem. W tym miejscu wskazano, że każdy URL (szablon /*) musi być przetwarzany przez filtr o nazwie struts2. Jest to filtr zdefiniowany w wierszach 3–6. Zatem w tym przypadku wszystkie URL przejdą przez kontroler Struts 2.
- wiersze 11–15: określają czas trwania sesji użytkownika, w tym przypadku 30 minut. Podczas kolejnych żądań użytkownik jest identyfikowany za pomocą tokenu sesji, który został mu przypisany przy pierwszym żądaniu i który następnie wysyła on systematycznie przy każdym nowym żądaniu. Pozwala to serwerowi internetowemu rozpoznać użytkownika i zarządzać jego „pamięcią”, zwaną sesją. Jeśli między dwoma żądaniami upłynie więcej niż 30 minut, generowany jest nowy token sesji dla użytkownika, który w ten sposób traci swoją „pamięć” i rozpoczyna nową sesję.
- wiersze 16–18: określają plik, który ma zostać wyświetlony, gdy użytkownik wywołuje aplikację internetową bez żądania konkretnego dokumentu. Tak więc, gdy żądany plik URL to [http://machine:port/exemple-01], a serwowany plik to URL, wówczas serwowany plik będzie miał nazwę [http://machine:port/exemple-01/example/HelloWord.JSP].
2.5. Plik konfiguracyjny [struts.xml]
![]() |
Plik [struts.xml] jest plikiem konfiguracyjnym Struts 2. Może znajdować się w dowolnym miejscu w katalogu ClassPath projektu. W powyższym projekcie NetBeans katalog ClassPath projektu składa się z dwóch gałęzi:
- Pakiety źródłowe
- Biblioteki
Każdy folder lub biblioteka znajdująca się w tych dwóch gałęziach stanowi zatem część struktury projektu ClassPath. Zazwyczaj plik [struts.xml] umieszcza się w <default package>. W tym przypadku jego zawartość jest następująca:
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<include file="example.xml"/>
<!-- Konfiguracja dla pakietu domyślnego. -->
<package name="default" extends="struts-default">
</package>
</struts>
- wiersze 5 i 10: tagiem głównym dokumentu jest tag <struts>
- wiersz 6: w tym miejscu umieszczany jest plik [example.xml]. Przynosi on zatem własną konfigurację. Wrócimy do tego później.
- wiersze 8–9: definiują pakiet o nazwie „default”. Pakiet pozwala skonfigurować grupę akcji Struts 2 o tym samym identyfikatorze URL. Na przykład [/chemin/Action1] i [/chemin/Action2]. Można zatem zdefiniować pakiet dla tych akcji:
<package name="employes" namespace="/employes" extends="struts-default">
... configuration
</package>
Powyższy pakiet nosi nazwę „pracownicy” i konfiguruje działania URL oraz /employes/Action. Pakiet ten może dziedziczyć po innym pakiecie za pomocą słowa kluczowego „extends”. W powyższym przykładzie pakiet employes dziedziczy po pakiecie struts-default. Pakiet ten znajduje się w pliku [struts-default.xml] w bibliotece struts2-core.jar:
![]() |
Pakiet „struts-default” zdefiniowany w pliku [struts-default.xml] konfiguruje różne elementy, w tym listę interceptorów uruchamianych podczas wywołania akcji. Wróćmy do struktury MVC aplikacji Struts 2:
![]() |
Aby przetworzyć żądanie URL o postaci [http://machine:port/.../Action], kontroler [FilterDispatcher] utworzy instancję klasy implementującej żądaną akcję i wykona jedną z jej metod, domyślnie metodę o nazwie execute. Wywołanie tej metody execute przejdzie przez szereg interceptorów:
![]() |
Zarówno interceptory, jak i akcja przetwarzają to samo żądanie. Lista interceptorów zdefiniowana w pakiecie struts-default jest w większości przypadków wystarczająca. Dlatego nasze pakiety Struts będą zawsze rozszerzać pakiet struts-default. Aby dowiedzieć się, czym zajmują się poszczególne interceptory, należy zapoznać się z rozdziałem 4 dokumentu [ref2].
Wróćmy do pliku konfiguracyjnego struts.xml:
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<include file="example.xml"/>
<!-- Konfiguracja pakietu domyślnego. -->
<package name="default" extends="struts-default">
</package>
</struts>
- wiersz 8: pakiet o nazwie default pełni szczególną rolę. Obsługuje on działania, które nie zostały skonfigurowane w innych pakietach. Tutaj, w wierszach 8–9, nie ma żadnej konfiguracji dla pakietu default. Można by zatem usunąć definicję tego pakietu.
Przyjrzyjmy się teraz plikowi [example.xml] dołączonemu w wierszu 6 pliku [struts.xml]:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="example" namespace="/example" extends="struts-default">
<action name="HelloWorld" class="example.HelloWorld">
<result>/example/HelloWorld.JSP</result>
</action>
</package>
</struts>
- wiersz 8: definiuje pakiet o nazwie example, który rozszerza pakiet struts-default. Ten pakiet obsługuje pliki URL typu /example/Action (przestrzeń nazw /example).
- wiersze 9–11: definiują akcję o nazwie HelloWorld (atrybut name), która odpowiada zatem URL /example/Helloworld. Akcja ta jest przetwarzana przez instancję klasy example.HelloWorld (atrybut class).
- Kontroler [FilterDispatcher] spowoduje wywołanie metody execute tej klasy.
- Ta metoda zwróci ciąg znaków zwany kluczem nawigacyjnym.
- Poszczególne klucze nawigacyjne należy zdefiniować za pomocą tagów <result name="klucz"/> (wiersz 10). W przypadku braku atrybutu name domyślnie używany jest klucz success. Tak jest w powyższym przypadku. Zatem metoda execute klasy example.HelloWorld musi zwrócić kontrolerowi [FilterDispatcher] klucz success.
- Kontroler [FilterDispatcher] powoduje wówczas wyświetlenie strony [/example/HelloWorld.JSP] (wiersz 10).
Jeśli połączymy dwa pliki [struts.xml] i [example.xml], usuńmy pakiet default, który wydaje się zbędny, wszystko przebiega tak, jakby plik [struts.xml] został zredukowany do samego pliku [example.xml].
2.6. Akcja HelloWorld
![]() |
Zgodnie z analizowanym plikiem [struts.xml] akcja HelloWorld jest uruchamiana, gdy plik URL żądany przez klienta to /example/HelloWorld. Wówczas wykonywana jest jej metoda execute. Musi ona zwrócić klucz nawigacyjny. Widzieliśmy, że istnieje tylko jeden: success, a zatem w odpowiedzi wysyłana była do użytkownika strona /example/HelloWorld.JSP.
Kod akcji HelloWorld wygląda następująco:
- wiersz 5: klasa HelloWorld wywodzi się z klasy ActionSupport biblioteki Struts 2. Tak jest praktycznie zawsze. Pozwala to na korzystanie z niektórych metod, takich jak metoda getText w wierszu 8.
- wiersze 7–10: metoda execute, która jest wykonywana po wywołaniu kontrolera Struts. Wiadomo, że musi ona zwracać ciąg znaków, stąd jej sygnatura w wierszu 7. W tym przypadku zwraca ona stałą SUCCESS, zdefiniowaną również w ActionSupport. W ten sam sposób zdefiniowano inne stałe dla wyniku metody execute:
SUCCESS | „success” |
ERROR | „error” |
INPUT | „input” |
LOGIN | „login” |
W tym przypadku metoda execute zwraca ciąg znaków success. Jeśli odwołamy się do pliku [struts.xml], to w odpowiedzi użytkownikowi zostanie zwrócona strona /example/HelloWorld.JSP.
- wiersz 8: metoda execute inicjuje pole message z wiersza 14 wartością getText(" HelloWorld.message "). Metoda getText należy do klasy nadrzędnej ActionSupport. Pozwala ona na pobranie tekstu z pliku zgodnie z używanym językiem. Domyślnie zostanie tutaj użyty plik package.properties znajdujący się w tym samym pakiecie co akcja. Plik ten występuje w dwóch wersjach:
Plik [package.properties] ma następującą treść:
Jest to ciąg wierszy tekstu w formacie clé=valeur. Jeśli zostanie użyty ten plik, wartość getText("HelloWorld.message") będzie brzmiała: Struts is up and running ...
Plik [package_es.properties] ma następującą postać:
Jeśli językiem używanym przez przeglądarkę klienta jest hiszpański (atrybut „es” w pliku package_es.properties), plik getText („HelloWorld.message”) będzie zawierał wartość ¡Struts está bien! ... We wszystkich pozostałych przypadkach zostanie użyty plik [package.properties].
2.7. Widok HelloWorld.JSP
![]() |
To ostatni element układanki Struts. Jest to widok wyświetlany po wysłaniu żądania do pliku URL /example/HelloWorld. Widzieliśmy już, przez jaki labirynt przechodziło początkowe żądanie, aby w końcu wyświetlić tę odpowiedź. Kod strony wygląda następująco:
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:text name="HelloWorld.message"/></title>
</head>
<body>
<h2><s:property value="message"/></h2>
<h3>Languages</h3>
<ul>
<li>
<s:url id="URL" action="HelloWorld">
<s:param name="request_locale">en</s:param>
</s:url>
<s:a href="%{URL}">English</s:a>
</li>
<li>
<s:url id="URL" action="HelloWorld">
<s:param name="request_locale">es</s:param>
</s:url>
<s:a href="%{URL}">Espanol</s:a>
</li>
</ul>
</body>
</html>
- strona wykorzystuje tagi HTML (wiersze 5, 6, ...) oraz tagi z biblioteki zdefiniowanej w wierszu 3. Wszystkie tagi <s:xx> należą do tej biblioteki.
- wiersz 7: tag <s:text> pozwala wyświetlić inny tekst w zależności od języka przeglądarki klienta. Atrybut name wskazuje klucz, którego należy szukać w plikach komunikatów. Również w tym przypadku wykorzystywane będą pliki o nazwie package_xx.properties. Przypomnijmy, że zawierają one tylko jedną wiadomość o kluczu HelloWorld.message.
- wiersz 11: znacznik <s:property name="właściwość"> pozwala zapisać wartość właściwości obiektu o nazwie ActionContext. W obiekcie tym znajdują się:
- właściwości akcji, która została wykonana. name="message" wyświetli wartość pola „message” bieżącej akcji. Do uzyskania tej wartości lub jej zainicjowania wykorzystywane są metody get i set powiązane z tym polem. Metody te muszą zatem istnieć.
- atrybuty bieżącego żądania oznaczone jako <s:property name="#request['clé']">
- atrybuty sesji użytkownika oznaczone jako <s:property name="#session['clé']">
- atrybuty samej aplikacji oznaczone jako <s:property name="#application['clé']">
- parametry wysłane przez przeglądarkę klienta oznaczone jako <s:property name="#parameters['clé']">
- notacja <s:property name="#attr['clé']"> wyświetla wartość obiektu wyszukanego kolejno na stronie, w zapytaniu, sesji i aplikacji.
- wiersze 16–18: tag <s:url> służy do zdefiniowania URL. Atrybut id nadaje nazwę URL, który zostanie utworzony. Nazwa ta jest następnie wykorzystywana w wierszu 19. Atrybut action określa, do jakiej akcji ma prowadzić URL.
- wiersz 17: tag <s:param ..> pozwala na dodanie parametrów do URL w postaci ?param1=valeur1¶m2=valeur2&.... W tym przypadku dodanym parametrem będzie ?request_locale=es.
Ostatecznie wygenerowany plik URL będzie wyglądał następująco:
Aby zrozumieć ten kod URL, należy pamiętać, że strona [HelloWorld.JSP] jest wyświetlana w dwóch przypadkach:
- (ciąg dalszy)
- na bezpośrednie żądanie URL [/exemple-01/example/HelloWorld.JSP]
- na żądanie akcji [/exemple-01/example/HelloWorld.action]
W obu przypadkach ścieżka do strony URL to /exemple-01/example.. Tag <s:url action= „... ”> dodaje do tej ścieżki akcję zdefiniowaną przez atrybut action. W ten sposób otrzymujemy /example-01/example/HelloWorld. Następnie dodaje sufiks .action do poprzedniej ścieżki URL, a także parametry z URL, jeśli takie istnieją. Powstały plik URL /przykład-01/example/HelloWorld.action?request_locale=en wywoła akcję HelloWorld zdefiniowaną w pliku [struts.xml], przekazując parametr request_locale=en. Ten ostatni nie zostanie przetworzony przez akcję HelloWorld, lecz przez jeden z interceptorów Strutsa, ten, który zarządza internacjonalizacją stron. Parametr request_locale zostanie rozpoznany i przetworzony. Językiem stron stanie się angielski (en).
- wiersz 19: definiuje link HTML. Atrybut href tagu <a> oczekuje ciągu znaków. W tym przypadku chcemy użyć wartości URL zdefiniowanej w wierszu 16 oraz identyfikatora URL. W tym celu wpisujemy: href="%{URL}". Zmienna URL jest obliczana, a jej wartość przypisywana do atrybutu href. W większości przypadków obliczanie zmiennych odbywa się domyślnie. Na przykład, gdy piszemy
wyświetlana jest wartość właściwości message, a nie ciąg znaków „message”. Jednak w innych przypadkach konieczne jest wymuszenie obliczenia zmiennych lub właściwości. Gdybyśmy napisali href= „URL”, to do atrybutu href zostałby przypisany ciąg znaków URL.
- wiersze 23–27: tworzą link HTML służący do zmiany języka stron na hiszpański.
2.8. Uruchomienie aplikacji
Uruchamiamy projekt:
![]() |
- w [1] uruchamiamy projekt [exemple-01]. Serwer WWW Tomcat uruchamia się wówczas automatycznie, o ile nie był już uruchomiony. Żądanie dotyczące URL, [/exemple-01] i [3] jest wysyłane. Wykorzystywany jest wówczas plik [web.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<URL-pattern>/*</URL-pattern>
</filter-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>example/HelloWorld.JSP</welcome-file>
</welcome-file-list>
</web-app>
- ponieważ w żądanym pliku URL [/exemple-01] nie podano żadnej strony, Tomcat wykorzysta znacznik <welcome_file-list> z wierszy 16 i 18. W związku z tym zostanie wyświetlona strona URL /exemple-01/example/HelloWorld.JSP.
- Ponieważ Struts 2 przetwarza wszystkie URL (wiersze 8 i 9), ten URL zostanie odfiltrowany przez Struts. Ponieważ nie odpowiada on akcji, ale stronie JSP, to właśnie ta strona zostanie wyświetlona.
- To, co widzimy w [2], to zatem strona HelloWorld.JSP, którą analizowaliśmy.
- W [4] widać, że ani tag <title><s:text name="HelloWorld.message"/></title>, ani tag <h2><s:property value="message"/></h2> nie zadziałały. Powodem tego jest fakt, że nie wywołano żadnej akcji. Lista interceptorów uruchamianych przed akcją nie została zatem wykonana, w szczególności ten, który obsługuje internacjonalizację. Znacznik internacjonalizacji <s:text ...> nie mógł zostać poprawnie przetworzony. Ponadto nie istnieje właściwość message, która odwołuje się do pola „message” klasy Action1. Stąd brak wyświetlenia.
Teraz przejdźmy do linku [English]. Otrzymujemy następującą stronę:
![]() |
- w [1], czyli żądanym URL. Wyjaśniliśmy już, jak powstaje ten adres URL. Tym razem żądana jest akcja Struts: akcja HelloWorld zdefiniowana w [example.xml].
<struts>
<package name="example" namespace="/example" extends="struts-default">
<action name="HelloWorld" class="example.HelloWorld">
<result>/example/HelloWorld.JSP</result>
</action>
</package>
</struts>
- wykonano metodę execute tej akcji. Widzieliśmy, że zwracała ona klucz success. Z wiersza 4 powyżej wynika, że strona /example/HelloWorld.JSP jest zwracana klientowi w odpowiedzi. Widzimy to w [3].
- W [1] widzimy, że żądana strona URL jest skonfigurowana za pomocą parametru request_locale=en. Językiem stron będzie odtąd angielski. Wybór języka jest bowiem zapisywany w sesji użytkownika, który zachowa ten wybór do momentu jego zmiany.
- W tagach [2] i [3] widać, jak działa internacjonalizacja. Tagi <title><s:text name="HelloWorld.message"/></title> oraz <h2><s:property value="message"/></h2> tym razem zadziałały.
Jeśli teraz wybierzemy link [Espanol], otrzymamy stronę w języku hiszpańskim:
![]() |
2.9. Conclusion
Przeanalizowaliśmy przykład wygenerowany automatycznie przez wtyczkę Struts 2 dla NetBeans. Stwierdziliśmy, że dość trudno jest prześledzić przebieg przetwarzania żądania. W grę wchodzą następujące elementy:
- konfiguracja [web.xml], [struts.xml]
- wykonana akcja: interceptory i metoda execute.
Na początku mechanizm działania Struts 2 może wydawać się skomplikowany. Przyzwyczajenie się do niego zajmuje trochę czasu. Teraz przedstawimy serię przykładów, z których każdy wyjaśnia konkretny aspekt Struts.















