VIRGIN FOREST OF JAVA
不要埋头苦干,要学习,学习,再学习。。。。。
powered by R.Zeus
4. 构造 Controller (控制器)部件
4.1 纵览

现在我们已经理解如何构造你的程序的Model(模型)和View(视图)部件,现在是时候来关注Controllder部件了。Struts包含一个主要功能是映射一个请求URI到一个Action类的servlet。因此,对于Controller你的主要责任是关于:

  • 为每一个可能接收的逻辑请求编写一个Action类(扩展自org.apache.action.Action)。
  • 为每一个可能提交的逻辑请求在XML中配置一个ActionMapping。这个XML配置文件通常命名为struts-config.xml
  • 更新你的web程序的部署描述文件(在XML里)来包含需要的Struts部件。
  • 在你的程序里增加恰当的Struts部件。
4.2 Action 类

Action类提供了两个可以被继承的方法,适应你的servlet环境:

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()方法处理一个请求,返回一个ActinForward对象表明控制权应该被转移到哪里(例如一个JSP)作为合适的相应。在MVC/Model 2设计模式中,一个典型的Action类会在perform()中执行如下逻辑:

  • 检查当前用户session的状态(比如,检查用户是否已经成功登录)。如果Action类发现尚未登录,请求可以被转移到一个JSP页面提示让用户输入用户名和密码。当用户试图访问一个程序的“中间部分”(可能是从一个书签)的时候,或者是session已经超时,servlet容器创建了一个新的session的时候者都可能发生。
  • 如果检查还未完成的话,继续检查form bean的属性。如果发现了问题,把相应的错误的信息关键字放在request的属性里,然后把控制权重新转到输入页面来让用户改正。
  • 进行请求需要的操作(比如在数据库中保存一行数据)。这可能由Action内部的逻辑代码自己来完成,但是一般应该调用一个商业逻辑bean的合适的方法。
  • 更新服务器端的对象,来创建下一步用户面对的界面(典型的是request范围或者session范围的bean,取决于你需要这些条目保存多长时间)。
  • 返回一个合适的ActionForward对象来挑选JSP页面根据刚刚更新的bean来生成回应。典型的,你需要通过你接收到的ActionMappingfindForward()方法或者从controller servlet自身(如果你使用一个全局的逻辑名字)来得到这样的一个引用。

在编写Action类代码时请记住以下几个设计要点:

  • Controller servlet只创建你的Action类的一个实例,用来对对付所有的请求。所以,你需要处理好你的代码,在一个多线程环境下你的Action应该运作良好,就象你编写一个servlet的service()方法一样。
  • 最有助于编写线程安全的程序的原则是在你的Action类中只使用本地变量(local variable),而非实例变量(instance variable)。本地变量在堆栈中创建并依附于每一个独立的请求线程(由你的JVM处理),所以你不用担心共享它们。
  • 你系统中表示Model(模型)的bean由可能因为访问数据库或者其他资源而抛出异常。你应该在你的perform()逻辑中捕获所有的异常,把它们记录到程序的log文件中去(同时给出对应的stack trace)。可以这样做:
    servlet.log("Error message text", exception);
  • 作为一项基本的规则,分配稀少的资源并且为每一个用户在它们的session里分配一个会导致规模问题。你应该在把你的控制转移到View(视图)部件之前就释放它们(比如数据库连接)——就算你调用的bean的方法抛出了异常也要这样。

除此之外,你还需要警惕你的Action类们变得太大。出现这种情况最容易的可能就是把功能逻辑放在Action本身,而不是在独立的商业逻辑bean。除了让Action类变得难以阅读和维护,这种方法还会让重用商业逻辑变得困难,因为它们被嵌入在部件内部(那个Action类),从而被限制在一个web程序环境中运行。

只要需要的所有属性都是在方法签名中传递的(指的是通过函数调用传递?——译者注),一个Action可以被分散在几个本地方法中。JVM把这些属性在堆栈中处理,所以它们就是是线程安全的。

Struts附带的例子程序只是设计用于展示某些原理,因为它自己的商业逻辑是被嵌入在Action类中的。这应该被理解为设计中的某种bug,而非Struts体系本身的特性,或者应该被模仿的方法。

4.3 ActionMapping 的实现

为了能成功的操作,关于如何把每一个请求URI映射到恰当的Action类,Struts controller servlet需要知道几件事情。这些需要的知识被封装在一个叫做ActionMapping的Java接口中,下列是最重要的几个属性:

  • type - 在这个映射中Action实现的完整java类名。
  • name - 这个action会用到的form bean在配置文件中使用的名字
  • path - 请求的URL用以选择(触发)这个映射。下面会有关于匹配如何工作的例子。
  • unknown - 当无法找到合适的其他action来处理请求时,某个action可以被配置为默认处理这个请求,这时候把这一项置为true。一个程序中只能有一个action作为默认处理action。
  • validate - 如果这个映射中涉及的action需要在调用前先调用validate()方法,把这一项置为true
  • forward - 当这个映射被调用的时候,转向一个请求URI。这是type属性的一个替代品。
4.4 Action Mappings 配置文件

controller servlet是如何知道你希望的映射的?写一个小java类来创建新的ActionMapping的实例,并且调用每一个setter方法来赋值是可行的,但是非常繁琐。为了简化这个步骤,Struts包含一个Digester模块,它能够读取一段基于XML的关于需要的映射的描述,并创建合适的对象。参阅API 文档得到关于Digester的更多信息。

开发者的责任是编写一个命名为struts-config.xml的XML文件,并把它放在你的程序的WEB-INF目录下。文档的格式由"struts-config_1_0.dtd"中的描述指定。最外层的XML元素必须是<struts-config>

在<struts-config>元素之内,有两个重要的元素被用于描述你的action: <form-beans>
这一段包含你的form bean的定义。你为每一个form bean使用一个<form-bean> 元素,包含下列属性:

  • name: 这个bean的一个唯一的标识名,它将会被用来在action映射中引用来指定这个bean.通常,这也是把这个对象存放在request或者session中的属性的关键字。
  • type: 你的form bean的完整的Java类名。
<action-mappings>
这一段包含你的action的定义。你为你的每一个需要定义的action使用一个<action> 元素。每一个action元素需要定义下列的属性:
  • path: 这个action的程序上下文相关的路径
  • type: 这个Action类的完整类名
  • name: Action中所用到的<form bean>元素的名字

示例程序中的struts-config.xml文件包含了下列的映射元素,它们是用来实现“登录”功能的,我们用它来说明需求。注意其它的Action的条目都被剔除了:

<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>

首先定义的是form bean .一个基础的"org.apache.struts.example.LogonForm"类被映射为逻辑名"logonForm"。这个名字也被用在session或者request作为这个form bean的属性名。

"global-forwards"部分用来创建全局可用的逻辑名映射。这儿的任何一个转移都可以通过调用你的action mapping的实例实现,比如actionMappingInstace.findForward("logicalName")

你可以看到,这个映射匹配/logon路径(实际上,因为实例程序用了后缀名映射,你指定的URI请求会是由/logon.do结尾的)。当收到一个匹配这个路径的请求时,会创建一个LogonAction的实例(仅当第一次),并且使用它。controller serverl会寻找一个session范围的名为logonForm的bean,如果需要的话,创建并保存这样的一个bean。

局部的"forward" 元素是可选的但是非常实用。在实例程序中,很多action包含一个局部的"success"和/或"failure"转移作为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>

只用了两个额外的属性,示例程序中的Action类变得几乎和页面设计者用的实际JSP页面的名字无关了。页面可以在重新设计的时候被改名(举个例子),只会给Action类带来微不足道的冲击。如果“下一个”JSP页面的名字是被硬编码在Action里面的话,所有的类就必须做出改动。当然,你可以决定在你的程序中使用怎样的局部转移属性。

另一个很有用的部分是<data-sources>,它定义了你的程序可以使用的数据源。下面是你如何在你的程序的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>

关于如何获取这个数据源,参阅访问关系数据库这一节。

4.5 Web 程序发布描述

设置你的程序的最后一步是配置程序的发布描述(保存在WEB-INF/web.xml文件中)来包含所有需要的Struts部件。用示例程序中的发布描述作为一个指南,我们发现下列条目需要创建或者修改。

4.5.1 配置Action servlet实例

增加一个条目来定义action servlet自己,包含合适的初始化参数。这样的一个条目可能是这样的:

<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>

