2. Перший приклад
Більшість наших прикладів обмежуватимуться лише веб-шаром, реалізованим за допомогою Struts 2:
![]() |
Коли ми засвоїмо основи, ми розглянемо більш складний приклад із багатошаровою архітектурою.
2.1. Створення прикладу
Створюємо наш перший додаток.
![]() |
- у [1] створюємо новий проєкт
- у [2] вибираємо тип Java Web / Web Application
- у [3] даємо назву проекту
- у [4] вказуємо місце розташування проекту.
- у [5] новий проєкт робимо головним.
![]() |
- у [6] вибираємо сервер Tomcat. Під час встановлення NetBeans 7.01 було встановлено два сервери: Apache Tomcat та Glassfish 3.1.
- У [7] вказуємо, що будемо працювати з фреймворком Struts 2. Цю можливість надає нам встановлення плагіна для Struts 2. Без цього плагіна фреймворк Struts 2 нам не пропонується.
- У [8] пропонується створити приклад проекту, який ми будемо вивчати.
- У [9] можна перевірити, які бібліотеки Struts 2 будуть використовуватися.
![]() |
- у [10] — згенерований проєкт. Ми ще до нього повернемося.
- у файлі [11] — бібліотеки проекту. Вони були інтегровані плагіном Struts 2. Якщо плагіна немає, ці бібліотеки можна знайти в папці [lib] завантаженого дистрибутива Struts 2. У такому разі слід виконати кроки 12 та 13.
2.2. Проєкт, згенерований у файловій системі
![]() |
- у [1], на вкладці [Projects] відображається «розробницький» вигляд проекту
- у [2] на вкладці [Files] відображається папка проєкту у файловій системі
- у [2A] гілка [Web Pages] представлена у [2] папкою [web] [2B]
- у [3A] гілка [Source Packages] представлена у [2] папкою [java] [3B]
2.3. Файл конфігурації [META-INF/context.xml]
![]() |
Цей файл має такий вигляд:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/exemple-01"/>
У рядку 2 вказано, що контекст веб-додатка — /exemple-01. Усі файли типу [http://machine:port/exemple-01/...] із номером URL оброблятиметься цим додатком. Цей контекст можна знайти у властивостях проєкту [2]: клацніть правою кнопкою миші на проєкті / Properties / Run.
2.4. Файл конфігурації [WEB-INF/web.xml]
![]() |
Кожен веб-додаток налаштовується за допомогою файлу [web.xml], що міститься в папці [WEB-INF] цього додатка. Був згенерований такий файл:
<?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>
- рядки 3–6 визначають фільтр, реалізований класом [org.apache.struts2.dispatcher.FilterDispatcher] з Struts 2. Саме цей клас виконуватиме роль контролера C для моделі MVC.
- Рядки 7–10: визначають зв’язок між моделлю URL та фільтром, який має обробляти URL, що відповідають цій моделі. Тут вказано, що всі запити URL (шаблон /*) мають оброблятися фільтром із назвою struts2. Це той самий фільтр, який визначено в рядках 3–6. Отже, тут усі запити URL проходитимуть через контролер Struts 2.
- рядки 11–15: визначають тривалість сеансу користувача, у даному випадку — 30 хвилин. Під час різних запитів користувач відстежується за допомогою сесійного токена, який йому було присвоєно під час першого запиту і який він потім систематично надсилає з кожним новим запитом. Це дозволяє веб-серверу розпізнавати користувача та керувати його «пам’яттю», яку називають сесією. Якщо між двома запитами минає більше 30 хвилин, для користувача генерується новий сесійний токен, і він втрачає свою «пам’ять», починаючи все спочатку.
- рядки 16–18: визначають файл, який має відображатися, коли користувач звертається до веб-додатку, не запитуючи жодного документа. Таким чином, коли запитується файл URL, а натомість надається файл [http://machine:port/exemple-01], то замість URL буде надано файл [http://machine:port/exemple-01/example/HelloWord.JSP].
2.5. Конфігураційний файл [struts.xml]
![]() |
Файл [struts.xml] є файлом конфігурації Struts 2. Він може знаходитися в будь-якому місці в ClassPath проекту. У наведеному вище проекті NetBeans ClassPath проекту складається з двох гілок:
- Пакетів джерел
- Бібліотеки
Отже, будь-яка папка чи бібліотека, розташована в цих двох гілках, є частиною ClassPath проекту. Зазвичай [struts.xml] розміщують у <default package>. Тут його вміст такий:
<!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"/>
<!-- Конфігурація для пакета за замовчуванням. -->
<package name="default" extends="struts-default">
</package>
</struts>
- рядки 5 та 10: кореневим тегом документа є тег <struts>
- рядок 6: файл [example.xml] вставляється саме тут. Отже, він приносить із собою власну конфігурацію. Ми повернемося до цього пізніше.
- рядки 8–9: визначають пакет, який тут має назву «default». Пакет дозволяє налаштувати групу дій Struts 2, що мають однаковий URL. Наприклад, [/chemin/Action1] та [/chemin/Action2]. Тож для цих дій можна визначити пакет:
<package name="employes" namespace="/employes" extends="struts-default">
... configuration
</package>
Вищезазначений пакет називається «employes» і налаштовує дії URL та /employes/Action. Пакет може успадковувати інший пакет за допомогою ключового слова «extends». У наведеному вище прикладі пакет employes успадковує пакет struts-default. Цей пакет міститься у файлі [struts-default.xml] бібліотеки struts2-core.jar:
![]() |
Пакет «struts-default», визначений у файлі [struts-default.xml], налаштовує різні параметри, зокрема список інтерцепторів, що виконуються під час виклику дії. Повернімося до структури MVC додатка Struts 2:
![]() |
Для обробки запиту URL у формі [http://machine:port/.../Action] контролер [FilterDispatcher] створить екземпляр класу, що реалізує запитувану дію, і виконає один із його методів — за замовчуванням метод із назвою execute. Виклик цього методу execute пройде через низку перехоплювачів:
![]() |
Всі інтерцептори та дія обробляють один і той самий запит. Переліку перехоплювачів, визначеного в пакеті struts-default, у більшості випадків достатньо. Тому наші пакети Struts завжди будуть успадковувати пакет struts-default. Щоб дізнатися, що роблять різні перехоплювачі, слід ознайомитися з розділом 4 файлу [ref2].
Повернемося до файлу конфігурації 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"/>
<!-- Конфігурація для пакета за замовчуванням. -->
<package name="default" extends="struts-default">
</package>
</struts>
- рядок 8: пакет із назвою default відіграє особливу роль. Він обробляє дії, які не були налаштовані в інших пакетах. Тут, у рядках 8–9, для пакета default не зроблено жодних налаштувань. Отже, визначення цього пакета можна видалити.
Тепер розглянемо файл [example.xml], включений у рядку 6 файлу [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>
- рядок 8: визначає пакет із назвою example, який розширює пакет struts-default. Цей пакет керує URL типу /example/Action (простір імен /example).
- рядки 9–11: визначають дію з назвою HelloWorld (атрибут name), яка, отже, відповідає URL та /example/Helloworld. Ця дія обробляється екземпляром класу example.HelloWorld (атрибут class).
- Контролер [FilterDispatcher] запустить метод execute цього класу.
- Цей метод поверне рядок символів, який називається ключем навігації.
- Різні ключі навігації повинні бути визначені за допомогою тегів <result name="ключ"/> (рядок 10). За відсутності атрибута name за замовчуванням використовується ключ success. Саме це відбувається у наведеному вище прикладі. Отже, метод execute класу example.HelloWorld повинен повернути контролеру [FilterDispatcher] ключ success.
- Контролер [FilterDispatcher] потім відображає сторінку [/example/HelloWorld.JSP] (рядок 10).
Якщо об’єднати два файли [struts.xml] та [example.xml] і видалити пакет default, який здається непотрібним, то все відбувається так, ніби файл [struts.xml] було скорочено до єдиного файлу [example.xml].
2.6. Дія HelloWorld
![]() |
Згідно з проаналізованим файлом [struts.xml], дія HelloWorld запускається, коли запитуваний клієнтом файл URL має ім’я /example/HelloWorld. Тоді виконується її метод execute. Він повинен повернути ключ навігації. Ми бачили, що існував лише один такий ключ: success, і тоді у відповідь користувачеві надсилалася сторінка /example/HelloWorld.JSP.
Код дії HelloWorld такий:
- рядок 5: клас HelloWorld походить від класу ActionSupport з Struts 2. Так буває практично завжди. Це дозволяє скористатися певними методами, такими як метод getText у рядку 8.
- рядки 7–10: метод execute, який виконується при виклику контролера Struts. Відомо, що він повинен повертати рядок символів, звідки й походить його сигнатура у рядку 7. Тут він поверне константу SUCCESS, яка також визначена у ActionSupport. Інші константи визначені таким чином для результату методу execute:
SUCCESS | «success» |
ERROR | "error" |
INPUT | "input" |
LOGIN | "login" |
Отже, у цьому випадку метод execute повертає рядок success. Якщо звернутися до файлу [struts.xml], то у відповідь користувачеві буде повернуто сторінку /example/HelloWorld.JSP.
- рядок 8: метод execute ініціалізує поле message у рядку 14 значенням getText(" HelloWorld.message "). Метод getText належить до батьківського класу ActionSupport. Він дозволяє отримати текст із файлу відповідно до використовуваної мови. За замовчуванням тут буде використано файл package.properties, розташований у тому самому пакеті, що й дія. Він існує у двох версіях:
Файл [package.properties] має такий вигляд:
Це послідовність рядків тексту у форматі clé=valeur. Якщо використовувати цей файл, значення getText("HelloWorld.message") буде таким: Struts is up and running ...
Файл [package_es.properties] має такий вигляд:
Якщо мовою, яку використовує браузер клієнта, є іспанська (атрибут es у package_es.properties), getText("HelloWorld.message") матиме значення ¡Struts está bien! ... У всіх інших випадках буде використано файл [package.properties].
2.7. Вигляд HelloWorld.JSP
![]() |
Це останній елемент пазлу Struts. Саме цей вигляд відображається, коли надходить запит на URL /example/HelloWorld. Ми побачили, яким лабіринтом пройшов початковий запит, щоб зрештою відобразити цю відповідь. Код сторінки такий:
<%@ 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>
- сторінка використовує HTML-теги (рядки 5, 6, ...) та теги з бібліотеки, визначеної у рядку 3. Усі теги <s:xx> належать до цієї бібліотеки.
- рядок 7: тег <s:text> дозволяє відображати різний текст залежно від мови браузера клієнта. Атрибут name вказує ключ, який потрібно шукати у файлах повідомлень. Тут також будуть використовуватися файли package_xx.properties. Нагадаємо, що вони містять лише одне повідомлення з ключем HelloWorld.message.
- рядок 11: тег <s:property name="властивість"> дозволяє записати значення властивості об’єкта з назвою ActionContext. У цьому об’єкті містяться:
- властивості дії, яка була виконана. name="message" відобразить значення поля message поточної дії. Для отримання або ініціалізації цього значення використовуються методи get та set, пов’язані з цим полем. Отже, ці методи мають існувати.
- атрибути поточного запиту, позначені як <s:property name="#request['clé']">
- атрибути сеансу користувача, позначені як <s:property name="#session['clé']">
- атрибути самої програми, позначені як <s:property name="#application['clé']">
- параметри, надіслані клієнтським браузером, позначені як <s:property name="#parameters['clé']">
- запис <s:property name="#attr['clé']"> відображає значення об’єкта, який шукається на сторінці, у запиті, сесії та додатку в такому порядку.
- рядки 16–18: тег <s:url > слугує для визначення URL. Атрибут id присвоює ім’я URL, що буде створено. Це ім’я потім використовується у рядку 19. Атрибут action вказує, на яку дію має вказувати URL.
- рядок 17: тег <s:param ..> дозволяє додавати параметри до URL у формі ?param1=valeur1¶m2=valeur2&.... У цьому випадку доданий параметр матиме вигляд ?request_locale=es.
У підсумку згенерований файл URL матиме такий вигляд:
Щоб зрозуміти цей URL, слід пам’ятати, що сторінка [HelloWorld.JSP] відображається у двох випадках:
- (продовження)
- за прямим запитом URL [/exemple-01/example/HelloWorld.JSP]
- за запитом дії [/exemple-01/example/HelloWorld.action]
В обох випадках шлях до URL — це /exemple-01/example.. Тег <s:url action= "... "> додає до цього шляху дію, визначену атрибутом action. Таким чином отримуємо /example-01/example/HelloWorld. Потім вона додає суфікс .action до попереднього URL, а також параметри URL, якщо такі є. Отриманий файл URL /example-01/example/HelloWorld.action?request_locale=en викликає дію HelloWorld, визначену у файлі [struts.xml], передаючи при цьому параметр request_locale=en. Останній не буде оброблений дією HelloWorld, а одним із перехоплювачів Struts, який відповідає за інтернаціоналізацію сторінок. Параметр request_locale буде розпізнано та оброблено. Мовою сторінок стане англійська (en).
- рядок 19: визначає HTML-посилання. Атрибут href тегу <a> очікує на рядок символів. Тут ми хочемо використати значення URL, визначене в рядку 16, та id URL. Для цього записуємо: href="%{URL}". Змінна URL обчислюється, і її значення присвоюється атрибуту href. У більшості випадків обчислення змінних відбувається неявно. Наприклад, коли ми пишемо
, відображається значення властивості message, а не рядок «message». Але в інших випадках потрібно примусово обчислювати змінні або властивості. Якби ми написали href= "URL", то атрибуту href було б присвоєно рядок URL.
- рядки 23–27: створюють HTML-посилання для зміни мови сторінок на іспанську.
2.8. Виконання програми
Запускаємо виконання проєкту:
![]() |
- у [1] запускається виконання проекту [exemple-01]. Веб-сервер Tomcat запускається автоматично, якщо він ще не запущений. Запитується URL [/exemple-01] [3]. Тоді використовується файл [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>
- оскільки у запитуваному URL [/exemple-01] не вказано жодної сторінки, Tomcat використає тег <welcome_file-list> із рядків 16 та 18. Отже, буде видано URL /exemple-01/example/HelloWorld.JSP.
- Оскільки Struts 2 обробляє всі URL (рядки 8 і 9), цей URL буде відфільтрований Struts. Оскільки він відповідає не дії, а сторінці JSP, то саме ця сторінка буде відображена.
- Отже, те, що ми бачимо в [2], — це сторінка HelloWorld.JSP, яку ми вже розглядали.
- У [4] ми бачимо, що тег <title><s:text name="HelloWorld.message"/></title> не спрацював, як і тег <h2><s:property value="message"/></h2>. Причина в тому, що жодна дія не була викликана. Отже, список перехоплювачів, які виконуються перед дією, не був виконаний, зокрема той, що відповідає за інтернаціоналізацію. Тег інтернаціоналізації <s:text ...> не вдалося обробити належним чином. Крім того, властивість message, яка посилається на поле message класу Action1, не існує. Звідси й відсутність відображення.
Тепер перейдемо за посиланням [English]. Ми потрапляємо на таку сторінку:
![]() |
- у [1], замість запитуваного URL. Ми вже пояснювали, як утворюється цей URL. Цього разу запитується дія Struts: дія HelloWorld, визначена в [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>
- був виконаний метод execute цієї дії. Ми бачили, що вона повертала ключ success. З рядка 4 вище можна зробити висновок, що сторінка /example/HelloWorld.JSP повертається клієнту у відповідь. Саме це ми бачимо в [3].
- У [1] ми бачимо, що запитувана сторінка URL налаштована за допомогою параметра request_locale=en. Відтепер мовою сторінок буде англійська. Цей вибір мови зберігається в сесії користувача, і він залишатиметься чинним, доки користувач його не змінить.
- У тегах [2] та [3] можна побачити, як працює інтернаціоналізація. Теги <title><s:text name="HelloWorld.message"/></title> та <h2><s:property value="message"/></h2> цього разу спрацювали.
Якщо тепер вибрати посилання [Espanol], ми отримаємо сторінку іспанською мовою:
![]() |
2.9. Conclusion
Ми розглянули приклад, автоматично згенерований плагіном Struts 2 для NetBeans. Ми виявили, що відстежити обробку запиту було досить складно. У цьому процесі беруть участь такі елементи:
- конфігурація [web.xml], [struts.xml]
- виконана дія: інтерцептори та метод execute.
Спочатку механізм роботи Struts 2 може здатися складним. Щоб звикнути до нього, потрібно трохи часу. Тепер ми наведемо серію прикладів, кожен з яких проілюструє певний аспект роботи Struts.















