Quick Links

4. Разработка Controller- компонентов

Home

4.1 Обзор

TR

Table of Contents

Теперь, когда вы понимаете, как создавать компоненты 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 в ваше приложение

Introduction

Model Components

View Components

Controller Components

Resources

Who We Are

4.2 Классы Action

Класс Action определяет 2 метода, которые могут исполняться в зависимости от вашей сервлет-среды:
public ActionForward perform(ActionMapping mapping,
                             ActionForm form,
                             ServletRequest request,
                             ServletResponse response)
  throws IOException, ServletException;

public ActionForward perform(ActionMapping mapping,
                             ActionForm form,
                             HttpServletRequest request,
                             HttpServletResponse response)
  throws IOException, ServletException;
Большинство проектов используют только версию с "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 опущены.
<struts-config>
  <form-beans>
    <form-bean
      name="logonForm"
      type="org.apache.struts.example.LogonForm" />
  </form-beans>      
  <global-forwards
      type="org.apache.struts.action.ActionForward" />
    <forward name="logon" path="/logon.jsp"
         redirect="false" /> 
  </global-forwards>      
  <action-mappings>     
    <action
        path="/logon" 
        type="org.apache.struts.example.LogonAction"
        name="logonForm"
       scope="request"
       input="/logon.jsp"
     unknown="false"
    validate="true" />          
  </action-mappings>
</struts-config>
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.
<!-- Edit mail subscription -->
<action    path="/editSubscription"
  type="org.apache.struts.example.EditSubscriptionAction"
  name="subscriptionForm"
  scope="request"
  validate="false">
  <forward name="failure" path="/mainMenu.jsp"/>
  <forward name="success" path="/subscription.jsp"/>
  </action>
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:
<struts-config>
  <data-sources>
    <data-source
      autoCommit="false"
     description="Example Data Source Description"
     driverClass="org.postgresql.Driver"
        maxCount="4"
        minCount="2"
        password="mypassword"
             url="jdbc:postgresql://localhost/mydatabase"
            user="myusername"/>
  </data-sources>
</struts-config>
For information on how to retrieve the data source, see the Accessing Relational Databases section.

4.5 The Web Application Deployment Descriptor

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:
<servlet>
  <servlet-name>action</servlet-name>
  <servlet-class>
    org.apache.struts.action.ActionServlet
  </servlet-class>
  <init-param>
    <param-name>application</param-name>
    <param-value>
      org.apache.struts.example.ApplicationResources
    </param-value>
  </init-param>
  <init-param>
    <param-name>config</param-name>
    <param-value>
      /WEB-INF/struts-config.xml
    </param-value>
  </init-param>
  <init-param>
    <param-name>debug</param-name>
    <param-value>2</param-value>
  </init-param>
  <init-param>
    <param-name>mapping</param-name>
    <param-value>
      org.apache.struts.example.ApplicationMapping
    </param-value>
  </init-param>
  <load-on-startup>2</load-on-startup>
</servlet>
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:
  <servlet-mapping>
     <servlet-name>action</servlet-name>
     <url-pattern>/execute/*</url-pattern>
   </servlet-mapping>
which means that a request URI to match the /logon path described earlier might look like this:
http://www.mycompany.com/myapplication/execute/logon
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:
  <servlet-mapping>
     <servlet-name>action</servlet-name>
     <url-pattern>*.do</url-pattern>
   </servlet-mapping>
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:
<taglib>
  <taglib-uri>
    /WEB-INF/struts-bean.tld
  </taglib-uri>
  <taglib-location>
    /WEB-INF/struts-bean.tld
  </taglib-location>
</taglib>
<taglib>
  <taglib-uri>
    /WEB-INF/struts-html.tld
  </taglib-uri>
  <taglib-location>
    /WEB-INF/struts-html.tld
  </taglib-location>
</taglib>
<taglib>
  <taglib-uri>
    /WEB-INF/struts-logic.tld
  </taglib-uri>
  <taglib-location>
    /WEB-INF/struts-logic.tld
  </taglib-location>
</taglib>
<taglib>
  <taglib-uri>
    /WEB-INF/struts-template.tld
  </taglib-uri>
  <taglib-location>
    /WEB-INF/struts-template.tld
  </taglib-location>
</taglib>
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.
Next: Struts Resources

Copyright (c) 2000-2002, Apache Software Foundation