Книга 1 - Начальные сведения
Книга 2 - Более профессиональный подход |
Студенческий отдел кадров Много страниц или даже очень много страниц
Struts наверно был самым первым фреймворком, с которым я стал знакомиться при погружении в мир Web для Java.
Если честно, то мне это доставило немало неприятных минут (даже часов/дней). Причина в том, что к тому моменту
я сам слабо представлял себе все, что связано с Web-программированием на Java (подозреваю, что
те, кто добрался до этой статьи понимают уже гораздо больше). Да и документация Struts до сих пор не блещет.
Сейчас уже появилось немало книг, но тогда, в 2002 году это была проблема. Ну да ладно - в итоге я все-таки
разобрался и теперь могу свои знания оформить в виде статьи.
Прежде чем углубиться непосредственно в сам Struts я бы хотел, чтобы вы попробовали "увидеть" архитектуру Web-приложения. /MyApp/MyCommand?param1=value1¶m2=value2
Теперь наша задача состоит в том, чтобы из этого запроса получить данные и поместить их в удобную структуру. Здорово бы сразу
в класс с таким набором полей, который напоминает набор пришедших параметров. Вторым совершенно логичным шагом будет проверить
наши данные - может они содержат какие-то ошибки или просто опасны для обработки. Т.к. наше приложение смотрит в большой мир,
желающих прислать не то будет гораздо больше. Учтите это обстоятельство. Далее следует вызов обработчика данных.
Определение класса, который будет обрабатывать наш запрос обычно происходит путем анализа URL - в нем муогут быть например
специальные слова. Если вспомните Spring, то там использовался UrlHandlerMapping. Наш класс все обработал (при этом он может
обратиться к нашему Business Layer - у нас же там есть все необходимые функции), получил какие-то данные и теперь нам предстоит
заключительный шаг - надо решить куда мы направляемся или другими словами - на какую JSP страницу нам идти и как удобно данные
поместит на нее.
Где скачать и библиотеки
Скачать Struts можно на сайте Struts. Распакуйте ZIP-файл в какой-нибудь
каталог. Что касается библитек, то можно просто скопировать все, что находится в подкаталоге
lib. Разве что jstl-1.0.2.jar можно не брать.
Мой каталог с библиотеками включает больше, чем надо для нашего примера.
Здесь вы видите и Hibernate, и Spring, но так уж сложилось и оптимизировать список я не вижу смысла.
antlr-2.7.6.jar Алгоритм работы и составные части StrutsКак я уже отмечал, Struts решает проблему организации запросов от пользователя, получения данных и отображения. По сути - это уже известный нам шаблон MVC (Model, View, Controller). В качестве контроллера выступает ActionServlet, который по заданной конфигурации (чуть позже мы ее разберем) обрабатывает данные от клиента, помещает их в объект определенного класса (а точнее в наследника от Action Form) и вызывает подходящий обработчик этого события - наследника от класса Action. Обработчик должен выполнить какие-то действия, заполнить данные и вернуть некоторый статус, характеризующий насколько удачно все выполнилось. По данному статусу происходит поиск той JSP-страницы, которая будет показана пользователю. И данные, которые возможно будут собраны обработчиком, будут отображены на этой странице. Дальше цикл повторяется - ActionServlet получит и предварительно соберет данные в контейнер-наследник от ActionForm, вызовет обработчик (наследник Action), получит от него статус, по статусу вызовет нужную JSP. Вуаля. В этой части мы напишем очень простое приложение, которое запросит у пользователя ID специальности и по этому ID найдет название. В базу данных мы ходить не будем, поэтому поиск будет достаточно формальным - мы просто пропишем его в нашем классе. В случае неудачного поиска попросим пользователя снова ввести данные. Итак, давайте еще раз подробнее рассмотрим составные части Struts. Я выделил 5 основные частей:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" 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_2_5.xsd"> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <session-config> <session-timeout>30</session-timeout> </session-config> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <taglib> <taglib-uri>/WEB-INF/tld/struts-html.tld</taglib-uri> <taglib-location>/WEB-INF/tld/struts-html.tld</taglib-location> </taglib> </web-app> Думаю, что что-то сверхудивительное вы не увидели. Отметим только параметр config в описании ActionServlet. Он указывает на файл конфигурации, который нам надо рассмотреть более подробно. Также отметим, что сервлет обрабатывает запросы, которые оканчиваются на .do <?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN" "http://struts.apache.org/dtds/struts-config_1_3.dtd"> <struts-config> <form-beans> <form-bean name="lookupForm" type="students.web.LookupForm"/> </form-beans> <action-mappings> <action path="/Lookup" type="students.web.LookupAction" name="lookupForm" validate="true" input="/index.jsp"> <forward name="success" path="/result.jsp"/> <forward name="failure" path="/index.jsp"/> </action> </action-mappings> <message-resources parameter="students.ApplicationResources"/> </struts-config>
Наш конфигурационный файл состоит из нескольких частей. Первая описана тэгом form-beans.
Она включает в себя описание всех классов, которые наследуются от ActionForm. Как я уже говорил,
задача этих классов служить контейнерами для хранения данных пришедших со страниц. В описании мы указываем имя
lookupForm под которым к этом классу можно будет обратиться и название класса.
Кроме этого можно указать несколько атрибутов, но для понимания сути это пока несущественно.
Ну и наконец тэг message-resources. Он служит для описания файла, в котором находятся строковые константы. Это удобный механизм интернационализации и упрощения использавония строк. Вопросы связанные с этим файлов мы рассмотрим в свое время. Рассмотрев вкратце вопросы конфигурации обратим наше внимание на "сладкую парочку" - Action/ActionForms. Сначала рассмотрим LookupForm. Как уже говорилось этот класс служит контейнером для параметров. Но кроме этого он может иметь функцию проверки (для этого он реализует метод validate. И функцию инициализации данных - reset. package students.web; import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionMessage; public class LookupForm extends ActionForm { private String professionId = null; private String professionName = null; public String getProfessionId() { return professionId; } public void setProfessionId(String professionId) { this.professionId = professionId; } public String getProfessionName() { return professionName; } public void setProfessionName(String professionName) { this.professionName = professionName; } @Override public void reset(ActionMapping mapping, HttpServletRequest request) { this.professionId = null; } @Override public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { ActionErrors ae = new ActionErrors(); if (professionId == null || professionId.trim().isEmpty()) { ae.add("professionId", new ActionMessage("errors.lookup.symbol.required")); } return ae; } }
В классе указано всего лишь два свойства - professionId и
professionName. Надо обратить внимание, что для каждого свойства есть пара
методов set/get. Теперь давайте рассмотрим файл LookupAction.java. В нем сосредоточена логика обработки нашего выдающегося запроса. Ключевым методом является execute. Именно в нем происходит вся логика. package students.web; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.struts.action.Action; import org.apache.struts.action.ActionErrors; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; import org.apache.struts.action.ActionMessage; public class LookupAction extends Action { protected String getProfession(Long professionId) { if (professionId.equals(1L)) { return "Chemist"; } return null; } @Override public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String profession = null; // Получаем нашу форму-контейнер с данными LookupForm lookupForm = (LookupForm) form; // Устанавливаем ответ по умолчанию String target = "success"; Long professionId = null; if (form != null) { try { professionId = Long.parseLong(lookupForm.getProfessionId()); profession = getProfession(professionId); } catch (Exception e) { } } // И формируем ответ в случае неудачи if (profession == null) { target = "failure"; ActionErrors errors = new ActionErrors(); errors.add(ActionErrors.GLOBAL_MESSAGE, new ActionMessage("errors.lookup.unknown", professionId)); saveErrors(request, errors); } else { lookupForm.setProfessionName(profession); } // Переходим на нужную страницу в зависимости от установленного target return (mapping.findForward(target)); } } Код в методе execute достаточно прозрачный - мы приводим переданный объект типа ActionForm к нашему типу, потом ищем профессию по указанному ID (у нас их немного) и потом формируем ответ - куда идти. Надеюсь, что основная концепция Struts уже более менее понятна. Нам осталось немного - посмотреть на тэги Struts, которые облегчают нам жизнь. У нас всего две страницы - index.jsp и result.jsp. Давайте посмотрим на них. Итак, index.jsp <%@page contentType="text/html" pageEncoding="UTF-8" language="java" %> <%@taglib uri="/WEB-INF/tld/struts-html.tld" prefix="html" %> <html> <head> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Struts Example Page</title> </head> </head> <body> <html:errors/> <html:form action="Lookup"> <table> <tr> <td>Symbol:</td> <td><html:text property="professionId" /></td> </tr> <tr> <td colspan="2" align="center"><html:submit /></td> </tr> </table> </html:form> </body> </html>
Как видите, здесь мы используем тэги Struts - они помечены префиксом html.
На полном и подробном описании мы пока не будем останавливаться, но очень советую
вам почитать о них - описание есть на официальном сайте. И достаточно неплохое.
Файл result.jsp еще проще. <%@page contentType="text/html" pageEncoding="UTF-8" language="java" %> <%@taglib uri="/WEB-INF/tld/struts-bean.tld" prefix="bean" %> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Struts Example Page</title> </head> <body> <table> <tr> <td>Profession : </td> <td><bean:write name="lookupForm" property="professionName"/></td> </tr> </table> </body> </html> Здесь мы используем тэг bean:write, который позволяет нам вывести свойство определенного класса. На класс указывает атрибут name а атрибут property указывает на свойство класса. На этом наше первое и очень поверхностное знакомство с пакетом Struts 1.x будем считать состоявшимся. Впереди нас ждут еще некоторые возможности этого заслуженного пакета. Архив с исходными кодами: Исходный код |