📄 building_controller.html
字号:
<html><head><META http-equiv="Content-Type" content="text/html; charset=GB2312"><meta content="Craig R. McClanahan" name="author"><meta content="Xiaogang Cao" name="translator"><title>Struts 用户指南 - 构造 Controller(控制器) 部件</title></head><body vlink="#023264" alink="#023264" link="#023264" text="#000000" bgcolor="#ffffff"><table cellspacing="5" width="100%" border="0"><tr><td colspan="2"><a href="http://jakarta.apache.org"><img border="0" align="left" src="../images/jakarta-logo.gif"></a><img border="0" align="right" src="../images/struts.gif"></td></tr><tr><td colspan="2"><hr></td></tr><tr><td valign="top" width="120">
<title>Struts 用户指南</title>
<table cellspacing="5" border="0"><tr><th align="left" colspan="2"><font color="#023264"><strong>快速连接</strong></font></th></tr>
<tr><td width="15" align="center"></td><td><font size="-1"><a href="../index.html">Home</a></font></td></tr>
<tr><td width="15" align="center"></td><td><font size="-1"><a href="index.html">目录</a></font></td></tr>
<tr><td width="15" align="center"></td><td><font size="-1"><a href="introduction.html">简介</a></font></td></tr>
<tr><td width="15" align="center"></td><td><font size="-1"><a href="building_model.html">Model 部件</a></font></td></tr>
<tr><td width="15" align="center"></td><td><font size="-1"><a href="building_view.html">View 部件</a></font></td></tr>
<tr><td width="15" align="center"></td><td><font size="-1"><a href="building_controller.html">Controller 部件</a></font></td></tr>
<tr><td width="15" align="center"></td><td><font size="-1"><a href="resources.html">资源</a></font></td></tr>
<tr><td width="15" align="center"></td><td><font size="-1"><a href="volunteers.html">我们是谁</a></font></td></tr>
</table>
</td><td valign="top"><body>
<a name="building_controller"></a><table width="100%" cellpadding="5" cellspacing="5" border="0"><tr><td bgcolor="#023264"><font size="+1" face="arial,helvetica,sanserif" color="#ffffff"><strong>4. 构造 Controller (控制器)部件</strong></font></td></tr></table><a name="overview"></a><table width="100%" cellpadding="5" cellspacing="5" border="0"><tr><td bgcolor="#023264"><font size="+1" face="arial,helvetica,sanserif" color="#ffffff"><strong>4.1 纵览</strong></font></td></tr><tr><td><blockquote>
<p>
现在我们已经理解如何构造你的程序的Model(模型)和View(视图)部件,现在是时候来关注<code>Controllder</code>部件了。Struts包含一个主要功能是映射一个请求URI到一个<code>Action</code>类的servlet。因此,对于Controller你的主要责任是关于:
</p>
<ul>
<li>为每一个可能接收的逻辑请求编写一个<code>Action</code>类(扩展自<code>org.apache.action.Action</code>)。</li>
<li>为每一个可能提交的逻辑请求在XML中配置一个ActionMapping。这个XML配置文件通常命名为<code>struts-config.xml</code>。</li>
<li>更新你的web程序的部署描述文件(在XML里)来包含需要的Struts部件。</li>
<li>在你的程序里增加恰当的Struts部件。</li>
</ul>
</blockquote></td></tr></table><a name="action_classes"></a><table width="100%" cellpadding="5" cellspacing="5" border="0"><tr><td bgcolor="#023264"><font size="+1" face="arial,helvetica,sanserif" color="#ffffff"><strong>4.2 Action 类</strong></font></td></tr><tr><td><blockquote>
<p><code>Action</code>类提供了两个可以被继承的方法,适应你的servlet环境:
</p>
<pre>
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;
</pre>
<p>
大部分工程可能只需要用到"HttpServletRequest"版本。
</p>
<p>
<code>Action</code>类的目标是通过它的<code>perform()</code>方法处理一个请求,返回一个<code>ActinForward</code>对象表明控制权应该被转移到哪里(例如一个JSP)作为合适的相应。在<i>MVC/Model 2</i>设计模式中,一个典型的<code>Action</code>类会在<code>perform()</code>中执行如下逻辑:
</p>
<ul>
<li>检查当前用户session的状态(比如,检查用户是否已经成功登录)。如果<code>Action</code>类发现尚未登录,请求可以被转移到一个JSP页面提示让用户输入用户名和密码。当用户试图访问一个程序的“中间部分”(可能是从一个书签)的时候,或者是session已经超时,servlet容器创建了一个新的session的时候者都可能发生。</li>
<li>如果检查还未完成的话,继续检查form bean的属性。如果发现了问题,把相应的错误的信息关键字放在request的属性里,然后把控制权重新转到输入页面来让用户改正。</li>
<li>进行请求需要的操作(比如在数据库中保存一行数据)。这可能由<code>Action</code>内部的逻辑代码自己来完成,但是一般应该调用一个商业逻辑bean的合适的方法。</li>
<li>更新服务器端的对象,来创建下一步用户面对的界面(典型的是request范围或者session范围的bean,取决于你需要这些条目保存多长时间)。</li>
<li>返回一个合适的<code>ActionForward</code>对象来挑选JSP页面根据刚刚更新的bean来生成回应。典型的,你需要通过你接收到的<code>ActionMapping</code>的<code>findForward()</code>方法或者从controller servlet自身(如果你使用一个全局的逻辑名字)来得到这样的一个引用。</li>
</ul>
<p>
在编写<code>Action</code>类代码时请记住以下几个设计要点:
</p>
<ul>
<li>Controller servlet只创建你的<code>Action</code>类的一个实例,用来对对付所有的请求。所以,你需要处理好你的代码,在一个多线程环境下你的<code>Action</code>应该运作良好,就象你编写一个servlet的<code>service()</code>方法一样。</li>
<li>最有助于编写线程安全的程序的原则是在你的<code>Action</code>类中只使用本地变量(local variable),而非实例变量(instance variable)。本地变量在堆栈中创建并依附于每一个独立的请求线程(由你的JVM处理),所以你不用担心共享它们。</li>
<li>你系统中表示Model(模型)的bean由可能因为访问数据库或者其他资源而抛出异常。你应该在你的<code>perform()</code>逻辑中捕获所有的异常,把它们记录到程序的log文件中去(同时给出对应的stack trace)。可以这样做:<br>
<code>servlet.log("Error message text", exception);</code></li>
<li>作为一项基本的规则,分配稀少的资源并且为每一个用户在它们的session里分配一个会导致规模问题。你应该在把你的控制转移到View(视图)部件之前就释放它们(比如数据库连接)——就算你调用的bean的方法抛出了异常也要这样。</li>
</ul>
<p>
除此之外,你还需要警惕你的<code>Action</code>类们变得太大。出现这种情况最容易的可能就是把功能逻辑放在<code>Action</code>本身,而不是在独立的商业逻辑bean。除了让<code>Action</code>类变得难以阅读和维护,这种方法还会让重用商业逻辑变得困难,因为它们被嵌入在部件内部(那个<code>Action</code>类),从而被限制在一个web程序环境中运行。
</p>
<p>
只要需要的所有属性都是在方法签名中传递的(指的是通过函数调用传递?——译者注),一个<code>Action</code>可以被分散在几个本地方法中。JVM把这些属性在堆栈中处理,所以它们就是是线程安全的。
</p>
<p>
Struts附带的例子程序只是设计用于展示某些原理,因为它自己的商业逻辑是被嵌入在<code>Action</code>类中的。这应该被理解为设计中的某种bug,而非Struts体系本身的特性,或者应该被模仿的方法。
</p>
</blockquote></td></tr></table><a name="actionmapping"></a><table width="100%" cellpadding="5" cellspacing="5" border="0"><tr><td bgcolor="#023264"><font size="+1" face="arial,helvetica,sanserif" color="#ffffff"><strong>4.3 ActionMapping 的实现</strong></font></td></tr><tr><td><blockquote>
<p>
为了能成功的操作,关于如何把每一个请求URI映射到恰当的<code>Action</code>类,Struts controller servlet需要知道几件事情。这些需要的知识被封装在一个叫做<code>ActionMapping</code>的Java接口中,下列是最重要的几个属性:
</p>
<ul>
<li><b>type</b> - 在这个映射中<code>Action</code>实现的完整java类名。</li>
<li><b>name</b> - 这个action会用到的form bean在配置文件中使用的名字</li>
<li><b>path</b> - 请求的URL用以选择(触发)这个映射。下面会有关于匹配如何工作的例子。</li>
<li><b>unknown</b> - 当无法找到合适的其他action来处理请求时,某个action可以被配置为默认处理这个请求,这时候把这一项置为<code>true</code>。一个程序中只能有一个action作为默认处理action。</li>
<li><b>validate</b> - 如果这个映射中涉及的action需要在调用前先调用<code>validate()</code>方法,把这一项置为<code>true</code>。</li>
<li><b>forward</b> - 当这个映射被调用的时候,转向一个请求URI。这是<b>type</b>属性的一个替代品。</li>
</ul>
</blockquote></td></tr></table><a name="config"></a><table width="100%" cellpadding="5" cellspacing="5" border="0"><tr><td bgcolor="#023264"><font size="+1" face="arial,helvetica,sanserif" color="#ffffff"><strong>4.4 Action Mappings 配置文件</strong></font></td></tr><tr><td><blockquote>
<p>
controller servlet是如何知道你希望的映射的?写一个小java类来创建新的<code>ActionMapping</code>的实例,并且调用每一个setter方法来赋值是可行的,但是非常繁琐。为了简化这个步骤,Struts包含一个Digester模块,它能够读取一段基于XML的关于需要的映射的描述,并创建合适的对象。参阅<a href="../api/index.html">API 文档</a>得到关于Digester的更多信息。
</p>
<p>
开发者的责任是编写一个命名为<code>struts-config.xml</code>的XML文件,并把它放在你的程序的WEB-INF目录下。文档的格式由"struts-config_1_0.dtd"中的描述指定。最外层的XML元素必须是<code><struts-config></code>。
</p>
<p>
在<struts-config>元素之内,有两个重要的元素被用于描述你的action:
<blockquote>
<b><form-beans></b><br>
这一段包含你的form bean的定义。你为每一个form bean使用一个<form-bean> 元素,包含下列属性:
<ul>
<li>
<b>name</b>: 这个bean的一个唯一的标识名,它将会被用来在action映射中引用来指定这个bean.通常,这也是把这个对象存放在request或者session中的属性的关键字。
</li>
<li>
<b>type</b>: 你的form bean的完整的Java类名。
</li>
</ul>
</blockquote>
<blockquote>
<b><action-mappings></b><br>
这一段包含你的action的定义。你为你的每一个需要定义的action使用一个<action> 元素。每一个action元素需要定义下列的属性:
<ul>
<li>
<b>path</b>: 这个action的程序上下文相关的路径
</li>
<li>
<b>type</b>: 这个Action类的完整类名
</li>
<li>
<b>name</b>: Action中所用到的<form bean>元素的名字
</li>
</ul>
</blockquote>
</p>
<p>
示例程序中的<code>struts-config.xml</code>文件包含了下列的映射元素,它们是用来实现“登录”功能的,我们用它来说明需求。注意其它的Action的条目都被剔除了:
</p>
<pre>
<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>
</pre>
<p>
首先定义的是form bean .一个基础的"<code>org.apache.struts.example.LogonForm</code>"类被映射为逻辑名"<code>logonForm</code>"。这个名字也被用在session或者request作为这个form bean的属性名。
</p>
<p>
"<code>global-forwards</code>"部分用来创建全局可用的逻辑名映射。这儿的任何一个转移都可以通过调用你的action mapping的实例实现,比如<code>actionMappingInstace.findForward("logicalName")</code>。
</p>
<p>
你可以看到,这个映射匹配<code>/logon</code>路径(实际上,因为实例程序用了后缀名映射,你指定的URI请求会是由<code>/logon.do</code>结尾的)。当收到一个匹配这个路径的请求时,会创建一个<code>LogonAction</code>的实例(仅当第一次),并且使用它。controller serverl会寻找一个session范围的名为<code>logonForm</code>的bean,如果需要的话,创建并保存这样的一个bean。
</p>
<p>
局部的"<code>forward</code>" 元素是可选的但是非常实用。在实例程序中,很多action包含一个局部的"success"和/或"failure"转移作为Action mapping的一部分。
</p>
<pre>
<!-- 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>
</pre>
<p>只用了两个额外的属性,示例程序中的<code>Action</code>类变得几乎和页面设计者用的实际JSP页面的名字无关了。页面可以在重新设计的时候被改名(举个例子),只会给<code>Action</code>类带来微不足道的冲击。如果“下一个”JSP页面的名字是被硬编码在<code>Action</code>里面的话,所有的类就必须做出改动。当然,你可以决定在你的程序中使用怎样的局部转移属性。
</p>
<p>
另一个很有用的部分是<code><data-sources></code>,它定义了你的程序可以使用的数据源。下面是你如何在你的程序的struts-config.xml中指定一个基本的数据源:
</p>
<pre>
<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>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -