Теперь, когда вы понимаете, как создавать компоненты
Model и View, настало время разобраться с компонентами Controller.
Struts включает в себя сервлет, который реализует основную функцию
сопоставления URI запроса и соответствующего Action-класса.
Следовательно, существуют следующие основные правила связанные с
реализацией Controller:
написать Action-класс
для каждого логического запроса, который может быть получен (этот
класс должен расширять org.apache.action.Action)
Сконфигурировать
ActionMapping (в виде XML) для каждого логического запроса,
который может быть послан (submit). XML-конфигурационный файл
обычно называется struts-config.xml.
Обновить файл описания
для приложения (web application deployment descriptor file) в
виде XML для вашего приложения и включить туда необходимые
компоненты Struts
Добавить соответствующие компоненты Struts в ваше
приложение
Большинство проектов используют только версию с
"HttpServletRequest".
Целью класса Action является обработка запроса, с
помощью его метода perform(), и возвращение объекта ActionForward,
который определяет, куда должно быть передано управление (т.е. на
какую JSP) , чтобы обеспечить соответствующий запросу ответ. В
паттерне проектирования MVC/Model 2 типичный класс Action
будет реализовывать в своем методе perform() следующую логику:
Проверять текущее
состояние пользовательской сессии (например, проверка того, что
пользователь успешно вошел в систему). Если класс Action
обнаружит, что пользователь не вошел в систему, то запрос может
быть перенаправлен на JSP-страницу, которая просит ввести имя
пользователя и пароль для входа в систему. Такая ситуация может
возникнуть, если пользователь пытается войти в приложение “в
середине” (например, через закладку), или потому что сессия
закончилась, и сервлет-контейнер создает новый экземпляр сессии.
Если проверка не
закончена, то при необходимости проверить свойства form bean.
Если обнаружены какие то проблемы, то сохранить соответствующие
ключи сообщений об ошибке как параметр запроса, и затем вернуть
управление обратно на форму ввода с тем, чтобы найденные ошибки
во вводе могли быть исправлены.
Выполнить обработку,
требуемую для этого запроса (например, сохранить строку в базу
данных). Это может быть сделано с помощью логики, непосредственно
реализованной внутри Action, но обычно выполняется путем вызова
соответствующего вызова Bean бизнес-логики.
Обновить объекты на
стороне сервера, котоыре будут использоваться для создания
следующей страницы пользовательского интерфейса (обычно beans в
контексте запроса или сессии, в зависимости от того, как долго вы
желаете хранить эти объекты) .
Вернуть
соответствующий объект ActionForward, который определяет
JSP-страницу, которая будет использована для генерации этого
ответа, основываясь на обновленных beans. Обычно, вы получаете
ссылку на такой объект путем вызова findForward() либо из объекта
ActionMapping, который вы получили (в случае, если используется
логическое имя, локальное для данного mapping), либо из
сервлет-контроллера (если вы используете логическое имя,
глобальное для всего приложения).
Важные вещи, о которых нужно помнить во время
кодирования Action-классов, включают следующее:
Сервлет-контроллер
создает только один экземпляр вашего Action-класса, и использует
ее для всех запросов. Таким образом, вы должны кодировать ваш
Action-класс так, чтобы он корректно работал в многопоточной
среде, также как вы должны кодировать метод сервлета service().
Наиболее важным
принципом, который следует соблюдать для получения
потоко-безопасного (thread-safe) кода, является использование
только локальных переменных, а не переменных экземпляра класса, в
ваших Action-классах. Локальные переменные создаются в стеке,
который присваиваются (вашей JVM) каждому потоку, обслуживающему
запрос, поэтому можно не беспокоится об их совместном
использовании.
Beans, которые
представляют Model вашей системы, могут возбуждать исключения
из-за проблем доступа у БД или к другим ресурсам. Необходимо
отлавливать все такие исключения в бизнес-логике внутри вашего
метода perform(), и протоколировать их в log (протоколе)
приложения (вместе с соответствующим stack trace) вызывая
servlet.log("Error message text", exception);
В качестве общей
рекомендации, можно отметить, что выделение дефицитного ресурса и
удержание его в течение нескольких запросов одного и то же
пользователя (в пользовательской сессии) может привести к
проблемам с масштабируемостью. Старайтесь освобождать ресурсы
(такие, как соединения с базой данных) прежде чем передавать
управление к соответствующему компоненту VIEW – даже если
метод bean который вы должны вызвать, возбуждает исключение.
Кроме того, вы должны
стараться избегать слишком больших классов Action.
Проще всего получить такие классы это встраивать функциональную
логику прямо в Action-классы, а не кодировать их отдельных bean'ах
бизнес-логики. Помимо того, что такой подход делает Action-классы
трудными для восприятия и поддержки, он также усложняет повторное
использование кода бизнес-логики, так как он внедрен внутрь
компонента (т.е. Action-класса), который привязан к web-среде
выполнения.
Action класс может быть
разложен на несколько локальных методов, так чтобы все необходимые
свойства передавались через сигнатуры методов. JVM обрабатывает
такие свойства с использованием стека, и поэтому они являются
thread-safe.
Пример приложения в поставке Struts не следует этому
принципу, так как бизнес-логика реализована внутри Action-классов.
Это должно рассматриваться как ошибка в дизайне примера, а не
свойство, присущее архитектуре Struts, или как образец для
подражания.
4.3
Реализация ActionMapping
Для того чтобы успешно работать, контроллеру Struts
необходимо знать о том, как каждый запрашиваемый URI должен быть
отображен на соответствующие Action-классы. Требуемые знания были
инкапсулированы в Java-интерфейсе под названием ActionMapping,
наиболее важные свойства которого следующие:
type –
полное имя Java-класса, реализующего Action для данного mapping
name –
имя form bean, которое будет использоваться данным Action, причем
это логическое имя bean, которое определено в конфигурационном
файле
path –
URI запроса, который сопоставлен данному mapping. Ниже приведены
примеры того, как работает сопоставление.
unknown –
булево свойство. Устанавливайте его в true, если action для
данного mapping является точкой входа в приложение по умолчанию,
и должен обрабатывать все запросы, которые не обработаны другим
action. Только один Action может быть объявлен как точка входа по
умолчанию внутри одного приложения.
validate –
установите это булево свойство в true, если нужно вызывать метод
validate() в action, ассоциированным с данным mapping.
forward – URI запроса, на который
должно быть передано управление, когда вызывается его mapping.
Это альтернатива объявлению свойства type.
4.4
Конфигурационный файл Action Mappings
Как сервлет-контроллер узнает о том, какие mappings вы
определили? Возможно (но скучно), написать маленький Java-класс,
который просто создаст экземпляры нужных нам ActionMapping, и
вызовет все необходимые setter-методы. Чтобы упростить эту задачу,
в Struts включен модуль Digester, который может прочитать
XML-описание желаемых mappings и создать соответствующие объекты.
Взгляните на API documentation
чтобы ближе познакомиться с Digester.
Задача разработчика – создать XML-файл с именем
struts-config.xml и поместить его в каталог WEB-INF своего
приложения. Формат этого документа ограничен его определением в
"struts-config_1_0.dtd". Корневым элементом в этом файле
должен быть <struts-config>.
Внутри элемента <struts-config> находятся 2
важных элемента, которые используются для описания ваших action:
<form-beans> Эта секция содержит определения
ваших form beans. Используйте элемент <form-bean> для
определения каждого bean, который имеет следующие важные атрибуты:
name:
уникальный идентификатор для данного bean, который будет
использоваться для ссылки на него в соответствующих action
mappings. Это также имя атрибута сессии или request, под которым
данный bean будет сохраняться.
type: полное имя
Java-класса для вашего form bean.
<action-mappings> Эта секция содержит
определения ваших action. Используйте элемент <action> для
каждого из определяемых action's. Каждый элемент <action>
требует определения следующих атрибутов:
path:
относительный путь внутри приложения (application
context-relative path)для данного action
type:
полное имя Java-класса для вашего Action
name: имя
элемента <form-bean>, который будет использоваться вместе с
этим action
Файл
struts-config.xml из примера приложения включает следующую
запись о mapping для функции “входа в приложение”,
которое используется для иллюстрации приведенных требований.
Обратите внимание, что записи для всех остальных actions опущены.
First the form bean is defined. A basic bean of class
"org.apache.struts.example.LogonForm" is
mapped to the logical name "logonForm".
This name is used as a session or request attribute name for the
form bean.
The "global-forwards" section
is used to create logical name mappings for commonly used jsp
pages. Each of these forwards is available through a call to your
action mapping instance, i.e.
actionMappingInstace.findForward("logicalName").
As you can see, this mapping matches the path /logon
(actually, because the example application uses extension mapping,
the request URI you specify in a JSP page would end in /logon.do).
When a request that matches this path is received, an instance of
the LogonAction class will be created (the first time
only) and used. The controller servlet will look for a session
scoped bean under key logonForm, creating and saving
a bean of the specified class if needed.
Optional but very useful are the local "forward"
elements. In the example application, many actions include a local
"success" and/or "failure" forward as part of
an Action mapping.
Using just these two extra properties, the Action
classes in the example application are almost totally independent
of the actual names of the JSP pages that are used by the page
designers. The pages can be renamed (for example) during a
redesign, with negligible impact on the Action
classes themselves. If the names of the "next" JSP pages
were hard coded into the Action classes, all of these
classes would also need to be modified. Of course, you can define
whatever local forward properties makes sense for your own
application.
One more section of good use is the <data-sources>
section, which specifies data sources that your application can
use.This is how you would specify a basic data source for your
application inside of struts-config.xml:
The final step in
setting up the application is to configure the application
deployment descriptor (stored in file WEB-INF/web.xml)
to include all the Struts components that are required. Using the
deployment descriptor for the example application as a guide, we
see that the following entries need to be created or modified.
4.5.1
Configure the Action Servlet Instance
Add an entry defining the action servlet itself,
along with the appropriate initialization parameters. Such an
entry might look like this:
The initialization parameters supported by the controller
servlet are described below. (You can also find these details
in the Javadocs for the
ActionServlet class.) Square brackets describe the default
values that are assumed if you do not provide a value for that
initialization parameter.
application
- Java class name of the application resources bundle base
class. [NONE]
bufferSize
- The size of the input buffer used when processing file
uploads. [4096]
config
- Context-relative path to the XML resource containing our
configuration information. [/WEB-INF/struts-config.xml]
content
- Default content type and character encoding to be set on
each response; may be overridden by a forwarded-to servlet or
JSP page. [text/html]
debug
- The debugging detail level for this servlet, which controls
how much information is logged. [0]
detail
- The debugging detail level for the Digester we utilize in
initMapping(), which logs to System.out instead
of the servlet log. [0]
factory
- The Java class name of the MessageResourcesFactory
used to create the application MessageResources
object.
[org.apache.struts.util.PropertyMessageResourcesFactory]
formBean
- The Java class name of the ActionFormBean implementation to
use [org.apache.struts.action.ActionFormBean].
forward
- The Java class name of the ActionForward implementation to
use [org.apache.struts.action.ActionForward]. Two convenient
classes you may wish to use are:
org.apache.struts.action.ForwardingActionForward
- Subclass of org.apache.struts.action.ActionForward
that defaults the redirect property to false
(same as the ActionForward default value).
org.apache.struts.action.RedirectingActionForward
- Subclass of org.apache.struts.action.ActionForward
that defaults the redirect property to true.
locale
- If set to true, and there is a user session,
identify and store an appropriate java.util.Locale
object (under the standard key identified by
Action.LOCALE_KEY) in the user's session if there
is not a Locale object there already. [true]
mapping
- The Java class name of the ActionMapping implementation to
use [org.apache.struts.action.ActionMapping]. Two convenient
classes you may wish to use are:
org.apache.struts.action.RequestActionMapping
- Subclass of org.apache.struts.action.ActionMapping
that defaults the scope property to "request".
org.apache.struts.action.SessionActionMapping
- Subclass of org.apache.struts.action.ActionMapping
that defaults the scope property to "session".
(Same as the ActionMapping default value).
maxFileSize
- The maximum size (in bytes) of a file to be accepted as a
file upload. Can be expressed as a number followed by a "K"
"M", or "G", which are interpreted to mean
kilobytes, megabytes, or gigabytes, respectively. [250M]
multipartClass
- The fully qualified name of the MultipartRequestHandler
implementation class to be used for processing file uploads.
[org.apache.struts.upload.DiskMultipartRequestHandler]
nocache
- If set to true, add HTTP headers to every
response intended to defeat browser caching of any response we
generate or forward to. [false]
null
- If set to true, set our application resources
to return null if an unknown message key is used.
Otherwise, an error message including the offending message
key will be returned. [true]
tempDir
- The temporary working directory to use when processing file
uploads. [The working directory provided to this web
application as a servlet context attribute]
validate
- Are we using the new configuration file format? [true]
validating - Should we use a
validating XML parse to process the configuration file
(strongly recommended)? [true]
4.5.2
Configure the Action Servlet Mapping
Note: The material in this section
is not specific to Struts. The configuration of servlet
mappings is defined in the Java Servlet Specification. This
section describes the most common means of configuring a Struts
application.
There are two common approaches to defining the
URLs that will be processed by the controller servlet -- prefix
matching and extension matching. An appropriate mapping entry
for each approach will be described below.
Prefix matching means that you want all URLs that
start (after the context path part) with a particular value to
be passed to this servlet. Such an entry might look like this:
where /myapplication is the context path under
which your application is deployed.
Extension mapping, on the other hand, matches
request URIs to the action servlet based on the fact that the
URI ends with a period followed by a defined set of characters.
For example, the JSP processing servlet is mapped to the *.jsp
pattern so that it is called to process every JSP page that is
requested. To use the *.do extension (which
implies "do something"), the mapping entry would look
like this:
and a request URI to match the /logon path
described earlier might look like this:
http://www.mycompany.com/myapplication/logon.do
4.5.3
Configure the Struts Tag Library
Next, you must add an entry defining the Struts tag
library. There are currently four taglibs that Struts is
packaged with.
The struts-bean taglib contains tags useful in
accessing beans and their properties, as well as defining new
beans (based on these accesses) that are accessible to the
remainder of the page via scripting variables and page scope
attributes. Convenient mechanisms to create new beans based on
the value of request cookies, headers, and parameters are also
provided.
The struts-html taglib contains tags used to create
struts input forms, as well as other tags generally useful in
the creation of HTML-based user interfaces.
The struts-logic taglib contains tags that are
useful in managing conditional generation of output text,
looping over object collections for repetitive generation of
output text, and application flow management.
The struts-template taglib contains tags that
define a template mechanism.
Below is how you would define all taglibs for use
within your application, in reality you would only specify the
taglib's that your application will use:
This tells the JSP system where to find the tag library
descriptor for this library (in your application's WEB-INF
directory, instead of out on the Internet somewhere).
4.5.4
Add Struts Components To Your Application
To use Struts, you must copy the .tld files that
you require into your WEB-INF directory, and copy
struts.jar (and all of the commons-*.jar
files) into your WEB-INF/lib directory.