﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-海鸥航际-文章分类-Struts专题</title><link>http://www.blogjava.net/sgsoft/category/26.html</link><description>JAVA站</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 12:07:43 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 12:07:43 GMT</pubDate><ttl>60</ttl><item><title>在Eclipse中开发struts应用程序</title><link>http://www.blogjava.net/sgsoft/articles/2575.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Tue, 29 Mar 2005 03:06:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/2575.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/2575.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/2575.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/2575.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/2575.html</trackback:ping><description><![CDATA[<SPAN class=myp111><FONT id=zoom><B>一． 下载并安装Easy Struts包</B> <BR><BR>1． Easy Struts是开放源代码组织sourceforge.net上的一个项目，目前最新的版本是0.64，可以在它的网站http://sourceforge.net/projects/easystruts上下载压缩文件org.easystruts.eclipse_0.6.4.zip (Eclipse的安装这里不再描述) <BR><BR>2． 将下载后的压缩包解压到{ECLIPSE}\plugins目录下，其中{ECLIPSE}为Eclipse所在的目录。应该注意的是解压后plugins目录下应该多一个子目录，目录名为：com.cross.easystruts.eclipse_[ver]，[ver]为相应的版本号。 <BR><BR>3． Easy Struts的初始化设置：启动Eclipse，通过菜单Windows-&gt;Preferences打开设置窗口。窗口的左边会多出一个选项：Easy Struts。如下图所示： <BR><BR>
<CENTER><IMG src="http://tech.ccidnet.com/pub/attachment/2004/5/292627.png"> <BR><BR>图1 </CENTER><BR><BR>在设置Easy Struts之前要先准备好struts所需的文件（可在Jakarta网站上下载），包括struts.jar，然后是几个用于定义标签库的描述文件包括：struts_html.tld、struts_bean.tld、struts_logic.tld。点击设置窗口左边的Easy Struts后出现如下图2：Easy Struts的初始化设置界面。根据所下载struts的版本选择struts 1.0或者struts 1.1，最新的版本都是struts 1.1。在这里我们选择struts 1.1并在struts 1.1所需的库文件中找到并添加struts.jar文件。在struts 1.1所需的类型库描述文件中建议至少加入前面提到的三个描述文件分别是：struts_html.tld、struts_bean.tld、struts_logic.tld，这三个文件会在创建struts应用项目时自动拷贝到项目的WEB-INF目录下。设置界面上的其他内容基本上按照默认的值即可，无需改动。好了，点击OK按钮关闭设置对话框使设置生效。至此我们已经完成Easy Struts的基本设置可以开始struts的开发了。 <BR><BR>
<CENTER><IMG src="http://tech.ccidnet.com/pub/attachment/2004/5/292629.png"> <BR><BR>图2 </CENTER><BR><BR><B>二． 配置和开发struts应用项目</B> <BR><BR>1． 新建JAVA项目并设置为支持Easy Struts：新建一个JAVA项目，暂且把这个项目命名为：StrutsDemo，新建完项目后除了Eclipse本身用来描述JAVA项目的两个文件.project和.classpath外并没有其他任何文件。为了使该项目集成Easy Struts，只需点击工具条上的新建图标，选择Java-&gt;Easy Struts-&gt;Add Easy Struts Support并点击下一步按钮，如下图3所示： <BR><BR>
<CENTER><IMG src="http://tech.ccidnet.com/pub/attachment/2004/5/292631.png"> <BR><BR>图3 </CENTER><BR><BR>点击下一步按钮后到了Easy Struts项目一些基本信息的输入界面，如下图4所示： <BR><BR>
<CENTER><IMG src="http://tech.ccidnet.com/pub/attachment/2004/5/292633.png"> <BR><BR>图4 </CENTER><BR><BR>在图4的界面中输入web应用程序的目录名，例如struts_demo，发布的时候只需要把该目录打包成WAR文件即可。根据应用服务器的实际情况选择web应用程序的版本，建议使用2.3，目前大部分应用服务器的主流版本都支持2.3版本的WEB应用程序。选择Struts 1.1。修改Easy Struts自动产生的程序包名，例如test.struts。点击完成按钮后Easy Struts会自动产生文件.easystrutsplugin以及创建刚刚在设置界面中输入的web应用服务器目录名的目录，该目录包含WEB-INF子目录，Easy Struts会自动拷贝几个tld文件到该目录下，我们还必须手工的将struts.jar文件拷贝到WEB-INF\lib目录下。 <BR><BR>同时产生一个配置文件：ApplicationResources.properties，这个文件可以用来存放用户自己的配置信息。该配置文件支持多语言，struts会自动根据环境配置信息来获取相应的配置文件，例如ApplicationResources_zh_CN.properties对应的是中文简体的配置信息。web.xml也在这个时候产生，该文件已经自动定义好ActionServlet入口，以及这个servlet所需的参数，用户无需再行配置，极大的方便了初学者学习和使用struts。要改变刚刚所输入的信息只需要打开项目的属性对话框即可修改这些配置。 <BR><BR>2． 开发struts应用程序：在开发struts应用程序之前还有一项准备工作要做，需要给项目的类路径中添加struts.jar以及servlet.jar这两个文件否则新建struts程序时将会有一大堆错误信息，并设置项目的默认输出文件夹为\struts_demo\WEB-INF\classes。在解压完后，Easy Struts插件已经在Eclipse开发环境中添加了几个用于创建struts相关程序的向导。点击工具条上的新建图标打开图3所示的对话框即可选择与struts相关的程序的开发，例如最常用的Action类的开发，Form类的开发等等。下面我们着重介绍一下Action类的开发。 <BR><BR>在新建对话框中选择新建Easy Action并点击下一步按钮 <BR><BR>
<CENTER><IMG src="http://tech.ccidnet.com/pub/attachment/2004/5/292635.png"> <BR><BR>图5</CENTER><BR><BR>在用例文本框(Use case)中输入Action类的名称，Easy Struts会自动根据你输入的值更新对应的URL请求的路径(path)以及Action类名(Type)。在这个界面中Module name文本框指的是当有多个struts_config文件时选择其他文件，默认的是使用struts_config.xml。而Form name是当Action类需要与一个Form类对应时做的选择。其他信息使用默认的值，点击下一步添加错误处理，以及页面的跳转处理。点击完成按钮即已完成Action类的向导，Easy Struts自动产生类XxxxAction.java，并在struts_config.xml做相应的配置信息。所有的这些向导也可以通过双击项目文件struts_config.xml，并在struts_config.xml界面中操作链接来完成，这是一个可视化的界面用来编辑struts_config.xml内容。如下图所示： <BR><BR>
<CENTER><IMG src="http://tech.ccidnet.com/pub/attachment/2004/5/292637.png"> <BR><BR>图6</CENTER><BR><BR><B>三． 总结</B> <BR><BR>Easy Struts现在还是一个开发中的版本，但是其具有的强大的功能以及易用性已经显露无疑。通过与Eclipse的结合使Struts的开发变成一件非常简单的事情。本文只是简单的介绍了如何开始来使用Easy Struts这个插件帮助我们开发struts应用程序，具体与struts相关的开发已经有非常多的文章介绍了，况且这也不是我们的本意，所以此处并没有做深入的叙述。总而言之，Easy Struts还有很多功能需要我们继续的发掘。 <BR><BR><B>四． 参考资料</B> <BR><BR>www.eclipse.org Eclipse的官方网站； <BR><BR>http://sourceforge.net/projects/easystruts Easy Struts项目网站 <BR><BR>http://jakarta.apache.org/struts/index.html Apache Struts网站 <BR><BR><B>关于作者：</B> <BR><BR>刘冬，珠海市创我科技发展有限公司软件工程师，主要从事J2EE方面的开发。电子邮件：winter.lau@163.com <BR></FONT></SPAN><img src ="http://www.blogjava.net/sgsoft/aggbug/2575.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-03-29 11:06 <a href="http://www.blogjava.net/sgsoft/articles/2575.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts原理与实践（8）</title><link>http://www.blogjava.net/sgsoft/articles/2388.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Wed, 23 Mar 2005 10:00:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/2388.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/2388.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/2388.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/2388.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/2388.html</trackback:ping><description><![CDATA[在上一篇文章中介绍JavaScript实现级联下拉菜单的例子，本篇继续介绍一个利用现存的JavaScript代码配合struts构成一个树型菜单的例子。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;大家知道，树型菜单在应用中有着十分广泛的用途。实现树型菜单的途径较多，本文介绍的一种觉得理解起来比较直观，与上篇文章的方法比较类似：就是将树型菜单的节点保存在数据库表中（当然，在实际项目中，节点的信息往往并不是放在一个单一的表中的。比如：在一个权限管理系统中，这些信息可能分别放在用户表、角色表、功能表等表中，只要设法让查询出来的结果与下面给出的表格的内容相似就可以了。只要稍微有些数据库方面的知识做到这点并不难,详细的实现细节超出了本文的主题,不在此细说）。通过数据访问对象将其从数据库中查出后放在一个集合对象中，并将该集合对象传递给客户端，再用一段现存的JavaScript代码--dtree(一个免费的JavaScript程序)来操作集合中的数据。大方向确定之后，我们就来具体着手来实现它。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;根据dtree的要求,我们来建一个数据库表来存储树的节点信息，表名为functions，其结构如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>id字段：varchar 10 主键--节点标识码
pid字段：varchar 10 not null--父节点标识码
name字段：varchar 20 not null
url字段：varchar 50 not
null--这个字段存储的是点击该节点时，要定位的资源（比如一个页面的url），
为了不使本文的篇幅过长，暂时不给出相应的页面，
您可以随便输入一个字母比如：a,以使本例能够正常运行。
title字段：varchar 20
target字段:varchar 10
icon字段：varchar 20
iconopen字段：varchar 20
opened字段：char 1</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;在表中输入如下一些记录以供后面的实验用： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>0、-1、我的权限、javascript: void(0);
00、0、用户管理、javascript: void(0);
0001、00、创建新用户；
0002、00、删除用户；
01、0、	文章管理、javascript: void(0);
0101、01、添加新文章；
0102、01、修改文章；
0103、01、删除文章；</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;到此，数据库方面的准备工作就告一段落。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;接下来的工作我们仍然在先前介绍的mystruts项目中进行。先编写一个名为：FunctionsForm的ActionForm，其代码如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package entity;
import org.apache.struts.action.*;
import javax.servlet.http.*;

public class FunctionsForm extends ActionForm {
  private String icon;
  private String iconOpen;
  private String id;
  private String name;
  private String opened;
  private String pid;
  private String target;
  private String title;
  private String url;
  public String getIcon() {
    return icon;
  }
  public void setIcon(String icon) {
    this.icon = icon;
  }
  public String getIconOpen() {
    return iconOpen;
  }
  public void setIconOpen(String iconOpen) {
    this.iconOpen = iconOpen;
  }
  public String getId() {
    return id;
  }
  public void setId(String id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public String getOpened() {
    return opened;
  }
  public void setOpened(String opened) {
    this.opened = opened;
  }
  public String getPid() {
    return pid;
  }
  public void setPid(String pid) {
    this.pid = pid;
  }
  public String getTarget() {
    return target;
  }
  public void setTarget(String target) {
    this.target = target;
  }
  public String getTitle() {
    return title;
  }
  public void setTitle(String title) {
    this.title = title;
  }
  public String getUrl() {
    return url;
  }
  public void setUrl(String url) {
    this.url = url;
  }
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;因为我们的树型节点的数据都存储在数据库表中，接下来，要做一个数据访问对象类，名称为：FunctionsDao.java，其代码如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package db;
import java.sql.*;
import java.util.*;
import entity.FunctionsForm;

public class FunctionsDao {
  private static Connection con = null;

  public FunctionsDao(Connection con) {
    this.con=con;
  }

  public static Collection findTree() {
    PreparedStatement ps=null;
    ResultSet rs = null;
    ArrayList list=new ArrayList();

    String sql="select * from functions";

    try{
      if(con.isClosed()){
        throw new IllegalStateException("error.unexpected");

      }
      ps=con.prepareStatement(sql);

      rs=ps.executeQuery();
      while(rs.next()){
        FunctionsForm functionsForm=new FunctionsForm();
        functionsForm.setId(rs.getString("id"));
        functionsForm.setPid(rs.getString("pid"));
        functionsForm.setName(rs.getString("name"));
        functionsForm.setUrl(rs.getString("url"));
        functionsForm.setTitle(rs.getString("title"));
        functionsForm.setTarget(rs.getString("target"));
        functionsForm.setIcon(rs.getString("icon"));
        functionsForm.setIconOpen(rs.getString("iconOpen"));
        functionsForm.setOpened(rs.getString("opened"));
        list.add(functionsForm);

      }
      return list;
    }
    catch(SQLException e){
        e.printStackTrace();
        throw new RuntimeException("error.unexpected");
    }
    finally{
      try{
        if(ps!=null)
          ps.close();
        if(rs!=null)
          rs.close();
      }catch(SQLException e){
        e.printStackTrace();
        throw new RuntimeException("error.unexpected");
      }
    }
  }
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;这里值得注意的是：在以往我们见到的一些显示树型菜单的程序，如：一些asp程序中往往简单地采用递归调用的方法来查找到树的各个节点。这对那些树的深度不确定的场合还是有些用处，但这种处理方法也有一个致命的弱点，那就是反复地进行数据库查询，对一些节点较多的应用，对应用程序性能的影响是非常大的，有时会慢得让人难以接受；而在实际的应用中大多数情况下树的深度往往是有限的，如：用于会计科目的树一般最多也在六层以下。又如：用作网页功能菜单的情况，网页设计的原则就有一条是：达到最终目的地，鼠标点击次数最好不要多于三次。因此，在实际设计存储树型结构的表时要考虑查询的效率。对能确定树的最大深度的情况下，要设法尽量优化查询语句，减少查询次数，以提高应用程序的性能同时减少数据库的负荷。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;本例对应的Action的名称为FunctionsAction，其代码如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package action;

import entity.*;
import org.apache.struts.action.*;
import javax.servlet.http.*;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;
import db.FunctionsDao;

public class FunctionsAction extends Action {
  public ActionForward execute(ActionMapping actionMapping, ActionForm actionForm,
  HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) 
  {
    DataSource dataSource;
    Connection cnn=null;
    ActionErrors errors=new ActionErrors();
    try{
      dataSource = getDataSource(httpServletRequest,"A");
      cnn = dataSource.getConnection();
      FunctionsDao functionsDao=new FunctionsDao(cnn);
      Collection col=functionsDao.findTree();
      httpServletRequest.setAttribute("treeList",col);

      return actionMapping.findForward("success");
    }
    catch(Throwable e){
      e.printStackTrace();
      //throw new RuntimeException("未能与数据库连接");
      ActionError error=new ActionError(e.getMessage());
      errors.add(ActionErrors.GLOBAL_ERROR,error);
    }
    finally{
      try{
        if(cnn!=null)
          cnn.close();
      }
      catch(SQLException e){
        throw new RuntimeException(e.getMessage());
      }
    }
    saveErrors(httpServletRequest,errors);
    return actionMapping.findForward("fail");
  }
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;在struts-config.xml文件中加入如下内容： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;form-beans&gt;    
    &lt;form-bean name="functionsForm" type="entity.FunctionsForm" /&gt;
  &lt;/form-beans&gt;
&lt;action-mappings&gt;
    &lt;action name="functionsForm" path="/functionsAction" scope="request"
	type="action.FunctionsAction" validate="false" &gt;
&lt;forward name="success" path="/testDTree.jsp" /&gt;
&lt;forward name="fail" path="/genericError.jsp" /&gt;
    &lt;/action&gt;
  &lt;/action-mappings&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;为了对应配置中的<FORWARD name="fail" path="/genericError.jsp" />,我们还要提供一个显示错误信息的jsp页面,其代码如下: <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html; charset=UTF-8" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;
genericError
&lt;/title&gt;
&lt;link href="css/mycss.css" rel="stylesheet" type="text/css"&gt;
&lt;/head&gt;
&lt;body bgcolor="#ffffff"&gt;
&lt;html:errors/&gt;
&lt;/body&gt;
&lt;/html&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;下面，我们来看一下我们显示树型菜单的页面代码，从配置中可以看出，页面的名称为testDTree.jsp，代码如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html; charset=UTF-8" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;
testDTree
&lt;/title&gt;
&lt;link rel="StyleSheet" href="css/dtree.css" type="text/css" /&gt;
&lt;/head&gt;
&lt;body bgcolor="#eeeeee"&gt;
&lt;body leftmargin="0" topmargin="0"&gt;&lt;table width="180"&gt;
&lt;tr&gt;&lt;td height="300" valign="top" nowrap&gt;
&lt;script type="text/javascript" src="js/dtree.js"&gt;&lt;/script&gt;
&lt;script type='text/javascript'&gt;
tree = new dTree('tree');
tree.config.folderLinks=false;
tree.config.useCookies=false;
&lt;logic:iterate id="functionsForm" name="treeList" scope="request"
type="entity.FunctionsForm"&gt;
    tree.add("&lt;bean:write name="functionsForm" property="id"/&gt;","&lt;bean:write
	name="functionsForm" property="pid"/&gt;","&lt;bean:write name="functionsForm"
	property="name"/&gt;","&lt;bean:write name="functionsForm"
	property="url"/&gt;","&lt;bean:write name="functionsForm"
	property="title"/&gt;","&lt;bean:write name="functionsForm"
	property="target"/&gt;","&lt;bean:write name="functionsForm" property="icon"/&gt;");
&lt;/logic:iterate&gt;
      document.write(tree);
&lt;/script&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;从
<SCRIPT src="js/dtree.js" type=text/javascript></SCRIPT>
 可以看出，我们要在mystruts目录下，建一个名为js的目录，并将下载的dtree文件dtree.js放在该目录中。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;再在mystruts目录下分别建一个名为img和名为css的目录,将dtree中用到的图标和层叠样式表单文件分别放在相应的目录中。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;有关dtree的使用方法,详见其说明文档，如:api.html。笔者在此要感谢dtree的作者为我们提供了一个结构如此清晰的javascript程序！ <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;现在，可以编译执行这个例子程序了，编译后在浏览器中输入：http://127.0.0.1:8080/mystruts/functionsAction.do就可以看到运行效果。效果图为: <BR><BR>
<CENTER><IMG src="http://tech.ccidnet.com/pub/attachment/2004/11/354685.gif"></CENTER><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;注:dtree的下载地址为: http://www.destroydrop.com/javascripts/tree/<img src ="http://www.blogjava.net/sgsoft/aggbug/2388.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-03-23 18:00 <a href="http://www.blogjava.net/sgsoft/articles/2388.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts原理与实践（7）</title><link>http://www.blogjava.net/sgsoft/articles/2387.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Wed, 23 Mar 2005 09:59:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/2387.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/2387.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/2387.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/2387.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/2387.html</trackback:ping><description><![CDATA[<SPAN class=myp111><FONT id=zoom>在上一篇文章中介绍校验时提到客户边的校验用到了JavaScript，实际上用Struts配合JavaScript还可以实现许多有用的功能，比如，级联下拉菜单的实现就是一个典型的例子： <BR><BR>本例假设要实现的是一个文章发布系统，我们要发布的文章分为新闻类和技术类，其中新闻类又分为时事新闻和行业动态；技术类又分为操作系统、数据库、和编程语言等，为了便于添加新的条目，所有这些都保存在数据库表中。 <BR><BR>为此，我们建立一个名为articleClass的表和一个名为articleSubClass的表。 <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>articleClass表的结构如下：
articleClassID字段：char类型，长度为2，主键
articleClassName字段：varchar类型，长度为20
articleSubClass表的结构如下：
articleClassID字段：char类型，长度为2
articleSubClassID字段：char类型，长度为2与articleClassID一起构成主键
articleSubClassName字段：varchar类型，长度为20</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>表建好后,在articleClass表中录入如下数据:如,01、新闻类；02、技术类 <BR><BR>在articleSubClass表中录入：01、01、时事新闻；01、02、行业动态；02、01、操作系统等记录。到这里，数据库方面的准备工作就已做好。 <BR><BR>有了前面做登录例子的基础，理解下面要进行的工作就没有什么难点了，我们现在的工作也在原来mystruts项目中进行。首先，建立需要用到的formbean即ArticleClassForm，其代码如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package entity;
import org.apache.struts.action.*;
import javax.servlet.http.*;
import java.util.Collection;

public class ArticleClassForm extends ActionForm {
  //为select的option做准备
  private Collection beanCollection;
  private String singleSelect = "";
  private String[] beanCollectionSelect = { "" };
  private String articleClassID;
  private String articleClassName;
  private String subI;//子类所在行数
  private String subJ;//子类所在列数
  private String articleSubClassID;
  private String articleSubClassName;

  public Collection getBeanCollection(){
    return beanCollection;
  }

  public void setBeanCollection(Collection beanCollection){
    this.beanCollection=beanCollection;
  }

  public String getSingleSelect() {
    return (this.singleSelect);
  }
  public void setSingleSelect(String singleSelect) {
    this.singleSelect = singleSelect;
  }
  public String[] getBeanCollectionSelect() {
    return (this.beanCollectionSelect);
  }
  public void setBeanCollectionSelect(String beanCollectionSelect[]) {
    this.beanCollectionSelect = beanCollectionSelect;
  }

  public String getArticleClassID() {
    return articleClassID;
  }
  public void setArticleClassID(String articleClassID) {
    this.articleClassID = articleClassID;
  }
  public String getArticleClassName() {
    return articleClassName;
  }
  public void setArticleClassName(String articleClassName) {
    this.articleClassName = articleClassName;
  }

  public String getSubI() {
    return subI;
  }
  public void setSubI(String subI) {
    this.subI = subI;
  }

  public String getSubJ() {
    return subJ;
  }
  public void setSubJ(String subJ) {
    this.subJ = subJ;
  }

  public String getArticleSubClassID() {
    return articleSubClassID;
  }
  public void setArticleSubClassID(String articleSubClassID) {
    this.articleSubClassID = articleSubClassID;
  }

  public String getArticleSubClassName() {
    return articleSubClassName;
  }
  public void setArticleSubClassName(String articleSubClassName) {
    this.articleSubClassName = articleSubClassName;
  }
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>将它放在包entity中。其次，我们的系统要访问数据库，因此也要建立相应的数据库访问对象ArticleClassDao,其代码如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package db;

import entity.ArticleClassForm;
import db.*;
import java.sql.*;

import java.util.Collection;
import java.util.ArrayList;
import org.apache.struts.util.LabelValueBean;
public class ArticleClassDao {
  private Connection con;

  public ArticleClassDao(Connection con) {
    this.con=con;
  }
  public Collection findInUseForSelect(){
    PreparedStatement ps=null;
    ResultSet rs=null;
    ArrayList list=new ArrayList();
    String sql="select * from articleClass order by articleClassID";
    try{
      if(con.isClosed()){
        throw new IllegalStateException("error.unexpected");
      }
      ps=con.prepareStatement(sql);
      rs=ps.executeQuery();

      while(rs.next()){
        String value=rs.getString("articleClassID");
        String label=rs.getString("articleClassName");
        list.add(new LabelValueBean(label,value));
      }
      return list;
    }
    catch(SQLException e){
      e.printStackTrace();
      throw new RuntimeException("error.unexpected");
    }
    finally{
      try{
        if(ps!=null)
          ps.close();
        if(rs!=null)
          rs.close();

      }
      catch(SQLException e){
        e.printStackTrace();
        throw new RuntimeException("error.unexpected");
      }
    }
  }

  public Collection findInUseForSubSelect(){
    PreparedStatement ps=null;
    ResultSet rs=null;
    PreparedStatement psSub=null;
    ResultSet rsSub=null;
    int i=0;//大类记数器
    int j=0;//小类记数器
    String classID="";
    String subClassID="";
    String subClassName="";

    ArrayList list=new ArrayList();
    ArticleClassForm articleClassForm;


    String sql="select * from articleClass order by articleClassID";
    try{
      if(con.isClosed()){
        throw new IllegalStateException("error.unexpected");
      }
      ps=con.prepareStatement(sql);
      rs=ps.executeQuery();

      while(rs.next()){
        i++;
        classID=rs.getString("articleClassID");
        String sqlSub="select * from articleSubClass where articleClassID=? 
		order by articleSubClassID";
        psSub=con.prepareStatement(sqlSub);
        psSub.setString(1,classID);
        rsSub=psSub.executeQuery();

        articleClassForm=new ArticleClassForm();
        articleClassForm.setSubI(""+i);
        articleClassForm.setSubJ(""+j);
        articleClassForm.setArticleSubClassID("请输入一个小类");
        articleClassForm.setArticleSubClassName("请输入一个小类");
        list.add(articleClassForm);

        while(rsSub.next()){
          subClassID=rsSub.getString("articleSubClassID");
          subClassName=rsSub.getString("articleSubClassName");
          j++;
          //optionStr="articleSubClassGroup[" + i + "][" + j + "]=
new Option('"+ subClassName +"','"+ subClassID+ "')";
          articleClassForm=new ArticleClassForm();
          articleClassForm.setSubI(""+i);
          articleClassForm.setSubJ(""+j);
          articleClassForm.setArticleSubClassID(subClassID);
          articleClassForm.setArticleSubClassName(subClassName);
          list.add(articleClassForm);
        }

        j=0;
      }
      return list;
    }
    catch(SQLException e){
      e.printStackTrace();
      throw new RuntimeException("error.unexpected");
    }
    finally{
      try{
        if(ps!=null)
          ps.close();
        if(rs!=null)
          rs.close();

      }
      catch(SQLException e){
        e.printStackTrace();
        throw new RuntimeException("error.unexpected");
      }
    }
  }
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>将它保存在db目录中。它们的目的是将文章的类和子类信息从数据库表中读出，以一定的格式保存在集合对象中以供页面显示。 <BR><BR>再次,我们要建立相应的jsp文件,文件名为selectArticleClass.jsp,代码如下: <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html; charset=UTF-8" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;
选择文件类别
&lt;/title&gt;
&lt;/head&gt;
&lt;body bgcolor="#ffffff"&gt;
&lt;h3&gt;
选择文件所属类型
&lt;/h3&gt;
&lt;html:errors/&gt;
&lt;table width="500" border="0" cellspacing="0" cellpadding="0"&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;html:form name="articleClassForm" type="entity.ArticleClassForm"
	action="selectArticleClassAction.do"&gt;
        &lt;table width="500" border="0" cellspacing="0" cellpadding="0"&gt;
          &lt;tr&gt;
            &lt;td align="right"&gt;文章大类*&lt;/td&gt;
            &lt;td&gt;
              &lt;html:select property="articleClassID" styleClass="word"
			 onchange="articleClassFormredirect(this.options.selectedIndex)"&gt;
                &lt;html:option value=""&gt;请选择一个大类&lt;/html:option&gt;
                &lt;html:optionsCollection name="articleClassForm" property="beanCollection" styleClass="word"/&gt;
              &lt;/html:select&gt;
            &lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
            &lt;td align="right"&gt;文章小类*&lt;/td&gt;
            &lt;td&gt;
              &lt;select name="articleSubClassID" Class="word" &gt;
                &lt;option value=""&gt;请选择一个小类&lt;/option&gt;
              &lt;/select&gt;
              &lt;SCRIPT language=JavaScript&gt;
              &lt;!--
              var articleSubClassGroups=document.articleClassForm.articleClassID.
			 options.length
              var articleSubClassGroup=new Array(articleSubClassGroups)
              for (i=0; i&lt;articleSubClassGroups; i++)
              articleSubClassGroup[i]=new Array()
              &lt;logic:iterate name="articleSubClassList" id="articleClassForm"
			  scope="request" type="entity.ArticleClassForm"&gt;
                articleSubClassGroup[&lt;bean:write name="articleClassForm"
				property="subI"/&gt;][&lt;bean:write name="articleClassForm"
				property="subJ"/&gt;]=new Option("&lt;bean:write name="articleClassForm"
			property="articleSubClassName"/&gt;","&lt;bean:write name="articleClassForm"
			property="articleSubClassID"/&gt;")
              &lt;/logic:iterate&gt;
              var articleSubClassTemp=document.articleClassForm.articleSubClassID
              function articleClassFormredirect(x){
                for (m=articleSubClassTemp.options.length-1;m&gt;0;m--)
                articleSubClassTemp.options[m]=null
                for (i=0;i&lt;articleSubClassGroup[x].length;i++){
                  articleSubClassTemp.options[i]=new
		Option(articleSubClassGroup[x][i].text,
                   articleSubClassGroup[x][i].value)
                }
                articleSubClassTemp.options[0].selected=true
              }
             //--&gt;
           &lt;/SCRIPT&gt;
            &lt;/td&gt;
          &lt;/tr&gt;
        &lt;/table&gt;
      &lt;/html:form&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>这里值得重点关注的是其中的JavaScript代码,有兴趣的可以仔细分析一下它们是怎样配合集合中的元素来实现级联选择的。 <BR><BR>最后，为了例子的完整。我们将涉及到action代码和必要的配置代码在下面列出：其中，action的文件名为SelectArticleClassAction.java,代码如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package action;
import entity.*;
import org.apache.struts.action.*;
import javax.servlet.http.*;
import javax.sql.DataSource;
import java.sql.Connection;
import db.ArticleClassDao;
import java.util.Collection;
import java.sql.SQLException;
public class SelectArticleClassAction extends Action {
  public ActionForward execute(ActionMapping actionMapping, ActionForm actionForm,
  HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
    /**@todo: complete the business logic here, this is just a skeleton.*/
    ArticleClassForm articleClassForm = (ArticleClassForm) actionForm;
    DataSource dataSource;
    Connection cnn=null;
    ActionErrors errors=new ActionErrors();
    try{
      dataSource = getDataSource(httpServletRequest,"A");
      cnn = dataSource.getConnection();
      ArticleClassDao articleClassDao=new ArticleClassDao(cnn);

      Collection col=articleClassDao.findInUseForSelect();
      articleClassForm.setBeanCollection(col);
      httpServletRequest.setAttribute("articleClassList",col);

      //处理子类选项
      Collection subCol=articleClassDao.findInUseForSubSelect();
      httpServletRequest.setAttribute("articleSubClassList",subCol);
      return actionMapping.findForward("success");
    }
    catch(Throwable e){
      e.printStackTrace();
      //throw new RuntimeException("未能与数据库连接");
      ActionError error=new ActionError(e.getMessage());
      errors.add(ActionErrors.GLOBAL_ERROR,error);
    }
    finally{
      try{
        if(cnn!=null)
          cnn.close();
      }
      catch(SQLException e){
        throw new RuntimeException(e.getMessage());
      }
    }
    saveErrors(httpServletRequest,errors);
    return actionMapping.findForward("fail");
  }
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>将其保存在action目录中。 <BR><BR>在struts-config.xml文件中做如下配置： <BR><BR>在<CCID_NOBR> 
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;form-beans&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR>中加入<CCID_NOBR> 
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;form-bean name="articleClassForm" type="entity.ArticleClassForm" /&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>在<CCID_NOBR> 
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&gt;&lt;action-mappings&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR>中加入： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;action name="articleClassForm" path="/selectArticleClassAction" scope="session"
	type="action.SelectArticleClassAction" validate="false"&gt;
&lt;forward name="success" path="/selectArticleClass.jsp" /&gt;
&lt;forward name="fail" path="/genericError.jsp" /&gt;
&lt;/action&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>为了对应配置中的<CCID_NOBR> 
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;forward name="fail" path="/genericError.jsp" /&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR>,我们还要提供一个显示错误信息的jsp页面,其代码如下: <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html; charset=UTF-8" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;
genericError
&lt;/title&gt;
&lt;link href="css/mycss.css" rel="stylesheet" type="text/css"&gt;
&lt;/head&gt;
&lt;body bgcolor="#ffffff"&gt;
&lt;html:errors/&gt;
&lt;/body&gt;
&lt;/html&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>现在一切就绪，可以编译执行了。在浏览器中输入：http://127.0.0.1:8080/mystruts/selectArticleClassAction.do就可以看到该例子的运行结果了。(</FONT></SPAN><img src ="http://www.blogjava.net/sgsoft/aggbug/2387.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-03-23 17:59 <a href="http://www.blogjava.net/sgsoft/articles/2387.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts原理与实践（6）</title><link>http://www.blogjava.net/sgsoft/articles/2386.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Wed, 23 Mar 2005 09:58:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/2386.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/2386.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/2386.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/2386.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/2386.html</trackback:ping><description><![CDATA[本文我们来讨论一下Struts中的输入校验问题。我们知道，信息系统有垃圾进垃圾出的特点，为了避免垃圾数据的输入，对输入进行校验是任何信息系统都要面对的问题。在传统的编程实践中，我们往往在需要进行校验的地方分别对它们进行校验，而实际上需要校验的东西大多都很类似，如必需的字段、日期、范围等等。因此，应用程序中往往到处充斥着这样一些显得冗余的代码。而与此形成鲜明对照的是Struts采用Validator框架（Validator框架现在是Jakarta Commons项目的一部分）来解决校验问题，它将校验规则代码集中到外部的且对具体的应用程序中立的.xml文件中，这样，就将那些到处出现的校验逻辑从应用程序中分离出来，任何一个Struts应用都可以使用这个文件，同时还为校验规则的扩展提供了便利。更难能可贵的是由于Validator框架将校验中要用到的一些消息等信息与资源绑定有机结合在一起，使得校验部分的国际化编程变得十分的便捷和自然。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;Validator框架大致有如下几个主要组件： <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;<B>Validators：</B>是Validator框架调用的一个Java类，它处理那些基本的通用的校验，包括required、mask（匹配正则表达式）、最小长度、最大长度、范围、日期等 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;<B>.xml配置文件：</B>主要包括两个配置文件，一个是validator-rules.xml，另一个是validation.xml。前者的内容主要包含一些校验规则，后者则包含需要校验的一些form及其组件的集合。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;<B>资源绑定：</B>提供（本地化）标签和消息，缺省地共享struts的资源绑定。即校验所用到的一些标签与消息都写在ApplicationResources.properity文件中。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;<B>Jsp tag：</B>为给定的form或者action path生成JavaScript validations。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;<B>ValidatorForm：</B>它是ActionForm的一个子类。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;为了对Validator框架有一个比较直观的认识，我们还是以前面的登陆例子的输入来示范一下Validator框架的使用过程： <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;首先,找一个validator-rules.xml文件放在mystruts\WEB-INF目录下,下面是该文件中涉及到的required验证部分代码的清单: <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;validator name="required"
&lt;!--①--&gt;
            classname="org.apache.struts.validator.FieldChecks"
               method="validateRequired"
         methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.apache.struts.action.ActionErrors,
javax.servlet.http.HttpServletRequest"
&lt;!--②--&gt;
                  msg="errors.required"&gt;
&lt;!--③--&gt;
         &lt;javascript&gt;&lt;![CDATA[
            function validateRequired(form) {
                var isValid = true;
                var focusField = null;
                var i = 0;
                var fields = new Array();
                oRequired = new required();
                for (x in oRequired) {
                	var field = form[oRequired[x][0]];
                	
                    if (field.type == 'text' ||
                        field.type == 'textarea' ||
                        field.type == 'file' ||
                        field.type == 'select-one' ||
                        field.type == 'radio' ||
                        field.type == 'password') {
                        
                        var value = '';
				// get field's value
				if (field.type == "select-one") {
				var si = field.selectedIndex;
				if (si &gt;= 0) {
				value = field.options[si].value;
							}
						} else {
							value = field.value;
						}
                        
                        if (trim(value).length == 0) {
                        
	                        if (i == 0) {
	                            focusField = field;
	                        }
	                        fields[i++] = oRequired[x][1];
	                        isValid = false;
                        }
                    }
                }
                if (fields.length &gt; 0) {
                   focusField.focus();
                   alert(fields.join('\n'));
                }
                return isValid;
            }
            
            // Trim whitespace from left and right sides of s.
            function trim(s) {
                return s.replace( /^\s*/, "" ).replace( /\s*$/, "" );
            }
            
            ]]&gt;
         &lt;/javascript&gt;

&lt;/validator&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;① 节的代码是引用一个服务器边的验证器,其对应的代码清单如下: <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public static boolean validateRequired(Object bean,
                                           ValidatorAction va, Field field,
                                           ActionErrors errors,
                                           HttpServletRequest request) {

        String value = null;
        if (isString(bean)) {
            value = (String) bean;
        } else {
            value = ValidatorUtil.getValueAsString(bean, field.getProperty());
        }
        
        if (GenericValidator.isBlankOrNull(value)) {
            errors.add(field.getKey(), Resources.getActionError(request, va, field));
            return false;
        } else {
            return true;
        }
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;② 节是验证失败后的出错信息,要将对应这些键值的信息写入到ApplicationResources.properity文件中,常见的错误信息如下: <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE># Standard error messages for validator framework checks
errors.required={0} is required.
errors.minlength={0} can not be less than {1} characters.
errors.maxlength={0} can not be greater than {1} characters.
errors.invalid={0} is invalid.
errors.byte={0} must be a byte.
errors.short={0} must be a short.
errors.integer={0} must be an integer.
errors.long={0} must be a long.
errors.float={0} must be a float.
errors.double={0} must be a double.
errors.date={0} is not a date.
errors.range={0} is not in the range {1} through {2}.
errors.creditcard={0} is an invalid credit card number.
errors.email={0} is an invalid e-mail address.</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;③ 节的代码用于客户边的JavaScript验证 <BR><BR>其次,在validation.xml文件中配置要验证的form极其相应的字段,下面是该文件中的代码: <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE form-validation PUBLIC "-//Apache Software Foundation
//DTD Commons Validator Rules Configuration 1.0//EN" 
"http://jakarta.apache.org/commons/dtds/validator_1_0.dtd"&gt;
&lt;form-validation&gt;
&lt;formset&gt;
&lt;form name="userInfoForm"&gt;
&lt;field property="username"
depends="required,mask,minlength,maxlength"&gt;
&lt;arg0 key="logon.jsp.prompt.username" resource="true"/&gt;
&lt;arg1 name="minlength" key="${var:minlength}" resource="false"/&gt;
&lt;arg1 name="maxlength" key="${var:maxlength}" resource="false"/&gt;
&lt;var&gt;
&lt;var-name&gt;mask&lt;/var-name&gt;
&lt;var-value&gt;^\w&lt;/var-value&gt;
&lt;/var&gt;
&lt;var&gt;
&lt;var-name&gt;minlength&lt;/var-name&gt;
&lt;var-value&gt;2&lt;/var-value&gt;
&lt;/var&gt;
&lt;var&gt;
&lt;var-name&gt;maxlength&lt;/var-name&gt;
&lt;var-value&gt;16&lt;/var-value&gt;
&lt;/var&gt;
&lt;/field&gt;
&lt;field property="password"
depends="required,minlength,maxlength"&gt;
&lt;arg0 key="logon.jsp.prompt.password" resource="true"/&gt;
&lt;arg1 name="minlength" key="${var:minlength}" resource="false"/&gt;
&lt;arg1 name="maxlength" key="${var:maxlength}" resource="false"/&gt;
&lt;var&gt;
&lt;var-name&gt;minlength&lt;/var-name&gt;
&lt;var-value&gt;2&lt;/var-value&gt;
&lt;/var&gt;
&lt;var&gt;
&lt;var-name&gt;maxlength&lt;/var-name&gt;
&lt;var-value&gt;16&lt;/var-value&gt;
&lt;/var&gt;
&lt;/field&gt;
&lt;/form&gt;
&lt;/formset&gt;
&lt;/form-validation&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;这里要注意的是：该文中的<ARG0 key="logon.jsp.prompt.username" resource="true" />和<ARG0 key="logon.jsp.prompt.password" resource="true" />中的键值都是取自资源绑定中的。前面还讲到了出错信息也是写入ApplicationResources.properity文件中，因此，这就为国际化提供了一个很好的基础。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;再次，为了使服务器边的验证能够进行，将用到的formBean从ActionForm的子类改为ValidatorForm的子类，即：<BR>&nbsp;&nbsp;&nbsp;&nbsp;将public class UserInfoForm extends ActionForm改为：public class UserInfoForm extends ValidatorForm <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;到此，进行服务器边的验证工作已经一切准备得差不多了，此时，只要完成最后步骤就可以实验服务器边的验证了。但大多数情况下，人们总希望把这些基本的简单验证放在客户边进行。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;为了能进行客户边的验证，我们还要对logon.jsp文件做适当的修改。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;将<CCID_NOBR> 
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;html:form action="/logonAction.do" focus="username"&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR>改为<BR>&nbsp;&nbsp;&nbsp;&nbsp;<CCID_NOBR> 
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;html:form action="/logonAction.do" focus="username" onsubmit="return validateUserInfoForm(this)"&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;在标签后加上：<BR>&nbsp;&nbsp;&nbsp;&nbsp;<CCID_NOBR> 
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;html:javascript dynamicJavascript="true" staticJavascript="true" formName="userInfoForm"/&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;最后，对struts的配置文件struts-config.xml作适当的修改：<BR>&nbsp;&nbsp;&nbsp;&nbsp;1、将<CCID_NOBR> 
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;action input="/logon.jsp" name="userInfoForm"
 path="/logonAction" scope="session" type="action.LogonAction" validate="false" &gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR>改为<BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;action input="/logon.jsp" name="userInfoForm" 
path="/logonAction" scope="session" type="action.LogonAction" validate="true" &gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR>其作用是要求进行校验 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;2、将下列代码放在struts-config.xml文件中的</STRUTS-CONFIG>标签前。其作用是将用于校验的各个组件结合在一起。 <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;plug-in className="org.apache.struts.validator.ValidatorPlugIn"&gt;
    &lt;set-property property="pathnames"
	value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml" /&gt;
&lt;/plug-in&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;到此为止，我们的一切工作准备就绪，您可以享受自己的劳动成果了，试着输入各种组合的用户名和口令，看看它们的验证效果。仔细体会你会发现，服务器边的验证要更全面一些，比如对password的字符长度的验证。 <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;<B>参考文献：</B><BR>《Struts in Action》Ted Husted Cedric Dumoulin George Franciscus David Winterfeldt著<BR>《Programming Jakarta Struts》Chuck Cavaness著 <BR><img src ="http://www.blogjava.net/sgsoft/aggbug/2386.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-03-23 17:58 <a href="http://www.blogjava.net/sgsoft/articles/2386.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts 原理与实践（5） </title><link>http://www.blogjava.net/sgsoft/articles/2385.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Wed, 23 Mar 2005 09:57:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/2385.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/2385.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/2385.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/2385.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/2385.html</trackback:ping><description><![CDATA[<SPAN class=myp111><FONT id=zoom>一个支持i18n的应用程序应该有如下一些特征：<BR>1增加支持的语言时要求不更改程序代码<BR>2字符元素、消息、和图象保存在原代码之外<BR>3依赖于不同文化的数据如：日期时间、小数、及现金符号等数据对用户的语言和地理位置应该有正确的格式<BR>4应用程序能迅速地适应新语言和/或新地区 <BR><BR>Struts主要采用两个i18n组件来实现国际化编程： <BR><BR>第一个组件是一个被应用程序控制器管理的消息类，它引用包含地区相关信息串的资源包。第二个组件是一个JSP定制标签，<?XML:NAMESPACE PREFIX = BEAN /><BEAN:MESSAGE></BEAN:MESSAGE>，它用于在View层呈现被控制器管理的实际的字符串。在我们前面的登录例子中这两方面的内容都出现过。 <BR><BR>用Struts实现国际化编程的标准做法是：生成一个java属性文件集。每个文件包含您的应用程序要显示的所有消息的键/值对。 <BR><BR>这些文件的命名要遵守如下规则，代表英文消息的文件可作为缺省的文件，它的名称是ApplicationResources.properties；其他语种的文件在文件名中都要带上相应的地区和语言编码串，如代表中文的文件名应为ApplicationResources_zh_CN.properties。并且其他语种的文件与ApplicationResources.properties文件要放在同一目录中。 <BR><BR>ApplicationResources.properties文件的键/值都是英文的，而其他语种文件的键是英文的，值则是对应的语言。如在我们前面的登录例子中的键/值对：logon.jsp.prompt.username=Username:在中文文件中就是：logon.jsp.prompt.username=用户名:当然，在实际应用时要把中文转换为AscII码。 <BR><BR>有了上一篇文章和以上介绍的一些基础知识后。我们就可以将我们的登录程序进行国际化编程了。 <BR><BR>首先，我们所有jsp页面文件的字符集都设置为UTF-8。即在页面文件的开始写如下指令行： <BR><BR><%@ page contentType="text/html; charset=UTF-8" %>，在我们的登录例子中已经这样做了，这里不需要再改动。 <BR><BR>其次，将所有的request的字符集也设置为UTF-8。虽然，我们可以在每个文件中加入这样的句子：request.setCharacterEncoding("UTF-8");来解决，但这样显得很麻烦。一种更简单的解决方法是使用filter。具体步骤如下： <BR><BR>在mystruts\WEB-INF\classes目录下再新建一个名为filters的目录，新建一个名为：SetCharacterEncodingFilter的类，并保存在该目录下。其实，这个类并不要您亲自来写，可以借用tomcat中的例子。现将该例子的程序节选如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package filters;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.UnavailableException;

/**
 * &lt;p&gt;Example filter that sets the character encoding to be used in parsing the
 * incoming request, either unconditionally or only if the client did not
 * specify a character encoding.  Configuration of this filter is based on
 * the following initialization parameters:&lt;/p&gt;
 * &lt;ul&gt;
 * &lt;li&gt;&lt;strong&gt;encoding&lt;/strong&gt; - The character encoding to be configured
 *     for this request, either conditionally or unconditionally based on
 *     the &lt;code&gt;ignore&lt;/code&gt; initialization parameter.  This parameter
 *     is required, so there is no default.&lt;/li&gt;
 * &lt;li&gt;&lt;strong&gt;ignore&lt;/strong&gt; - If set to "true", any character encoding
 *     specified by the client is ignored, and the value returned by the
 *     &lt;code&gt;selectEncoding()&lt;/code&gt; method is set.  If set to "false,
 *     &lt;code&gt;selectEncoding()&lt;/code&gt; is called &lt;strong&gt;only&lt;/strong&gt; if the
 *     client has not already specified an encoding.  By default, this
 *     parameter is set to "true".&lt;/li&gt;
 * &lt;/ul&gt;
 *
 * &lt;p&gt;Although this filter can be used unchanged, it is also easy to
 * subclass it and make the &lt;code&gt;selectEncoding()&lt;/code&gt; method more
 * intelligent about what encoding to choose, based on characteristics of
 * the incoming request (such as the values of the &lt;code&gt;Accept-Language&lt;/code&gt;
 * and &lt;code&gt;User-Agent&lt;/code&gt; headers, or a value stashed in the current
 * user's session.&lt;/p&gt;
 *
 * @author Craig McClanahan
 * @version $Revision: 1.2 $ $Date: 2001/10/17 22:53:19 $
 */

public class SetCharacterEncodingFilter implements Filter {


    // ----------------------------------------------------- Instance Variables


    /**
     * The default character encoding to set for requests that pass through
     * this filter.
     */
    protected String encoding = null;


    /**
     * The filter configuration object we are associated with.  If this value
     * is null, this filter instance is not currently configured.
     */
    protected FilterConfig filterConfig = null;


    /**
     * Should a character encoding specified by the client be ignored?
     */
    protected boolean ignore = true;


    // --------------------------------------------------------- Public Methods


    /**
     * Take this filter out of service.
     */
    public void destroy() {

        this.encoding = null;
        this.filterConfig = null;

    }


    /**
     * Select and set (if specified) the character encoding to be used to
     * interpret request parameters for this request.
     *
     * @param request The servlet request we are processing
     * @param result The servlet response we are creating
     * @param chain The filter chain we are processing
     *
     * @exception IOException if an input/output error occurs
     * @exception ServletException if a servlet error occurs
     */
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain)
        throws IOException, ServletException {

        // Conditionally select and set the character encoding to be used
        if (ignore || (request.getCharacterEncoding() == null)) {
            String encoding = selectEncoding(request);
            if (encoding != null)
                request.setCharacterEncoding(encoding);
        }

        // Pass control on to the next filter
        chain.doFilter(request, response);

    }


    /**
     * Place this filter into service.
     *
     * @param filterConfig The filter configuration object
     */
    public void init(FilterConfig filterConfig) throws ServletException {

        this.filterConfig = filterConfig;
        this.encoding = filterConfig.getInitParameter("encoding");
        String value = filterConfig.getInitParameter("ignore");
        if (value == null)
            this.ignore = true;
        else if (value.equalsIgnoreCase("true"))
            this.ignore = true;
        else if (value.equalsIgnoreCase("yes"))
            this.ignore = true;
        else
            this.ignore = false;

    }


    // ------------------------------------------------------ Protected Methods


    /**
     * Select an appropriate character encoding to be used, based on the
     * characteristics of the current request and/or filter initialization
     * parameters.  If no character encoding should be set, return
     * &lt;code&gt;null&lt;/code&gt;.
     * &lt;p&gt;
     * The default implementation unconditionally returns the value configured
     * by the &lt;strong&gt;encoding&lt;/strong&gt; initialization parameter for this
     * filter.
     *
     * @param request The servlet request we are processing
     */
    protected String selectEncoding(ServletRequest request) {

        return (this.encoding);

    }

}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>其中，request.setCharacterEncoding(encoding);是一个关键句子。 <BR><BR>为了让该类工作，我们还要在web.xml文件中对它进行配置，配置代码如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;filter&gt;
    &lt;filter-name&gt;Set Character Encoding&lt;/filter-name&gt;
    &lt;filter-class&gt;filters.SetCharacterEncodingFilter&lt;/filter-class&gt;
    &lt;init-param&gt;
      &lt;param-name&gt;encoding&lt;/param-name&gt;
      &lt;param-value&gt;UTF-8&lt;/param-value&gt;
    &lt;/init-param&gt;
  &lt;/filter&gt;
  &lt;filter-mapping&gt;
    &lt;filter-name&gt;Set Character Encoding&lt;/filter-name&gt;
    &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
  &lt;/filter-mapping&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>最后，就是准备资源包文件，我们以创建一个中文文件为例： <BR><BR>将ApplicationResources.properties文件打开，另存为ApplicationResources_zh.properties，这只是一个过渡性质的文件。将文件中键/值对的值都用中文表示。更改完后的代码如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>#Application Resource for the logon.jsp
logon.jsp.title=登录页
logon.jsp.page.heading=欢迎 世界!
logon.jsp.prompt.username=用户名:
logon.jsp.prompt.password=口令:
logon.jsp.prompt.submit=提交
logon.jsp.prompt.reset=复位

#Application Resource for the main.jsp
main.jsp.title=主页
main.jsp.welcome=欢迎:

#Application Resource for the LogonAction.java
error.missing.username=&lt;li&gt;&lt;font color="red"&gt;没有输入用户名&lt;/font&gt;&lt;/li&gt;
error.missing.password=&lt;li&gt;&lt;font color="red"&gt;没有输入口令&lt;/font&gt;&lt;/li&gt;

#Application Resource for the UserInfoBo.java
error.noMatch=&lt;li&gt;&lt;font color="red"&gt;没有匹配的用户&lt;/font&gt;&lt;/li&gt;

#Application Resource for the UserInfoBo.java
error.logon.invalid=&lt;li&gt;&lt;font color="red"&gt;用户名/口令是无效的&lt;/font&gt;&lt;/li&gt;
error.removed.user=&lt;li&gt;&lt;font color="red"&gt;找不到该用户&lt;/font&gt;&lt;/li&gt;
error.unexpected=&lt;li&gt;&lt;font color="red"&gt;不可预期的错误&lt;/font&gt;&lt;/li&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>使用native2ascii工具将上面文件中的中文字符转换为ascii码，并生成一个最终使用的资源文件ApplicationResources_zh_CN.properties。 <BR><BR>具体做法是打开一个dos窗口，到mystruts\WEB-INF\classes目录下，运行如下语句： <BR><BR>native2ascii -encoding GBK ApplicationResources_zh.properties ApplicationResources_zh_CN.properties <BR><BR>生成的文件ApplicationResources_zh_CN.properties的内容如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>#Application Resource for the logon.jsp
logon.jsp.title=\u767b\u5f55\u9875
logon.jsp.page.heading=\u6b22\u8fce \u4e16\u754c!
logon.jsp.prompt.username=\u7528\u6237\u540d:
logon.jsp.prompt.password=\u53e3\u4ee4:
logon.jsp.prompt.submit=\u63d0\u4ea4
logon.jsp.prompt.reset=\u590d\u4f4d

#Application Resource for the main.jsp
main.jsp.title=\u4e3b\u9875
main.jsp.welcome=\u6b22\u8fce:

#Application Resource for the LogonAction.java
error.missing.username=&lt;li&gt;&lt;font color="red"&gt;\u6ca1\u6709\u8f93\u5165\u7528\u6237\u540d&lt;/font&gt;&lt;/li&gt;
error.missing.password=&lt;li&gt;&lt;font color="red"&gt;\u6ca1\u6709\u8f93\u5165\u53e3\u4ee4&lt;/font&gt;&lt;/li&gt;

#Application Resource for the UserInfoBo.java
error.noMatch=&lt;li&gt;&lt;font color="red"&gt;\u6ca1\u6709\u5339\u914d\u7684\u7528\u6237&lt;/font&gt;&lt;/li&gt;

#Application Resource for the UserInfoBo.java
error.logon.invalid=&lt;li&gt;&lt;font color="red"&gt;\u7528\u6237\u540d/\u53e3\u4ee4\u662f\u65e0\u6548\u7684&lt;/font&gt;&lt;/li&gt;
error.removed.user=&lt;li&gt;&lt;font color="red"&gt;\u627e\u4e0d\u5230\u8be5\u7528\u6237&lt;/font&gt;&lt;/li&gt;
error.unexpected=&lt;li&gt;&lt;font color="red"&gt;\u4e0d\u53ef\u9884\u671f\u7684\u9519\u8bef&lt;/font&gt;&lt;/li&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>从这里可以看出，所有的中文字都转换成了对应的Unicode码。 <BR><BR>现在，再运行登录例子程序，您会发现它已经是显示的中文了。在浏览器的"工具"--"Internet选项"的"语言首选项"对话框中，去掉"中文（中国）"加上英文，再试登录程序，此时，又会显示英文。这就是说不同国家（地区）的客户都可以看到自己语言的内容，这就实现了国际化编程的基本要求。如果还要显示其他语言，可采用类似处理中文的方法进行，这里就不细讲了。 <BR><BR>本文中的例子程序所采用的数据库仍然是MS SQLServer2000，数据库字符集为gbk。实验表明，对简、繁体中文，英文及日文字符都能支持。 <BR><BR><B>参考文献：</B><BR>《Programming Jakarta Struts》Chuck Cavaness著<BR>《Mastering Jakarta Struts》James Goodwill著 <BR></FONT></SPAN><img src ="http://www.blogjava.net/sgsoft/aggbug/2385.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-03-23 17:57 <a href="http://www.blogjava.net/sgsoft/articles/2385.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts原理与实践（4）</title><link>http://www.blogjava.net/sgsoft/articles/2384.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Wed, 23 Mar 2005 09:52:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/2384.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/2384.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/2384.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/2384.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/2384.html</trackback:ping><description><![CDATA[<SPAN class=myp111><FONT id=zoom>本篇我们来讨论一下struts的国际化编程问题，即所谓的i18n编程问题，这一篇我们讨论其基础部分。与这个问题紧密相关的是在各java论坛中被频繁提及的中文乱码问题，因为，英、美编程人员较少涉及到中文乱码问题，因此，这方面的英文资料也是非常奇缺的，同时也很少找到这方面比较完整的中文资料，本文也尝试对中文乱码问题做一些探讨。要解决上述问题，需要有一定的字符集方面的知识，下面，我们就先介绍字符集的有关情况： <BR><BR><B>一、从ASCII到Unicode（UTF-8）</B> <BR><BR>电子计算机技术是从美国开始发展起来的，因为美国使用的文字为英文，美国规定的计算机信息交换用的字符编码集是人们熟知的扩展ASCII码，它以8bit字节为单位存储，ASCII的0-31及127为控制符，32-126为可见字符，包括所有的英文字母，阿拉伯数字和其他一些常见符号，128-255的ASCII码则没有定义。 <BR><BR>ASCII对英语国家是够用了，但对其他西欧国家却不够用，因此，人们将ASCII扩展到0-255的范围，形成了ISO-8859-1字符集。值得一提的是，因为考虑到程序中处理的信息大多是西文信息，因此有些WEB容器（如：Tomcat4.x）在处理所接收到的request字符串时，如果您没指定request的编码方式则系统就缺省地采用ISO-8859-1，明白这一点对理解后面的问题会有帮助。 <BR><BR>相比西方的拼音文字，东方的文字（如中文）的字符数要大得多，根本不可能在一个字节内将它们表示出来，因此，它们以两个字节为单位存储，以中文国标字符集GB2312为例，它的第一个字节为128-255。系统可以据此判断，若第一个字节大于127，则把与该字节后紧接着的一个字节结合起来共两个字节组成一个中文字符。这种由多个字节存储一个字符的字符集叫多字节字符集（MultiByte Charsets），对应的象ASCII这种用一个字节存储一个字符的字符集叫单字节字符集（SingleByte Charsets）。在GB2312字符集中，ASCII字符仍然用一个字节存储，换句话说该ASCII是该字符集的子集。 <BR><BR>GB2312只包含数千个常用汉字，往往不能满足实际需要，因此，人们对它进行扩展，这就有了我们现在广泛使用的GBK字符集，GBK是现阶段Windows及其他一些中文操作系统的缺省字符集。它包含2万多个字符，除了保持和GB2312兼容外，还包含繁体中文字，日文字符和朝鲜字符。值得注意的是GBK只是一个规范而不是国家标准，新的国家标准是GB18030-2000，它是比GBK包含字符更多的字符集。 <BR><BR>我国的台湾地区使用的文字是繁体字，其字符集是BIG5，而日本采用的字符集则是SJIS。它们的编码方法与GB2312类似，它们的ASCII字符部分是兼容的，但扩展部分的编码则是不兼容的，比如这几种字符集中都有"中文"这两个字符，但他们在各自的字符集中的编码并不相同，这就是用GB2312写成的网页用BIG5浏览时，看到的是乱糟糟的信息的原因。 <BR><BR>可见，在字符集的世界里，呈现给我们的是一个群雄割据的局面，各字符集拥有一块自己的地盘。这给各国和各地区交换信息带来了很大的困难，同时，也给国际化（本地化）编程造成了很大的麻烦。 <BR><BR>常言道："分久必合"，随着国际标准ISO10646定义的通用字符集（Universal Character Set即UCS）的出现，使这种局面发生了彻底的改观。UCS 是所有其他字符集标准的一个超集. 它保证与其他字符集是双向兼容的. 就是说, 如果你将任何文本字符串翻译到 UCS格式, 然后再翻译回原编码, 你不会丢失任何信息。UCS 包含了用于表达所有已知语言的字符。不仅包括拉丁语、希腊语、 斯拉夫语、希伯来语、阿拉伯语、亚美尼亚语和乔治亚语的描述、还包括中文、 日文和韩文这样的象形文字、 以及平假名、片假名、 孟加拉语、 旁遮普语果鲁穆奇字符(Gurmukhi)、 泰米尔语、印.埃纳德语(Kannada)、Malayalam、泰国语、 老挝语、 汉语拼音(Bopomofo)、Hangul、 Devangari、Gujarati、Oriya、Telugu 以及其他数也数不清的语。对于还没有加入的语言， 由于正在研究怎样在计算机中最好地编码它们， 因而最终它们都将被加入。 <BR><BR>ISO 10646 定义了一个 31 位的字符集。 然而， 在这巨大的编码空间中， 迄今为止只分配了前 65534 个码位 (0x0000 到 0xFFFD)。 这个 UCS 的 16位子集称为 基本多语言面 (Basic Multilingual Plane, BMP)。 将被编码在 16 位 BMP 以外的字符都属于非常特殊的字符(比如象形文字)， 且只有专家在历史和科学领域里才会用到它们。 <BR><BR>UCS 不仅给每个字符分配一个代码， 而且赋予了一个正式的名字。 表示一个 UCS 值的十六进制数， 通常在前面加上 "U+", 就象 U+0041 代表字符"拉丁大写字母A"。 UCS 字符 U+0000 到 U+007F 与 US-ASCII(ISO 646) 是一致的， U+0000 到 U+00FF 与 ISO 8859-1(Latin-1) 也是一致的。这里要注意的是它是以16bit为单位存储，即便对字母"A"也是用16bit，这是与前面介绍的所有字符集不同的地方。 <BR><BR>历史上，在国际标准化组织研究ISO10646标准的同时，另一个由多语言软件制造商组成的协会也在从事创立单一字符集的工作，这就是现在人们熟知的Unicode。幸运的是，1991年前后ISO10646和Unicode的参与者都认识到，世界上不需要两个不同的单一字符集。他们合并双方的工作成果，并为创立单一编码表而协同工作。两个项目仍都存在并独立地公布各自的标准，都同意保持ISO10646和Unicode的码表兼容，并紧密地共同调整任何未来的扩展。这与当年在PC机上的操作系统MS-dos与PC-dos的情形有些相象。后面，我们将视ISO10646和Unicode为同一个东西。 <BR><BR>有了Unicode，字符集问题接近了完美的解决，但不要高兴得过早。由于历史的原因：一些操作系统如：Unix、Linux等都是基于ASCII设计的。此外，还有一些数据库管理系统软件如：Oracle等也是围绕ASCII来设计的(从其8i的白皮书上介绍的设置系统字符集和字段的字符集中可以间接地看到这一点)。在这些系统中直接用Unicode会导致严重的问题。用这些编码的字符串会包含一些特殊的字符， 比如 '\0' 或 '/'， 它们在 文件名和其他 C 库函数参数里都有特别的含义。 另外， 大多数使用 ASCII 文件的 UNIX 下的工具， 如果不进行重大修改是无法读取 16 位的字符的。 基于这些原因， 在文件名, 文本文件, 环境变量等地方，直接使用Unicode是不合适的。 <BR><BR>在 ISO 10646-1 Annex R 和 RFC 2279 里定义的 UTF-8 （Unicode Transformation Form 8-bit form）编码没有这些问题。 <BR><BR>UTF-8 有以下一些特性： <BR><BR>UCS 字符 U+0000 到 U+007F (ASCII) 被编码为字节 0x00 到 0x7F (ASCII 兼容)。 这意味着只包含 7 位 ASCII 字符的文件在 ASCII 和 UTF-8 两种编码方式下是一样的。 <BR><BR>所有 &gt;U+007F 的 UCS 字符被编码为一个多个字节的串， 每个字节都有标记位集。 因此，ASCII 字节 (0x00-0x7F) 不可能作为任何其他字符的一部分。 <BR><BR>表示非 ASCII 字符的多字节串的第一个字节总是在 0xC0 到 0xFD 的范围里, 并指出这个字符包含多少个字节。 多字节串的其余字节都在 0x80 到 0xBF 范围里。 这使得重新同步非常容易， 并使编码无国界，且很少受丢失字节的影响。 <BR><BR>UTF-8 编码字符理论上可以最多到 6 个字节长， 然而 16 位 BMP 字符最多只用到 3 字节长。 <BR><BR>字节 0xFE 和 0xFF 在 UTF-8 编码中从未用到。 <BR><BR>通过，UTF-8这种形式，Unicode终于可以广泛的在各种情况下使用了。在讨论struts的国际化编程之前，我们先来看看我们以前在jsp编程中是怎样处理中文问题以及我们经常遇到的：<BR><BR><SPAN class=myp111><FONT id=zoom><B>二、中文字符乱码的原因及解决办法</B> <BR><BR>java的内核是Unicode的，也就是说，在程序处理字符时是用Unicode来表示字符的，但是文件和流的保存方式是使用字节流的。在java的基本数据类型中，char是Unicode的，而byte是字节，因此，在不同的环节java要对字节流和char进行转换。这种转换发生时如果字符集的编码选择不当，就会出现乱码问题。 <BR><BR>我们常见的乱码大致有如下几种情形：<BR>1、汉字变成了问号"？"<BR>2、有的汉字显示正确，有的则显示错误<BR>3、显示乱码（有些是汉字但并不是你预期的）<BR>4、读写数据库出现乱码 <BR><BR>下面我们逐一对它们出现的原因做一些解释： <BR><BR>首先，我们讨论汉字变成问号的问题。 <BR><BR>Java中byte与char相互转换的方法在sun.io包中。其中，byte到char的常用转换方法是：<BR>public static ByteToCharConverter getConverter(String encoding); <BR><BR>为了便于大家理解，我们先来做一个小实验：比如，汉字"你"的GBK编码为0xc4e3，其Unicode编码是\u4f60。我们的实验是这样的，先有一个页面比如名为a_gbk.jsp输入汉字"你"，提交给页面b_gbk.jsp。在b_gbk.jsp文件中以某种编码方式得到"你"的字节数组，再将该数组以某种编码方式转换成char，如果得到的char值是0x4f60则转换是正确的。 <BR><BR>a_gbk.jsp的代码如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html; charset=GBK" language="java" import="java.sql.*" errorPage="" %&gt;
&lt;table width="611" border="0" align="center" cellpadding="0" cellspacing="0"&gt;
  &lt;tr&gt;
    &lt;td&gt;&amp;nbsp;&lt;/td&gt;
    &lt;td class="bigword"&gt;&amp;nbsp;&lt;/td&gt;
    &lt;td&gt;&amp;nbsp;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td width="100"&gt;&amp;nbsp;&lt;/td&gt;
    &lt;td class="bigword"&gt;Input&lt;/td&gt;
    &lt;td width="100"&gt;&amp;nbsp;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&amp;nbsp;&lt;/td&gt;
    &lt;td class="bigword"&gt;&amp;nbsp;&lt;/td&gt;
    &lt;td&gt;&amp;nbsp;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;table width="611" border="0" align="center" cellpadding="0" cellspacing="0"&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;form method="post" action="b_gbk.jsp"&gt;
        &lt;table width="611" border="0" cellpadding="0" cellspacing="0"&gt;
          &lt;tr&gt;
            &lt;td width="100" align="right"&gt;&lt;/td&gt;
            &lt;td&gt;&lt;input name="ClsID" type="text" class="word" id="ClsID" maxlength="2" &gt;
              *&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
            &lt;td width="100" align="right"&gt;&amp;nbsp;&lt;/td&gt;
            &lt;td&gt;&lt;input name="btn" type="submit" value="OK"&gt;
             &lt;/td&gt;
          &lt;/tr&gt;
        &lt;/table&gt;
      &lt;/form&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>b_gbk.jsp的代码如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html; charset=GBK" import="sun.io.*,java.util.*" %&gt;
&lt;%
String a=(String)request.getParameter("ClsID");
byte b[]=a.getBytes("ISO8859-1");
for(int j=0;j&lt;b.length;j++){
  out.println(Integer.toHexString(b[j])+"&lt;br&gt;");
}
ByteToCharConverter convertor=ByteToCharConverter.getConverter("GBK");
char[] c=convertor.convertAll(b);
out.println("b length:"+b.length+"&lt;br&gt;");
out.println("c length:"+c.length+"&lt;br&gt;");
for(int i=0;i&lt;c.length;i++){
 	out.println(Integer.toHexString(c[i])+"&lt;br&gt;");
}
String a1=new String(a.getBytes("ISO8859-1"),"GBK");
%&gt;
&lt;%="a是:"+a%&gt;&lt;br&gt;
&lt;%="a1是:"+a1%&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>在浏览器中打开a_gbk.jsp并输入一个"你"字，点击OK按钮提交表单，则会出现如图1所示的结果： <BR><BR><IMG src="http://tech.ccidnet.com/pub/attachment/2004/9/340196.jpg"> <BR><BR>图1 <BR><BR>从图1可以看出，在b_gbk.jsp中这样将byte转换为char是正确的，即得到的char是\u4f60。这里要注意的是：byte b[]=a.getBytes("ISO8859-1");中的编码是ISO8859-1，这就是我们前面提到的有些web容器在您没有指定request的字符集时它就采用缺省的ISO8859-1。 <BR><BR>从图1中我们还看到表达式<%="a是:"+a%>中的a并没有正确地显示"你"而是变成"??"这是什么原因呢？这里的a是作为一个String被显示的，我们来看看我们常用的String构造函数： <BR><BR>String(byte[] bytes,String encoding); <BR><BR>在国标平台上，该函数会认为bytes是按GBK编码的，如果后一个参数省略，它也会认为是encoding是GBK。 <BR><BR>对前一个参数就相当于将b_gbk.jsp文件的这句byte b[]=a.getBytes("ISO8859-1");中的ISO8859-1改为GBK，这样显然在GBK字符集中找不到相应的目的编码，它给出的结果是0x3f、0x3f。因此，就会显示为"??"，这也就是造成乱码的第一种现象的原因。我们的例子是演示的从byte到char的转换过程，相反的过程也会造成同样的问题，限于篇幅，就不在此讨论了，大家自己可以做类似的实验来验证。 <BR><BR>解决该问题的方法就是象例子中a1那样，在获取byte数组时，指定编码为ISO8859-1。 <BR><BR>接下来，我们讨论有些汉字能正常显示，有些不能正常显示的问题。 <BR><BR>如果我们将String a1=new String(a.getBytes("ISO8859-1"),"GBK");中的GBK改为GB2312则象朱镕基的"镕"字就不能正常显示，这是因为该字是GBK中的字符而在GB2312中不存在。 <BR><BR>解决上述两种问题的方法就是象a1那样构造String，也就是人们常说的同时也是常用的转码的方法。采用这种方法会在程序中到处出现这种语句，特别是在Struts中，Struts有一个回写表单的功能，在回写时也要做这种转换，这样的语句差不多要多一倍。因此，这是个比较笨拙的方法，有没有简捷一些的方法呢？其实是有的，只要在取得request的字符串前加上request.setCharacterEncoding("GBK");这句，指定request的字符集。则<%="a是:"+a%>中的a就能正常显示，a1反而不能正常显示。此时要将byte b[]=a.getBytes("ISO8859-1");中的ISO8859-1变成GBK，从byte到char的转换才是正确的，这就是此时a能正常显示而a1反而不能正常显示的原因。如果此时要a1正常显示则必须将String a1=new String(a.getBytes("ISO8859-1"),"GBK");中的ISO8859-1改为GBK。 <BR><BR>很显然，使用request.setCharacterEncoding("GBK");只能解决GBK字符问题，要解决i18n问题则要使用UTF-8来取代GBK。我们接着做上述实验，将a_gbk.jsp和b_gbk.jsp分别另存为a.jsp和b.jsp将文件中的GBK改为UTF-8，更改后的代码分别如下： <BR><BR>a.jsp代码： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html; charset=UTF-8" language="java" import="java.sql.*" errorPage="" %&gt;

&lt;table width="611" border="0" align="center" cellpadding="0" cellspacing="0"&gt;
  &lt;tr&gt;
    &lt;td&gt;&amp;nbsp;&lt;/td&gt;
    &lt;td class="bigword"&gt;&amp;nbsp;&lt;/td&gt;
    &lt;td&gt;&amp;nbsp;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td width="100"&gt;&amp;nbsp;&lt;/td&gt;
    &lt;td class="bigword"&gt;Input&lt;/td&gt;
    &lt;td width="100"&gt;&amp;nbsp;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&amp;nbsp;&lt;/td&gt;
    &lt;td class="bigword"&gt;&amp;nbsp;&lt;/td&gt;
    &lt;td&gt;&amp;nbsp;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;table width="611" border="0" align="center" cellpadding="0" cellspacing="0"&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;form method="post" action="b.jsp"&gt;
        &lt;table width="611" border="0" cellpadding="0" cellspacing="0"&gt;
          &lt;tr&gt;
            &lt;td width="100" align="right"&gt;&lt;/td&gt;
            &lt;td&gt;&lt;input name="ClsID" type="text" class="word" id="ClsID" maxlength="2" &gt;
              *&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
            &lt;td width="100" align="right"&gt;&amp;nbsp;&lt;/td&gt;
            &lt;td&gt;&lt;input name="btn" type="submit" value="OK"&gt;
             &lt;/td&gt;
          &lt;/tr&gt;
        &lt;/table&gt;
      &lt;/form&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
b.jsp代码：
&lt;ccid_nobr&gt;
&lt;table width="400" border="1" cellspacing="0" cellpadding="2" 
 bordercolorlight = "black" bordercolordark = "#FFFFFF" align="center"&gt;
&lt;tr&gt;
    &lt;td bgcolor="e6e6e6" class="code" style="font-size:9pt"&gt;
    &lt;pre&gt;&lt;ccid_code&gt;  &lt;%@ page contentType="text/html; charset=UTF-8" import="sun.io.*,java.util.*" %&gt;

&lt;%
request.setCharacterEncoding("UTF-8");
String a=(String)request.getParameter("ClsID");
byte b[]=a.getBytes("UTF-8");
for(int j=0;j&lt;b.length;j++){
  out.println(Integer.toHexString(b[j])+"&lt;br&gt;");
}
ByteToCharConverter convertor=ByteToCharConverter.getConverter("UTF-8");
char[] c=convertor.convertAll(b);
out.println("b length:"+b.length+"&lt;br&gt;");
out.println("c length:"+c.length+"&lt;br&gt;");
for(int i=0;i&lt;c.length;i++){
  out.println(Integer.toHexString(c[i])+"&lt;br&gt;");
}
String a1=new String(a.getBytes("UTF-8"),"UTF-8");
%&gt;
&lt;%="a是:"+a%&gt;&lt;br&gt;
&lt;%="a1是:"+a1%&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>再在a.jsp中输入"你"字，你会发现显示结果中，一个汉字是用三个byte表示的，它们的值分别是0xe4、0xbd、0xa0，也就是说用UTF-8来表示汉字，每个汉字要比GBK多占用一个byte，这也是使用UTF-8要多付出的一点代价吧。 <BR><BR>现在，我们讨论一下第三个问题，即显示乱码，有些莫名其妙的汉字并不是你预期的结果。 <BR><BR>在上例中将String a1=new String(a.getBytes("UTF-8"),"UTF-8");改为String a1=new String(a.getBytes("UTF-8"),"GBK");再输入"你"字，则a1会显示成"浣?"，您只要看一看"浣"的UTF-8码和GBK码就会知道其中的奥秘了。 <BR><BR>下面，我们讨论一下最后一个问题，就是读写数据库时出现乱码。 <BR><BR>现在一些常用的数据库都支持数据库encoding，也就是说在创建数据库时可以指定它自己的字符集设置，数据库数据以指定的编码形式存储。当应用程序访问数据库时，在入口和出口处都会有encoding转换。如果，在应用程序中字符本来已变成了乱码，当然也就无法正确地转换为数据库的字符集了。数据库的encoding可根据需要来设置，比如要支持简、繁体中文、日、韩、英语选GBK，如果还要支持其他语言最好选UTF-8。 <BR><BR>本篇文章对字符集及中文乱码问题做了一下探讨，为实现国际化编程的实践打下一个基础。下一篇文章，我们将介绍struts中实现国际化编程的具体步骤，并将我们前面介绍的登录例子进行国际化。 <BR><BR><B>参考文献：</B> <BR><BR>UTF-8 and Unicode FAQ <BR><BR>《JSP动态网站技术入门与提高》太阳工作室 孙晓龙 赵莉编著 </FONT></SPAN></FONT></SPAN><img src ="http://www.blogjava.net/sgsoft/aggbug/2384.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-03-23 17:52 <a href="http://www.blogjava.net/sgsoft/articles/2384.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts原理与实践（3） </title><link>http://www.blogjava.net/sgsoft/articles/2383.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Wed, 23 Mar 2005 09:51:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/2383.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/2383.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/2383.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/2383.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/2383.html</trackback:ping><description><![CDATA[<SPAN class=myp111><FONT id=zoom><B>一、JDBC的工作原理</B> <BR><BR>Struts在本质上是java程序，要在Struts应用程序中访问数据库，首先，必须搞清楚Java Database Connectivity API(JDBC)的工作原理。正如其名字揭示的，JDBC库提供了一个底层API，用来支持独立于任何特定SQL实现的基本SQL功能。提供数据库访问的基本功能。它是将各种数据库访问的公共概念抽取出来组成的类和接口。JDBC API包括两个包：java.sql(称之为JDBC内核API)和javax.sql（称之为JDBC标准扩展）。它们合在一起，包含了用Java开发数据库应用程序所需的类。这些类或接口主要有：<BR>Java.sql.DriverManager<BR>Java.sql.Driver<BR>Java.sql.Connection<BR>Java.sql.Statement<BR>Java.sql.PreparedStatement<BR>Java.sql.ResultSet等 <BR><BR>这使得从Java程序发送SQL语句到数据库变得比较容易，并且适合所有SQL方言。也就是说为一种数据库如Oracle写好了java应用程序后，没有必要再为MS SQL Server再重新写一遍。而是可以针对各种数据库系统都使用同一个java应用程序。这样表述大家可能有些难以接受，我们这里可以打一个比方：联合国开会时，联合国的成员国的与会者（相当我们这里的具体的数据库管理系统）往往都有自己的语言（方言）。大会发言人（相当于我们这里的java应用程序）不可能用各种语言来发言。你只需要使用一种语言（相当于我们这里的JDBC）来发言就行了。那么怎么保证各成员国的与会者都听懂发言呢，这就要依靠同声翻译（相当于我们这里的JDBC驱动程序）。实际上是驱动程序将java程序中的SQL语句翻译成具体的数据库能执行的语句，再交由相应的数据库管理系统去执行。因此，使用JDBC API访问数据库时，我们要针对不同的数据库采用不同的驱动程序，驱动程序实际上是适合特定的数据库JDBC接口的具体实现，它们一般具有如下三种功能：<BR>
<LI>建立一个与数据源的连接<BR>
<LI>发送SQL语句到数据源<BR>
<LI>取回结果集 <BR><BR>那么，JDBC具体是如何工作的呢？ <BR><BR>Java.sql.DriverManager装载驱动程序，当Java.sql.DriverManager的getConnection()方法被调用时，DriverManager试图在已经注册的驱动程序中为数据库（也可以是表格化的数据源）的URL寻找一个合适的驱动程序，并将数据库的URL传到驱动程序的acceptsURL()方法中，驱动程序确认自己有连接到该URL的能力。生成的连接Connection表示与特定的数据库的会话。Statement(包括PreparedStatement和CallableStatement)对象作为在给定Connection上执行SQL语句的容器。执行完语句后生成ResultSet结果集对象，通过结果集的一系列getter就可以访问表中各列的数据。 <BR><BR>这里，是讲的JDBC的基本工作过程，实际应用中，往往会使用JDBC扩展对象如DataSource等，限于篇幅，就不在此详细讨论了。 <BR><BR><B>二、访问数据库所要做的基本配置</B> <BR><BR>我们以访问MS SQL Server2000数据库为例，介绍其基本的配置情况。首先，要到微软网站去下载JDBC的驱动程序，运行setup.exe将得到的三个文件：msbase.jar、mssqlserver.jar及msutil.jar放在<CATALINA_HOME>/webapps/mystruts/WEB-INF/lib目录下。 <BR><BR>在struts-config.xml文件中配置数据源 <BR><BR>这里，有一点要引起大家的注意的，就是，struts-config.xml中配置的各个项目是有一定的顺序要求的，几个主要项目的顺序大致是这样的： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>data-sources
form-beans
action-mappings
message-resources
plug-in</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>在配置时要遵守上述顺序 <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;data-sources&gt;
    &lt;data-source key="A" type="org.apache.commons.dbcp.BasicDataSource"&gt;
      &lt;set-property property="driverClassName"
          value="com.microsoft.jdbc.sqlserver.SQLServerDriver" /&gt;
      &lt;set-property property="url"
          value="jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=mystruts;SelectMethod=cursor" /&gt;
      &lt;set-property property="username" value="sa" /&gt;
      &lt;set-property property="password" value="yourpwd" /&gt;
      &lt;set-property property="maxActive" value="10" /&gt;
      &lt;set-property property="maxWait" value="5000" /&gt;
      &lt;set-property property="defaultAutoCommit" value="false" /&gt;
      &lt;set-property property="defaultReadOnly" value="false" /&gt;
    &lt;/data-source&gt;
  &lt;/data-sources&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>我们来对这段配置代码做一个简单的说明： <BR><BR><DATA-SOURCE type="org.apache.commons.dbcp.BasicDataSource" key="A">这句中，如果您的struts应用程序中只配置一个数据源则key="A"可以不要，而配置多个数据源时就要用这个键值区别，也就是说，可以为一个应用程序配置多个数据源让它访问多个数据库。 <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;set-property property="url" 
        value="jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=mystruts;
        SelectMethod=cursor" /&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>这句中的sqlserver://127.0.0.1:1433;DatabaseName=mystruts;的数据库服务器名（本例是用代表本机的ip地址）和数据库名称要与您的具体情况相同。同时，还要注意访问数据库的用户名和口令也要合乎您的实际情况。 <BR><BR><SET-PROPERTY property="maxActive" value="10" />表示最大的活动连接数，这也说明这些连接是池化（pooling）的。 <BR><BR><SET-PROPERTY property="defaultAutoCommit" value="false" />表示对数据库的增、删、改操作必须显式地提交。即必须使用connect.commit();这样的命令才能真正让数据库表中的记录作相应的改变。设置成这样方便用户组织自己的数据库事务。 <BR><BR><B>三、现在我们就来扩展前面我们讲的那个登录的例子，让它访问存储在数据库表中的用户名和口令信息，同时也让它给出的出错信息更明确一些。</B> <BR><BR>为此，我们先要做一些准备工作，如果您还没有安装MS SQL Server2000请先安装，并下载最新的补丁包。再建一个名为mystruts的数据库，并在该数据库中建一个名为userInfo的表，该表有两个字段既：username和password，它们的字段类型都为varchar(10),其中username为主键。在该表中输入一条记录，username和password的字段值分别为lhb和awave。到此准备工作就基本做好了。 <BR><BR>为了访问数据库，首先，要修改Action类，修改后的代码清单如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package action;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionServlet;
import bussness.UserInfoBo;
import entity.UserInfoForm;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

public final class LogonAction extends Action {

  public ActionForward execute(ActionMapping mapping,
         ActionForm form,
         HttpServletRequest request,
         HttpServletResponse response)
         throws IOException, ServletException {
    UserInfoForm userInfoForm = (UserInfoForm) form;
    //从web层获得用户名和口令
    String username = userInfoForm.getUsername().trim();
    String password = userInfoForm.getPassword().trim();
    //声明错误集对象
    ActionErrors errors = new ActionErrors();
    //声明数据源和连接对象
    DataSource dataSource;
    Connection cnn=null;

    //校验输入
    if(username.equals("")){
      ActionError error=new ActionError("error.missing.username");
      errors.add(ActionErrors.GLOBAL_ERROR,error);
    }
    if(password.equals("")){
      ActionError error=new ActionError("error.missing.password");
      errors.add(ActionErrors.GLOBAL_ERROR,error);
    }

    //调用业务逻辑
    if(errors.size()==0){
      String validated = "";
      try{
        //取得数据库连接
        dataSource = getDataSource(request,"A");
        cnn = dataSource.getConnection();

        UserInfoBo userInfoBo=new UserInfoBo(cnn);
        validated =userInfoBo.validatePwd(username,password);
        if(validated.equals("match")){
          //一切正常就保存用户信息并转向成功的页面
          HttpSession session = request.getSession();
          session.setAttribute("userInfoForm", form);
                return mapping.findForward("success");
        }
      }

      catch(Throwable e){
        //处理可能出现的错误
        e.printStackTrace();
        ActionError error=new ActionError(e.getMessage());
        errors.add(ActionErrors.GLOBAL_ERROR,error);
      }
    }
    //如出错就转向输入页面，并显示相应的错误信息
    saveErrors(request, errors);
    return new ActionForward(mapping.getInput());
  }
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>注意：dataSource = getDataSource(request,"A");这句中，如果配置中只有一个数据源，且没有key="A"，则这句应写为dataSource = getDataSource(request); <BR><BR>从清单上可以看出，主要就是增加了访问数据库的代码。同时，我们的业务对象的形式也发生了一个变化，原来没有参数，现在有一个代表数据库连接的参数cnn，因此我们也要对业务对象进行适当地修改。 <BR><BR>更改后的业务对象代码清单如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package bussness;

import entity.UserInfoForm;
import java.sql.Connection;
import java.sql.SQLException;
import java.lang.Exception;
import db.UserInfoDao;

public class UserInfoBo {
  private Connection cnn=null;

  public UserInfoBo(Connection cnn){
    this.cnn=cnn;
  }

  public String validatePwd(String username,String password){

    String validateResult="";
   
    try{
      UserInfoDao userInfoDao = new UserInfoDao(cnn);
      validateResult=userInfoDao.validatePwd(username,password);
      if(validateResult.equals("error.logon.invalid")){
        //如果用户名与口令不匹配则报此错
        throw new RuntimeException("error.logon.invalid"); 
      }
      else if(validateResult.equals("error.removed.user")){
        //如果找不到用户则报此错，这样用户看到的出错信息会更详细
        throw new RuntimeException("error.removed.user"); 
      }
    }
    catch(Exception e){
      throw new RuntimeException(e.getMessage());
    }
    finally{
      try{
        if(cnn!=null){
          cnn.close();
        }
      }
      catch(SQLException sqle){
        sqle.printStackTrace();
        throw new RuntimeException("error.unexpected");
      }
    }
    return validateResult;
  }
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>这个业务对象的代码还是比较简单的，重点要讲的就是它在validatePwd方法中调用了一个名叫UserInfoDao的对象，它就是真正进行数据库操作的数据访问对象。其代码清单如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package db;
import entity.UserInfoForm;
import java.sql.*;

public class UserInfoDao {
  private Connection con;

  public UserInfoDao(Connection con) {
    this.con=con;
  }
  
  public String validatePwd(String username,String password){
    PreparedStatement ps=null;
    ResultSet rs=null;
    String validated="error.logon.invalid";
    UserInfoForm userInfoForm=null;
    String sql="select * from userInfo where username=?";
    try{
      if(con.isClosed()){
        throw new IllegalStateException("error.unexpected");

      }
      ps=con.prepareStatement(sql);
      ps.setString(1,username);
      rs=ps.executeQuery();
      if(rs.next()){
        if(!rs.getString("password").trim().equals(password)){
          return validated;//口令不正确返回口令不匹配信息
          
        }
        else{

          validated = "match";//口令正确返回口令匹配信息
          return validated;
        }
      }else{
        
        validated="error.removed.user";//没有找到该用户
        return validated;
        
      }

    }catch(SQLException e){
        e.printStackTrace();
        throw new RuntimeException("error.unexpected");
    }finally{
      try{
        if(ps!=null)
          ps.close();
        if(rs!=null)
          rs.close();
      }catch(SQLException e){
        e.printStackTrace();
        throw new RuntimeException("error.unexpected");
      }
    }
  }
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>下面，简单地分析一下数据访问对象的工作过程： <BR><BR>要访问数据库，一般要经历的如下几个步骤：<BR>
<LI>获得到数据库的连接<BR>
<LI>创建SQL语句<BR>
<LI>执行SQL语句<BR>
<LI>管理结果集 <BR><BR>其中，得到数据库的连接本例中是在Action类中完成的，代码如下：<BR>dataSource = getDataSource(request,"A");<BR>cnn = dataSource.getConnection(); <BR><BR>Action在调用业务对象时将连接作为一个参数传给业务对象，再由业务对象传给数据库访问对象。 <BR><BR>要说明一点的是，要将struts-legacy.jar文件放在<CATALINA_HOME>/webapps/mystruts/WEB-INF/lib目录下。 <BR><BR>我们要在<CATALINA_HOME>/webapps/mystruts/WEB-INF/classes目录下再建一个名叫db的子目录，将数据访问类以UserInfoDao.java文件名保存在该子目录中。按照上篇文章介绍的方法，编译各个包中的.java文件。就可以启动Tomcat重新运行您的程序了。 <BR><BR>细心一点的读者可能都注意到了，到目前为止，我们程序中的各种消息都不是用中文表示的，在下一篇文章中，我们将讨论Struts的国际化编程即所谓的i18n编程，对我们在编程中经常遇到的乱码问题也一同作些分析。 <BR><BR><B>参考文献：</B><BR>《JSP Web 编程指南》---电子工业出版社 Jayson Falkner等著 司光亚 牛红等译<BR>《Java数据库编程宝典》John O'Donahue等著 甑广启 于耀等译<BR>《Struts in Action》Ted Husted Cedric Dumoulin George Franciscus David Winterfeldt著<BR>《Programming Jakarta Struts》Chuck Cavaness著<BR>《Mastering Jakarta Struts》James Goodwill著<BR>《Struts Kick Start》James Turner Kevin Bedell著 <BR></LI></FONT></SPAN><img src ="http://www.blogjava.net/sgsoft/aggbug/2383.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-03-23 17:51 <a href="http://www.blogjava.net/sgsoft/articles/2383.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts原理与实践（1----2） </title><link>http://www.blogjava.net/sgsoft/articles/2382.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Wed, 23 Mar 2005 09:44:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/2382.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/2382.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/2382.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/2382.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/2382.html</trackback:ping><description><![CDATA[<SPAN class=myp111><FONT id=zoom><SPAN class=myp111><FONT id=zoom><B>一、 什么是Struts</B> <BR><BR>框架（Framework）是可重用的，半完成的应用程序，可以用来产生专门的定制程序。 <BR><BR>您只要细心地研究真实的应用程序，就会发现程序大致上由两类性质不同的组件组成，一类与程序要处理的具体事务密切相关，我们不妨把它们叫做业务组件；另一类是应用服务。比如说：一个税务征管系统和一个图书管理系统会在处理它们的业务方面存在很大的差异，这些直接处理业务的组件由于业务性质的不同不大可能在不同的系统中重用，而另一些组件如决定程序流向的控制、输入的校验、错误处理及标签库等这些只与程序相关的组件在不同的系统中可以很好地得到重用。人们自然会想要是把这些在不同应用程序中有共性的一些东西抽取出来，做成一个半成品程序，这样的半成品就是所谓的程序框架，再做一个新的东西时就不必白手起家，而是可以在这个基础上开始搭建。实际上，有些大型软件企业选择自己搭建这样的框架。但大多数中小型软件企业或者其他组织，没有条件自己建立框架。 <BR><BR>Struts作为一个开放原代码的应用框架，在最近几年得到了飞速的发展，在JSP Web应用开发中应用得非常广泛，有的文献上说它已经成为JSP Web应用框架的事实上的标准。那么，究竟什么是Struts呢？ <BR><BR>要回答这个问题还得从JSP Web应用的两种基本的结构模式:Model 1和Model 2说起，为了给读者一些实实在在的帮助，并力图让学习曲线变得平坦一些，我想采用实例驱动的方法来逐步深入地回答有关问题，因为，学一门技术的最好方法莫过于在实践中学习、在实践中体会，逐步加深对其精神实质的理解和把握，而不是一上来就引入一大堆新概念让大家觉得无所适从，或者死记硬背一大堆概念而面对一个真正的实际需求束手无策。正如，一个人即使在书本上学成了游泳博士，只要他不下水，我想他也是不大可能真正会游泳的。 <BR><BR>Model 1结构如图1所示： <BR><BR><IMG src="http://tech.ccidnet.com/pub/attachment/2004/8/320186.jpg"> <BR><BR>图1 <BR><BR>mode1 1是一个以JSP文件为中心的模式，在这种模式中JSP页面不仅负责表现逻辑，也负责控制逻辑。专业书籍上称之为逻辑耦合在页面中，这种处理方式，对一些规模很小的项目如：一个简单的留言簿，也没什么太大的坏处，实际上，人们开始接触一些对自己来说是新的东西的时候，比如，用JSP访问数据库时，往往喜欢别人能提供一个包含这一切的单个JSP页面，因为这样在一个页面上他就可以把握全局，便于理解。但是，用Model 1模式开发大型时，程序流向由一些互相能够感知的页面决定，当页面很多时要清楚地把握其流向将是很复杂的事情，当您修改一页时可能会影响相关的很多页面，大有牵一发而动全身的感觉，使得程序的修改与维护变得异常困难；还有一个问题就是程序逻辑开发与页面设计纠缠在一起，既不便于分工合作也不利于代码的重用，这样的程序其健壮性和可伸缩性都不好。 <BR><BR>Grady Booch等人在UML用户指南一书中，强调建模的重要性时，打了一个制作狗窝、私人住宅、和大厦的形象比喻来说明人们处理不同规模的事物时应该采用的合理方法一样，人们对不同规模的应用程序也应该采用不同的模式。 <BR><BR>为了克服Model 1的缺陷，人们引入了Model 2，如图2所示： <BR><BR><IMG src="http://tech.ccidnet.com/pub/attachment/2004/8/320188.jpg"> <BR><BR>图2 <BR><BR>它引入了"控制器"这个概念，控制器一般由servlet来担任，客户端的请求不再直接送给一个处理业务逻辑的JSP页面，而是送给这个控制器，再由控制器根据具体的请求调用不同的事务逻辑，并将处理结果返回到合适的页面。因此，这个servlet控制器为应用程序提供了一个进行前-后端处理的中枢。一方面为输入数据的验证、身份认证、日志及实现国际化编程提供了一个合适的切入点；另一方面也提供了将业务逻辑从JSP文件剥离的可能。业务逻辑从JSP页面分离后，JSP文件蜕变成一个单纯完成显示任务的东西，这就是常说的View。而独立出来的事务逻辑变成人们常说的Model,再加上控制器Control本身，就构成了MVC模式。实践证明，MVC模式为大型程序的开发及维护提供了巨大的便利。 <BR><BR>其实，MVC开始并不是为Web应用程序提出的模式，传统的MVC要求M将其状态变化通报给V，但由于Web浏览器工作在典型的拉模式而非推模式，很难做到这一点。因此有些人又将用于Web应用的MVC称之为MVC2。正如上面所提到的MVC是一种模式，当然可以有各种不同的具体实现，包括您自己就可以实现一个体现MVC思想的程序框架，Struts就是一种具体实现MVC2的程序框架。它的大致结构如图三所示： <BR><BR><IMG src="http://tech.ccidnet.com/pub/attachment/2004/8/320190.gif"> <BR><BR>图三 <BR><BR>图三基本勾勒出了一个基于Struts的应用程序的结构，从左到右，分别是其表示层（view）、控制层(controller)、和模型层(Model)。其表示层使用Struts标签库构建。来自客户的所有需要通过框架的请求统一由叫ActionServlet的servlet接收（ActionServlet Struts已经为我们写好了，只要您应用没有什么特别的要求，它基本上都能满足您的要求），根据接收的请求参数和Struts配置(struts-config.xml)中ActionMapping，将请求送给合适的Action去处理，解决由谁做的问题，它们共同构成Struts的控制器。Action则是Struts应用中真正干活的组件，开发人员一般都要在这里耗费大量的时间，它解决的是做什么的问题，它通过调用需要的业务组件（模型）来完成应用的业务，业务组件解决的是如何做的问题，并将执行的结果返回一个代表所需的描绘响应的JSP（或Action）的ActionForward对象给ActionServlet以将响应呈现给客户。 <BR><BR>过程如图四所示： <BR><BR><IMG src="http://tech.ccidnet.com/pub/attachment/2004/8/320192.jpg"> <BR><BR>图四 <BR><BR>这里要特别说明一下的是：就是Action这个类，上面已经说到了它是Struts中真正干活的地方，也是值得我们高度关注的地方。可是，关于它到底是属于控制层还是属于模型层，存在两种不同的意见，一种认为它属于模型层，如：《JSP Web编程指南》；另一些则认为它属于控制层如：《Programming Jakarta Struts》、《Mastering Jakarta Struts》和《Struts Kick Start》等认为它是控制器的一部分，还有其他一些书如《Struts in Action》也建议要避免将业务逻辑放在Action类中，也就是说，图3中Action后的括号中的内容应该从中移出，但实际中确有一些系统将比较简单的且不打算重用的业务逻辑放在Action中，所以在图中还是这样表示。显然，将业务对象从Action分离出来后有利于它的重用，同时也增强了应用程序的健壮性和设计的灵活性。因此，它实际上可以看作是Controller与Model的适配器，如果硬要把它归于那一部分，笔者更倾向于后一种看法，即它是Controller的一部分，换句话说，它不应该包含过多的业务逻辑，而应该只是简单地收集业务方法所需要的数据并传递给业务对象。实际上，它的主要职责是：<BR>
<LI>校验前提条件或者声明<BR>
<LI>调用需要的业务逻辑方法<BR>
<LI>检测或处理其他错误<BR>
<LI>路由控制到相关视图 <BR><BR>上面这样简单的描述，初学者可能会感到有些难以接受，下面举个比较具体的例子来进一步帮助我们理解。如：假设，我们做的是个电子商务程序，现在程序要完成的操作任务是提交定单并返回定单号给客户，这就是关于做什么的问题，应该由Action类完成，但具体怎么获得数据库连接，插入定单数据到数据库表中，又怎么从数据库表中取得这个定单号（一般是自增数据列的数据），这一系列复杂的问题，这都是解决怎么做的问题，则应该由一个（假设名为orderBo）业务对象即Model来完成。orderBo可能用一个返回整型值的名为submitOrder的方法来做这件事，Action则是先校验定单数据是否正确，以免常说的垃圾进垃圾出；如果正确则简单地调用orderBo的submitOrder方法来得到定单号；它还要处理在调用过程中可能出现任何错误；最后根据不同的情况返回不同的结果给客户。 <BR><BR><B>二、为什么要使用Struts框架</B> <BR><BR>既然本文的开始就说了，自己可以建这种框架，为什么要使用Struts呢？我想下面列举的这些理由是显而易见的：首先，它是建立在MVC这种公认的好的模式上的，Struts在M、V和C上都有涉及，但它主要是提供一个好的控制器和一套定制的标签库上，也就是说它的着力点在C和V上，因此，它天生就有MVC所带来的一系列优点，如：结构层次分明，高可重用性，增加了程序的健壮性和可伸缩性，便于开发与设计分工，提供集中统一的权限控制、校验、国际化、日志等等；其次，它是个开源项目得到了包括它的发明者Craig R.McClanahan在内的一些程序大师和高手持续而细心的呵护，并且经受了实战的检验，使其功能越来越强大，体系也日臻完善；最后，是它对其他技术和框架显示出很好的融合性。如，现在，它已经与tiles融为一体，可以展望，它很快就会与JSF等融会在一起。当然，和其他任何技术一样，它也不是十全十美的，如：它对类和一些属性、参数的命名显得有些随意，给使用带来一些不便；还有如Action类execute方法的只能接收一个ActionForm参数等。但瑕不掩瑜，这些没有影响它被广泛使用。 <BR><BR><B>三、Struts的安装与基本配置</B> <BR><BR>我们主要针对Struts1.1版本进行讲解，这里假定读者已经配置好java运行环境和相应的Web容器，本文例子所使用的是j2sdk和Tomcat4.1.27。下面，将采用类似于step by step的方式介绍其基础部分。 <BR><BR><I>安装Struts</I><BR>到http://jakarta.apache.org/ 下载Struts的安装文件，本文例子使用的是1.1版。 <BR><BR>接下来您要进行如下几个步骤来完成安装：<BR>1、解压下载的安装文件到您的本地硬盘<BR>2、生成一个新的Web应用，假设我们生成的应用程序的根目录在<CATALINA_HOME>/Webapps/mystruts目录。在server.xml文件中为该应用新建一个别名如/mystruts<BR>3、从第1步解压的文件中拷贝下列jar文件到<CATALINA_HOME>/Webapps/mystruts/WEB-INF/lib目录，主要文件有如下一些。 <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>struts.jar
commons－beanutils.jar
commons－collections.jar
commons－dbcp.jar
commons－digester.jar
commons－logging.jar
commons－pool.jar
commons－services.jar
commons－validator.jar</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>4、创建一个web.xml文件，这是一个基于servlet的Web应用程序都需要的部署描述文件，一个Struts Web应用，在本质上也是一个基于servlet的Web应用，它也不能例外。 <BR><BR>Struts有两个组件要在该文件中进行配置，它们是：ActionServlet和标签库。下面是一个配置清单： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3
//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"&gt;
&lt;web-app&gt;
  &lt;servlet&gt;
    &lt;servlet-name&gt;action&lt;/servlet-name&gt;
    &lt;servlet-class&gt;org.apache.struts.action.ActionServlet&lt;/servlet-class&gt;
    &lt;init-param&gt;
      &lt;param-name&gt;config&lt;/param-name&gt;
      &lt;param-value&gt;/WEB-INF/struts-config.xml&lt;/param-value&gt;
    &lt;/init-param&gt;
    &lt;init-param&gt;
      &lt;param-name&gt;debug&lt;/param-name&gt;
      &lt;param-value&gt;2&lt;/param-value&gt;
    &lt;/init-param&gt;
    &lt;load-on-startup&gt;2&lt;/load-on-startup&gt;
  &lt;/servlet&gt;
  &lt;servlet-mapping&gt;
    &lt;servlet-name&gt;action&lt;/servlet-name&gt;
    &lt;url-pattern&gt;*.do&lt;/url-pattern&gt;
  &lt;/servlet-mapping&gt;
  &lt;taglib&gt;
    &lt;taglib-uri&gt;/WEB-INF/struts-bean.tld&lt;/taglib-uri&gt;
    &lt;taglib-location&gt;/WEB-INF/struts-bean.tld&lt;/taglib-location&gt;
  &lt;/taglib&gt;
  &lt;taglib&gt;
    &lt;taglib-uri&gt;/WEB-INF/struts-html.tld&lt;/taglib-uri&gt;
    &lt;taglib-location&gt;/WEB-INF/struts-html.tld&lt;/taglib-location&gt;
  &lt;/taglib&gt;
  &lt;taglib&gt;
    &lt;taglib-uri&gt;/WEB-INF/struts-logic.tld&lt;/taglib-uri&gt;
    &lt;taglib-location&gt;/WEB-INF/struts-logic.tld&lt;/taglib-location&gt;
  &lt;/taglib&gt;
&lt;/web-app&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>上面我们在web.xml中完成了对servlet和标签库的基本配置，而更多的框架组件要在struts-config.xml中进行配置： <BR><BR>5、创建一个基本的struts-config.xml文件，并把它放在<CATALINA_HOME>/Webapps/mystruts/WEB-INF/目录中，该文件是基于Struts应用程序的配置描述文件，它将MVC结构中的各组件结合在一起，开发的过程中会不断对它进行充实和更改。在Struts1.0时，一个应用只能有一个这样的文件，给分工开发带来了一些不便，在Struts1.1时，可以有多个这样的文件，将上述缺点克服了。需在该文件中配置的组件有：data-sources <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>global-execptions
form-beans
global-forwards
action-mappings
controller
message-resources
plug-in</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>配置清单如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1
//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"&gt;
&lt;struts-config&gt;
  &lt;message-resources parameter="ApplicationResources" /&gt;
&lt;/struts-config&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>到此为止，我们已经具备了完成一个最简单Struts应用的所需的各种组件。前面已经提到，在开发过程中我们会不断充实和修改上面两个配置描述文件。下面我们将实际做一个非常简单的应用程序来体验一下Struts应用开发的真实过程，以期对其有一个真实的认识。在完成基础部分的介绍后，笔者会给出一些在实际开发中经常用到而又让初学者感到有些难度的实例。最后，会介绍Struts与其他框架的关系及结合它们生成应用程序的例子。</FONT></SPAN><BR><BR>下面，我们就从一个最简单的登录例子入手，以对Struts的主要部分有一些直观而清晰的认识。这个例子功能非常简单，假设有一个名为lhb的用户，其密码是awave，程序要完成的任务是，呈现一个登录界面给用户，如果用户输入的名称和密码都正确返回一个欢迎页面给用户，否则，就返回登录页面要求用户重新登录并显示相应的出错信息。这个例子在我们讲述Struts的基础部分时会反复用到。之所以选用这个简单的程序作为例子是因为不想让过于复杂的业务逻辑来冲淡我们的主题。 <BR><BR>因为Struts是建立在MVC设计模式上的框架，你可以遵从标准的开发步骤来开发你的Struts Web应用程序，这些步骤大致可以描述如下：<BR>1定义并生成所有代表应用程序的用户接口的Views，同时生成这些Views所用到的所有ActionForms并将它们添加到struts-config.xml文件中。<BR>2在ApplicationResource.properties文件中添加必要的MessageResources项目<BR>3生成应用程序的控制器。<BR>4在struts-config.xml文件中定义Views与 Controller的关系。<BR>5生成应用程序所需要的model组件<BR>6编译、运行你的应用程序. <BR><BR>下面，我们就一步步按照上面所说的步骤来完成我们的应用程序： <BR><BR>第一步，我们的应用程序的Views部分包含两个.jsp页面：一个是登录页面logon.jsp，另一个是用户登录成功后的用户功能页main.jsp，暂时这个页面只是个简单的欢迎页面。 <BR><BR>其中，logon.jsp的代码清单如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html; charset=UTF-8" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %&gt;
&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;&lt;bean:message key="logon.jsp.title"/&gt;&lt;/TITLE&gt;
&lt;html:base/&gt;
&lt;/HEAD&gt;
&lt;BODY&gt;
&lt;h3&gt;&lt;bean:message key="logon.jsp.page.heading"/&gt;&lt;/h3&gt;
&lt;html:errors/&gt;
&lt;html:form action="/logonAction.do" focus="username"&gt;
&lt;TABLE border="0" width="100%"&gt;
&lt;TR&gt;
&lt;TH align="right"&gt;&lt;bean:message key="logon.jsp.prompt.username"/&gt;&lt;/TH&gt;
&lt;TD align="left"&gt;&lt;html:text property="username"/&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TH align="right"&gt;&lt;bean:message key="logon.jsp.prompt.password"/&gt;&lt;/TH&gt;
&lt;TD align="left"&gt;&lt;html:password property="password"/&gt;&lt;/TD&gt;
&lt;/TR&gt;
&lt;TR&gt;
&lt;TD align="right"&gt;
  &lt;html:submit&gt;&lt;bean:message key="logon.jsp.prompt.submit"/&gt;&lt;/html:submit&gt;
&lt;/TD&gt;
&lt;TD align="left"&gt;
  &lt;html:reset&gt;&lt;bean:message key="logon.jsp.prompt.reset"/&gt;&lt;/html:reset&gt;
&lt;/TD&gt;
&lt;/TR&gt;
&lt;/TABLE&gt;
&lt;/html:form&gt;
&lt;/BODY&gt;
&lt;/HTML&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>main.jsp的代码清单如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html; charset=UTF-8" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %&gt;

&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;&lt;bean:message key="main.jsp.title"/&gt;&lt;/TITLE&gt;
&lt;html:base/&gt;
&lt;/HEAD&gt;
&lt;BODY&gt;
&lt;logic:present name="userInfoForm"&gt;
&lt;H3&gt;
  &lt;bean:message key="main.jsp.welcome"/&gt; 
  &lt;bean:write name="userInfoForm" property="username"/&gt;!
&lt;/H3&gt;
&lt;/logic:present&gt;
&lt;/BODY&gt;
&lt;/HTML&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>首先，我们看一下logon.jsp文件，会发现它有这么两个鲜明的特点：一是文件头部有诸如：<BR><%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %><BR><%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %><BR><BR>这样的指令代码，他们的作用就是指示页面要用到struts的自定义标签，标签库uri是一个逻辑引用，标签库的描述符（tld）的位置在web.xml文件中给出，见上篇文章的配置部分。struts的标签库主要由四组标签组成,它们分别是：<BR></LI>
<LI>bean标签，作用是在jsp中操纵bean<BR>
<LI>logic标签，作用是在jsp中进行流程控制<BR>
<LI>html标签，作用是显示表单等组件<BR>
<LI>template标签，作用是生成动态模板 <BR><BR>关于每类标签的具体作用及语法，因受篇幅限制，不在这里详细讨论，大家可参考struts手册之类的资料。只是心里要明白所谓标签其后面的东西就是一些类，这点与bean有些相似，它们在后端运行，生成标准的html标签返回给浏览器。 <BR><BR>要使用它们显然要把它们的标签库描述文件引入到我们的系统中，这是些以.tld为扩展名的文件，我们要把它们放在<CATALINA_HOME>/webapps/mystruts/WEB-INF/目录下。引入struts标签后原来普通的html标签如文本框的标签变成了这样的形式<?XML:NAMESPACE PREFIX = HTML /><HTML:TEXT property="username"></HTML:TEXT>。 <BR><BR>Jsp文件的第二个特点，就是页面上根本没有直接写用于显示的文字如：username,password等东西，而是用<?XML:NAMESPACE PREFIX = BEAN /><BEAN:MESSAGE key="logon.jsp.prompt.username"></BEAN:MESSAGE>这种形式出现。这个特点为国际化编程打下了坚实的基础，关于国际化编程后面的文章还会专门讨论。 <BR><BR>这个简单的应用所用到的ActionForm为UserInfoForm,代码清单如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package entity;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import javax.servlet.http.HttpServletRequest;

public class UserInfoForm extends ActionForm{

  private String username;
  private String password;


  public String getUsername() {
    return (this.username);
  }
  public void setUsername(String username) {
    this.username = username;
  }

  public String getPassword() {
    return (this.password);
  }
  public void setPassword(String password) {
    this.password = password;
  }
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>在你的应用程序的WEB-INF目录下再建一个classes目录，在新建的这个classes目录下再建如下几个目录entity(用于存放ActionForm类)、action目录（用于存放Action类）、bussness目录（用于存放作为Model的业务对象类）。Classes目录下的子目录就是所谓的包，以后，还会根据需要增加相应的包。 <BR><BR>现在，将UserInfoForm.java保存到entity目录中。 <BR><BR>把如下代码添加到<CATALINA_HOME>/webapps/mystruts/WEB-INF/struts-config.xml文件中 <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;form-beans&gt;
    &lt;form-bean name="userInfoForm" type="entity.UserInfoForm" /&gt;
  &lt;/form-beans&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>特别要提醒一下的是：关于ActionForm的大小写，一定要按照上面的写，以免造成不必要的麻烦。 <BR><BR>到此，我们完成了第一步工作。 <BR><BR>第二步，我们建一个名为ApplicationResource.properties的文件，并把它放在<CATALINA_HOME>/webapps/mystruts/WEB-INF/classes目录下。它在struts-config.xml的配置信息我们已在第一篇文章的末尾说了，就是：<BR><MESSAGE-RESOURCES parameter="ApplicationResources" /><BR><BR>目前我们在ApplicationResource.properties文件中加入的内容是： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>#Application Resource for the logon.jsp
logon.jsp.title=The logon page
logon.jsp.page.heading=Welcome World!
logon.jsp.prompt.username=Username:
logon.jsp.prompt.password=Password:
logon.jsp.prompt.submit=Submit
logon.jsp.prompt.reset=Reset

#Application Resource for the main.jsp
main.jsp.title=The main page
main.jsp.welcome=Welcome:</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>到此，我们已完成了第二个步骤。 <BR><BR>第三步，我们开始生成和配置Controller组件。 <BR><BR>在前面我们已经提到，Struts应用程序的控制器由org.apache.struts.action.ActionServlet和org.apache.struts.action.Action类组成，其中，前者已由Struts准备好了，后者Struts只是为我们提供了个骨架，我们要做的是为实现应用程序的特定功能而扩展Action类，下面是实现我们登录程序的Action类的代码清单： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package action;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionServlet;
import bussness.UserInfoBo;
import entity.UserInfoForm;
public final class LogonAction extends Action {
  
  public ActionForward execute(ActionMapping mapping,
         ActionForm form,
         HttpServletRequest request,
         HttpServletResponse response)
         throws IOException, ServletException {
    UserInfoForm userInfoForm = (UserInfoForm) form;     	
    //从web层获得用户名和口令
    String username = userInfoForm.getUsername().trim();
    String password = userInfoForm.getPassword().trim();
    //声明错误集对象
    ActionErrors errors = new ActionErrors();
    //校验输入
    if(username.equals("")){
      ActionError error=new ActionError("error.missing.username");
      errors.add(ActionErrors.GLOBAL_ERROR,error);
    }
    if(password.equals("")){
      ActionError error=new ActionError("error.missing.password");
      errors.add(ActionErrors.GLOBAL_ERROR,error);
    }
    
    //调用业务逻辑
    if(errors.size()==0){
      String validated = "";
      try{
        UserInfoBo userInfoBo=new UserInfoBo();
        validated =userInfoBo.validatePwd(username,password);
        if(validated.equals("match")){
          //一切正常就保存用户信息并转向成功的页面	
          HttpSession session = request.getSession();
          session.setAttribute("userInfoForm", form);          
      	  return mapping.findForward("success");
        } 
      }
      
      catch(Throwable e){
        //处理可能出现的错误
        e.printStackTrace();
        ActionError error=new ActionError(e.getMessage());
        errors.add(ActionErrors.GLOBAL_ERROR,error);
      }
    }  
    //如出错就转向输入页面，并显示相应的错误信息
    saveErrors(request, errors);    
    return new ActionForward(mapping.getInput());    
  } 
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>这个action类中有两个错误消息键要加到ApplicationResource.properties文件中，清单如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>#Application Resource for the LogonAction.java
error.missing.username=&lt;li&gt;&lt;font color="red"&gt;missing username&lt;/font&gt;&lt;/li&gt;
error.missing.password=&lt;li&gt;&lt;font color="red"&gt;missing password&lt;/font&gt;&lt;/li&gt;&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>第四步：在struts-config.xml文件中定义Views与 Controller的关系，也就是配置所谓的ActionMapping。它们在struts-config.xml中的位置是排在<FORM-BEANS>… </FORM-BEANS>标签后，我们的登录程序的配置清单如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;action-mappings&gt;
    &lt;action input="/logon.jsp" name="userInfoForm" path="/logonAction" scope="session" 
	type="action.LogonAction" validate="false"&gt;
      &lt;forward name="success" path="/main.jsp" /&gt;      
    &lt;/action&gt;
  &lt;/action-mappings&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>第五步：生成应用程序所需要的model组件，该组件是完成应用程序业务逻辑的地方，现在我的登录程序的业务逻辑很简单，就是判断用户是不是lhb并且其口令是不是awave如果是就返回一个表示匹配的字符串"match"，否则，就抛出出错信息。其代码清单如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>package bussness;

import entity.UserInfoForm;

public class UserInfoBo {

  public UserInfoBo(){
    
  }  	

  public String validatePwd(String username,String password){
    	
    String validateResult=""; 
       
    if(username.equals("lhb")&amp;&amp;password.equals("awave")){
      validateResult="match";
    }
    else{
      
      throw new RuntimeException("error.noMatch");
    }    	
    
    return validateResult;   
    
  }
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>将其放在bussness包中。 <BR><BR>我们同样要将其表示错误信息的键值设置在ApplicationResource.properties文件中，清单如下： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>#Application Resource for the UserInfoBo.java
error.noMatch=&lt;li&gt;&lt;font color="red"&gt;no matched user&lt;/font&gt;&lt;/li&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>到此为止，我们已经完成了这个简单登录程序的所有组件。下面就可以享受我们的劳动成果了。 <BR><BR>第六步、编译运行应用程序。 <BR><BR>常规的做法是用Ant来装配和部署Struts应用程序，如果按这个套路，这篇文章就会显得十分冗长乏味，同时也没有太大的必要，因为，用一个IDE一般可以很方便地生成一个应用。因此，我们采用简便的方法，直接编译我们的.java文件。不过这里要注意一点的是：实践证明，要使得编译过程不出错，还必须将struts.jar文件放一份拷贝到<CATALINA_HOME>/common/lib目录中，并在环境变量中设置CLASSPATH 其值是<CATALINA_HOME>/common/lib/struts.jar;配置好后就可以分别编译entity、bussness及action目录下的.java文件了。编译完成后：打开<CATALINA_HOME>/conf目录下的server.xml文件，在</HOST>前加上如下语句为我们的应用程序建一个虚拟目录： <BR><BR><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;Context path="/mystruts" docBase="mystruts" debug="0"
                 reloadable="true"&gt;                 
		&lt;/Context&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR><BR><BR>启动，tomcat。在浏览器中输入：http://localhost:8080/mystruts/logon.jsp<BR>如果前面的步骤没有纰漏的话，一个如图所示的登录画面就会出现在你的眼前。 <BR><BR><IMG src="http://tech.ccidnet.com/pub/attachment/2004/8/320304.gif"> <BR><BR>如果，不输入任何内容直接点击Submit按钮，就会返回到logon.jsp并显示missing username和missing password错误信息；如果输入其他内容，则会返回no matched user的错误;如果输入的用户名是lhb且口令是awave则会显示表示登录成功的欢迎页面。 <BR><BR>上面虽然是一个功能很简单的应用程序，但麻雀虽小，五脏俱全，基本涉及到了struts的主要组成部分。下面我们就来分析一下程序的特点和基本的工作原理。 <BR><BR>首先，我们在浏览器中输入.jsp文件时，后台将struts的自定义标签"翻译"成普通的html标签返回给浏览器，而一些提示信息如作为输入框label的username、password还有按钮上提示信息还有错误信息等都来自MessageResources即ApplicationResource.properties文件中对应的键值。当我们点击Submit按钮时，从web.xml的配置可以看出，请求将被ActionServlet截获。它通过表单中提供的action参数在struts-config.xml文件中查找对应的<ACTION-MAPPINGS>项目，如果有对应的ActionForm，它就用表单中数据填充ActionForm的对应属性，本例中的ActionForm为userInfoForm，相应的属性是username和password，这就是所谓的实例化ActionForm。然后，将控制交给对应的Action，本例中是LogonAction，它做的主要工作是对ActionForm中取出的username和password做了一下校验，这里只是简单检验它们是否为空（这些简单的格式化方面的校验应该放在客户端进行，而且struts也为我们提供了一个很好的模式，后面如果有可能会详细介绍）。如果不为空则调用判断用户及口令是否正确的业务逻辑模块UserInfoBo，同时，它会捕获可能出现的错误，然后根据业务逻辑返回的结果将程序导向不同的页面，本例中如果业务逻辑返回的结果是"match"则依据<ACTION-MAPPINGS>中的<FORWARD name="success" path="/main.jsp" />返回main.jsp页面给浏览器同时在session对象中保存了用户的登录信息；否则，返回输入页面并显示相应的出错信息，完成了上篇文章所说的它的四个主要职责。 <BR><BR>大家一定注意到了，在本例的业务逻辑模块UserInfoBo中，将用户与密码是写死在程序中的，在一个真实的应用程序中是不会这样做的，那些需要永久保存的信息如，username及口令等都会保存在数据库文件之类的永久介质中，下一篇文章我们将介绍在struts中如何访问数据库。</FONT></SPAN></LI><img src ="http://www.blogjava.net/sgsoft/aggbug/2382.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-03-23 17:44 <a href="http://www.blogjava.net/sgsoft/articles/2382.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts 原理的解析与其实践指导大全(1)</title><link>http://www.blogjava.net/sgsoft/articles/2381.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Wed, 23 Mar 2005 09:37:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/2381.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/2381.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/2381.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/2381.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/2381.html</trackback:ping><description><![CDATA[本篇文章简单地介绍一下在struts中使用tiles的问题。<BR><BR>1、tiles框架简介：<BR><BR>大家在编程实践中都会对代码重用有一些切身体会，Web页面的制作当然也不例外。比如：做一个网站，大部分页面都是由页头、主体页及版权页组成。其中，页头及版权页的内容是不变的，而每页的主体内容页则是不相同的。根据代码重用的要求，我们不应该在每页重写页头及版权页的代码。Jsp也的确给我们提供了静态和动态包含的方式让我们来重用这些代码，也就是通过<BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ include file=…%&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>和 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;jsp:include page=…/&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>的形式来达到代码重用的目的。但不幸的是：无论采用上述那种形式，都存在一些不尽如人意的地方。如，你如果用这些方式从事过国际化编程，你将会为字符集的问题搞得无所适从。为了解决这些问题，一个名为tiles的框架应运而生。所谓tile，其实就是网页中的一个区域。Tiles框架有如下一些特点： <BR><BR>具有模板能力 <BR><BR>能动态构建和装载页面 <BR><BR>屏幕定义 <BR><BR>对tile和布局的重用支持 <BR><BR>支持国际化 <BR><BR>从tiles框架的发展历史来看，它开始并不是struts的组成部分。正如我们在本系列的第一篇所说的那样："它对其他技术和框架显示出很好的融合性"。现在，它已经成为struts的基本组成部分之一。 <BR><BR>2、在struts中使用tiles框架的条件 <BR><BR>首先，确保如下一些文件在应用程序的WEB-INF/lib目录中： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>commons-digester.jar
commons-beanutils.jar
commons-collections.jar
commons-logging.jar</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>其次，还要将tiles的tld文件struts-tiles.tld以及struts-template.tld放在应用程序的WEB-INF目录中。这里顺便对本系列前面一些文章中读者提出的问题做一些解释： <BR><BR>本系列文章的每个选题都是根据笔者在论坛中收集的一些热点问题，结合自己开发的一些项目（有很多就是为了便于初学者的理解将实际项目做一些适当的简化）写成的。因为对那些包之类的文件对那个主题是必须的往往没有足够的时间来一一加以验证，有时写多了，有时又写掉了。 <BR><BR>所以，有些读者反映照着文章介绍的步骤不能得到正确的结果。首先，在此向这些读者表示歉意，笔者以后会尽量注意一些。说实在的，要完全避免也有困难，毕竟写这些东西只是业余的事情，笔者还有许多本职工作要干，请大家原谅。有一个折中的方案就是：读者在明白基本原理之后采用诸如jbx之类的开发工具，这样在很大程度上可以避免这些问题的干扰，毕竟正常开发大多还是要使用这些工具的，熟悉它们也没有什么坏处。<BR><BR>3、使用tiles构建网页的过程<BR><BR>下面，我们通过构建一个本篇开头提及的那种简单页面来体会一下tiles构建网页的过程。<BR><BR>首先，在struts-config.xml中加入tiles插件，代码如下：<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;plug-in className="org.apache.struts.tiles.TilesPlugin"&gt;
    &lt;set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml" /&gt;
  &lt;/plug-in&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>其次，创建tiles-defs.xml文件，并把它放在应用程序的WEB-INF目录下，其代码如下： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE tiles-definitions PUBLIC "-
//Apache Software Foundation
//DTD Tiles Configuration 1.1
//EN" "http://jakarta.apache.org/struts/dtds/tiles-config_1_1.dtd"&gt;
&lt;tiles-definitions&gt;  
  &lt;definition name="testLayout" path="/testLayout.jsp"&gt;
    &lt;put name="title" value="my tiles test" /&gt;
    &lt;put name="header" value="/header.jsp" /&gt;
    &lt;put name="body" value="/body.jsp" /&gt;
    &lt;put name="footer" value="/right.jsp" /&gt;
  &lt;/definition&gt;
&lt;/tiles-definitions&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>再次，创建一个名为testLayout.jsp的布局模板文件，代码如下： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles"%&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;&lt;tiles:get name="title"/&gt;
&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;table&gt;
    &lt;tr&gt;
      &lt;td height="80" width="778" bgcolor="#aa0000"&gt;
        &lt;tiles:insert attribute="header"/&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td height="360" width="778" bgcolor="#00aa00"&gt;
        &lt;tiles:insert attribute="body"/&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td height="80" width="778" bgcolor="#0000aa"&gt;
        &lt;tiles:insert attribute="footer"/&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/table&gt; 
&lt;/body&gt;
&lt;/html&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>从该文件的代码可以看出： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;tiles:insert 标签中的attribute属性值
是由tiles-defs.xml中定义的逻辑名来表示的</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>这为基于该模板的网页动态地装入tile奠定了基础。最后,创建几个用于实验的.jsp文件，它们包括header.jsp、body.jsp、body2.jsp以及right.jsp。 <BR><BR>header.jsp的代码如下： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html; charset=UTF-8" %&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;
header
&lt;/title&gt;
&lt;/head&gt;
&lt;body bgcolor="#ffffff"&gt;
&lt;h1&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>这是页头部分 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>body.jsp的代码如下： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html; charset=UTF-8" %&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;
body
&lt;/title&gt;
&lt;/head&gt;
&lt;body bgcolor="#ffffff"&gt;
&lt;h1&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>这是内容部分 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>body2.jsp的代码如下： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html; charset=UTF-8" %&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;
body
&lt;/title&gt;
&lt;/head&gt;
&lt;body bgcolor="#ffffff"&gt;
&lt;h1&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>这是内容部分2 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>right.jsp的代码如下： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html; charset=UTF-8" %&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;
right
&lt;/title&gt;
&lt;/head&gt;
&lt;body bgcolor="#ffffff"&gt;
&lt;h1&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>这是版权页 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR>4、演示tiles的使用效果<BR><BR>为此，我们还要建两个.jsp文件，它们分别名为：page1.jsp和page2.jsp<BR><BR>其中，page1.jsp的代码如下：<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html; charset=UTF-8" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %&gt;
&lt;tiles:insert page="/testLayout.jsp" flush="true"&gt;
  &lt;tiles:put name="title" content="My first page" direct="true"/&gt;
  &lt;tiles:put name="header" value="/header.jsp"/&gt;
  &lt;tiles:put name="body" value="/body.jsp"/&gt;
  &lt;tiles:put name="footer" value="/right.jsp"/&gt;
&lt;/tiles:insert&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>page2.jsp的代码如下： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;%@ page contentType="text/html; charset=UTF-8" %&gt;
&lt;%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %&gt;
&lt;tiles:insert page="/testLayout.jsp" flush="true"&gt;
  &lt;tiles:put name="title" content="My second page" direct="true"/&gt;
  &lt;tiles:put name="header" value="/header.jsp"/&gt;
  &lt;tiles:put name="body" value="/body2.jsp"/&gt;
  &lt;tiles:put name="footer" value="/right.jsp"/&gt;
&lt;/tiles:insert&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>从这两个文件可以看出： <BR><BR>文件的布局是与模板相对应，而这两个文件的body部分则对应着不同的.jsp文件。可以想象，如果要对页头和版权信息做修改，只要对header.jsp和right.jsp做相应的修改，则修改的结果则会反映到page1.jsp和page2.jsp页面上。 <BR><BR>以上，只是对tiles的使用做了一个简单的介绍，它还有其他一些特征如：tile的定义可以扩展等等，值得大家深入探讨。至于上面各种文件中出现的一些标签，大多可以从标签本身看出其含义，实在不清楚的也可以参考这方面的有关资料，如果详细在这里讲解这些东西会显得冗长乏味。 <BR><BR>现在，如果您在浏览器中输入：http://127.0.0.1:8080/mystruts/page1.jsp就可以看到网页效果了；对比输入：http://127.0.0.1:8080/mystruts/page2.jsp 看看网页的变化。就可以体会到使用tiles框架的作用了。 <BR><BR>下一篇文章，笔者准备介绍一下用tiles框架构建frame形式的网页的问题。之所以准备写它们是因为frame形式的网页应用还是比较广泛，而且笔者发现这方面的参考文献比较少见。另外，本系列第八篇发表后，就有读者来信提出过这样的要求，希望整理出来的东西能对这部分读者有所帮助。 <BR><BR>参考文献： <BR><BR>《Programming Jakarta Struts》Chuck Cavaness著 <BR><img src ="http://www.blogjava.net/sgsoft/aggbug/2381.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-03-23 17:37 <a href="http://www.blogjava.net/sgsoft/articles/2381.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于struts+spring+ibatis的 J2EE 开发</title><link>http://www.blogjava.net/sgsoft/articles/2257.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Sun, 20 Mar 2005 08:46:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/2257.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/2257.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/2257.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/2257.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/2257.html</trackback:ping><description><![CDATA[1. 前言<BR><BR>Struts 是目前Java Web MVC框架中不争的王者。经过长达五年的发展，Struts已经逐渐成长为一个稳定、成熟的框架，并且占有了MVC框架中最大的市场份额。但是Struts某些技术特性上已经落后于新兴的MVC框架。面对Spring MVC、Webwork2 这些设计更精密，扩展性更强的框架，Struts受到了前所未有的挑战。但站在产品开发的角度而言，Struts仍然是最稳妥的选择。本文的原型例子JpetStore 4.0就是基于Struts开发的，但是不拘泥于Struts的传统固定用法，例如只用了一个自定义Action类，并且在form bean类的定义上也是开创性的，令人耳目一新，稍后将具体剖析一下。<BR><BR>Spring Framework 实际上是Expert One-on-One J2EE Design and Development 一书中所阐述的设计思想的具体实现。Spring Framework的功能非常多。包含AOP、ORM、DAO、Context、Web、MVC等几个部分组成。Web、MVC暂不用考虑，JpetStore 4.0用的是更成熟的Struts和JSP；DAO由于目前Hibernate、JDO、ibatis的流行，也不考虑，JpetStore 4.0用的就是ibatis。因此最需要用的是AOP、ORM、Context。Context中，最重要的是Beanfactory，它能将接口与实现分开，非常强大。目前AOP应用最成熟的还是在事务管理上。<BR><BR>ibatis 是一个功能强大实用的SQL Map工具，不同于其他ORM工具（如hibernate），它是将SQL语句映射成Java对象，而对于ORM工具，它的SQL语句是根据映射定义生成的。ibatis 以SQL开发的工作量和数据库移植性上的让步，为系统设计提供了更大的自由空间。有ibatis代码生成的工具，可以根据DDL自动生成ibatis代码，能减少很多工作量。<BR><BR>2. JpetStore简述<BR><BR>2.1. 背景<BR><BR>最初是Sun公司的J2EE petstore，其最主要目的是用于学习J2EE，但是其缺点也很明显，就是过度设计了。接着Oracle用J2EE petstore来比较各应用服务器的性能。微软推出了基于.Net平台的 Pet shop，用于竞争J2EE petstore。而JpetStore则是经过改良的基于struts的轻便框架J2EE web应用程序，相比来说，JpetStore设计和架构更优良，各层定义清晰，使用了很多最佳实践和模式，避免了很多"反模式"，如使用存储过程，在java代码中嵌入SQL语句，把HTML存储在数据库中等等。最新版本是JpetStore 4.0。<BR><BR>2.2. JpetStore开发运行环境的建立<BR><BR>1、开发环境<BR><BR>Java SDK 1.4.2<BR>Apache Tomcat 4.1.31<BR>Eclipse-SDK-3.0.1-win32<BR>HSQLDB 1.7.2<BR><BR>2、Eclipse插件<BR><BR>EMF SDK 2.0.1：Eclipse建模框架，lomboz插件需要，可以使用runtime版本。<BR>lomboz 3.0：J2EE插件，用来在Eclipse中开发J2EE应用程序<BR>Spring IDE 1.0.3：Spring Bean配置管理插件<BR>xmlbuddy_2.0.10：编辑XML，用免费版功能即可<BR>tomcatPluginV3：tomcat管理插件<BR>Properties Editor：编辑java的属性文件,并可以预览以及自动存盘为Unicode格式。免去了手工或者ANT调用native2ascii的麻烦。<BR>2.3. 架构<BR><BR>
<CENTER><IMG src="http://tech.ccidnet.com/pub/attachment/2005/2/397089.jpg"></CENTER><BR><BR>
<CENTER>图1 JpetStore架构图</CENTER><BR><BR>图1 是JPetStore架构图。参照这个架构图，让我们稍微剖析一下源代码，得出JpetStore 4.0的具体实现图（图2），思路一下子就豁然开朗了。前言中提到的非传统的struts开发模式，关键就在struts Action类和form bean类上。<BR><BR>struts Action类只有一个：BeanAction。没错，确实是一个！与传统的struts编程方式很不同。再仔细研究BeanAction类，发现它其实是一个通用类，利用反射原理，根据URL来决定调用formbean的哪个方法。BeanAction大大简化了struts的编程模式，降低了对struts的依赖（与struts以及WEB容器有关的几个类都放在com.ibatis.struts包下，其它的类都可以直接复用）。利用这种模式，我们会很容易的把它移植到新的框架如JSF，spring。<BR><BR>这样重心就转移到form bean上了，它已经不是普通意义上的form bean了。查看源代码，可以看到它不仅仅有数据和校验/重置方法，而且已经具有了行为，从这个意义上来说，它更像一个BO(Business Object)。这就是前文讲到的，BeanAction类利用反射原理，根据URL来决定调用form bean的哪个方法（行为）。form bean的这些方法的签名很简单，例如：<BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public String myActionMethod() 
 {
   //..work
   return "success";
 }</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>方法的返回值直接就是字符串，对应的是forward的名称，而不再是ActionForward对象，创建ActionForward对象的任务已经由BeanAction类代劳了。另外，程序还提供了ActionContext工具类，该工具类封装了request 、response、form parameters、request attributes、session attributes和 application attributes中的数据存取操作，简单而线程安全，form bean类使用该工具类可以进一步从表现层框架解耦。在这里需要特别指出的是，BeanAction类是对struts扩展的一个有益尝试，虽然提供了非常好的应用开发模式，但是它还非常新，一直在发展中。 <BR><BR>
<CENTER><IMG src="http://tech.ccidnet.com/pub/attachment/2005/2/397091.gif"></CENTER><BR><BR>
<CENTER>图2 JpetStore 4.0具体实现<BR><BR>2.4. 代码剖析<BR><BR>下面就让我们开始进一步分析JpetStore4.0的源代码，为下面的改造铺路。BeanAction.java是唯一一个Struts action类，位于com.ibatis.struts包下。正如上文所言，它是一个通用的控制类，利用反射机制，把控制转移到form bean的某个方法来处理。详细处理过程参考其源代码，简单明晰。<BR><BR>Form bean类位于com.ibatis.jpetstore.presentation包下，命名规则为***Bean。Form bean类全部继承于BaseBean类，而BaseBean类实际继承于ActionForm，因此，Form bean类就是Struts的 ActionForm，Form bean类的属性数据就由struts框架自动填充。而实际上，JpetStore4.0扩展了struts中ActionForm的应用： Form bean类还具有行为，更像一个BO,其行为（方法）由BeanAction根据配置（struts-config.xml）的URL来调用。虽然如此，我们还是把Form bean类定位于表现层。Struts-config.xml的配置里有3种映射方式，来告诉BeanAction把控制转到哪个form bean对象的哪个方法来处理。以这个请求连接为例http://localhost/jpetstore4/shop/viewOrder.do<BR><BR>1. URL Pattern<BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction"
    name="orderBean" scope="session"
    validate="false"&gt;
    &lt;forward name="success" path="/order/ViewOrder.jsp"/&gt;
  &lt;/action&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>此种方式表示，控制将被转发到"orderBean"这个form bean对象 的"viewOrder"方法（行为）来处理。方法名取"path"参数的以"/"分隔的最后一部分。 <BR><BR>2. Method Parameter <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction"
    name="orderBean" parameter="viewOrder" scope="session"
    validate="false"&gt;
    &lt;forward name="success" path="/order/ViewOrder.jsp"/&gt;
  &lt;/action&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>此种方式表示，控制将被转发到"orderBean"这个form bean对象的"viewOrder"方法（行为）来处理。配置中的"parameter"参数表示form bean类上的方法。"parameter"参数优先于"path"参数。 <BR><BR>3. No Method call <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction"
    name="orderBean" parameter="*" scope="session"
    validate="false"&gt;
    &lt;forward name="success" path="/order/ViewOrder.jsp"/&gt;
  &lt;/action&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>此种方式表示，form bean上没有任何方法被调用。如果存在"name"属性，则struts把表单参数等数据填充到form bean对象后，把控制转发到"success"。否则，如果name为空，则直接转发控制到"success"。这就相当于struts内置的org.apache.struts.actions.ForwardAction的功能 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;action path="/shop/viewOrder" type="org.apache.struts.actions.ForwardAction"
    parameter="/order/ViewOrder.jsp " scope="session" validate="false"&gt;
 &lt;/action&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>Service类位于com.ibatis.jpetstore.service包下，属于业务层。这些类封装了业务以及相应的事务控制。Service类由form bean类来调用。 <BR><BR>com.ibatis.jpetstore.persistence.iface包下的类是DAO接口，属于业务层，其屏蔽了底层的数据库操作，供具体的Service类来调用。DaoConfig类是工具类（DAO工厂类），Service类通过DaoConfig类来获得相应的DAO接口，而不用关心底层的具体数据库操作，实现了如图2中{耦合2}的解耦。 <BR><BR>com.ibatis.jpetstore.persistence.sqlmapdao包下的类是对应DAO接口的具体实现，在JpetStore4.0中采用了ibatis来实现ORM。这些实现类继承BaseSqlMapDao类，而BaseSqlMapDao类则继承ibatis DAO 框架中的SqlMapDaoTemplate类。ibatis的配置文件存放在com.ibatis.jpetstore.persistence.sqlmapdao.sql目录下。这些类和配置文件位于数据层 <BR><BR>Domain类位于com.ibatis.jpetstore.domain包下，是普通的javabean。在这里用作数据传输对象（DTO），贯穿视图层、业务层和数据层，用于在不同层之间传输数据。剩下的部分就比较简单了，请看具体的源代码，非常清晰。 <BR><BR>2.5. 需要改造的地方<BR><BR>JpetStore4.0的关键就在struts Action类和form bean类上，这也是其精华之一（虽然该实现方式是试验性，待扩充和验证），在此次改造中我们要保留下来，即控制层一点不变，表现层获取相应业务类的方式变了（要加载spring环境），其它保持不变。要特别关注的改动是业务层和持久层，幸运的是JpetStore4.0设计非常好，需要改动的地方非常少，而且由模式可循，如下：<BR><BR>1. 业务层和数据层用Spring BeanFactory机制管理。<BR>2. 业务层的事务由spring 的aop通过声明来完成。<BR>3. 表现层（form bean）获取业务类的方法改由自定义工厂类来实现（加载spring环境）。<BR><BR>3. JPetStore的改造<BR><BR>3.1. 改造后的架构<BR><BR>
<CENTER><IMG src="http://tech.ccidnet.com/pub/attachment/2005/2/397093.gif"></CENTER><BR><BR>其中红色部分是要增加的部分，蓝色部分是要修改的部分。下面就让我们逐一剖析。<BR><BR>3.2. Spring Context的加载<BR>为了在Struts中加载Spring Context，一般会在struts-config.xml的最后添加如下部分：<BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"&gt;
&lt;set-property property="contextConfigLocation"
value="/WEB-INF/applicationContext.xml" /&gt;
&lt;/plug-in&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>Spring在设计时就充分考虑到了与Struts的协同工作，通过内置的Struts Plug-in在两者之间提供了良好的结合点。但是，因为在这里我们一点也不改动JPetStore的控制层(这是JpetStore4.0的精华之一)，所以本文不准备采用此方式来加载ApplicationContext。我们利用的是spring framework 的BeanFactory机制,采用自定义的工具类（bean工厂类）来加载spring的配置文件，从中可以看出Spring有多灵活，它提供了各种不同的方式来使用其不同的部分/层次，您只需要用你想用的，不需要的部分可以不用。 <BR><BR>具体的来说，就是在com.ibatis.spring包下创建CustomBeanFactory类，spring的配置文件applicationContext.xml也放在这个目录下。以下就是该类的全部代码，很简单： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public final class CustomBeanFactory {
	static XmlBeanFactory factory = null;
	static {
		Resource is = new
InputStreamResource( CustomBeanFactory.class.getResourceAsStream("applicationContext.xml"));
		factory = new XmlBeanFactory(is);			
	}
	public static Object getBean(String beanName){
		return factory.getBean(beanName);
	}
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>实际上就是封装了Spring 的XMLBeanFactory而已，并且Spring的配置文件只需要加载一次，以后就可以直接用CustomBeanFactory.getBean("someBean")来获得需要的对象了(例如someBean)，而不需要知道具体的类。CustomBeanFactory类用于{耦合1}的解耦。CustomBeanFactory类在本文中只用于表现层的form bean对象获得service类的对象，因为我们没有把form bean对象配置在applicationContext.xml中。但是，为什么不把表现层的form bean类也配置起来呢，这样就用不着这CustomBeanFactory个类了，Spring会帮助我们创建需要的一切？问题的答案就在于form bean类是struts的ActionForm类！如果大家熟悉struts，就会知道ActionForm类是struts自动创建的：在一次请求中，struts判断，如果ActionForm实例不存在，就创建一个ActionForm对象，把客户提交的表单数据保存到ActionForm对象中。因此formbean类的对象就不能由spring来创建，但是service类以及数据层的DAO类可以，所以只有他们在spring中配置。所以，很自然的，我们就创建了CustomBeanFactory类，在表现层来衔接struts和spring。就这么简单，实现了另一种方式的{耦合一}的解耦。 <BR><BR>3.3. 表现层 <BR><BR>面分析到，struts和spring是在表现层衔接起来的，那么表现层就要做稍微的更改，即所需要的service类的对象创建上。以表现层的AccountBean类为例：原来的源代码如下 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>private static final AccountService accountService 
= AccountService.getInstance();
private static final CatalogService catalogService 
= CatalogService.getInstance();</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>改造后的源代码如下 <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>private static final AccountService accountService 
= (AccountService)CustomBeanFactory.getBean("AccountService");
private static final CatalogService catalogService
= (CatalogService)CustomBeanFactory.getBean("CatalogService");</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>其他的几个presentation类以同样方式改造。这样，表现层就完成了。关于表现层的其它部分如JSP等一概不动。也许您会说，没有看出什么特别之处的好处啊？你还是额外实现了一个工厂类。别着急，帷幕刚刚开启，spring是在表现层引入，但您发没发现： <BR><BR>presentation类仅仅面向service类的接口编程，具体"AccountService"是哪个实现类，presentation类不知道，是在spring的配置文件里配置。（本例中，为了最大限度的保持原来的代码不作变化，没有抽象出接口）。Spring鼓励面向接口编程，因为是如此的方便和自然，当然您也可以不这么做。 <BR><BR>CustomBeanFactory这个工厂类为什么会如此简单，因为其直接使用了Spring的BeanFactory。Spring从其核心而言，是一个DI容器，其设计哲学是提供一种无侵入式的高扩展性的框架。为了实现这个目标，Spring 大量引入了Java 的Reflection机制，通过动态调用的方式避免硬编码方式的约束，并在此基础上建立了其核心组件BeanFactory，以此作为其依赖注入机制的实现基础。org.springframework.beans包中包括了这些核心组件的实现类，核心中的核心为BeanWrapper和BeanFactory类。 <BR>3.4. 持久层在讨论业务层之前，我们先看一下持久层，如下图所示：<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR>
<CENTER><IMG src="http://tech.ccidnet.com/pub/attachment/2005/2/397095.jpg"></CENTER><BR><BR>在上文中，我们把iface包下的DAO接口归为业务层，在这里不需要做修改。ibatis的sql配置文件也不需要改。要改的是DAO实现类，并在spring的配置文件中配置起来。<BR><BR>1、修改基类<BR><BR>所有的DAO实现类都继承于BaseSqlMapDao类。修改BaseSqlMapDao类如下：<BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public class BaseSqlMapDao extends SqlMapClientDaoSupport
{
  protected static final int PAGE_SIZE = 4;
  protected SqlMapClientTemplate smcTemplate 
  = this.getSqlMapClientTemplate();
  public BaseSqlMapDao()
  { 
	}
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>使BaseSqlMapDao类改为继承于Spring提供的SqlMapClientDaoSupport类，并定义了一个保护属性smcTemplate，其类型为SqlMapClientTemplate。 <BR><BR>2、修改DAO实现类 <BR><BR>所有的DAO实现类还是继承于BaseSqlMapDao类，实现相应的DAO接口，但其相应的DAO操作委托SqlMapClientTemplate来执行，以AccountSqlMapDao类为例，部分代码如下： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public List getUsernameList() 
{
    return smcTemplate.queryForList("getUsernameList", null);
}
  public Account getAccount(String username, String password)
{
    Account account = new Account();
    account.setUsername(username);
    account.setPassword(password);
    return (Account)
	smcTemplate.queryForObject
	("getAccountByUsernameAndPassword", account);
}
  public void insertAccount(Account account)
{
 smcTemplate.update("insertAccount", account);
 smcTemplate.update("insertProfile", account);
 smcTemplate.update("insertSignon", account);
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>就这么简单，所有函数的签名都是一样的，只需要查找替换就可以了！<BR>、除去工厂类以及相应的配置文件<BR><BR>除去DaoConfig.java这个DAO工厂类和相应的配置文件dao.xml，因为DAO的获取现在要用spring来管理。<BR><BR>4、DAO在Spring中的配置（applicationContext.xml）<BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;bean id="dataSource" 
        class="org.springframework.jdbc.datasource.DriverManagerDataSource"&gt;
        &lt;property name="driverClassName"&gt;
            &lt;value&gt;org.hsqldb.jdbcDriver&lt;/value&gt;
        &lt;/property&gt;
        &lt;property name="url"&gt;
            &lt;value&gt;jdbc:hsqldb:hsql://localhost/xdb&lt;/value&gt;
        &lt;/property&gt;
        &lt;property name="username"&gt;
            &lt;value&gt;sa&lt;/value&gt;
        &lt;/property&gt;
        &lt;property name="password"&gt;
            &lt;value&gt;&lt;/value&gt;
        &lt;/property&gt;
    &lt;/bean&gt;    
    &lt;!-- ibatis sqlMapClient config --&gt;
    &lt;bean id="sqlMapClient" 
        class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"&gt;
        &lt;property name="configLocation"&gt;
            &lt;value&gt; 
   classpath:com\ibatis\jpetstore\persistence\sqlmapdao\sql\sql-map-config.xml
            &lt;/value&gt;
        &lt;/property&gt;
        &lt;property name="dataSource"&gt;
            &lt;ref bean="dataSource"/&gt;
        &lt;/property&gt;    
    &lt;/bean&gt;
    &lt;!-- Transactions --&gt;
    &lt;bean id="TransactionManager" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager"&gt;
        &lt;property name="dataSource"&gt;
            &lt;ref bean="dataSource"/&gt;
        &lt;/property&gt;
    &lt;/bean&gt;
    &lt;!-- persistence layer --&gt;
    &lt;bean id="AccountDao" 
        class="com.ibatis.jpetstore.persistence.sqlmapdao.AccountSqlMapDao"&gt;
        &lt;property name="sqlMapClient"&gt;
            &lt;ref local="sqlMapClient"/&gt;
        &lt;/property&gt;
    &lt;/bean&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>1. 我们首先创建一个数据源dataSource，在这里配置的是hsqldb数据库。如果是ORACLE数据库，driverClassName的值是"oracle.jdbc.driver.OracleDriver"，URL的值类似于"jdbc:oracle:thin:@wugfMobile:1521:cdcf"。数据源现在由spring来管理，那么现在我们就可以去掉properties目录下database.properties这个配置文件了；还有不要忘记修改sql-map-config.xml，去掉<PROPERTIES resource="properties/database.properties" />对它的引用。 <BR><BR>2. sqlMapClient节点。这个是针对ibatis SqlMap的SqlMapClientFactoryBean配置。实际上配置了一个sqlMapClient的创建工厂类。configLocation属性配置了ibatis映射文件的名称。dataSource属性指向了使用的数据源，这样所有使用sqlMapClient的DAO都默认使用了该数据源，除非在DAO的配置中另外显式指定。 <BR><BR>3. TransactionManager节点。定义了事务，使用的是DataSourceTransactionManager。 <BR><BR>4. 下面就可以定义DAO节点了，如AccountDao，它的实现类是com.ibatis.jpetstore.persistence.sqlmapdao.AccountSqlMapDao，使用的SQL配置从sqlMapClient中读取，数据库连接没有特别列出，那么就是默认使用sqlMapClient配置的数据源datasource。 <BR><BR>这样，我们就把持久层改造完了，其他的DAO配置类似于AccountDao。怎么样？简单吧。这次有接口了：） AccountDao接口－&gt;AccountSqlMapDao实现。 <BR><BR>3.5. 业务层 <BR><BR>业务层的位置以及相关类，如下图所示：在这个例子中只有3个业务类，我们以OrderService类为例来改造，这个类是最复杂的，其中涉及了事务。 <BR><BR>1、在ApplicationContext配置文件中增加bean的配置： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>&lt;bean id="OrderService" 
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"&gt;
        &lt;property name="transactionManager"&gt;
            &lt;ref local="TransactionManager"&gt;&lt;/ref&gt;
        &lt;/property&gt;
        &lt;property name="target"&gt;
            &lt;bean class="com.ibatis.jpetstore.service.OrderService"&gt;
                &lt;property name="itemDao"&gt;
                    &lt;ref bean="ItemDao"/&gt;
                &lt;/property&gt;
                &lt;property name="orderDao"&gt;
                    &lt;ref bean="OrderDao"/&gt;
                &lt;/property&gt;
                &lt;property name="sequenceDao"&gt;
                    &lt;ref bean="SequenceDao"/&gt;
                &lt;/property&gt;
            &lt;/bean&gt;
        &lt;/property&gt;
        &lt;property name="transactionAttributes"&gt;
            &lt;props&gt;
                &lt;prop key="insert*"&gt;PROPAGATION_REQUIRED&lt;/prop&gt;
            &lt;/props&gt;
        &lt;/property&gt;
    &lt;/bean&gt;</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>定义了一个OrderService，还是很容易懂的。为了简单起见，使用了嵌套bean，其实现类是com.ibatis.jpetstore.service.OrderService，分别引用了ItemDao，OrderDao，SequenceDao。该bean的insert*实现了事务管理(AOP方式)。TransactionProxyFactoryBean自动创建一个事务advisor， 该advisor包括一个基于事务属性的pointcut,因此只有事务性的方法被拦截。 <BR><BR>2、业务类的修改，以OrderService为例： <BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public class OrderService {

   /* Private Fields */
  private ItemDao itemDao;
  private OrderDao orderDao;
  private SequenceDao sequenceDao;

  /* Constructors */

  public OrderService() {
  }

/**
 * @param itemDao 要设置的 itemDao。
 */
public final void setItemDao(ItemDao itemDao) {
	this.itemDao = itemDao;
}
/**
 * @param orderDao 要设置的 orderDao。
 */
public final void setOrderDao(OrderDao orderDao) {
	this.orderDao = orderDao;
}
/**
 * @param sequenceDao 要设置的 sequenceDao。
 */
public final void setSequenceDao(SequenceDao sequenceDao) {
	this.sequenceDao = sequenceDao;
}
//剩下的部分
…….
}</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>红色部分为修改部分。Spring采用的是Type2的设置依赖注入，所以我们只需要定义属性和相应的设值函数就可以了，ItemDao，OrderDao，SequenceDao的值由spring在运行期间注入。构造函数就可以为空了，另外也不需要自己编写代码处理事务了（事务在配置中声明），daoManager.startTransaction();等与事务相关的语句也可以去掉了。和原来的代码比较一下，是不是处理精简了很多！可以更关注业务的实现。 <BR><BR>4. 结束语 <BR><BR>ibatis是一个功能强大实用的SQL Map工具，可以直接控制SQL,为系统设计提供了更大的自由空间。其提供的最新示例程序JpetStore 4.0,设计优雅，应用了迄今为止很多最佳实践和设计模式，非常适于学习以及在此基础上创建轻量级的J2EE WEB应用程序。JpetStore 4.0是基于struts的，本文在此基础上，最大程度保持了原有设计的精华以及最小的代码改动量，在业务层和持久化层引入了Spring。在您阅读了本文以及改造后的源代码后，会深切的感受到Spring带来的种种好处：自然的面向接口的编程，业务对象的依赖注入，一致的数据存取框架和声明式的事务处理，统一的配置文件…更重要的是Spring既是全面的又是模块化的，Spring有分层的体系结构，这意味着您能选择仅仅使用它任何一个独立的部分，就像本文，而它的架构又是内部一致。 <BR><BR>(T117) <BR></CENTER><img src ="http://www.blogjava.net/sgsoft/aggbug/2257.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-03-20 16:46 <a href="http://www.blogjava.net/sgsoft/articles/2257.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>运用Jakarta Struts的七大实战心法</title><link>http://www.blogjava.net/sgsoft/articles/543.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Fri, 21 Jan 2005 07:44:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/543.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/543.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/543.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/543.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/543.html</trackback:ping><description><![CDATA[<CENTER>
<H4 style="FONT-SIZE: 14pt">运用Jakarta Struts的七大实战心法</H4></CENTER><BR>(原文 http://www.onjava.com/pub/a/onjava/2002/10/30/jakarta.html?page=1) <BR>(作者Chuck Cavaness, 编译 邱文宇) <BR><BR><BR>编者按：当作者 Chuck Cavaness（著有《Programming Jakarta Struts》一书）所在的网络公司决定采用Struts框架之后，Chuck曾经花费了好几个月来研究如何用它来构建公司的应用系统。本文叙述的正是作者在运用Struts过程中来之不易的若干经验和心得。如果你是个负责通过jsp和servlet开发Web应用的Java程序员，并且也正在考虑采用基于Struts的构建方法的话，那么你会在这里发现很多颇有见地同时也很有价值的信息。 <BR><BR>1. 只在必要的时候才考虑扩展Struts框架 <BR><BR>一个好的framework有很多优点，首先，它必须能够满足用户的可预见的需求。为此 Struts为Web 应用提供了一个通用的架构，这样开发人员可以把精力集中在如何解决实际业务问题上。其次，一个好的framework还必须能够在适当的地方提供扩展接口，以便应用程序能扩展该框架来更好的适应使用者的实际需要。 <BR><BR>如果Struts framework在任何场合，任何项目中都能很好的满足需求，那真是太棒了。但是实际上，没有一个框架声称能做到这一点。一定会有一些特定的应用需求是框架的开发者们无法预见到的。因此，最好的办法就是提供足够的扩展接口，使得开发工程师能够调整struts来更好的符合他们的特殊要求。 <BR><BR>在Struts framework中有很多地方可供扩展和定制。几乎所有的配置类都能被替换为某个用户定制的版本，这只要简单的修改一下Struts的配置文件就可以做到。 <BR><BR>其他组件如ActionServlet和 RequestProcessor 也能用自定义的版本代替. 甚至连Struts 1.1里才有的新特性也是按照扩展的原则来设计的。例如，在异常处理机制中就允许用户定制异常处理的句柄，以便更好的对应用系统发生的错误做出响应。 <BR><BR>作为框架的这种可调整特性在它更适合你的应用的同时也在很大的程度上影响了项目开发的效果。首先，由于您的应用是基于一个现有的成熟的、稳定的framework如Struts，测试过程中发现的错误数量将会大大减少，同时也能缩短开发时间和减少资源的投入。因为你不再需要投入开发力量用于编写基础框架的代码了。 <BR><BR>然而, 实现更多的功能是要花费更大的代价的。我们必须小心避免不必要的滥用扩展性能， Struts是由核心包加上很多工具包构成的，它们已经提供了很多已经实现的功能。因此不要盲目的扩展Struts框架，要先确定能不能采用其他方法使用现有的功能来实现。 在决定编写扩展代码前务必要确认Struts的确没有实现你要的功能。否则重复的功能会导致混乱将来还得花费额外的精力清除它。 <BR><BR>2. 使用异常处理声明 <BR><BR>要定义应用程序的逻辑流程，成熟的经验是推荐在代码之外，用配置的方法来实现，而不是写死在程序代码中的。在J2EE中，这样的例子比比皆是。从实现EJB的安全性和事务性行为到描述JMS消息和目的地之间的关系，很多运行时的处理流程都是可以在程序之外定义的。 <BR><BR>Struts 创建者从一开始就采用这种方法，通过配置Struts的配置文件来定制应用系统运行时的各个方面。这一点在版本1.1的新特性上得到延续，包括新的异常处理功能。在Struts framework以前的版本中，开发人员不得不自己处理Struts应用中发生的错误情况。在最新的版本中，情况大大的改观了，Struts Framework提供了内置的一个称为 ExceptionHandler 的类， 用于系统缺省处理action类运行中产生的错误。这也是在上一个技巧中我们提到的framework许多可扩展接口之一。 <BR><BR>Struts缺省的 ExceptionHandler类会生成一个ActionError对象并保存在适当的范围（scope）对象中。这样就允许JSP页面使用错误类来提醒用户出现什么问题。如果你认为这不能满足你的需求，那么可以很方便的实现你自己的ExcepionHandler类。 <BR><BR>具体定制异常处理的方法和机制 <BR><BR>要定制自己的异常处理机制，第一步是继承org.apache.struts.action.ExceptionHandler类。这个类有2个方法可以覆盖，一个是excute()另外一个是storeException(). 在多数情况下，只需要覆盖其中的excute()方法。下面是ExceptionHandler类的excute()方法声明： <BR><BR><BR><BR>正如你看到的，该方法有好几个参数，其中包括原始的异常。方法返回一个ActionForward对象，用于异常处理结束后将controller类带到请求必须转发的地方去。 <BR><BR>当然您可以实现任何处理，但一般而言，我们必须检查抛出的异常,并针对该类型的异常进行特定的处理。缺省的，系统的异常处理功能是创建一个出错信息，同时把请求转发到配置文件中指定的地方去。 定制异常处理的一个常见的例子是处理嵌套异常。假设该异常包含有嵌套异常，这些嵌套异常又包含了其他异常，因此我们必须覆盖原来的execute()方法，对每个异常编写出错信息。 <BR><BR>一旦你创建了自己的ExceptionHandler 类，就应该在Struts配置文件中的部分声明这个类，以便让Struts知道改用你自定义的异常处理取代缺省的异常处理. <BR><BR>可以配置你自己的ExceptionHandler 类是用于Action Mapping特定的部分还是所有的Action对象。如果是用于Action Mapping特定的部分就在&lt;action&gt;元素中配置。如果想让这个类可用于所有的Action对象,可以在&lt;global-sections&gt; 元素中指定。例如，假设我们创建了异常处理类CustomizedExceptionHandler用于所有的Action类, &lt;global-exceptions&gt;元素定义如下所示： <BR><BR><BR><BR>在&lt;exception /&gt;元素中可以对很多属性进行设置。在本文中，最重要的属性莫过于handler属性, handler属性的值就是自定义的继承了ExceptionHandler类的子类的全名。 假如该属性没有定义，Struts会采用自己的缺省值。当然，其他的属性也很重要，但如果想覆盖缺省的异常处理的话，handler无疑是最重要的属性。 <BR><BR>最后必须指出的一点是，你可以有不同的异常处理类来处理不同的异常。在上面的例子中，CustomizedExceptionHandler用来处理任何java.lang.Exception的子类. 其实，你也可以定义多个异常处理类，每一个专门处理不同的异常树。下面的XML片断解释了如何配置以实现这一点。 <BR><BR><BR><BR>在这里，一旦有异常抛出，struts framework将试图在配置文件中找到ExceptionHandler，如果没有找到，那么struts将沿着该异常的父类链一层层往上找直到发现匹配的为止。因此，我们可以定义一个层次型的异常处理关系结构，在配置文件中已经体现了这一点。 <BR><BR>3. 使用应用模块（Application Modules） <BR><BR>Struts 1.1的一个新特性是应用模块的概念。应用模块允许将单个Struts应用划分成几个模块，每个模块有自己的Struts配置文件，JSP页面，Action等等。这个新特性是为了解决大中型的开发队伍抱怨最多的一个问题，即为了更好的支持并行开发允许多个配置文件而不是单个配置文件。 <BR><BR>注：在早期的beta版本中，该特性被称为子应用（sub-applications），最近的改名目的是为了更多地反映它们在逻辑上的分工。 <BR><BR>显然，当很多开发人员一起参加一个项目时，单个的Struts配置文件很容易引起资源冲突。应用模块允许Struts按照功能要求进行划分，许多情况已经证明这样更贴近实际。例如，假设我们要开发一个典型的商店应用程序。可以将组成部分划分成模块比如catalog（商品目录）, customer（顾客）, customer service（顾客服务）, order（订单）等。每个模块可以分布到不同的目录下，这样各部分的资源很容易定位，有助于开发和部署。图1 显示了该应用的目录结构。 <BR><BR>图 1. 一个典型的商店应用程序的目录结构 <BR><IMG height=242 src="http://www.huihoo.com/java/struts/jakarta1.jpg" width=156> <BR><BR><BR>注：如果你无需将项目划分成多个模块，Struts框架支持一个缺省的应用模块。这就使得应用程序也可以在1.0版本下创建，具有可移植性，因为应用程序会自动作为缺省的应用模块。 <BR><BR>为了使用多应用模块功能，必须执行以下几个准备步骤： <BR><BR>&amp;#8226; 为每个应用模块创建独立的Struts配置文件。 <BR><BR>&amp;#8226; 配置Web 部署描述符 Web.xml文件。 <BR><BR>&amp;#8226; 使用org.apache.struts.actions.SwitchAction 来实现程序在模块之间的跳转. <BR><BR>创建独立的Struts配置文件 <BR><BR>每个Struts应用模块必须拥有自己的配置文件。允许创建自己的独立于其他模块的Action，ActionForm，异常处理甚至更多。 <BR><BR>继续以上面的商店应用程序为例，我们可以创建以下的配置文件：一个文件名为struts-config-catalog.xml，包含catalog（商品目录）、items(商品清单)、和其它与库存相关的功能的配置信息；另一个文件名为struts- config-order.xml, 包含对order（订单）和order tracking（订单跟踪）的设置。第三个配置文件是struts-config.xml,其中含有属于缺省的应用模块中的一般性的功能。 <BR><BR>配置Web部署描述符 <BR><BR>在Struts的早期版本中，我们在Web.xml中指定Struts配置文件的路径。好在这点没变，有助于向后兼容。但对于多个应用模块，我们需要在Web部署描述符中增加新的配置文件的设定。 <BR><BR>对于缺省的应用（包括Struts的早期版本），Struts framework 在Web.xml文件中查找带有config的元素&lt;init-param&gt;，用于载入Action mapping 和其它的应用程序设定。作为例子，以下的XML片断展现一个典型的&lt;init-param&gt;元素： <BR><BR><BR><BR>注：如果在现有的&lt;init-param&gt;元素中找不到"config"关键字，Struts framework将缺省地使用/WEB/struts-config.xml <BR><BR>为了支持多个应用模块(Struts 1.1的新特性)，必须增加附加的&lt;init-param&gt;元素。与缺省的&lt;init-param&gt;元素不同的是，附加的&lt;init-param&gt;元素与每个应用模块对应，必须以config/xxx的形式命名，其中字符串xxx代表该模块唯一的名字。例如，在商店应用程序的例子中，&lt;init-param&gt;元素可定义如下（注意粗体字部分）： <BR><BR>第一个 &lt;init-param&gt;元素对应缺省的应用模块。第二和第三个元素分别代表非缺省应用模块catalog 和 order。 <BR><BR>当Struts载入应用程序时，它首先载入缺省应用模块的配置文件。然后查找带有字符串config/xxx 形式的附加的初始化参数。对每个附加的配置文件也进行解析并载入内存。这一步完成后，用户就可以很随意地用config/后面的字符串也就是名字来调用相应的应用模块。 <BR><BR>多个应用模块之间调用Action类 <BR><BR>在为每个应用模块创建独立的配置文件之后，我们就有可能需要调用不同的模块中Action。为此必须使用Struts框架提供的SwitchAction类。Struts 会自动将应用模块的名字添加到URL,就如Struts 自动添加应用程序的名字加到URL一样。应用模块是对框架的一个新的扩充，有助于进行并行的团队开发。如果你的团队很小那就没必要用到这个特性，不必进行模块化。当然，就算是只有一个模块，系统还是一样的运作。 <BR><BR>4. 把JSP放到WEB-INF后以保护JSP源代码 <BR><BR>为了更好地保护你的JSP避免未经授权的访问和窥视， 一个好办法是将页面文件存放在Web应用的WEB-INF目录下。 <BR><BR>通常JSP开发人员会把他们的页面文件存放在Web应用相应的子目录下。一个典型的商店应用程序的目录结构如图2所示。跟catalog （商品目录）相关的JSP被保存在catalog子目录下。跟customer相关的JSP，跟订单相关的JSP等都按照这种方法存放。 <BR><BR><BR><BR>图 2.基于不同的功能 JSP 被放置在不同的目录下 <BR><IMG height=242 src="http://www.huihoo.com/java/struts/jakarta2.jpg" width=156> <BR><BR><BR>这种方法的问题是这些页面文件容易被偷看到源代码，或被直接调用。某些场合下这可能不是个大问题，可是在特定情形中却可能构成安全隐患。用户可以绕过Struts的controller直接调用JSP同样也是个问题。 <BR><BR>为了减少风险，可以把这些页面文件移到WEB-INF 目录下。基于Servlet的声明，WEB-INF不作为Web应用的公共文档树的一部分。因此，WEB-INF 目录下的资源不是为客户直接服务的。我们仍然可以使用WEB-INF目录下的JSP页面来提供视图给客户，客户却不能直接请求访问JSP。 <BR><BR>采用前面的例子，图3显示将JSP页面移到WEB-INF 目录下后的目录结构 <BR><BR>图 3. JSP存放在 WEB-INF 目录下更为安全 <BR><IMG height=237 src="http://www.huihoo.com/java/struts/jakarta3.jpg" width=149> <BR><BR>如果把这些JSP页面文件移到WEB-INF 目录下，在调用页面的时候就必须把"WEB-INF"添加到URL中。例如，在一个Struts配置文件中为一个logoff action写一个Action mapping。其中JSP的路径必须以"WEB-INF"开头。如下所示：请注意粗体部分. <BR><BR>这个方法在任何情况下都不失为Struts实践中的一个好方法。是唯一要注意的技巧是你必须把JSP和一个Struts action联系起来。即使该Action只是一个很基本的很简单JSP，也总是要调用一个Action，再由它调用JSP。 <BR><BR>最后要说明的是，并不是所有的容器都能支持这个特性。WebLogic早期的版本不能解释Servlet声明，因此无法提供支持，据报道在新版本中已经改进了。总之使用之前先检查一下你的Servlet容器。 <BR><BR>5. 使用 Prebuilt Action类提升开发效率 <BR><BR>Struts framework带有好几个prebuilt Action类，使用它们可以大大节省开发时间。其中最有用的是org.apache.struts.actions.ForwardAction 和 org.apache.struts.actions.DispatchAction. <BR><BR>使用 ForwardAction <BR><BR>在应用程序中，可能会经常出现只要将Action对象转发到某个JSP的情况。在上一点中曾提到总是由Action调用JSP是个好习惯。如果我们不必在Action中执行任何业务逻辑，却又想遵循从Action访问页面的话，就可以使用ForwardAction，它可以使你免去创建许多空的Action类。运用ForwardAction的好处是不必创建自己的Action类，你需要做的仅仅是在Struts配置文件中配置一个Action mapping。 <BR><BR>举个例子，假定你有一个JSP文件index.jsp ，而且不能直接调用该页面，必须让程序通过一个Action类调用，那么，你可以建立以下的Action mapping来实现这一点： <BR><BR>正如你看到的，当 /home 被调用时, 就会调用ForwardAction 并把请求转发到 index.jsp 页面. <BR><BR>再讨论一下不通过一个Action类直接转发到某个页面的情况，必须注意我们仍然使用&lt;action&gt;元素中的forward属性来实现转发的目标。这时&lt;action&gt;元素定义如下： <BR><BR>以上两种方法都可以节省你的时间，并有助于减少一个应用所需的文件数。 <BR><BR>使用 DispatchAction <BR><BR>DispatchAction是Struts包含的另一个能大量节省开发时间的Action类。与其它Action类仅提供单个execute()方法实现单个业务不同，DispatchAction允许你在单个Action类中编写多个与业务相关的方法。这样可以减少Action类的数量，并且把相关的业务方法集合在一起使得维护起来更容易。 <BR><BR>要使用DispatchAction的功能，需要自己创建一个类，通过继承抽象的DispatchAction得到。对每个要提供的业务方法必须有特定的方法signature。例如，我们想要提供一个方法来实现对购物车添加商品清单，创建了一个类ShoppingCartDispatchAction提供以下的方法： <BR><BR>那么，这个类很可能还需要一个deleteItem()方法从客户的购物车中删除商品清单，还有clearCart()方法清除购物车等等。这时我们就可以把这些方法集合在单个Action类，不用为每个方法都提供一个Action类。 <BR><BR>在调用ShoppingCartDispatchAction里的某个方法时，只需在URL中提供方法名作为参数值。就是说，调用addItem()方法的 URL看起来可能类似于： <BR><BR>http://myhost/storefront/action/cart?method=addItem <BR><BR>其中method参数指定ShoppingCartDispatchAction中要调用的方法。参数的名称可以任意配置，这里使用的"method"只是一个例子。参数的名称可以在Struts配置文件中自行设定。 <BR><BR>6.使用动态ActionForm <BR><BR>在Struts framework中，ActionForm对象用来包装HTML表格数据（包括请求），并返回返回动态显示给用户的数据。它们必须是完全的JavaBean，并继承.Struts　里面的ActionForm类，同时，用户可以有选择地覆盖两个缺省方法。 <BR><BR>该特性能节省很多时间，因为它可以协助进行自动的表现层的验证。ActionForm的唯一缺点是必须为不同的HTML表格生成多个ActionForm 类以保存数据。例如，如果有一个页面含有用户的注册信息，另一个页面则含有用户的介绍人的信息，那么就需要有两个不同的ActionForm类。这在大的应用系统中就会导致过多的ActionForm类。Struts 1.1对此做出了很好的改进，引入了动态ActionForm类概念 <BR><BR>通过Struts framework中的DynaActionForm类及其子类可以实现动态的ActionForm ，动态的ActionForm允许你通过Struts的配置文件完成ActionForm的全部配置；再也没有必要在应用程序中创建具体的ActionForm类。具体配置方法是：在Struts的配置文件通过增加一个&lt;form-bean&gt;元素，将type属性设定成DynaActionForm或它的某个子类的全名。下面的例子创建了一个动态的ActionForm名为logonForm，它包含两个实例变量：username 和 password. <BR><BR><BR><BR>动态的ActionForm可以用于Action类和JSP，使用方法跟普通的ActionForm相同，只有一个小差别。如果使用普通的ActionForm对象则需要提供get 和 set方法取得和设置数据。以上面的例子而言，我们需要提供getUsername() 和 setUsername()方法取得和设置username变量，同样地有一对方法用于取得和设置password变量. <BR><BR>这里我们使用的是DynaActionForm，它将变量保存在一个Map类对象中，所以必须使用DynaActionForm 类中的get(name) 和 set(name)方法，其中参数name是要访问的实例变量名。例如要访问DynaActionForm中username的值，可以采用类似的代码： <BR><BR>String username = (String)form.get("username"); <BR><BR>由于值存放在一个Map对象，所以要记得对get()方法返回的Object对象做强制性类型转换。 <BR><BR>DynaActionForm有好几个很有用的子类。其中最重要的是DynaValidatorForm ，这个动态的ActionForm和Validator 一起利用公共的Validator包来提供自动验证。这个特性使你得以在程序代码之外指定验证规则。将两个特性结合使用对开发人员来说将非常有吸引力。 <BR><BR>7. 使用可视化工具 <BR><BR>自从Struts 1.0 分布以来，就出现了不少可视化工具用于协助创建，修改和维护Struts的配置文件。配置文件本身是基于XML格式，在大中型的开发应用中会增大变得很笨拙。为了更方便的管理这些文件，一旦文件大到你无法一目了然的时候，建议试着采用其中的一种GUI 工具协助开发。商业性的和开放源代码的工具都有不少，表1列出了可用的工具和其相关链接，从那里可以获取更多信息。 <BR><BR>表 1. Struts GUI 工具 <BR>
<TABLE cellSpacing=0 cellPadding=4 border=1>
<TBODY>
<TR bgColor=#cccccc>
<TH align=left>应用程序</TH>
<TH align=left>性质</TH>
<TH align=left>网址</TH></TR>
<TR>
<TD style="FONT-SIZE: 14pt">Adalon</TD>
<TD style="FONT-SIZE: 14pt">商业软件</TD>
<TD style="FONT-SIZE: 14pt"><A href="http://www.synthis.com/products/adalon">http://www.synthis.com/products/adalon</A> </TD></TR>
<TR>
<TD style="FONT-SIZE: 14pt">Easy Struts</TD>
<TD style="FONT-SIZE: 14pt">开放源码</TD>
<TD style="FONT-SIZE: 14pt"><A href="http://easystruts.sourceforge.net/">http://easystruts.sourceforge.net/</A> </TD></TR>
<TR>
<TD style="FONT-SIZE: 14pt">Struts Console</TD>
<TD style="FONT-SIZE: 14pt">免费</TD>
<TD style="FONT-SIZE: 14pt"><A href="http://www.jamesholmes.com/struts/console">http://www.jamesholmes.com/struts/console</A> </TD></TR>
<TR>
<TD style="FONT-SIZE: 14pt">JForms</TD>
<TD style="FONT-SIZE: 14pt">商业软件</TD>
<TD style="FONT-SIZE: 14pt"><A href="http://www.solanasoft.com/">http://www.solanasoft.com/</A> </TD></TR>
<TR>
<TD style="FONT-SIZE: 14pt">Camino</TD>
<TD style="FONT-SIZE: 14pt">商业软件</TD>
<TD style="FONT-SIZE: 14pt"><A href="http://www.scioworks.com/scioworks_camino.html">http://www.scioworks.com/scioworks_camino.html</A> </TD></TR>
<TR>
<TD style="FONT-SIZE: 14pt">Struts Builder</TD>
<TD style="FONT-SIZE: 14pt">开放源码</TD>
<TD style="FONT-SIZE: 14pt"><A href="http://sourceforge.net/projects/rivernorth/">http://sourceforge.net/projects/rivernorth/</A> </TD></TR>
<TR>
<TD style="FONT-SIZE: 14pt">StrutsGUI</TD>
<TD style="FONT-SIZE: 14pt">免费</TD>
<TD style="FONT-SIZE: 14pt"><A href="http://www.alien-factory.co.uk/struts/struts-index.html">http://www.alien-factory.co.uk/struts/struts-index.html</A> </TD></TR></TBODY></TABLE><BR>相关资源 <BR><BR>要获取更为全面的Struts GUI 工具列表 (包括免费的和商业性的), 请访问 Struts resource page <img src ="http://www.blogjava.net/sgsoft/aggbug/543.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-01-21 15:44 <a href="http://www.blogjava.net/sgsoft/articles/543.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>运用类反射机制简化Struts应用程序的开发  </title><link>http://www.blogjava.net/sgsoft/articles/347.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Sun, 16 Jan 2005 05:23:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/347.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/347.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/347.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/347.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/347.html</trackback:ping><description><![CDATA[<table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr valign="top"> <td><span class="atitle">运用类反射机制简化Struts应用程序的开发</span> </td> <td width="8"><img height="1" alt="" src="http://www-900.ibm.com/developerWorks/cn/i/c.gif" width="8" border="0" /></td> <td valign="bottom" align="right" width="180"></td> <td width="6"><img height="1" alt="" src="http://www-900.ibm.com/developerWorks/cn/i/c.gif" width="6" border="0" /></td></tr><!-- Black line separator--> <tr valign="top"> <td bgcolor="#000000" colspan="5"><img height="1" alt="" src="http://www-900.ibm.com/developerWorks/cn/i/c.gif" width="100" border="0" /></td></tr> <tr valign="top"> <td bgcolor="#ffffff" colspan="5"><img height="8" alt="" src="http://www-900.ibm.com/developerWorks/cn/i/c.gif" width="100" border="0" /></td></tr></tbody></table><!-- END HEADER AREA--><!-- START BODY AREA--> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr valign="top"> <td width="10"><img height="1" alt="" src="http://www-900.ibm.com/developerWorks/cn/i/c.gif" width="10" border="0" /></td> <td width="100%"> <table cellspacing="0" cellpadding="0" width="168" align="right" border="0"> <tbody> <tr><!-- Sidebar Gutter--> <td width="8"><img height="21" alt="" src="http://www-900.ibm.com/developerWorks/cn/i/c.gif" width="5" /></td> <td width="160"><!-- Start TOC--> <table cellspacing="0" cellpadding="0" width="160" border="0"> <tbody> <tr> <td width="160" bgcolor="#000000" height="1"><img height="1" alt="" src="http://www-900.ibm.com/developerWorks/cn/i/c.gif" width="160" /></td></tr> <tr> <td align="middle" background="" height="5"><b>内容：</b> </td></tr> <tr> <td width="160" bgcolor="#666666" height="1"><img height="1" alt="" src="http://www-900.ibm.com/developerWorks/cn/i/c.gif" width="160" /></td></tr> <tr> <td align="right"> <table cellspacing="0" cellpadding="3" width="98%" border="0"> <tbody> <tr> <td><a href="http://www-900.ibm.com/developerWorks/cn/java/l-struts-reflect/#1">一、Struts中引入ActionForm类的意义与缺陷</a></td></tr> <tr> <td><a href="http://www-900.ibm.com/developerWorks/cn/java/l-struts-reflect/#2">二、类反射的概念</a></td></tr> <tr> <td><a href="http://www-900.ibm.com/developerWorks/cn/java/l-struts-reflect/#3">三、如何应用类反射机制简化Struts应用程序的开发</a></td></tr> <tr> <td><a href="http://www-900.ibm.com/developerWorks/cn/java/l-struts-reflect/#4">四、结语</a></td></tr> <tr> <td><a href="http://www-900.ibm.com/developerWorks/cn/java/l-struts-reflect/#rating">对于本文的评价</a></td></tr> <tr> <td><a href="http://www-900.ibm.com/developerWorks/cn/java/l-struts-reflect/#author1">关于作者</a></td></tr></tbody></table></td></tr></tbody></table><!-- End TOC--><!-- Start Related Content Area--> <table cellspacing="0" cellpadding="0" width="160" border="0"> <tbody> <tr> <td width="160" bgcolor="#000000" height="1"><img height="1" alt="" src="http://www-900.ibm.com/developerWorks/cn/i/c.gif" width="160" /></td></tr> <tr> <td align="middle" background="" height="5"><a class="nav" href="http://www-900.ibm.com/developerWorks/cn/java/index.shtml"><b>Java 专区中还有:</b></a></td></tr> <tr> <td width="160" bgcolor="#666666" height="1"><img height="1" alt="" src="http://www-900.ibm.com/developerWorks/cn/i/c.gif" width="160" /></td></tr> <tr> <td align="right"> <table cellspacing="0" cellpadding="3" width="98%" border="0"> <tbody> <tr> <td><a href="http://www-900.ibm.com/developerWorks/cn/cnedu.nsf/java-onlinecourse-bytitle?OpenView&amp;Count=500">教学</a></td></tr> <tr> <td><a href="http://www-900.ibm.com/developerWorks/cn/cntools.nsf/dw/java-all-byname?OpenDocument&amp;count=500">工具与产品</a></td></tr> <tr> <td><a href="http://www-900.ibm.com/developerWorks/cn/cntools.nsf/dw/java-beans-bytitle?OpenDocument&amp;count=500">代码与组件</a></td></tr> <tr> <td><a href="http://www-900.ibm.com/developerWorks/cn/cnpapers.nsf/java-papers-bynewest?OpenView&amp;Count=500">所有文章</a></td></tr> <tr> <td><a href="http://www-900.ibm.com/developerWorks/cn/cntips.nsf/java-tips-bydate?OpenView&amp;Count=500">实用技巧</a></td></tr></tbody></table></td></tr></tbody></table><!-- End Related dW Content Area--> <table cellspacing="0" cellpadding="0" width="160" border="0"> <tbody> <tr> <td width="150" bgcolor="#000000" colspan="2" height="2"><img height="2" alt="" src="http://www-900.ibm.com/developerWorks/cn/i/c.gif" width="160" /></td></tr> <tr> <td width="150" bgcolor="#ffffff" colspan="2" height="2"><img height="2" alt="" src="http://www-900.ibm.com/developerWorks/cn/i/c.gif" width="160" /></td></tr></tbody></table><!-- END STANDARD SIDEBAR AREA--></td></tr></tbody></table> <p><a href="http://www-900.ibm.com/developerWorks/cn/java/l-struts-reflect/#author1">邢波涛</a> (<a href="mailto:xbt@bhsky.com">xbt@bhsky.com</a>)<br /><br />2003 年 11 月</p> <blockquote>本文讲述了如何利用Java的反射的机制来简化Structs应用程序的开发。</blockquote> <p><a name="1"><span class="atitle2">一、Struts中引入ActionForm类的意义与缺陷：</span></a><br />在Struts应用程序中，ActionForm是一个很重要的概念,它的主要功能就是为Action的操作提供与客户表单相映射的数据（如果在客户指定的情况下，还包括对数据进行校验）。Action根据业务逻辑的需要，对数据状态进行修改，在改变系统状态后，ActionForm则自动的回写新的数据状态并保持。程序员对JSP与ActionForm Bean的对应关系，通常感到很迷惑，JSP与ActionForm到底是1:1,还是N:1,对此，Struts本身对此并没有提出自己的观点。无论是一对一，还是多对一，Struts本身并不关心，它都能很好得工作。Struts在它的开发文档中指出，对于较小规模的开发，开发人员可以根据自己的需要，每个模块只写一个ActionForm Bean，甚至整个应用程序只写一个ActionForm Bean.当然，Struts也不反对每个ActionForm Bean只对应一个JSP,他们之间的对应关系，由开发人员自己决定。 </p> <p>在我看来，正如Entity EJB对J2EE的重大贡献一样，Entity EJB使得程序员对二维关系数据库的存取对象化了，程序员可以使用Set 或者Get等面向对象的方法来操纵关系数据库的数据，而ActionForm也使得程序员对网页的数据存取奇迹般的对象化了，程序员同样也可以使用Set 或者Get等面向对象的方法存取网页上的数据，这是一个开发模式方式上的重大转变。基于此，我个人认为ActionForm与JSP即VIEW层的关系最好是一对一的关系，这样，在理解上会更清晰一些。但是，这样也会带来一个很现实的问题，在一个应用程序中，也许有非常多得JSP页面，如果每个ActionForm 都只对应一个JSP页面，那么系统的Java代码就会急剧膨胀起来，而且，每个ActionForm都是只有很简单的Set或者Get方法存取数据，那么，如何简化Struts应用程序的开发呢？ </p> <p>在Struts1.1 中，Struts引入了DynaActionForm和Dyna Bean,试图解决这个问题，在我看来，DynaActionForm的引入，破坏了对网页存取对象化的概念，使开发人员重新回到了使用HashTable、Map、Collection、ArrayList等集合对象来实现对数据进行存取的老路上来。虽然应用程序的灵活性大大增加了，但是代码的可读性也大大降低了，开发人员之间的交流难度也增加了。 </p> <p>在传统的应用程序对ActionForm Bean的访问中，我们通常都写成如下的形式： </p> <p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"> <tbody> <tr> <td><pre><code> Connection conn=DriverManager.getConnection("JDBC URL "); sql=" select * from some tables "; PreparedStatement stmt = conn.prepareStatement(sql); ResultSet rs = stmt.executeQuery(); ArrayList array=new ArrayList(); while (rs.next()) { AActionForm actionForm =new AActionForm (); actionForm.setId(rs.getString("Id")); actionForm.setName(rs.getString("Name")); array.add(actionForm); } </code></pre></td></tr></tbody></table></p> <p>在Action 的Execute方法中，我们 把这个集合用request.setAttribute("array", array)存储起来，然后在JSP页面中，我们用iterate Tag把数据循环现实出来。代码通常都是这个样子： </p> <p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"> <tbody> <tr> <td><pre><code> &lt;logic:present name=" array " scope="request"&gt; &lt;logic:iterate name=" array " id=" array " type="com.bhsky.webis.Goods"&gt; &lt;tr align="center"&gt; &lt;td class="table2"&gt; &lt;bean:write name=" array " property="goodsid"/&gt; &lt;/td&gt; &lt;td class="table2"&gt; &lt;bean:write name=" array " property="goodsname"/&gt; &lt;/td&gt; &lt;/tr&gt; &lt;/logic:iterate&gt; &lt;/logic:present&gt; </code></pre></td></tr></tbody></table></p> <p>在Struts中，对数据的访问和显示的写法通常都是很固定的，在VIEW层，我们是没有办法简化自己的代码的，在Action层，其写法通常也很固定，只是做一个页面的跳转，商业逻辑和对数据得访问，通常都是放在JavaBean中。那么，在此，我提出一种运用类反射的机制，使应用程序对ActionForm Bean的赋值自动化，即应用程序通过一个简单的接口，使用一个通用的方法，就可以完成对ActionForm Bean的赋值，而不必在每个使用ActionFormBean的地方，都把数据库中的值手动赋值给ActionForm Bean，然后再在JSP页面中显示出来。虽然它不能减少ActionForm Bean的数量，但是，它至少使应用程序对ActionForm Bean的赋值自动化了，从而减少了程序出错概率，提高了程软件开发效率。 </p> <p><a name="2"><span class="atitle2">二、类反射的概念：</span></a><br />关于类反射的概念，在此我就不详细介绍了，它不是本文的重点，IBM developerWorks网站上有大量介绍类反射概念的文章，大家可以找出来参考一下。其实，Struts本身就大量利用了类反射的机制。 </p> <p><a name="3"><span class="atitle2">三、如何应用类反射机制简化Struts应用程序的开发:</span></a><br /></p> <p><span class="atitle3">1、 先定义Action FormBean:</span></p> <p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"> <tbody> <tr> <td><pre><code> package com.bhsky.webis.system; import org.apache.struts.action.*; import javax.servlet.http.*; public class UsersActionForm extends ActionForm { private String usr_id; private String usr_name; public void setUsr_id(String usr_id) { this.usr_id = usr_id; } public String getUsr_id() { return usr_id; } public String getUsr_memo() { return usr_memo; } public void setUsr_name(String usr_name) { this.usr_name = usr_name; } } </code></pre></td></tr></tbody></table></p> <p><span class="atitle3">2、 编写通用的为ActionFormBean赋值的方法：</span></p> <p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"> <tbody> <tr> <td><pre><code> ///////////////////////////////////////////////////////////////////////////// //Function: 完成ResultSet对象向ArrayList对象为集合的对象的转化 //Para:sql,指定的查询Sql //Para:className,Sql相对应得JavaBean/FormBean类的名字 //Return:以类className为一条记录的结果集，完成ResultSet对象向ArrayList对象为集//合的className对象的转化 ////////////////////////////////////////////////////////////////////////////// public ArrayList Select(String sql,String className){ ArrayList paraList=new ArrayList(); try{ if (conn == null){ Connection(); } PreparedStatement stmt = conn.prepareStatement(sql); ResultSet rs = stmt.executeQuery(); String recordValue=""; Object c1=null; paraList=new ArrayList(); ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); while (rs.next()){ c1=Class.forName(className).newInstance(); for (int i=1; i&lt;=columnCount; i++) { if(rs.getString(rsmd.getColumnName(i))!=null){ recordValue=rs.getString(rsmd.getColumnName(i)); }else{ recordValue=""; } Method m=c1.getClass().getMethod(getSetMethodName(rsmd.getColumnName(i)), new Class[]{recordValue.getClass()}); m.invoke (c1, new Object[]{recordValue}); } paraList.add(c1); } }catch(SQLException ex){ }catch(ClassNotFoundException e){ }catch(NoSuchMethodException e) { }catch(InvocationTargetException e){ }catch (IllegalAccessException e){ }catch(InstantiationException e){ } finaly{ closeConnection(); return paraList; } } </code></pre></td></tr></tbody></table></p> <p><span class="atitle3">3、 在JavaBean封装的商业逻辑中调用Select 方法，然后在JSP页面上显示出来：</span></p> <p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"> <tbody> <tr> <td><pre><code> //Function:取得用户列表 //Para: //Return:返回用户列表 ///////////////////////////////////////////////////////////////////////////// public ArrayList getUsers(){ ArrayList ret=null; DatabaseManage db=new DatabaseManage(); String sql=" select usr_id,usr_name " +" from users " ; ret=db.Select(sql," com.bhsky. webis.system.UsersActionForm"); return ret; } </code></pre></td></tr></tbody></table></p> <p><span class="atitle3">4、 在Action的execute方法中调用getUsers()方法：</span></p> <p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"> <tbody> <tr> <td><pre><code> public ActionForward execute(ActionMapping actionMapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse httpServletResponse) { /**@todo: complete the business logic here, this is just a skeleton.*/ UsersActionForm uaf=(UsersActionForm)actionForm; SystemService ubb=new SystemService(); ArrayList userList=ubb.getUsers(); request.setAttribute("userList",userList); ActionForward actionForward=actionMapping.findForward(url); return actionForward; } </code></pre></td></tr></tbody></table></p> <p><span class="atitle3">5、 在JSP中显示：</span></p> <p> <table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1"> <tbody> <tr> <td><pre><code> &lt;table width="700" class="1" border="1" cellspacing="1" align="center"&gt; &lt;tr&gt; &lt;td class="list" &gt;用户ID&lt;/td&gt; &lt;td class="list" &gt;姓&nbsp;&nbsp;名&lt;/td&gt; &lt;/tr&gt; &lt;logic:present name="userList" scope="request"&gt; &lt;logic:iterate name="userList" id="userList" type="com.bhsky.webis.system.UsersActionForm"&gt; &lt;tr&gt; &lt;td class="cell1" height="22"&gt;&lt;bean:write name="userList" property="usr_id"/&gt;&lt;/td&gt; &lt;td class="cell1" height="22"&gt;&lt;bean:write name="userList" property="usr_name"/&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/logic:iterate&gt; &lt;/logic:present&gt; &lt;/table&gt; </code></pre></td></tr></tbody></table></p> <p><a name="4"><span class="atitle2">四、结语：</span></a><br />我们通过运用类反射机制，在一个Struts应用开发中，完成了一个通用查询方法的实现。它使得程序员摆脱了在每个应用程序中都要编写枯燥的set、get等方法来访问ActionForm Bean,从而简化了Struts应用程序的开发。 </p><!-- AUTHOR BIOS--><!-- Make author heading singular or plural as needed--> <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td><a name="author1"><span class="atitle2">关于作者</span></a> <br />邢波涛 ， 您可以通过 <a href="mailto:xbt@bhsky.com">xbt@bhsky.com</a> 与他联系。 </td></tr></tbody></table></td></tr></tbody></table><img src ="http://www.blogjava.net/sgsoft/aggbug/347.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-01-16 13:23 <a href="http://www.blogjava.net/sgsoft/articles/347.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>struts-menu+ibatis+少量的代码=通用的自定义菜单和动态加载的树</title><link>http://www.blogjava.net/sgsoft/articles/208.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Tue, 11 Jan 2005 09:20:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/208.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/208.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/208.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/208.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/208.html</trackback:ping><description><![CDATA[<p>struts-menu+ibatis+少量的代码=通用的自定义菜单和动态加载的树</p> <p>前言：<br />知识准备：首先你需要懂一些struts的基本知识，会用struts-menu，并理解站长对struts-menu的分析那篇文章，还要知道ibatis的基本知识，如果不懂，请去google或者站长的论坛里找相关的文章。</p> <p>树形结构在实际开发中很常用，但是树形结构的开发往往也是难题，尤其是在显示这一条上，很难做到通用。通常有两种典型的树型结构。一种是论坛的帖子，其结构往往通过父子ID号相连,数据在一张表里。一种是级别，比如论坛中的Category-&gt;Forum-&gt;Thread这种结构，数据放在不同的表里。因为论坛恰好包含了这两种结构。因此。我们就能Jive的表结构来做这个例子。首先我们通过RsMetaDataTest来扫描数据库，得到需要的XML配置文件。拿一个xml为例，解释一下</p> <p>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />&lt;!DOCTYPE sql-map<br />PUBLIC "-//iBATIS.com//DTD SQL Map 1.0//EN"<br />"<a href="http://www.ibatis.com/dtd/sql-map.dtd">http://www.ibatis.com/dtd/sql-map.dtd</a>"&gt;<br />&lt;sql-map name="jivecategory"&gt;<br />&lt;!-- =============================================<br />&nbsp;&nbsp;&nbsp; mapped-statement find <br />============================================= --&gt;<br />&lt;dynamic-mapped-statement name="findjivecategoryDao"&nbsp; result-class="java.util.HashMap"&gt;<br />&nbsp;&nbsp; select $listfield$&nbsp; from JIVECATEGORY<br />&nbsp;&nbsp; &lt;dynamic prepend="where"&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;isPropertyAvailable prepend="and" property="CATEGORYID" &gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;isNotNull prepend="" property="CATEGORYID" &gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CATEGORYID=#CATEGORYID#<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/isNotNull&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/isPropertyAvailable&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;isPropertyAvailable prepend="and" property="NAME" &gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;isNotNull prepend="" property="NAME" &gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NAME=#NAME#<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/isNotNull&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/isPropertyAvailable&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;isPropertyAvailable prepend="and" property="DESCRIPTION" &gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;isNotNull prepend="" property="DESCRIPTION" &gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DESCRIPTION=#DESCRIPTION#<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/isNotNull&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/isPropertyAvailable&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;isPropertyAvailable prepend="and" property="CREATIONDATE" &gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;isNotNull prepend="" property="CREATIONDATE" &gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CREATIONDATE=#CREATIONDATE#<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/isNotNull&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/isPropertyAvailable&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;isPropertyAvailable prepend="and" property="MODIFIEDDATE" &gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;isNotNull prepend="" property="MODIFIEDDATE" &gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MODIFIEDDATE=#MODIFIEDDATE#<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/isNotNull&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/isPropertyAvailable&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;isPropertyAvailable prepend="and" property="LFT" &gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;isNotNull prepend="" property="LFT" &gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LFT=#LFT#<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/isNotNull&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/isPropertyAvailable&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;isPropertyAvailable prepend="and" property="RGT" &gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;isNotNull prepend="" property="RGT" &gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RGT=#RGT#<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/isNotNull&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/isPropertyAvailable&gt;<br />&nbsp;&nbsp; &lt;/dynamic&gt;&nbsp;&nbsp; <br />&lt;/dynamic-mapped-statement&gt;</p> <p>&lt;/sql-map&gt;</p> <p>&nbsp;</p> <p>可见有一个名叫findjivecategoryDao，这是一个典型的动态查询。返回对象是HashMap。其中$listfield$表示动态读取的字段。可以这么说，通过这个查询。有关这样表的任何方式的查询都已经解决了。由于这个演示只用到四张表，因些我们在sql-map-config-storedb.xml也只加载了四张表的定义。</p> <p>&lt;sql-map resource="sqlmap/jivecategory.xml" /&gt;<br />&lt;sql-map resource="sqlmap/jiveforum.xml" /&gt; <br />&lt;sql-map resource="sqlmap/jivethread.xml" /&gt; <br />&lt;sql-map resource="sqlmap/jivemessage.xml" /&gt; </p> <p>然后定义MenuDefine类，这个类是一个通用的定义，其主要属性如下。可以通过它建立一个四张表的树形关系。</p> <p>//sql Map的名称 <br />private String sqlMapName;<br />//调用的查询名称<br />private String SqlName;<br />//子菜单的名称<br />private String submenuName; //对应字段，其中key为主表的字段，value是从表的字段。<br />private HashMap keymap;<br />//菜单的名称<br />private String MenuName;<br />//标题<br />private String Title;<br />//标题字段<br />private String TitleField;<br />//需要读取的字段<br />private String listField;<br />//是否需要显示<br />private boolean needShow=true;</p> <p>&nbsp;</p> <p><br />然后建立一个XML的文件(此处简化了它的功能，就是把上面这个类序列化了一下)。把它放在classes目录下。</p> <p>&nbsp; </p> <p>&lt;?xml version="1.0" encoding="UTF-8"?&gt; <br />&lt;java version="1.4.2_03" class="java.beans.XMLDecoder"&gt; <br />&nbsp;&lt;object class="java.util.HashMap"&gt; <br />&nbsp; &lt;void method="put"&gt; <br />&nbsp;&nbsp; &lt;string&gt;message&lt;/string&gt; <br />&nbsp;&nbsp; &lt;object class="com.ewuxi.champion.MenuDefine"&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="keymap"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;object class="java.util.HashMap"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;void method="put"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;MESSAGEID&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;PARENTMESSAGEID&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;/object&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="listField"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;MESSAGEID,SUBJECT&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="menuName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;message&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="sqlMapName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;jivemessage&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="sqlName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;findjivemessageDao&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="submenuName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;message&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="title"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;文章&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="titleField"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;SUBJECT&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp; &lt;/object&gt; <br />&nbsp; &lt;/void&gt; <br />&nbsp; &lt;void method="put"&gt; <br />&nbsp;&nbsp; &lt;string&gt;category&lt;/string&gt; <br />&nbsp;&nbsp; &lt;object class="com.ewuxi.champion.MenuDefine"&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="keymap"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;object class="java.util.HashMap"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;void method="put"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;CATEGORYID&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;CATEGORYID&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;/object&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="listField"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;CATEGORYID,NAME&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="menuName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;category&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="sqlMapName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;jivecategory&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="sqlName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;findjivecategoryDao&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="submenuName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;forum&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="title"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;大分类&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="titleField"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;NAME&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp; &lt;/object&gt; <br />&nbsp; &lt;/void&gt; <br />&nbsp; &lt;void method="put"&gt; <br />&nbsp;&nbsp; &lt;string&gt;forum&lt;/string&gt; <br />&nbsp;&nbsp; &lt;object class="com.ewuxi.champion.MenuDefine"&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="keymap"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;object class="java.util.HashMap"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;void method="put"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;FORUMID&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;FORUMID&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;/object&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="listField"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;FORUMID,NAME&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="menuName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;forum&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="sqlMapName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;jiveforum&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="sqlName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;findjiveforumDao&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="submenuName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;thread&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="title"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;子分类&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="titleField"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;NAME&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp; &lt;/object&gt; <br />&nbsp; &lt;/void&gt; <br />&nbsp; &lt;void method="put"&gt; <br />&nbsp;&nbsp; &lt;string&gt;thread&lt;/string&gt; <br />&nbsp;&nbsp; &lt;object class="com.ewuxi.champion.MenuDefine"&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="keymap"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;object class="java.util.HashMap"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;void method="put"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;THREADID&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;THREADID&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;void method="put"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;FORUMID&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;FORUMID&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;void method="put"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;ROOTMESSAGEID&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;MESSAGEID&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;/object&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="listField"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;THREADID,ROOTMESSAGEID&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="menuName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;thread&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="needShow"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;boolean&gt;false&lt;/boolean&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="sqlMapName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;jivethread&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="sqlName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;findjivethreadDao&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="submenuName"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;message&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="title"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;栏目&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp;&nbsp; &lt;void property="titleField"&gt; <br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;string&gt;ROOTMESSAGEID&lt;/string&gt; <br />&nbsp;&nbsp;&nbsp; &lt;/void&gt; <br />&nbsp;&nbsp; &lt;/object&gt; <br />&nbsp; &lt;/void&gt; <br />&nbsp;&lt;/object&gt; <br />&lt;/java&gt; </p> <p>&nbsp;&nbsp;&nbsp; </p> <p>&nbsp;</p> <p>关联关系是category表通过CATEGORYID与forum关联，forum通过FORUMID与thread关联，thread是一张特殊的表。它将不显示在树中，只是一个过渡关联，用于读出新建的文章。</p> <p>thread通过FORUMID、FORUMID、ROOTMESSAGEID与message表关联(FORUMID、FORUMID、MESSAGEID)。而message表是一个自关联的表。MESSAGEID与PARENTMESSAGEID关联建立父子关系。</p> <p>然后我们建立一个session类作为主要类</p> <p>public class TreeDemoSession {<br />&nbsp;//通过名称和参数来得到树<br />&nbsp;public MenuComponent getMenu(String name, Map keys) throws Exception {<br />&nbsp;&nbsp;Map menuMap =<br />&nbsp;&nbsp;&nbsp;(Map) (new XmlUtils().read(Service.getPath() + "/menu.xml"));<br />&nbsp;&nbsp;MenuComponent menu = new MenuComponent();</p> <p>&nbsp;&nbsp;if (menuMap.get(name) != null) {<br />&nbsp;&nbsp;&nbsp;MenuDefine rootMenudefine = (MenuDefine) menuMap.get(name);<br />&nbsp;&nbsp;&nbsp;menu.setTitle(rootMenudefine.getTitle());<br />&nbsp;&nbsp;&nbsp;menu.setName(rootMenudefine.getMenuName());</p> <p>&nbsp;&nbsp;&nbsp;menu = submenuAdd(menu, keys, menuMap, name);<br />&nbsp;&nbsp;}<br />&nbsp;&nbsp;return menu;<br />&nbsp;}</p> <p>&nbsp;/**一个典型的递归函数。用以组织树。<br />&nbsp; * @param menu<br />&nbsp; * @param map<br />&nbsp; * @param menuMap<br />&nbsp; * @param menuName<br />&nbsp; * @return<br />&nbsp; * @throws DaoException<br />&nbsp; * @throws Exception<br />&nbsp; */<br />&nbsp;private MenuComponent submenuAdd(<br />&nbsp;&nbsp;MenuComponent menu,<br />&nbsp;&nbsp;Map map,<br />&nbsp;&nbsp;final Map menuMap,<br />&nbsp;&nbsp;String menuName)<br />&nbsp;&nbsp;throws DaoException, Exception {<br />&nbsp;&nbsp;try {<br />&nbsp;&nbsp;&nbsp;//得到菜单定义<br />&nbsp;&nbsp;&nbsp;MenuDefine menudefine = (MenuDefine) menuMap.get(menuName);<br />&nbsp;&nbsp;&nbsp;//listfield,表示需要读取哪几个字段<br />&nbsp;&nbsp;&nbsp;map.put("listfield", menudefine.getListField());<br />&nbsp;&nbsp;&nbsp;//查询，返回列表。<br />&nbsp;&nbsp;&nbsp;List list = DaoCommon.findbyName(map, menudefine.getSqlName());</p> <p>&nbsp;&nbsp;&nbsp;int namei = 0;<br />&nbsp;&nbsp;&nbsp;for (Iterator iter = list.iterator(); iter.hasNext();) {<br />&nbsp;&nbsp;&nbsp;&nbsp;Map element = (Map) iter.next();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //建立当前节点<br />&nbsp;&nbsp;&nbsp;&nbsp;MenuComponent submenu = new MenuComponent();<br />&nbsp;&nbsp;&nbsp;&nbsp;submenu.setName(menu.getName() + String.valueOf(namei++));<br />&nbsp;&nbsp;&nbsp;&nbsp;submenu.setTitle(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String.valueOf(element.get(menudefine.getTitleField())));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//如果不需要显示，则使用父节点作为当前节点<br />&nbsp;&nbsp;&nbsp;&nbsp;if (!menudefine.isNeedShow())<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;submenu = menu;<br />&nbsp;&nbsp;&nbsp;&nbsp;//如果有子菜单，则递归调用。&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;if (menudefine.getSubmenuName() != null) {</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;submenu =<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;submenuAdd(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;submenu,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getSubMenuInfo(menudefine, element),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;menuMap,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;menudefine.getSubmenuName());<br />&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;//将当前节点放到树中。(如果不需要显示就不用放)<br />&nbsp;&nbsp;&nbsp;&nbsp;if (menudefine.isNeedShow())<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;menu.addMenuComponent(submenu);<br />&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;return menu;<br />&nbsp;&nbsp;} catch (DaoException e) {</p> <p>&nbsp;&nbsp;&nbsp;throw e;<br />&nbsp;&nbsp;} catch (Exception e) {</p> <p>&nbsp;&nbsp;&nbsp;throw e;<br />&nbsp;&nbsp;}<br />&nbsp;}</p> <p>&nbsp;/**将父菜单的关键字段的值作为参数给子菜单<br />&nbsp; * @param menudefine<br />&nbsp; * @param element<br />&nbsp; * @return<br />&nbsp; */<br />&nbsp;private HashMap getSubMenuInfo(MenuDefine menudefine, Map element) {<br />&nbsp;&nbsp;HashMap map = new HashMap();<br />&nbsp;&nbsp;for (Iterator iter = menudefine.getKeymap().keySet().iterator();<br />&nbsp;&nbsp;&nbsp;iter.hasNext();<br />&nbsp;&nbsp;&nbsp;) {<br />&nbsp;&nbsp;&nbsp;String key = (String) iter.next();<br />&nbsp;&nbsp;&nbsp;map.put(menudefine.getKeymap().get(key), element.get(key));<br />&nbsp;&nbsp;}<br />&nbsp;&nbsp;return map;<br />&nbsp;}<br />} </p> <p>&nbsp;</p> <p>三个函数，非常简单，主函数读取配置文件的内容。一个递归函数用来建立树形结构。这棵树只有两个属性被设置。一个是名字和标题。其中标题采用从数据库里读出的字段。名字则采用流水号。读取数据库只有一句,其中map是参数的一个列表。后面是sql的名字。</p> <p>List list = DaoCommon.findbyName(map, menudefine.getSqlName());</p> <p>而真正的实现代码也非常简单</p> <p>public static List findbyName(Object vo,String name) throws DaoException {<br />try {<br />SqlMap sqlMap = DaoCommon.getSqlMap(DaoCommon.getDefautDao());<br />return (List) sqlMap.executeQueryForList(name, vo);<br />} catch (Exception e) {<br />throw new DaoException(e);<br />}<br />}</p> <p>下面我们来做Action的工作</p> <p>public ActionForward execute(<br />ActionMapping mapping,<br />ActionForm form,<br />HttpServletRequest request,<br />HttpServletResponse response)<br />throws Exception { Service.initSet();<br />DaoCommon.startTransaction();</p> <p>HashMap parMap = new HashMap();<br />Enumeration enumeration = request.getParameterNames();<br />while (enumeration.hasMoreElements()) {<br />String element = (String) enumeration.nextElement();<br />parMap.put(element, request.getParameter(element));<br />}<br />TreeDemoSession session=new TreeDemoSession();<br />request.setAttribute("com.ewuxi.champion.menu",session.getMenu(request.getParameter("menuName"),parMap));</p> <p>DaoCommon.rollBack();</p> <p>return mapping.findForward(request.getParameter("type"));<br />}</p> <p>&nbsp;</p> <p><br />这个函数也非常简单，就是把从request传来的内容生成一个Map对象。然后调用session,将返回结果以com.ewuxi.champion.menu为名字保存到request中去。</p> <p>最后我们需要生成一个自定义的taglib。实际上很简单。只是因为struts-menu自身的taglib是写死了，我们不能利用，不过只要改一个地方就可以了,copy UseMenuDisplayerTag到我们的目录下。</p> <p>MenuRepository repository =<br />(MenuRepository) pageContext.getServletContext().getAttribute(MenuRepository.MENU_REPOSITORY_KEY); if (repository == null) {<br />throw new JspException("Could not obtain the menu repository");<br />}</p> <p>MenuComponent menu = repository.getMenu(this.name);</p> <p>&nbsp;</p> <p>找到上面这一段，改成</p> <p><br />MenuComponent menu =<br />(MenuComponent) pageContext.findAttribute(this.name);</p> <p><br />就OK了。然后需要建立一个JSP文件。我们把xtree.jsp借用过来。唯一需要改的就是&lt;cp:displayMenu name="com.ewuxi.champion.menu"/&gt;，当然还有几个link的路径。因为此处用treeDemo来所以就是href="/treeDemo/styles/xtree.css"</p> <p>&lt;head&gt;<br />&lt;title&gt;XTree (with Velocity) Example&lt;/title&gt; &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8" /&gt;</p> <p>&lt;link rel="stylesheet" type="text/css" media="screen"<br />href="/treeDemo/styles/global.css" /&gt;<br />&lt;link rel="stylesheet" type="text/css" media="screen"<br />href="/treeDemo/styles/xtree.css" /&gt;</p> <p>&lt;script type="text/javascript" src="/treeDemo/scripts/xtree.js"&gt;&lt;/script&gt;</p> <p>&lt;/head&gt;<br />&lt;body&gt;</p> <p>&lt;div class="container"&gt;<br />Simple menu with Velocity:&lt;br /&gt;<br />&lt;script type="text/javascript"&gt;<br />&lt;menu:useMenuDisplayer name="Velocity" config="/templates/xtree.html"<br />bundle="org.apache.struts.action.MESSAGE"&gt;<br />if (document.getElementById) {<br />&lt;cp:displayMenu name="com.ewuxi.champion.menu"/&gt;<br />} else {<br />var msg = "Your browser does not support document.getElementById().\n";<br />msg += "You must use a modern browser for this menu.";<br />alert(msg);<br />}</p> <p><br />&lt;/menu:useMenuDisplayer&gt;<br />&lt;/script&gt;<br />&lt;/div&gt;</p> <p>&nbsp;</p> <p>&nbsp;</p> <p>下面就可以自由的看效果了。</p> <p>&lt;p&gt;&lt;a href="demo.do?type=demo&amp;menuName=category" target="_blank"&gt;大分类列表&lt;/a&gt;<br />&lt;/p&gt;<br />&lt;p&gt;&lt;a href="demo.do?type=demo&amp;menuName=forum" target="_blank"&gt;子分类列表&lt;/a&gt; &lt;/p&gt;<br />&lt;p&gt;&lt;a href="demo.do?type=demo&amp;menuName=forum&amp;FORUMID=1" target="_blank"&gt;只看java分类&lt;/a&gt; &lt;/p&gt;<br />&lt;p&gt;&lt;a href="demo.do?type=demo&amp;menuName=thread" target="_blank"&gt;所有文章&lt;/a&gt; &lt;/p&gt;</p> <p>上面是几种不同的参数。主要的差别是menuName不同。然后也可以加数据库需要的参数，比如java分类的forumId=1。就在参数中加FORUMID=1,注意大小写要跟XML中的动态参数相同，此处全是大写。</p> <p>在线演示看这样<a href="http://demo.ewuxi.com:8000/treejivedemo/">http://demo.ewuxi.com:8000/treejivedemo/</a>,源码下载</p> <p>&nbsp;</p><img src ="http://www.blogjava.net/sgsoft/aggbug/208.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-01-11 17:20 <a href="http://www.blogjava.net/sgsoft/articles/208.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts-menu源码分析</title><link>http://www.blogjava.net/sgsoft/articles/207.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Tue, 11 Jan 2005 09:15:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/207.html</guid><description><![CDATA[<h4 align="center">Struts-menu源码分析<!-- InstanceEndEditable --></h4><!-- InstanceBeginEditable name="EditRegion3" --> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;好的代码读起来让人如饮醍醐，读完以后神清气爽。如果你想提高你的编程水平，如果你想提高你的设计能力，如果你也想成为大师，那么就去阅读代码吧。以本人十几年来的编程经验，阅读代码能让你得到的比阅读文章(那怕是大师的文章)得到的更多。优秀而且实用的代码有很多，比如Junit,比如Jive，比如petStore，甚至是tomcat的Example、Log4j的Example。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一段广告完毕，下面就为大家分析一下struts-menu的源码，作为送给大家的圣诞礼物吧。Struts-Menu也来自一位大师的作品， Matt Raible。有很多优秀的作品，比如使用struts和hibernate的struts-resume。官方网站是<a href="http://raibledesigns.com/wiki/Wiki.jsp?page=Main" target="_blank">http://raibledesigns.com/wiki/Wiki.jsp?page=Main</a>。Struts-Menu的最新版本是2.1。功能是使用struts技术，构建树形菜单。应该说是一个非常实用的技术，极大的方便了广大的开发人员。与此同时，个人认为它的作用还不止于些。比如，同时它也是一个使用<a href="http://maven.apache.org/" target="_blank">Maven</a>和<a href="http://jakarta.apache.org/velocity/index.html" target="_blank">velocity</a>的一个很好的例子。</p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;首先，我们去看一下它的效果。<a href="http://www.raibledesigns.com/struts-menu/" target="_blank">http://www.raibledesigns.com/struts-menu/</a>。可以看到，如此丰富多彩的菜单效果，都是在演示一个配置文件里的内容。这是一个非常好的数据与表示相分离的实现。我们打开它的源码来看。首先看一下它的包图</p> <p><a href="http://champion.ewuxi.com/old/opensource/struts-new/images/2.jpg" target="_blank"><img height="425" src="http://champion.ewuxi.com/old/opensource/struts-new/images/2.jpg" width="851" border="0" /></a></p> <p>共有五个包，其中menu自然是完成数据组织功能，是核心之一，displayer是显示方式包，完成数据显示大部分功能。也是核心之一。taglib意义明显。example自然是一些example。util是读取资源文件的包。因些，我们重点研究的包只有三个menu,displayer和taglib。</p> <p>首先我们来看menu包的类图</p> <p><a href="http://champion.ewuxi.com/old/opensource/struts-new/images/1.jpg" target="_blank"><img height="768" src="http://champion.ewuxi.com/old/opensource/struts-new/images/1.jpg" width="1024" border="0" /></a></p> <p>首先是MenuPlugIn这个类。这个类的功能很明显，就是一个struts的plug-in。可以看到，它只有一个参数menuConfig，就是menu的配置文件路径。果然，在struts-conf文件中有这么一段</p> <table width="100%" bgcolor="#cccccc" border="1"> <tbody> <tr> <td><pre> &lt;!-- ========== Plug Ins Configuration ================================== --&gt;</pre> <p>&lt;plug-in className="net.sf.navigator.menu.MenuPlugIn"&gt;<br />&lt;set-property property="menuConfig" value="/WEB-INF/menu-config.xml"/&gt;<br />&lt;/plug-in&gt;</p></td></tr></tbody></table> <p>&nbsp;</p> <p>说明配置文件来自于/WEB-INF/menu-config.xml，当然，我们可以找到相应路径下找到这个文件。如果你以前没有做过struts的plug-in，现在该知道怎么做了吧，就这么简单。通过阅读初始化函数，知道它的功能就是调用<span class="style1"><span class="style3"><font face="Arial" size="1">MenuRepository来建立菜单。因此。我们知道MenuRepository必然是一个组织管理管理菜单的组织类。</font></span></span></p> <table width="100%" bgcolor="#cccccc" border="1"> <tbody> <tr> <td><pre class="style1">public void init(ActionServlet servlet, ModuleConfig config) throws ServletException { if (log.isDebugEnabled()) { <span class="style3"><font size="1"> log.debug("Starting struts-menu initialization"); } this.servlet = servlet; repository = new MenuRepository(); repository.setLoadParam(menuConfig); repository.setServlet(servlet); try { repository.load(); servlet.getServletContext().setAttribute( MenuRepository.MENU_REPOSITORY_KEY, repository); if (log.isDebugEnabled()) { log.debug("struts-menu initialization successfull"); } } catch (LoadableResourceException lre) { throw new ServletException( "Failure initializing struts-menu: " + lre.getMessage()); } }</font></span></pre></td></tr></tbody></table> <p>打开<span class="style1"><span class="style3"><font face="Arial" size="1">MenuRepository</font></span></span>类，我们可以看到这个类也很简单，不过已经有少可以学习的了。首先是FastHashMap，可以看到，这个类里有三个FastHashMap。顾名思议，是快速HashMap了，再看一下，它来自org.apache.commons.collections.FastHashMap;。看到org.apache.commons这个著名的包了?如果你以前从没使用过它，那么建议你花上一段时间去研究使用它，我保证物有所值。</p> <table width="100%" border="1"> <tbody> <tr> <td>protected FastHashMap menus = new FastHashMap();<br />protected FastHashMap displayers = new FastHashMap();<br />protected FastHashMap templates = new FastHashMap();</td></tr></tbody></table> <p>接下来我们看到log的定义。对了，log，调试的核心之一。而下面这一句则是commons log的最常用的使用方法。快快让你的程序使用上commons log吧，第一，它功能强大，第二，它使用简单，就是这么简单。</p> <table width="100%" border="1"> <tbody> <tr> <td>private Log log = LogFactory.getLog(getClass().getName());</td></tr></tbody></table> <p>下面看一个的函数</p> <table width="100%" border="1"> <tbody> <tr> <td><pre class="style1"> protected Digester initDigester() { Digester digester = new Digester(); digester.setClassLoader(Thread.currentThread().getContextClassLoader()); digester.push(this); //digester.setDebug(getDebug()); // 1 digester.addObjectCreate("MenuConfig/Menus/Menu", "net.sf.navigator.menu.MenuComponent", "type"); digester.addSetProperties("MenuConfig/Menus/Menu"); digester.addSetNext("MenuConfig/Menus/Menu", "addMenu"); // 2 digester.addObjectCreate("MenuConfig/Menus/Menu/Item", "net.sf.navigator.menu.MenuComponent", "type"); digester.addSetProperties("MenuConfig/Menus/Menu/Item"); digester.addSetNext("MenuConfig/Menus/Menu/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent"); // 3 digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item", "net.sf.navigator.menu.MenuComponent", "type"); digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item"); digester.addSetNext("MenuConfig/Menus/Menu/Item/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent"); // 4 digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item", "net.sf.navigator.menu.MenuComponent", "type"); digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item"); digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent"); // 5 digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item", "net.sf.navigator.menu.MenuComponent", "type"); digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item"); digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent"); // 6 digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item", "net.sf.navigator.menu.MenuComponent", "type"); digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item"); digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent"); // 7 digester.addObjectCreate("MenuConfig/Menus/Menu/Item/Item/Item/Item", "net.sf.navigator.menu.MenuComponent", "type"); digester.addSetProperties("MenuConfig/Menus/Menu/Item/Item/Item/Item"); digester.addSetNext("MenuConfig/Menus/Menu/Item/Item/Item/Item", "addMenuComponent", "net.sf.navigator.menu.MenuComponent"); digester.addObjectCreate("MenuConfig/Displayers/Displayer", "net.sf.navigator.displayer.MenuDisplayerMapping", "mapping"); digester.addSetProperties("MenuConfig/Displayers/Displayer"); digester.addSetNext("MenuConfig/Displayers/Displayer", "addMenuDisplayerMapping", "net.sf.navigator.displayer.MenuDisplayerMapping"); digester.addSetProperty("MenuConfig/Displayers/Displayer/SetProperty", "property", "value"); return digester; }</pre></td></tr></tbody></table> <p>这里又是一个经典,<span class="style1"><font face="Arial">digester</font></span>，Digester的使用，如果你需要读一个XML配置文件，并且不想与DOM直接打交道的话，Digester将是一个很好的选择。实际上我们看到load函数调用一句 digester.parse(input);就已经把menu-config.xml建立到内存里了，就这么简单。如果你想要初始化你的系统，这种方法是不是可以学习呢？"工欲善其事，必先利其器"。我们可以看到Raible是怎么样利用现有的工具来减轻开发量的。</p> <p>由于MenuRepository举重若轻的初始化过程，甚至都没有让我们看到树形结构是怎么建立到内存里去的。不过不要着急，类图给我们了明示。</p> <p>看到MenuBase类了吗？对了，看名字就知道是一个Menu的基类。可以看到，它是一个简单的JavaBean。而且相信它的每个属性大家根据名字也能猜出来。所以重点讲解是MenuComponent，一个简化的 "Composite"模式。</p> <p><img height="257" src="http://champion.ewuxi.com/old/opensource/struts-new/images/pattern_3.gif" width="571" /></p> <p>如上图所示。由于此处的Leaf没有任何方法，只有属性。因此Leaf和Composite收缩成了一个MenuComponent类。大家都知道，Composite模式是实现树形结构最好的方法。如果你以前没有机会实现或者没有从Composite模式得到好处，那么，从这里看一下用Composite模式得到的好处。首先看它的简单，MenuComponet的实际代码很少，加起来不到十行。</p> <table width="100%" border="1"> <tbody> <tr> <td><pre class="style1">public void addMenuComponent(MenuComponent menuComponent) { menuComponents.add(menuComponent); menuComponent.setParent(this); if ((menuComponent.getName() == null) || (menuComponent.getName().equals(""))) { menuComponent.setName(this.name + menuComponents.size()); } } public MenuComponent[] getMenuComponents() { MenuComponent[] menus = (MenuComponent[]) menuComponents.toArray(_menuComponent); return menus; }</pre></td></tr></tbody></table> <p>如果你用十行来实现一个树型结构(并且还是通用的)，你愿不愿意？就是通过简单的这么一些代码，实现的在内存中建立树型结构的目标。</p> <p>下面我们来看DispLay包，这个包的功能也是很清楚的，就是用来显示啦。这个包的类图非常漂亮，遗憾的是也非常大。只能缩小了给大家看了。</p> <p><a href="http://champion.ewuxi.com/old/opensource/struts-new/images/3.jpg" target="_blank"><img height="652" src="http://champion.ewuxi.com/old/opensource/struts-new/images/3.jpg" width="831" border="0" /></a></p> <p>从类图中可以看到一个非常极漂亮的面象对象的设计思路。通过一个接口，利用模板方法。最后具体实现树型结构的显示。其主要方法是displayComponents和display这两方法，init方法则实现了初始化的工作，读取javascript和图片等文件。displayComponents是一个迭代函数。从而可以遍历一个MenuCompont树。并将其显示出来。</p> <p>应该说，Menu包是一个M层，而Dispplya包是一个view层，而加上TagLib包，就实现了MVC的完整结构。</p> <p>两个Tag类很清楚，首先我们从怎么使用它来看它们实现的功能</p> <table width="100%" bgcolor="#cccccc" border="1"> <tbody> <tr> <td>&lt;menu:useMenuDisplayer name="ListMenu" <br />bundle="org.apache.struts.action.MESSAGE"&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;menu:displayMenu name="ToDoListMenuFile"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;menu:displayMenu name="ToDoListMenuEdit"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;menu:displayMenu name="CaseDetailMenuCase"/&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;menu:displayMenu name="Standalone"/&gt;<br />&lt;/menu:useMenuDisplayer&gt;</td></tr></tbody></table> <p>显而易见。useMenuDisplayer这个类是实现使用哪一种显示方式。在menu-config里我们看到ListMenu的定义</p> <table width="100%" bgcolor="#cccccc" border="1"> <tbody> <tr> <td>&lt;Displayer name="ListMenu"<br />type="net.sf.navigator.displayer.ListMenuDisplayer"/&gt;</td></tr></tbody></table> <p>displayMenu则是取得一菜单，并将其显示出来，同样在menu-config也能找到。</p> <table width="100%" border="1"> <tbody> <tr> <td><pre>&lt;Menu name="ToDoListMenuEdit" title="EDIT"&gt;<br /> &lt;Item name="TDLselect" title="SELECT_ALL" image="images/select-all.png"<br /> location="index.jsp" width="100" /&gt;<br /> &lt;Item name="TDLprefs" title="USER_PREFERENCES" image="images/prefs.png"<br /> location="index.jsp" width="150" /&gt;<br /> &lt;Item title="Action Test" action="setPermissions?displayer=${displayer}"/&gt;<br />&lt;/Menu&gt;</pre></td></tr></tbody></table> <p>查看 DisplayMenu的代码，可以看到。它完成的功能只是从context里取得MenuComponent对象，然后通过 displayer.display(menu);把它交给一个MenuDisplayer的实例来负责画出来。</p> <p>因此，Control层很好的完成了控制的功能。</p> <p>综上所述。通过这样一个优美的设计，把各个功能都他离开来了。如果我们需要增加一种显示方式，只要继承MenuDisplayer或它的一个子类，然后写出我们的方法，而不需要修改系统的其他部分。同样的，如果我们的菜单不准备存放在ServletContext而准备存放在比如Session里了，那么我们也只需要修改control部分和生成部分(即<span class="style1"><span class="style3"><font face="Arial" size="1">MenuRepository</font></span></span>)部分。而不影响Display部分。</p> <p>OK，对struts-menu的介绍结束了，下一篇文章将是如果使用struts-menu和数据库技术动态生成菜单了。请大家继续关注我的网站。</p> <p>&nbsp;</p><!-- InstanceEndEditable --> <p align="left">&nbsp;</p> <p align="right">紫龙，于 <script language="JavaScript" type="text"> document.write (document.lastModified); </script> 12/22/2003 16:45:09 <p align="right"><a href="http://champion.ewuxi.com/">蓝色天空</a>版权所有</p> <p align="left"><input class="buttonface" onclick="history" type="button" value="返回" name="Button"> </p><!-- InstanceEnd --><img src ="http://www.blogjava.net/sgsoft/aggbug/207.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-01-11 17:15 <a href="http://www.blogjava.net/sgsoft/articles/207.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts动态控制表格大小(作者：作者：James M. Turner。译者：陈姣姣 ) </title><link>http://www.blogjava.net/sgsoft/articles/198.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Tue, 11 Jan 2005 07:59:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/198.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/198.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/198.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/198.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/198.html</trackback:ping><description><![CDATA[<span class="myp111"><font id="zoom">在Succeeding with Struts的前面安装部分，我间接提到了DynaForms在运行期内可以动态的控制表格大小。换句话说，就是能够根据需要得到5行、或者10行、或者15行长的表格。可能有点不明智，我把这种策略的实际实现作为一种练习留给了读者自己。在接下来的几个月内，我收到了几十个读者的请求，他们请求给出详细的实现细节，所以这个月我将用两种不同的方法来实现动态调整的表格。 <br />第一个方法就是我在前面的栏目中提到的那个方法，将尺寸参数留给DynaForm 的form-property 属性来实现。为了演示详细过程，我们来看看一个非常简单的应用：添加关于不同Star Wars 演员的注释。在这个应用中我们感兴趣的关键事实是：演员的数量在表格配置中动态设定，而不是在struts-config.xml文件中动态设定。 <br />首先，我们先来看看struts-config.xml 文件： <br /><br /><ccid_nobr></ccid_nobr> <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1"> <tbody> <tr> <td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6"><pre><ccid_code></ccid_code>&lt;?xml version="1.0" encoding="UTF-8"?&gt; &lt;!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"&gt; &lt;struts-config&gt; &lt;form-beans&gt; &lt;form-bean name="dynamicArrayForm" type="org.apache.struts.validator.DynaValidatorForm"&gt; &lt;form-property name="people" type="demo.Person[]"/&gt; &lt;/form-bean&gt; &lt;/form-beans&gt; &lt;action-mappings&gt; &lt;action path="/setupForm" type="demo.SetupFormAction" name="dynamicArrayForm" scope="session" validate="false"&gt; &lt;forward name="success" path="/displayForm.jsp"/&gt; &lt;/action&gt; &lt;action path="/processActorComments" type="demo.ProcessFormAction" name="dynamicArrayForm" scope="session" validate="false"&gt; &lt;forward name="success" path="/displayForm.jsp"/&gt; &lt;/action&gt; &lt;/action-mappings&gt; &lt;/struts-config&gt;</pre></td></tr></tbody></table><br /><br />如你所见，这是一个相当简单的配置文件，只定义了一个表格和两个动作。第一个动作，/setupForm，用来在初始显示之前配置表格；另一个动作，/processActorComments 用来处理用户输入的注释。 <br />在这个文件中有两个重要的事情需要注意，它们对于事态的发展很关键： <br />1. people 表格属性定义为demo.Person[] 类型(即demo.Person的一个排列)，但不给出任何size 参数。这就为要创建的排列产生了一个占位符，但是没有任何例示的实排列。 <br />2. 这两个动作将表格定义在会话期范围内。这是很关键的，因为用户在填写数值之后提交表格时，数值在动作执行之前已经填充到表格内了。这就意味着没有机会手动创建具有恰当空位数的排列，正如你在表格显示之前在SetupFormAction 类中看到的情况一样。换句话说，当表格提交时，必须已经有恰当的空位来接受表格值，唯一能保证这个的方法就是在会话期范围内就已经有了这个表格。 <br />基本上在Person bean 中是没有值的，他只是一个具有lastName、 firstName、 dateOfBirth、gender 和comment字段的普通bean。源文件包括在WAR 文件内。 <br />现在我们来看看SetupFormAction 类，它在表格第一次显示之前调用。 <br /><br /><br /><ccid_nobr></ccid_nobr> <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1"> <tbody> <tr> <td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6"><pre><ccid_code></ccid_code>package demo; /** * Copyright 2004, James M. Turner. * All Rights Reserved * * A Struts action that sets up a DynaForm which is globally scoped */ import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.*; import org.apache.struts.action.*; import org.apache.struts.validator.DynaValidatorForm; public class SetupFormAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { DynaValidatorForm df = (DynaValidatorForm) form; Person[] p = new Person[3]; p[0] = new Person(); p[0].setDateOfBirth("07/13/1942"); p[0].setLastName("Ford"); p[0].setFirstName("Harrison"); p[0].setGender("M"); p[1] = new Person(); p[1].setDateOfBirth("10/21/1956"); p[1].setLastName("Fisher"); p[1].setFirstName("Carrie"); p[1].setGender("F"); p[2] = new Person(); p[2].setDateOfBirth("09/25/1951"); p[2].setLastName("Hamill"); p[2].setFirstName("Mark"); p[2].setGender("M"); df.set("people", p); return mapping.findForward("success"); } }</pre></td></tr></tbody></table><br /><br />这一次也没有许多东西要看的。execute 方法要做的第一件事情，和任何基于DynaForm的动作所做的一样，就是将泛型ActionForm 类放到DynaValidatorForm内。这就使得我们可以在表格上使用get和set 方法。第二件事情就是，创建一个具有三个元素的类型Person 的排列。在这个方法中，尺寸是硬布线的，在实际应用中可以从数据库中选择一个尺寸。我们需要考虑的重要事情是排列应该在代码中创建，而不是由Struts引擎自己创建。这样行数可根据应用要求由代码随意指定。 <br />一旦排列已经确定，方法将创建三个Person 类实例并赋与数值。同样，在实际的应用中可通过一个循环来实现，这个循环不断地从数据库中读取行和填充表格行。最后，动作返回成功，导致Struts转移控制到displayForm.jsp 页。 <br /><br /><ccid_nobr></ccid_nobr> <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1"> <tbody> <tr> <td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6"><pre><ccid_code></ccid_code>&lt;!-- Copyright 2004, James M Turner. All Rights Reserved --&gt; &lt;%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %&gt; &lt;%@ taglib uri="/WEB-INF/c.tld" prefix="c" %&gt; &lt;head&gt; &lt;title&gt;Star Wars Actor Fact Page&lt;/title&gt; &lt;/head&gt; &lt;H1&gt;&lt;center&gt;Start Wars Actor Fact Page&lt;/title&gt; &lt;html:form action="/processActorComments" &gt; &lt;table border="1" width="80%"&gt; &lt;tr&gt;&lt;th&gt;Last Name&lt;/th&gt;&lt;th&gt;First Name&lt;/th&gt;&lt;th&gt;Date of Birth&lt;/th&gt;&lt;th&gt;Comment&lt;/th&gt;&lt;/tr&gt; &lt;c:forEach var="people" items="${dynamicArrayForm.map.people}"&gt; &lt;tr&gt;&lt;td&gt;&lt;c:out value="${people.lastName}"/&gt;&lt;/td&gt; &lt;td&gt;&lt;c:out value="${people.firstName}"/&gt;&lt;/td&gt; &lt;td&gt;&lt;c:out value="${people.dateOfBirth}"/&gt;&lt;/td&gt; &lt;td&gt;&lt;html:text name="people" indexed="true" property="comment"/&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/c:forEach&gt; &lt;/table&gt; &lt;P/&gt; &lt;html:submit value="Update Comments"/&gt; &lt;/html:form&gt;</pre></td></tr></tbody></table><br /><br />同样，这里也没有很多东西要看的，他与我们上一篇文章查看固定长度的行时的代码完全一样。该页迭代行（记住在JSTL中我们必须使用map 属性来获得到DynaForm 属性的访问），显示演员的姓、名和出生日期，并提供文本域以便输入注释。 <br />当我们聚焦我们的浏览器合请求时，http://localhost:8080/struts/setupForm.do (假设你把struts.war 文件放在你本地机器的Tomcat 内)，将会出现下列页面： <br /><br /><ccid_nobr></ccid_nobr> <h3>Start Wars Actor Fact Page <table width="40%" border="1"> <tbody> <tr> <th>Last Name</th> <th>First Name</th> <th>Date of Birth</th> <th>Comment</th></tr> <tr> <td>Ford</td> <td>Harrison</td> <td>07/13/1942</td> <td><input name="people" height="95507296"> </td></tr> <tr> <td>Fisher</td> <td>Carrie</td> <td>10/21/1956</td> <td><input name="people" height="95506144"> </td></tr> <tr> <td>Hamill</td> <td>Mark</td> <td>09/25/1951</td> <td><input name="people" height="95141832"> </td></tr></tbody></table> <p><input type="submit" value="Update Comments" height="95505840"> </p></h3><br /></font></span>一旦表格提供，另一个简单的Struts动作来处理结果： <br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><ccid_nobr></ccid_nobr> <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1"> <tbody> <tr> <td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6"><pre><ccid_code></ccid_code>package demo; /** * Copyright 2004, James M. Turner. * All Rights Reserved * * A Struts action that sends the new comments to the console */ import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.*; import org.apache.struts.action.*; import org.apache.struts.validator.DynaValidatorForm; public class ProcessFormAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { DynaValidatorForm df = (DynaValidatorForm) form; Person[] p = (Person[]) df.get("people"); for (int i = 0; i &lt; p.length; i++) { System.out.println(p[i].getFirstName() + " " + p[i]. getLastName() + ":" + p[i].getComment()); } return mapping.findForward("success"); } }</pre></td></tr></tbody></table><br /><br />在实际的应用中，这就是数据写回到数据库的地方。在这种情况下，他只将数据倒在控制台上所以我们可以看到他是正确收到的。假设我们为每个演员都填充了恰当的值，我们在控制台上会看到下列内容： <br /><br /><ccid_nobr></ccid_nobr> <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1"> <tbody> <tr> <td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6"><pre><ccid_code></ccid_code>Harrison Ford:Indiana Jones Carrie Fisher:Postcards from the Edge Mark Hamill:Wing Commander</pre></td></tr></tbody></table><br /><br />正如我在文章开头提到的一样，还有另一个方法可以解决这个问题，而且它不需要使用会话期范围内的表格。这个方法就是使用HashMaps 来存储行。我们来看看使用HashMaps编写的同一段代码： <br />首先，我们添加一个新表格到struts-config.xml： <br /><br /><ccid_nobr></ccid_nobr> <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1"> <tbody> <tr> <td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6"><pre><ccid_code></ccid_code>&lt;form-bean name="dynamicHashmapForm" type="org.apache.struts.validator.DynaValidatorForm"&gt; &lt;form-property name="people" type="java.util.HashMap"/&gt; &lt;form-property name="comments" type="java.util.HashMap"/&gt; &lt;/form-bean&gt;</pre></td></tr></tbody></table><br /><br />现在，我们不使用beans的排列，改为使用HashMap 来存储每个人的数据。另外，我们需要一个新的HashMap 来存储注释，原因我稍后再解释。我们也需要一个新的动作来填充数据： <br /><br /><ccid_nobr></ccid_nobr> <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1"> <tbody> <tr> <td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6"><pre><ccid_code></ccid_code>package demo; /** * Copyright 2004, James M. Turner. * All Rights Reserved * * A Struts action that sets up a DynaForm which is globally scoped */ import java.io.IOException; import java.util.HashMap; import javax.servlet.ServletException; import javax.servlet.http.*; import org.apache.struts.action.*; import org.apache.struts.validator.DynaValidatorForm; public class SetupHashFormAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { DynaValidatorForm df = (DynaValidatorForm) form; HashMap hm = (HashMap) df.get("people"); Person p = new Person(); p = new Person(); p.setDateOfBirth("07/13/1942"); p.setLastName("Ford"); p.setFirstName("Harrison"); p.setGender("M"); hm.put("1", p); p = new Person(); p.setDateOfBirth("10/21/1956"); p.setLastName("Fisher"); p.setFirstName("Carrie"); p.setGender("F"); hm.put("2", p); p = new Person(); p.setDateOfBirth("09/25/1951"); p.setLastName("Hamill"); p.setFirstName("Mark"); p.setGender("M"); hm.put("3", p); return mapping.findForward("success"); } }</pre></td></tr></tbody></table><br /><br />基本上，这段代码与前面的代码相同，除了我们将Person 对象存储到HashMap 中，而不是排列中之外。我们也不需要创建HashMap，因为它可以作为表格初始化的一部分来动态实现。 <br />在JSP本身中相应的技巧部分为： <br /><br /><ccid_nobr></ccid_nobr> <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1"> <tbody> <tr> <td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6"><pre><ccid_code></ccid_code>&lt;!-- Copyright 2004, James M Turner. All Rights Reserved --&gt; &lt;%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %&gt; &lt;%@ taglib uri="/WEB-INF/struts-html-el.tld" prefix="html-el" %&gt; &lt;%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %&gt; &lt;%@ taglib uri="/WEB-INF/c.tld" prefix="c" %&gt; &lt;%@ taglib prefix="fmt" uri="/WEB-INF/fmt.tld" %&gt; &lt;head&gt; &lt;title&gt;Star Wars Actor Fact Page&lt;/title&gt; &lt;/head&gt; &lt;H1&gt;&lt;center&gt;Start Wars Actor Fact Page&lt;/title&gt; &lt;html:form action="/processHashActorComments" &gt; &lt;table border="1" width="80%"&gt; &lt;tr&gt;&lt;th&gt;Last Name&lt;/th&gt;&lt;th&gt;First Name&lt;/th&gt; &lt;th&gt;Date of Birth&lt;/th&gt;&lt;th&gt;Comment&lt;/th&gt;&lt;/tr&gt; &lt;c:forEach var="people" items="${dynamicHashmapForm.map.people}"&gt; &lt;tr&gt;&lt;td&gt;&lt;c:out value="${people.value.lastName}"/&gt;&lt;/td&gt; &lt;td&gt;&lt;c:out value="${people.value.firstName}"/&gt;&lt;/td&gt; &lt;td&gt;&lt;c:out value="${people.value.dateOfBirth}"/&gt;&lt;/td&gt; &lt;td&gt;&lt;html-el:text property="comments(${people.value.lastName}, ${people.value.firstName})" /&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/c:forEach&gt; &lt;/table&gt; &lt;P/&gt; &lt;html:submit value="Update Comments"/&gt; &lt;/html:form&gt;</pre></td></tr></tbody></table><br /><br />记住：在初始化时填充的HashMap 值，只要表格显示就会消失，因为表格是请求范围的，而不是会话期范围的。特别是对于我们来说这就意味着所有的Person 对象都会消失。所以，如果我们粘贴文本域到Person bean 的注释属性上，在提交表格时我们将得到一个空的指针异常，因为Person 对象不再位于HashMap 内(实际上，我们得到的是一个全新的空的HashMap.)。所以，我们需要将注释存储在一个单独的并行HashMap 内，它将注释当作简单的字符串来存储。 <br />在上述的代码中还须注意几件事情。首先，因为现在正迭代HashMap条，来自c:forEach 标记的值实际上是用于堆栈条的占位符，同时具有两个属性。key 属性的值用来访问堆栈（在我们的例子中如字符"1", "2", "3"等等），value 属性的值存储在关键字之下。所以，在这种情况下，我们必须使用value 属性来得到Person bean 的实属性。 <br />而且，我们需要构造一个用于文本框的有效的Struts属性域。在html-el 标记库中使用JSTL 扩展就可以实现。在这种情况下，我们通过一个由演员的最后一个名字、逗号和第一个名字组成的字符串来存储注释。 <br />最后，我们需要一个新的动作来处理结果： <br /><br /><ccid_nobr></ccid_nobr> <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1"> <tbody> <tr> <td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6"><pre><ccid_code></ccid_code>package demo; /** * Copyright 2004, James M. Turner. * All Rights Reserved * * A Struts action that sends the new comments to the console */ import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import javax.servlet.ServletException; import javax.servlet.http.*; import org.apache.struts.action.*; import org.apache.struts.validator.DynaValidatorForm; public class ProcessHashFormAction extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { DynaValidatorForm df = (DynaValidatorForm) form; HashMap hm = (HashMap) df.get("comments"); Iterator it = hm.keySet().iterator(); while (it.hasNext()) { String key = (String) it.next(); String comment = (String) hm.get(key); System.out.println(key + ":" + comment); } return mapping.findForward("success"); } }</pre></td></tr></tbody></table> <p><br /><br />同样，这里最大的差别是数据都是作为HashMaps 来存储的。代码获取关键字(lastname,firstname)，然后显示关键字和在控制台注释: <br /><br />Fisher,Carrie:Leia <br />Ford,Harrison:Han <br />Hamill,Mark:Luke <br /><br />请注意，当控制返回到JSP页时，打印一个空白表格。这是因为我们在初始化操作中创建的HashMap 已经没有了，在处理结果时我们不能重新创建它。你可以将该数据保存在会话期变量中，但是接着你要返回到你使用第一个方案的地方。最好是选择一个关键字，在表格提交时它可以允许你在后台对象上获得，并且能够总是重新创建需要的任何其他的表格数据。 <br />哪种方式更好？基于排列的方案允许你将所有的数据都保存在一个bean 内，而基于堆栈的方法避免了任何会话期范围的数据。你觉得哪种方案最好就采用哪种。 <br />注意：包含运行这些例子所需的所有代码和库的WAR 文件在http://www.blackbear.com/struts.war.上可以找到。 <br /><br /><br /><b>关于作者</b>:James Turner 是Benefit Systems有限公司软件开发总监。他对Apache Struts 项目颇有贡献。他已经出版了两本面向WEB的JAVA技术的书：MySQL and JSP Web Applications, 和Struts Kick Start。他的第三本书，Java Server Faces Kick Start，在2003年冬季由Sams出版发行 </p><img src ="http://www.blogjava.net/sgsoft/aggbug/198.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-01-11 15:59 <a href="http://www.blogjava.net/sgsoft/articles/198.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>应用Struts的安全隐患及解决方案 </title><link>http://www.blogjava.net/sgsoft/articles/195.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Fri, 07 Jan 2005 09:48:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/195.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/195.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/195.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/195.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/195.html</trackback:ping><description><![CDATA[<span class="myp11"><font id="zoom">重要提示：根据lironghai的评论，经验证，发现在WebLogic下由于安全限制不能使用本文所推荐的目录安排。为了保证系统的可移植性，不建议大家采用本文的方式。 <br /><br /><b>问题</b> <br /><br />使用Struts框架时，权限通常控制在Action级（比如将权限验证放在Action的基类中，这样新的Action都继承于这个Action基类，所有Action就可以专注于业务逻辑，而不需要重复地进行权限控制了），这也符合MVC中的角色划分。然而，这会产生一个安全隐患。因为权限控制在Action中，所以，页面也就没有安全屏障了。一般的新增数据、更新数据不会有什么问题，因为这些数据必须通过HTML的Form提交到Struts的中心控制器，最终由相应的Action来处理，所以Action中就可以验证该用户的权限了。然而，对于一些不需要Action进行数据存取，或者有的页面没有严格按照MVC的角色划分而在页面中有获取数据的代码，那么这个页面就危险了。比如，显示一张通知页面，通常可以通过配置权限，使部分授权的用户才可以看到该级别的通知。这个通知页面是不需要从数据库中获取数据的。所以，可以不通过Action的调用来显示，而是直接敲入显示该通知的页面的链接就可以看到了。甚至不需要登录系统，不用管是否有查看该通知的权限！ <br /><br />比如下图中，请求1： <br /><br /><ccid_nobr></ccid_nobr> <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1"> <tbody> <tr> <td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6"><pre><ccid_code></ccid_code>http://.../showannouncement.do</pre></td></tr></tbody></table><br /><br />是通过正常的途径访问的，需要经过权限验证。而请求2： <br /><br /><ccid_nobr></ccid_nobr> <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="400" align="center" bordercolorlight="#000000" border="1"> <tbody> <tr> <td class="code" style="FONT-SIZE: 9pt" bgcolor="#e6e6e6"><pre><ccid_code></ccid_code>http://.../announcementshow.jsp</pre></td></tr></tbody></table><br /><br />则完全绕过了权限检查，任何人，不需要登录系统就可以访问到该信息了。 <br /><br /><img height="281" src="http://tech.ccidnet.com/pub/attachment/2003/12/263985.gif" width="432" /> <br /><br /><b>解决方案</b> <br /><br />解决的办法也不是没有，非常简单。大家都知道JSP Web容器会对应用的WEB-INF目录下的所有文件加以保护的，任何用户都不能直接通过浏览器访问WEB-INF目录（包括子目录）下的任何资源，然而这些资源可以被JSP Web容器访问。所以，解决办法已经出来了。可以把你的JSP页面放到WEB-INF目录下，在struts-config.xml的action的配置中注意要把转向的页面指向类似： <br /><br />/WEB-INF/pages/announcementshow.jsp。 <br /><br />这样就保证了所有数据的安全了。 <br /><br />使用本文的方法，甚至可以对静态的页面，比如html的页面进行保护，设置权限以限制访问</font></span><img height="1" src="http://www.cnblogs.com/SGSoft/aggbug/88320.html" width="1" /><img src ="http://www.blogjava.net/sgsoft/aggbug/195.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-01-07 17:48 <a href="http://www.blogjava.net/sgsoft/articles/195.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个掌握Struts企业级Web开发框架的实例(作者：俞良松　2002年09月26日　本文选自：开放系统世界)</title><link>http://www.blogjava.net/sgsoft/articles/196.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Fri, 07 Jan 2005 09:46:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/196.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/196.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/196.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/196.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/196.html</trackback:ping><description><![CDATA[<p>Struts是源代码开放的企业级Web应用开发框架，它的设计目的是从整体上减轻构造企业Web应用的负担。本文通过一个Struts应用的实例，帮助你迅速掌握Struts。 <br /><br />　　Struts是在Jakarta项目下开发的源代码开放软件，由一系列的框架类、辅助类和定制的JSP标记库构成，定位在基于Model 2设计模式的J2EE应用开发。Model 2体系是MVC（Model-View-Controller）体系的一种应用。在MVC体系中，数据模型（Model）、表现逻辑（View）和控制逻辑（Controller）是分离的组件，但它们可以互相通信。Struts力图从整体上减轻构造企业级Web应用的负担，并提供国际化和数据库连接池支持。 <br /><br />　　Struts体系可以看成两个相对独立的部分：第一个部分是Struts API，用于编写支持Struts的应用组件；第二部分是Struts的JSP标记库，由html、bean、logic和template四个标记组成。Struts的两个部分有着各自不同的用户。对于规模较小的项目，同一个用户可能同时使用这两个部分；但对于规模较大的项目，通常开发者使用API组件，而负责HTML页面布局的人使用标记库。 <br /><br />　　Struts的设计目标是为Model 2 Web应用开发提供一个强大的框架。同时，Struts还包含了一些实用组件，例如Digest，但这些组件并不从属于上面提到的两个部分。 <br /><br />　　<b>Struts应用的体系结构</b> <br /><br />　　对于从传统编程环境转入Web开发的人来说，Web编程中令人很不习惯的一个特点是缺乏“程序”。传统的应用总是有主入口点、流程控制和出口点。但在Web网站上，用户可能从任何地方进入，按照一种完全随机的次序访问各个页面，甚至可能跳过多个页面，也可能在一、两个小时内毫无动静。这是HTTP访问的基本特征，无论是Struts还是其他Web编程框架，都无法改变这一点。然而，Struts能够隐藏Web访问固有的“混乱”，帮助开发者建立起清晰和明确的秩序和规则。 <br /><br />　　在Struts应用中，有一个称为ActionServlet的主调度程序（或称为分配器），如图1所示。不过，并非所有的请求都必须通过ActionServlet。用户的请求目标可以是非Struts的页面，也可以是那些使用了Struts标记库但不使用Struts请求分配服务的页面。这正是Struts体系的优点之一：按需使用。许多编程框架要求你要么不用，要么全部使用，而且一旦你决定使用，以后要悔改从前的错误就会付出高昂的代价。Struts按需使用的优点与这类系统形成了强烈对比。 <br /><br />　　<img height="145" src="http://software.ccidnet.com/pub/attachment/2002/9/35159.gif" width="400" /> <br /><br />　　图1 Struts框架中的请求处理 <br /><br />　　Struts应用由下面这些基本模块构成： <br /><br />　　1.配置信息; <br /><br />　　2.Servlet，主要是Struts的ActionServlet; <br /><br />　　3.动作类（Action），执行逻辑和控制（请求分配）功能，它们由ActionServlet调用； <br /><br />　　4.JSP页面（属于View），常常通过动作类分派； <br /><br />　　5.JSP标记库，根据需要使用； <br /><br />　　6.各种形式的JavaBean，包括用户定义的JavaBean。 <br /><br />　　典型的Struts应用要用到三种配置文件：web.xml、struts-config.xml和可选的应用资源文件。 <br /><br />　　web.xml是Web应用的标准配置文件，是所有J2EE Web应用必需的组成部分。应用服务器通过该配置文件把URL映射到Servlet和JSP，通过该配置文件为Servlet和JSP指定启动参数。为Struts应用提供的基本web.xml文件很简单，真正必需的只有一个主ActionServlet定义，以及一个确保Struts请求传递到ActionServlet的映射。按照惯例，以“.do”结尾的URL都是Struts请求，例如/login.do。应用服务器利用web.xml文件中的映射，把该请求传递给ActionServlet。接着，ActionServlet决定如何分配该请求。ActionServlet的决定依据是struts-config.xml中定义的规则，和/或是通过ActionServlet派生类额外定义的分配逻辑。 <br /><br />　　struts-config.xml称为Struts配置文件。Struts应用是一个依靠struts-config.xml文件把组件连接起来的网络。struts-config.xml文件为Web应用的组件定义了逻辑名称，也定义了它们在Struts框架下的属性和关系，就像web.xml文件在Web应用框架之内定义组件一样。struts-config.xml文件包含了与Struts框架有关的应用信息，这些信息分四个类： <br /><br />　　1.数据源信息，它是可选的。在这里可以指定一个或者多个JDBC数据源，使得数据库定义信息集中化。对于数据库访问，Struts还有一个额外的优点，即支持基本的数据库连接池功能。 <br /><br />　　2. Form Bean是JavaBean的一种特殊类型，它简化了Web表单的处理。 <br /><br />　　3. Global Forwards是全局性的转发定义信息。Struts动作按照一种“请求—转发”机制运行。为了最大限度地分　　离动作模块与转发目标，这里使用了一种映射机制，允许通过同义词引用转发目标。一些目标页面可能被多个动作类引用，例如登录页面，因此可以在全局转发定义部分把逻辑目标页面映射到物理目标页面，避免把这部分信息加入到动作定义部分。 <br /><br />　　4. Actions定义了Struts应用体系的请求分配信息，它们是核心分配器的补充定义，负责处理各种具体的请求类型。 </p> <p></p> <p><b>一个简单的应用</b> <br /><br />　　基于Struts的Web应用和普通Web应用有着许多同样的要求，但Struts应用也有自己特殊的需求。一个可部署的Web应用应该可组织和构成一个WAR文件。WAR文件是带有图2所示目录结构的JAR包。对于Struts Web应用来说，Web-INF目录下还要加上一些额外的文件，例如struts-confg.xml文件和标记库描述器（TLD）文件。注意：应用的资源应该放入应用的类路径下，也就是Web-INF/lib目录或Web-INF/class目录下的JAR包内。对于大多数简单的Struts页面，我们只用到Struts标记库，而按照MVC的术语就不需要涉及Model和Controller部分，只涉及View。请看图3所示的主页例子。虽然这个页面没有表单，但Struts仍能够在设计这类页面时提供帮助。 <br /><br />　　<img height="92" src="http://software.ccidnet.com/pub/attachment/2002/9/35160.gif" width="400" /> <br /><br />　　图2 Struts应用的目录结构 <br /><br />　　<img height="161" src="http://software.ccidnet.com/pub/attachment/2002/9/35161.gif" width="300" /> <br /><br />　　图3 一个简单的View <br /><br />　　要管理会话，最简单的途径是使用Cookie。会话标识符被传递到客户端之后，客户端把它保存到Cookie，以后的每次请求就把Cookie发送到服务器。然而，和其他的许多Web解决方案一样，Cookie方案也不是万能的，因为一些用户可能不信任Cookie，关闭浏览器的Cookie支持。由于这种情况，URL改写技术就出现了。使用URL改写技术时，整个网站的所有URL后面都将加上会话标识符。虽然这个方案不像采用Cookie方案那样简单、稳固，但它确实行得通。URL改写技术的不稳固是有两方面的原因。首先，和Cookie不同，URL没有过期时间，如果一个带有会话标识的URL被截取后又重新在以后的访问中使用，那么这种URL不会很有用，因为会话一般在一定的时间后会被作废。其次，如果有一个URL链接的后面没有带上会话标识符，整个链都会中断，客户程序无法再次获取会话标识符，除非它备份了带有会话标识符的URL访问历史。 <br /><br />Servlet能够只通过一次方法调用完成URL改写。从技术上讲，JSP也一样能够办到这一点，但一个好的JSP页面应该不包含Java代码，或包含尽量少的Java代码。为此，Struts提供了一个链接标记。本例使用了该标记来维持客户端和服务器之间的会话状态信息。 <br /><br />　　<b>绑定View、Model和Controller</b> <br /><br />　　前面的简单页面不需要Struts分配器，因为它只有简单的链接。图4显示了一个比较复杂的“类别”页面。它列举出了数据库中的类别条目，并将这些条目分别链接到对应的编辑页面。为显示这个页面，我们就要用到Struts的ActionServlet分配机制。 <br /><br />　　<img height="200" src="http://software.ccidnet.com/pub/attachment/2002/9/35162.gif" width="300" /> <br /><br />　　图4 类别页面 <br /><br />　　在web.xml文件中，放入一项表示所有以“.do”结尾的URL请求必须发送给Struts分配器的声明。这里的分配器可以是org.apache.struts.action.ActionServlet或其扩展类。Struts分配器在启动时读取struts-config.xml文件，并构造出一个动作映射图。本例指定了一个名为ShowCategories的动作类，来处理“ShowCategories”动作。可以看出，Struts应用的基本工作模式是：主分配器调用一个动作分配器，动作分配器确定或构造出Model部分（一个JavaBean或其它Java对象），并把它提供给View（通常是一个JSP页面）。 <br /><br />　　本例使用Bean的情况稍微有点复杂，它有多个数据项，因此我们不是使用单个提供数据的Bean，而是要生成一组Bean。遗憾的是，JSP页面以HTML为基础，HTML没有提供循环或其他控制逻辑。不过，Struts的logic:iterate允许对数组进行迭代操作，如下面的代码片断所示： <br /><br /><ccid_nobr></ccid_nobr> <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="#000000" border="1"> <tbody> <tr> <td class="code" bgcolor="#e6e6e6"><pre><ccid_code></ccid_code>&lt;table&gt; &lt;logic:iterate id="category" type="com.strutsdemo.Category" name="&lt;%= Constants.CATALOG_CATEGORIES %&gt;" scope="application"&gt; &lt;tr&gt; &lt;td&gt; &lt;html:link page="/editCategory.do" name="category" property="mapping"&gt; 编辑 &lt;/html:link&gt; &lt;html:link page="/removeCategory.do" name="category" property="mapping"&gt; 删除 &lt;/html:link&gt; &lt;bean:write name="category" property="category"/&gt; &lt;/td&gt; &lt;/tr&gt; &lt;/logic:iterate&gt; &lt;/table&gt;</pre></td></tr></tbody></table><br /><br />　　上面讨论了Model 2体系的Model和View部分，下面来看看Controller部分。Struts体系有一个主控制器，即ActionServlet。ActionServlet负责选择和调用合适的动作控制器—即org.apache.struts.action.Action的扩展类。动作控制器实现了process()方法。process()方法分析从URL请求传入的每一个参数，执行必要的业务逻辑，并返回一个指定了调用链中下一个链接的动作（通常是View）。在本例中，我们想要从数据库提取数据，创建管理这些数据的JavaBean，把多个JavaBean整理成一个数组，再把数组保存到请求的上下文，从而使得作为View的JSP页面能够方便地进行页面布局 </p> <p></p> <p>为保证上述操作顺利进行，在struts-config.xml文件中加入声明，指定由哪一个动作处理器来处理指定的动作： <br /><br /><ccid_nobr></ccid_nobr> <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="#000000" border="1"> <tbody> <tr> <td class="code" bgcolor="#e6e6e6"><pre><ccid_code></ccid_code>&lt;action path="/showCategories" scope="request" type="com.strutsdemo.ShowCategoriesAction" unknown="false" validate="false"&gt; &lt;forward name="viewCategories" path="/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ShowCategories.jsp"/&gt; &lt;/action&gt;</pre></td></tr></tbody></table><br /><br />　　<b>表单处理</b> <br /><br />　　表单处理过程充分体现出Struts的优势。在Web应用中，大部分复杂的HTML处理任务都涉及到表单。表单编辑过程具有类似图5所示的请求或应答结构。更新操作的过程与创建操作的过程相似，但对于更新操作来说，“创建Model”这一步骤变成“装入Model”，而“保存Model”变成了“更新Model”。请注意Web应用的特点：操作过程随时可能中止，这既可能是因为用户通过显式的动作取消了当前的操作，也可能是因为用户没有提交表单，例如用户跳转到了一个不是用来处理当前表单的URL。 <br /><br />　　<img height="107" src="http://software.ccidnet.com/pub/attachment/2002/9/35163.gif" width="400" /> <br /><br />　　图5 表单处理流程 <br /><br />　　表单编辑过程分三个阶段：这里分别称之为准备（Preparation）、表现（Presentation）和存储（Preservation）。准备和存储阶段都属于Struts动作，而表现阶段主要是客户端的活动。表1显示了该过程中涉及的各种部件： <br /><br />　　表单Bean（Form Bean）是一种特殊的JavaBean类型，它简化了表单处理。Form Bean从org.apache.struts. action.ActionForm类扩展而来。Form Bean有几个有用的特点，例如，通过reset()方法可以把Bean的属性设置成默认值，通过validate()方法让Bean验证属性的合法性。更重要的是，ActionServlet确保Form Bean被创建且可供它的动作方法调用。HTML标记库还能够确保Form Bean被正确地初始化并从Form View获取数据。 <br /><br />　　Form Bean应当属于Model部分，然而，由于它有validate()方法，因此从某些特征来看它更接近分配器。不过，不必太在乎这些概念上的问题。Model 2并不完全等同于MVC，而且一些人已经在责难MVC不外乎是几种简单设计模式的混合物。不管怎样，从应用实践的角度来讲，系统的稳定性远比概念的严格性更重要。在本例中，这个问题更加富有代表性，因为我们把持久化机制也包装到Form Bean里面。从技术上看，Bean数据的持久化副本就是一个View，因此，从这个意义上来讲，我们现在有了一个结合了分配器和View特点的Model。这种设计方式看起来似乎否定了引入Struts之类框架的理由，但实际上，这种设计方式两方面的特点弥补了许多遗憾。 <br /><br />　　首先，由于验证代码和SQL代码在很大程度上依赖于Form Bean拥有的属性，所以把它们作为一个单元管理会带来很大的方便。由于这里只对Form Bean的属性感兴趣，“重量级”的分配器和View部件都得到了有效的隔离。其次，Form Bean与HTML标记库一起使用时，Form Bean可以包含其他对象。这些对象可以通过“.”符号应用。使用预定义的Java对象时，“.”引用方式能够带来很大的方便，因为Java不支持多重继承。“.”引用方式避免了手工编写大量get/set代码的繁杂工作。 <br /><br />　　当内部对象是EJB时，“.”引用方式带来的方便更加突出，因为在JSP页面中引用EJB时，EJB往往显得很“笨重”。如果EJB嵌入到了Form Bean里面，许多这方面的遗憾就不再存在。更重要的是，它分离了Controller和Model，而且View持久化也简缩到了最简单的程度，因为EJB容器可以处理所有持久化方面的细节。这样，Form Bean就几乎成了一个纯粹的分配器，一切都变得整洁和清晰。 <br /><br />　　如果EJB有大量的属性，而且按照ActionServlet通常对Form Bean所做的那样，按照每个属性分别更新的方式进行更新，就会出现大量的RMI调用开销。对于要求较高的应用，更好的选择是利用EJB 2.0本地接口，或者在EJB之前加上一个传统的JavaBean（通常是会话EJB），并把该Bean传递给实体Bean的UpdateAllProperties()业务方法。后面这种方案允许在单个RMI调用中完成所有的更新操作. </p> <p></p> <p><u>准备阶段</u> <br /><br />　　一次典型的编辑会话要求有一个动作处理器准备View，即一个作为View的JSP页面，还要求有第二个动作处理器存储更新后的View。当然，存储操作之后会有第二个属于View的页面被显示，例如一个“数据已经更新，点击此处继续”的页面（参见表1）。 <br /><br />　　 表1：基于Form Bean的编辑过程要用到的部件 <br /><br /><ccid_nobr></ccid_nobr> <table class="content02" width="300" border="1"> <tbody> <tr> <td width="150" height="15">部件</td> <td width="150" height="15">说明</td></tr> <tr> <td width="150" height="15">CatalogForm</td> <td width="150" height="15">Form Bean</td></tr> <tr> <td width="150" height="15">EditCategoryAction</td> <td width="150" height="15">准备阶段</td></tr> <tr> <td width="150" height="15">EditCategory.jsp</td> <td width="150" height="15">编辑</td></tr> <tr> <td width="150" height="15">SaveCategoryAction</td> <td width="150" height="15">存储阶段</td></tr> <tr> <td width="150" height="15">EditDone.jsp</td> <td width="150" height="15">确认数据已经保存</td></tr> <tr> <td width="150" height="15">EditFailed.jsp</td> <td width="150" height="15">“数据没有保存”错误</td></tr></tbody></table><br /><br />　　下面的代码片断显示了如何在struts-config.xml文件中配置准备阶段： <br /><br /><ccid_nobr></ccid_nobr> <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="#000000" border="1"> <tbody> <tr> <td class="code" bgcolor="#e6e6e6"><pre><ccid_code></ccid_code>&lt;action path="/editCategory" scope="request" name="catForm" type="com.strutsdemo.EditCategoryAction" unknown="false" validate="false"&gt; &lt;forward name="success" path="/EditCategory.jsp"/&gt; &lt;/action&gt;</pre></td></tr></tbody></table><br /><br />　　在准备阶段，容器尝试从Session或Request找出指定的Form Bean，这是因为在动作中指定了“name=...”。ActionServlet在struts-config.xml文件的<form-beans></form-beans>区域寻找Form Bean的别名，利用Form Bean的别名寻找对应的Java类。如果用户的请求带有参数，其名字匹配Form Bean属性名字的参数将被设置为属性值。Struts扩展了“属性名字”的含义，使得访问Form Bean内嵌对象的属性成为可能。本文的例子也用到了Struts的这一优点。 <br /><br />　　准备好Form Bean之后，ActionServlet接着调用动作的process()方法，Form Bean作为参数之一传入process()方法。在这里，我们对Form Bean的属性作最后的调整，调用业务方法，委派作为View的EditCategory，从而生成一个以Form Bean中合适数据为基础的HTML页面。这个页面被传递给客户端，接下来就进入了“表现”阶段。 <br /><br />　　<u>表现阶段</u> <br /><br />　　这一阶段用户编辑表单并提交。如果服务器端的应用认为用户提交的内容存在问题，它把表单再次显示给用户，加上适当的提示信息；重复该过程，直至用户提交了合法的表单，或取消了表单处理过程。编辑过程的中止可能是由于用户跳转到了其他页面，或者启动了一个取消动作（例如点击了一个由html:cancel标记定义的按钮）。虽然在理论上，View的验证和再次显示操作应该属于表现阶段，但在Struts应用中，这部分功能在存储阶段实现最方便。 <br /><br />　　<u>存储阶段</u> <br /><br />　　准备阶段创建了一个带有“name=”属性定义的动作CatForm，存储阶段要加入另外两个属性，即：“validate=‘true’”和“input=”属性。 <br /><br /><ccid_nobr></ccid_nobr> <table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="#000000" border="1"> <tbody> <tr> <td class="code" bgcolor="#e6e6e6"><pre><ccid_code></ccid_code>&lt;action path="/saveCategory" scope="request" name="catForm" type="com.strutsdemo.SaveCategoryAction" unknown="false" input="/EditCategory1.jsp" validate="true"&gt; &lt;forward name="success" path="/CategoryUpdated.jsp"/&gt; &lt;/action&gt;</pre></td></tr></tbody></table><br /><br />　　设置了“validate=‘true’”属性选项之后，服务器端就会增加一个处理步骤。重新用来自View的数据构造出Form Bean，或更新From Bean的时候，Form Bean的validate()方法会被调用。validate()方法执行必要的合法性验证操作。如果用户的输入数据中存在错误，validate()方法就创建一个或多个ActionError对象。这些ActionError对象包含了错误信息源ID和表单输入域的名称。这些ActionError对象被收集和整理到一个ActionErrors对象，随后ActionErrors对象由validate()方法返回。如果用户输入的数据不包含错误，validate()返回null。 <br /><br />　　由于指定了“input=”属性，一旦出现了错误，动作会被忽略，而“input=”指定的View被显示。这个View既包含Form Bean，也包含当前出现的错误对象集合。一般地，这个输入页面就是原来执行编辑功能的JSP页面。 <br /><br />　　大多数Struts的html标记有对应的HTML标记，但Struts有一个HTML没有的标记，即<cancel>标记。要中止表单编辑过程，用户既可以手工输入URL，也可以点击不指向存储动作处理器的链接。因此，用<cancel>标记定义的“取消”按钮，不是取消编辑操作的唯一方法。 <br /><br />　　假设validate()方法没有发现任何错误，且用户没有点击“取消”按钮，存储动作的process()方法将被调用。在本例的process()方法中，我们调用了Form Bean的save()方法把数据写入持久性存储设备，然后根据写入操作是否成功，显示“存储操作成功”或“存储操作失败”的View。</cancel></cancel> </p> <p></p> <p><b>构造和运行Struts应用</b> <br /><br />　　要构造和运行本文的示例应用，你必须了解如何使用Jakarta的Ant工具。如果你还不了解Ant，现在该是学习它的时候了！赶紧到网站下载Ant，通常要解开压缩，设置一下ANT_HOME环境变量，然后把Ant加入到执行路径就可以了。 <br /><br />本文示例的build.xml需要稍微定制一下，修改指示本地Tomcat位置的配置，使它能够找到在Tomcat下编译所必需的类。另外，你还要有一份Struts的JAR。你可以去下载最新的版本。 <br /><br />　　struts-config.xml文件是粘合Struts应用各个部分的配置文件。在部署完成后的Web应用中，struts-config.xml在Web-INF目录下。你应该修改一下数据源配置，使之符合你当前使用的DBMS环境。数据模型和SQL模式文件在下载包的DBMS目录下，SQL文件针对PostgreSQL DBMS编写。 <br /><br />　　示例中src/com/strutsdemo/ShowCategoriesAction. java是一个简单的分配器。ActionForward()是请求分配方法，从ActionServlet调用。该方法可以完成主要的工作，例如分析请求参数、执行计算，以及构造出View使用的JavaBean。另外，该方法还要根据处理结果，确定下一个要显示的是什么页面：可能是预设的多个页面之一，也可能是一个错误信息页面。 <br /><br />　　<u>ActionForward()的请求分配过程</u> <br /><br />　　当然，最复杂的处理过程与表单有关。ActionForward方法的请求分配过程是： <br /><br />　　1. ActionServlet，对请求进行解码。由于为动作指定了Form Bean，ActionServlet处理Form Bean（参见下面有关“ActionServlet如何使用Form Bean”的说明）。然后，请求传递给了EditCategoryAction。 <br /><br />　　2. EditCategoryAction；准备处理View，或者从数据库装入现有数据，或者创建新的数据项。动作处理器利用Mapping.findForward把控制传递给EditCategory.jsp。 <br /><br />　　3. DitCategory.jsp，显示出Form Bean，允许用户编辑数据。用户提交数据后，控制转到ActionServlet。 <br /><br />　　4. ActionServlet，对请求进行解码。这一次，Form Bean将从View的数据初始化，因为它是一个Struts的JSP表单页面。由于有Form Bean，且struts-config.xml中指定了“validate=‘true’”，名为“catForm”的Form Bean的validate()方法被调用。如果用户提交的数据未能通过合法性验证，则控制转到EditCategory1.jsp。 <br /><br />　　5. EditCategory1.jsp，它只是EditCategory.jsp略加修改后的一个版本。如果有必要，原始编辑页面和带有错误提示的编辑页面可以使用同一个View。Struts的JSP标记能够帮助我们轻松地办到这一点。该页面提交给/saveCategory.do。这样，用户就在这几个页面之间绕圈子，直到他跳转到一个与编辑操作无关的页面，或者他提交的数据通过了合法性验证。 <br /><br />　　6. 如果Form Bean合法性验证通过，ActionServlet把请求（包括Form Bean）传递给SaveCategoryAction。在这个例子中，“save”可能意味着创建操作，也可能意味着更新操作，具体由URL提供的选项决定。写入数据的操作通过调用Form Bean的store()方法完成。注意：实际的应用应当使用某种类型的事务管理机制（或使用EJB，因为EJB有内建的事务管理机制），以避免并发访问带来的问题。 <br /><br />　　<u>ActionServlet如何使用Form Bean</u> <br /><br />　　涉及Form Bean的ActionServlet处理过程包含六个步骤： <br /><br />　　1. 找到或创建Form Bean； <br /><br />　　2. 据从HTTP请求传入的相应数据，更新Form Bean的各个属性； <br /><br />　　3. 检查用户是否点击了“取消”按钮。如是，跳过步骤4和步骤5； <br /><br />　　4. 验证Form Bean数据的合法性； <br /><br />　　5. 如数据未能通过合法性验证，发送“input=”参数中指定的View； <br /><br />　　6. 否则，把Form Bean传递给动作处理器 </p><img height="1" src="http://www.cnblogs.com/SGSoft/aggbug/88319.html" width="1" /><img src ="http://www.blogjava.net/sgsoft/aggbug/196.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-01-07 17:46 <a href="http://www.blogjava.net/sgsoft/articles/196.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在Eclipse中开发struts应用程序 </title><link>http://www.blogjava.net/sgsoft/articles/197.html</link><dc:creator>海天一鸥</dc:creator><author>海天一鸥</author><pubDate>Fri, 07 Jan 2005 09:40:00 GMT</pubDate><guid>http://www.blogjava.net/sgsoft/articles/197.html</guid><wfw:comment>http://www.blogjava.net/sgsoft/comments/197.html</wfw:comment><comments>http://www.blogjava.net/sgsoft/articles/197.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sgsoft/comments/commentRss/197.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sgsoft/services/trackbacks/197.html</trackback:ping><description><![CDATA[<span class="myp111"><font id="zoom"><b>一． 下载并安装Easy Struts包</b> <br /><br />1． Easy Struts是开放源代码组织sourceforge.net上的一个项目，目前最新的版本是0.64，可以在它的网站http://sourceforge.net/projects/easystruts上下载压缩文件org.easystruts.eclipse_0.6.4.zip (Eclipse的安装这里不再描述) <br /><br />2． 将下载后的压缩包解压到{ECLIPSE}\plugins目录下，其中{ECLIPSE}为Eclipse所在的目录。应该注意的是解压后plugins目录下应该多一个子目录，目录名为：com.cross.easystruts.eclipse_[ver]，[ver]为相应的版本号。 <br /><br />3． Easy Struts的初始化设置：启动Eclipse，通过菜单Windows-&gt;Preferences打开设置窗口。窗口的左边会多出一个选项：Easy Struts。如下图所示： <br /><br /> <center><img height="249" src="http://tech.ccidnet.com/pub/attachment/2004/5/292627.png" width="191" /> <br /><br />图1 </center><br /><br />在设置Easy Struts之前要先准备好struts所需的文件（可在Jakarta网站上下载），包括struts.jar，然后是几个用于定义标签库的描述文件包括：struts_html.tld、struts_bean.tld、struts_logic.tld。点击设置窗口左边的Easy Struts后出现如下图2：Easy Struts的初始化设置界面。根据所下载struts的版本选择struts 1.0或者struts 1.1，最新的版本都是struts 1.1。在这里我们选择struts 1.1并在struts 1.1所需的库文件中找到并添加struts.jar文件。在struts 1.1所需的类型库描述文件中建议至少加入前面提到的三个描述文件分别是：struts_html.tld、struts_bean.tld、struts_logic.tld，这三个文件会在创建struts应用项目时自动拷贝到项目的WEB-INF目录下。设置界面上的其他内容基本上按照默认的值即可，无需改动。好了，点击OK按钮关闭设置对话框使设置生效。至此我们已经完成Easy Struts的基本设置可以开始struts的开发了。 <br /><br /> <center><img height="400" src="http://tech.ccidnet.com/pub/attachment/2004/5/292629.png" width="401" /> <br /><br />图2 </center><br /><br /><b>二． 配置和开发struts应用项目</b> <br /><br />1． 新建JAVA项目并设置为支持Easy Struts：新建一个JAVA项目，暂且把这个项目命名为：StrutsDemo，新建完项目后除了Eclipse本身用来描述JAVA项目的两个文件.project和.classpath外并没有其他任何文件。为了使该项目集成Easy Struts，只需点击工具条上的新建图标，选择Java-&gt;Easy Struts-&gt;Add Easy Struts Support并点击下一步按钮，如下图3所示： <br /><br /> <center><img height="400" src="http://tech.ccidnet.com/pub/attachment/2004/5/292631.png" width="400" /> <br /><br />图3 </center><br /><br />点击下一步按钮后到了Easy Struts项目一些基本信息的输入界面，如下图4所示： <br /><br /> <center><img height="376" src="http://tech.ccidnet.com/pub/attachment/2004/5/292633.png" width="450" /> <br /><br />图4 </center><br /><br />在图4的界面中输入web应用程序的目录名，例如struts_demo，发布的时候只需要把该目录打包成WAR文件即可。根据应用服务器的实际情况选择web应用程序的版本，建议使用2.3，目前大部分应用服务器的主流版本都支持2.3版本的WEB应用程序。选择Struts 1.1。修改Easy Struts自动产生的程序包名，例如test.struts。点击完成按钮后Easy Struts会自动产生文件.easystrutsplugin以及创建刚刚在设置界面中输入的web应用服务器目录名的目录，该目录包含WEB-INF子目录，Easy Struts会自动拷贝几个tld文件到该目录下，我们还必须手工的将struts.jar文件拷贝到WEB-INF\lib目录下。 <br /><br />同时产生一个配置文件：ApplicationResources.properties，这个文件可以用来存放用户自己的配置信息。该配置文件支持多语言，struts会自动根据环境配置信息来获取相应的配置文件，例如ApplicationResources_zh_CN.properties对应的是中文简体的配置信息。web.xml也在这个时候产生，该文件已经自动定义好ActionServlet入口，以及这个servlet所需的参数，用户无需再行配置，极大的方便了初学者学习和使用struts。要改变刚刚所输入的信息只需要打开项目的属性对话框即可修改这些配置。 <br /><br />2． 开发struts应用程序：在开发struts应用程序之前还有一项准备工作要做，需要给项目的类路径中添加struts.jar以及servlet.jar这两个文件否则新建struts程序时将会有一大堆错误信息，并设置项目的默认输出文件夹为\struts_demo\WEB-INF\classes。在解压完后，Easy Struts插件已经在Eclipse开发环境中添加了几个用于创建struts相关程序的向导。点击工具条上的新建图标打开图3所示的对话框即可选择与struts相关的程序的开发，例如最常用的Action类的开发，Form类的开发等等。下面我们着重介绍一下Action类的开发。 <br /><br />在新建对话框中选择新建Easy Action并点击下一步按钮 <br /><br /> <center><img height="400" src="http://tech.ccidnet.com/pub/attachment/2004/5/292635.png" width="382" /> <br /><br />图5</center><br /><br />在用例文本框(Use case)中输入Action类的名称，Easy Struts会自动根据你输入的值更新对应的URL请求的路径(path)以及Action类名(Type)。在这个界面中Module name文本框指的是当有多个struts_config文件时选择其他文件，默认的是使用struts_config.xml。而Form name是当Action类需要与一个Form类对应时做的选择。其他信息使用默认的值，点击下一步添加错误处理，以及页面的跳转处理。点击完成按钮即已完成Action类的向导，Easy Struts自动产生类XxxxAction.java，并在struts_config.xml做相应的配置信息。所有的这些向导也可以通过双击项目文件struts_config.xml，并在struts_config.xml界面中操作链接来完成，这是一个可视化的界面用来编辑struts_config.xml内容。如下图所示： <br /><br /> <center><img height="294" src="http://tech.ccidnet.com/pub/attachment/2004/5/292637.png" width="450" /> <br /><br />图6</center><br /><br /><b>三． 总结</b> <br /><br />Easy Struts现在还是一个开发中的版本，但是其具有的强大的功能以及易用性已经显露无疑。通过与Eclipse的结合使Struts的开发变成一件非常简单的事情。本文只是简单的介绍了如何开始来使用Easy Struts这个插件帮助我们开发struts应用程序，具体与struts相关的开发已经有非常多的文章介绍了，况且这也不是我们的本意，所以此处并没有做深入的叙述。总而言之，Easy Struts还有很多功能需要我们继续的发掘。 <br /><br /><b>四． 参考资料</b> <br /><br />www.eclipse.org Eclipse的官方网站； <br /><br />http://sourceforge.net/projects/easystruts Easy Struts项目网站 <br /><br />http://jakarta.apache.org/struts/index.html Apache Struts网站 <br /><br /><b>关于作者：</b> <br /><br />刘冬，珠海市创我科技发展有限公司软件工程师，主要从事J2EE方面的开发。电子邮件：winter.lau@163.com</font></span><img height="1" src="http://www.cnblogs.com/SGSoft/aggbug/88314.html" width="1" /><img src ="http://www.blogjava.net/sgsoft/aggbug/197.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sgsoft/" target="_blank">海天一鸥</a> 2005-01-07 17:40 <a href="http://www.blogjava.net/sgsoft/articles/197.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>