controller servlet支持的初始化参数如下所示。(你也可以在Javadocs中查找ActionServlet类得到详细信息。)方括号表示如果你没有给出初始值时使用的默认值。

  • application - 程序的资源包的基础类的Java类名。 [NONE]
  • bufferSize - 处理文件上传的输入缓冲区大小。[4096]
  • config - 指向包含我们的配置信息的XML资源的相对于上下文的路径。[/WEB-INF/struts-config.xml]
  • content - 默认输出响应的的文档类性和字符集编码。它可以被一个转移到的servlet或者JSP重载。[text/html]
  • debug - 这个servlet的debug信息详细程度,它控制多少信息应该被记录。[0]
  • detail - 我们在initMapping()中使用的Digester的debug信息详细程度,它输出到System.out而非servlet的log。[0]
  • factory - 用于创建程序的MessageResources对象的MessageResourcesFactory的类名。 [org.apache.struts.util.PropertyMessageResourcesFactory]
  • formBean - ActionFormBean继承的根的Java类名。[org.apache.struts.action.ActionFormBean].
  • forward - ActionForwarder继承的根的Java类名。 [org.apache.struts.action.ActionForward]. 两个你可能选择的类是: Two convenient classes you may wish to use are:
    • org.apache.struts.action.ForwardingActionForward - org.apache.struts.action.ActionForward的子类,它的 redirect属性被默认置为false(和Actionforward 默认值一样)。
    • org.apache.struts.action.RedirectingActionForward - org.apache.struts.action.ActionForward的子类,它的redirect属性被默认置为true
  • locale - 如果被置为 true, 并且有一个用户session,如果没有Locale对象的话,放置一个合适的java.util.Locale object (用Action.LOCALE_KEY作为标识关键字)到用户session中去。[true]
  • mapping - ActionMapping继承的根的Java类名。[org.apache.struts.action.ActionMapping]. 两个你可能选择的类是:
    • org.apache.struts.action.RequestActionMapping - org.apache.struts.action.ActionMapping 的子类,scope属性默认是"request"。
    • org.apache.struts.action.SessionActionMapping - of org.apache.struts.action.ActionMapping 的子类,scope属性默认是"session"(和ActionMapping默认值一样)。
  • maxFileSize - 一次上传文件可能的最大大小(单位是字节)。可以在数字后面加上"K","M',或者"G", 代表千字节,兆字节和千兆字节。[250M]
  • multipartClass - 用于文件上传的MultipartRequestHandler 的实现的完整Java类名。[org.apache.struts.upload.DiskMultipartRequestHandler]
  • nocache - 如果设置为true, 在每一个响应中加上HTTP头信息来访置我们的任何响应或者转移被浏览器缓存。[false]
  • null - 如果设置为true, 如果(资源)信息的关键字无法找到,返回一个null。否则,返回一个带有此关键字的错误信息。[true]
  • tempDir - 文件上传时的临时目录。 [这个web程序的servlet上下文属性指定的工作目录]
  • validate - 我们是否适用性的配置文件格式?[true]
  • validating - 我们是否使用一个带有校验的XML 解释器来处理配置文件(强烈建议)? [true]
4.5.2 配置Action Servlet 映射

注意: 这一段中的材料不是针对Struts的。配置servlet映射是在Java Servlet规格书中定义的。这一段描述了最普通的针对配置一个Struts程序的方法。

又两种一般的方法来配置一个URL成为被controller servlet处理的URL - 前缀匹配和后缀匹配。每种方法相应的配置条目会在下面描述。

前缀匹配意味着你希望所有的以某一个特定值开头的URL(在上下文路径部分之后)都被送往这个servlet。这样的条目可能是这样:

  <servlet-mapping>
     <servlet-name>action</servlet-name>
     <url-pattern>/execute/*</url-pattern>
   </servlet-mapping>

就是说前面我们提到过的匹配/logon的URI请求会被这样描述:

http://www.mycompany.com/myapplication/execute/logon

这里/myapplication是你的程序部署的上下文路径。

后缀匹配映射,相反,在URL以某一个特定的字符串结尾时把URI请求匹配到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.mycompany.com/myapplication/logon.do
4.5.3 配置 Struts 标签库

下一步,你必须增加一个条目来定义Struts标签库。当前Struts打包了4个标签库。

struts-bean 标签库包含访问bean和它们的属性的有用的标签,也包含利用这些访问定义新的bean以便在页面剩下的部分用脚本中的变量和页面范围的属性来访问。还有用request coolie,header和parameter的值来创建bean的便利机制。

struts-html 标签库包含用户创建struts输入表单,还有其他通常很在创建基于HTML的用户界面上很有用的标签。

struts-logic 标签库包括用于有条件的生成输出代码,循环整个对象容器来输出重复的文本,还有程序流管理。

struts-template 标签库包含定义了一个模版机制的标签。

下面是在你的程序中如何定义所有这些标签。在实际工作中你可以只指定你的程序要用到的。

<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>

这告诉JSP系统到哪里去寻找标签库的定义(在你的程序的WEB-INF目录,而不是Internet上的某处)。

4.5.4 把Struts部件增加到你的程序中

为了使用Struts,你必须拷贝你需要的.tld文件到你的WEB-INF目录,并且拷贝struts.jar(和其它所有的commons-*.jar文件)到你的WEB-INF/lib目录。

posted on 2005-08-13 03:17 R.Zeus 阅读(650) 评论(0)  编辑  收藏 所属分类: STRUTS

只有注册用户登录后才能发表评论。


网站导航:
博客园   IT新闻   Chat2DB   C++博客   博问