副标题#e#
一、概述
在Struts 架构中,Controller主要是ActionServlet,可是对付业务逻辑的操纵则主要由Action、ActionMapping、ActionForward这几个组件协调完成。个中,Action饰演了真正的业务逻辑的实现者,而ActionMapping和ActionForward则指定了差异业务逻辑或流程的运行偏向。
应用措施的 Controller 部门会合于从客户端吸收请求(典范环境下是一个运行欣赏器的用户),抉择执行什么贸易逻辑成果,然后将发生下一步用户界面的责任委派给一个适当的View组件。在Struts中,controller的根基组件是一个 ActionServlet 类的servlet。这个servlet通过界说一组映射(由Java接口 ActionMapping 描写)来设置。每个映射界说一个与所请求的URI相匹配的路径和一个 Action 类(一个实现 Action 接口的类)完整的类名,这个类认真执行预期的贸易逻辑,然后将节制分配给适当的View组件来建设响应。
Struts也支持利用包括有运行框架所必须的尺度属性之外的附加属性的 ActionMapping 类的本领。这答允我们生存特定于我们的应用措施的附加信息,同时仍可操作框架其余的特性。别的,Struts答允我们界说节制将重定向到的逻辑名,这样一个行为要领可以请求"主菜单"页面,而不需要知道相应的JSP页面的实际名字是什么。这个成果极大地辅佐我们疏散节制逻辑(下一步做什么)和显示逻辑(相应的页面的名称是什么)。下图1是Struts的controller组件示意图:
二、建设Controller组件
Struts包罗一个实现映射一个请求URI到一个行为类的主要成果的servlet。因此我们的与Controller有关的主要责任是:
为每一个大概吸收的逻辑请求写一个 Action 类(也就是,一个 Action 接口的实现);写一个界说类名和与每个大概的映射相关的其它信息的 ActionMapping 类(也就是,一个 ActionMapping 接口的实现);写行为映射设置文件(用XML)用来设置controller servlet。
为应用措施更新web应用措施展开描写符文件(用XML)用来包罗必须的Struts组件,我们给应用措施添加适当的Struts组件。
1、Action 实现
Action 接口界说一个单一的必需由一个 Action 类实现的要领,就象下面这样:
public ActionForward perform(ActionServlet servlet,
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException;
一个 Action 类的方针是处理惩罚这个请求,然后返回一个标识JSP页面的 ActionForward 工具,节制应该重定向这个JSP页面以生成相应的响应。Struts 架构为应用系统中的每一个Action类只建设一个实例。因为所有的用户都利用这一个实例,所以你必需确定你的Action 类运行在一个多线程的情况中。下图2显示了一个execute()要领如何被会见:
图2 Action实例的execute()要领
#p#副标题#e#
留意,客户本身担任的Action子类,必需重写execute()要领,因为Action类在默认环境下是返回null的。
在 Model 2 设计模式中,一个典范的 Action 类将在它的 perform() 要领中实现下面的逻辑:
验证用户session的当前状态(譬喻,查抄用户已经乐成地注册)。假如 Action 类发明没有注册存在,请求应该重定向到显示用户名和口令用于注册的JSP页面。应该这样做是因为用户大概试图从"中间"(也就是,从一个书签)进入我们的应用措施,可能因为session已经超时而且servlet容器建设了一个新的session。假如验证还没有产生(由于利用一个实现 ValidatingActionForm 接口的form bean),验证这个 form bean 的属性是必需的。假如发明一个问题,看成一个请求属性生存符合的堕落信息要害字,然后将节制重定向回输入表单这样错误可以被更正。
执行要求的处理惩罚来处理惩罚这个请求(譬喻在数据库里生存一行)。这可以用嵌入 Action 类自己的代码来完成,可是凡是应该挪用一个贸易逻辑bean的一个符合的要领来执行。更新将用来建设下一个用户界面页面的处事器端工具(典范环境下是request范畴或session范畴beans,界说我们需要在多长时间内保持这些项目可得到)。返回一个标识生成响应的JSP页面的适当的 ActionForward 工具,基于新近更新的beans。典范环境下,我们将通过吸收到的 ActionMapping 工具(假如我们利用一个局部于与这个映射上的逻辑名)可能在controller servlet 自己(假如我们利用一个全局于应用措施的逻辑名)上挪用 findForward() 获得一个对这样一个工具的引用。
当为 Action 类编程时要记着的设计要点包罗以下这些:
#p#分页标题#e#
controller servlet仅仅建设一个我们的 Action 类的实例,用于所有的请求。这样我们需要编写我们的 Action 类使其可以或许在一个多线程情况中正确运行,就象我们必需安详地编写一个servlet的 service() 要领一样。
辅佐线程安详编程的最重要的原则就是在我们的 Action 类中仅仅利用局部变量而不是实例变量。局部变量建设于一个分派给每个请求线程的栈中,所以没有须要担忧会共享它们。
尽量不该该,代表我们的系统中Model部门的的beans仍有大概抛出违例。我们应该在我们的 perform() 要领的逻辑中捕获所有这样的违例,而且通过执行以下语句将它们记录在应用措施的日志文件中(包罗相应的栈跟踪信息):
servlet.log("Error message text", exception);
作为一个通用的法则,分派很少的资源并在来自同一个用户(在用户的session中)的请求间保持它们会导致可伸缩性的问题。别的,我们将会想要防备呈现很是大的 Action 类。最简朴的实现途径是将我们的成果逻辑嵌入到 Action 类自己,而不是将其写在独立的贸易逻辑beans中。除了使 Action 类难于领略和维护外,这种要领也使得难于重用这些贸易逻辑代码,因为代码被嵌入到一个组件(Action 类)中并被绑缚运行于web应用措施情况中。
包罗在Struts中的例子措施某种水平上延伸了这个设计原则,因为贸易逻辑自己是嵌入到 Action 类中的。这应该被看作是在这个样本应用措施设计中的一个bug,而不是一个Struts体系布局中的固有特性,可能是一个值得仿效的要领。
2、ActionMapping实现
为了乐成地运行,Struts的controller servlet需要知道关于每个URI该奈何映射到一个适当的 Action 类的几件事。需要相识的常识封装在一个叫做 ActionMapping 的Java接口中,它有以部属性:
actionClass :用于这个映射的 Action 类完整的Java类名。第一次一个特定的映射被利用,一个这个类的实例将被建设并为今后重用而生存。
formAttribute :session范畴的bean的名字,当前的这个映射的 ActionForm 被生存在这个bean之下。假如这个属性没有被界说,没有 ActionForm 被利用。
formClass :用于这个映射的 ActionForm 类完整的Java类名。假如我们在利用对form beans的支持,这个类的一个实例将被建设并生存(在当前的用户会话中)
path :匹配选择这个映射的请求的URI路径。看下面如何匹配的例子。
Struts在一个叫做 ActionMappingBase 的类中包罗了一个 ActionMapping 接口的利便的实现。假如我们不需要为我们本身的映射界说任何附加的属性,尽量把这个类作为我们的 ActionMapping 类好了,就向下面部门描写的那样设置。然而,界说一个 ActionMapping 实现(多数是扩展 ActionMappingBase 类)来包括附加的属性也是大概的。controller servlet知道奈何自动设置这些定制属性,因为它利用Struts的Digester模块来读设置文件。
包罗在Struts的例子措施中,这个特性用来界说两个附加的属性:
failure :假如Action类检测到它吸收的输入字段的一些问题,节制应该被重定向到的上下文相关的URI。典范环境下是请求发向的JSP页面名,它将引起表单被从头显示(包括Action类配置的堕落动静和大部门最近的来自ActionForm bean的输入值)。
success :假如Action类乐成执行请求的成果,节制应该被重定向到的上下文相关的URI。典范环境下是筹备这个应用措施的会话流的下一个页面的JSP页面名。
利用这两个特另外属性,例子措施中的 Action 类险些完全独立于页面设计者利用的实际的JSP页面名。 这个页面可以在从头设计时被重定名,然而险些不会影响到 Action 类自己。假如"下一个"JSP页面的名字被硬编码到 Action 类中,所有的这些类也需要被修改。
3、ActionForward实现
目标是节制器将Action类的处理惩罚功效转发至目标地。
Action类得到ActionForward实例的句柄,然后可用三种要领返回到ActionServlet,所以我们可以这样利用ActionForward():ActionServlet按照名称获取一个全局转发;ActionMappin实例被传送到perform()要领,并按照名称找到一个当地转发。
另一种是挪用下面的一个结构器来建设它们本身的一个实例:
public ActionForward()
public ActionForward(String path)
public ActionForward(String path,Boolean redirect)
4、Action映射设置文件
controller servlet奈何知道我们想要获得的映射?写一个简朴地初始化新的 ActionMapping 实例而且挪用所有适当的set要领的小的Java类是大概的(可是很贫苦)。为了使这个处理惩罚简朴些,Struts包罗一个Digester模块可以或许处理惩罚一个想获得的映射的基于XML的描写,同时建设适当的工具。
#p#分页标题#e#
开拓者的责任是建设一个叫做 action.xml 的XML文件,而且把它放在我们的应用措施的WEB-INF目次中。(留意这个文件并不需要 DTD,因为实际利用的属性对付差异的用户可以是差异的)最外面的XML元素必需是<action-mappings>,在这个元素之中是嵌入的0个或更多的 <action> 元素 — 每一个对应于我们但愿界说的一个映射。
来自例子措施的 action.xml 文件包罗"注册"成果的以下映射条目,我们用来说明这个需求:
<action-mappings>
<forward name="logon" path="/logon.jsp"/>
<action path="/logon" actionClass="org.apache.struts.example.LogonAction"
formAttribute="logonForm" formClass="org.apache.struts.example.LogonForm" inputForm="/logon.jsp">
<forward name="success" path="/mainMenu.jsp"/>
</action>
</action-mappings>
就象我们所看到的,这个映射匹配路径 /logon (实际上,因为例子措施利用扩展匹配,我们在一个JSP页面指定的请求的URI竣事于/logon.do)。当吸收到一个匹配这个路径的请求时,一个 LogonAction 类的实例将被建设(仅仅在第一次)并被利用。controller servlet将在要害字 logonForm 下查找一个session范畴的bean,假如需要就为指定的类建设并生存一个bean。
这个 action 元素也界说了一个逻辑名"success",它在 LogonAction 类中被用来标识当一个用户乐成注册时利用的页面。象这样利用一个逻辑名答允将 action 类断绝于任何由于从头设计位置而大概产生的页面名改变。
这是第二个在任何 action 之外宣告的 forward 元素,这样它就可以被所有的action全局地得到。在这个环境下,它为注册页面界说了一个逻辑名。当我们挪用 mapping.findForward() 时在我们的 action 代码中,Struts首先查找这个action当地界说的逻辑名。假如没有找到,Struts会自动为我们查找全局界说的逻辑名。
5、Web应用措施展开描写符
配置应用措施最后的步调是设置应用措施展开描写符(生存在文件WEB-INF/web.xml中)以包罗所有必须的Struts组件。作为一个指南利用例子措施的展开描写符,我们看到下面的条目需要被建设或修改。
1)设置ActionServlet实例
添加一个条目界说actionservlet自己,同时包罗适当的初始化参数。这样一个条目看起来象是这样:
<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/action.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>
controller servlet支持的初始化参数在下面描写,拷贝自 ActionServlet 类的 Javadocs 。方括号描写假如我们没有为谁人初始化参数提供一个值时假设的缺省值。
application :应用措施资源包基类的Java类名。[NONE]
config :包括设置信息的XML资源的上下文相关的路径。[/WEB-INF/action.xml]
debug :这个servlet的调试级别,它节制记录几多信息到日志中。[0]
digester : 我们在 initMapping() 中操作的Digester的调试级别,它记录到System.out而不是
servlet的日志中。[0]
forward :利用的ActionForward实现的Java类名。[org.apache.struts.action.ActionForward]
mapping :利用的ActionMapping实现的Java类名。[org.apache.struts.action.ActionMappingBase]
nocache : 假如配置为 true,增加HTTP头信息到所有响应中使欣赏器对付生成或重定向到的任何响应不做缓冲。[false]
null :假如配置为 true,配置应用措施资源使得假如未知的动静要害字被利用则返回 null。不然,一个包罗不接待的动静要害字的堕落动静将被返回。[true]
2)设置ActionServlet映射
有两种凡是的要领来界说将被controller servlet处理惩罚的URL:前缀匹配和扩展匹配。每种要领的一个适当的映射条目将在下面被描写。
前缀匹配意思是我们想让所有以一个非凡值开头(在上下文路径部门之后)的URL通报给这个servlet。这样一个条目看起来可以象是这样:
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>/execute/*</url-pattern>
</servlet-mapping>
它意味着一个匹配前面描写的 /logon 路径的请求的URL看起来象是这样:
http://www.mystudy.com/myapplication/execute/logon
这里 /myapplication是我们的应用措施展开地址的上下文路径。
#p#分页标题#e#
另一方面,扩展映射基于URL以一个随着界说的一组字符的句点竣事的事实而将URL匹配到action servlet 。譬喻,JSP处理惩罚servlet映射到 *.jsp 模式这样它在每个JSP页面请求时被挪用。为了利用 *.do 扩展(它意味着"做某件事")映射条目看起来应该象是这样:
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
而且一个匹配以前描写的 /logon 路径的请求的URI可以看起来象是这样:
http://www.mystudy.com/myapplication/logon.do
3)设置Struts标志库
下一步,我们必需添加一个界说Struts标志库的条目。这个条目看起来应该象是这样:
<taglib>
<taglib-uri>/WEB-INF/struts.tld</taglib-uri>
<taglib-location>/WEB-INF/struts.tld</taglib-location>
</taglib>
它汇报JSP系统到那边去找这个库的标志库描写符(在我们的应用措施的WEB-INF目次)。
4)添加Struts组件到我们的应用措施中
为了在我们的应用措施运行时利用Struts,我们必需将 struts.tld 文件拷贝到我们的 WEB-INF 目次,将struts.jar 文件拷贝到我们的 WEB-INF/lib 。