﻿<?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-kapok-随笔分类-AppFuse</title><link>http://www2.blogjava.net/kapok/category/696.html</link><description>垃圾桶,嘿嘿，我藏的这么深你们还能找到啊，真牛！</description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 03:21:05 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 03:21:05 GMT</pubDate><ttl>60</ttl><item><title>为大型项目提供的 Ant 1.6 新特性</title><link>http://www.blogjava.net/kapok/archive/2005/06/14/6106.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 14 Jun 2005 04:22:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/06/14/6106.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/6106.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/06/14/6106.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/6106.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/6106.html</trackback:ping><description><![CDATA[<P><SPAN class=topstoryhead><A href="http://www.oracle.com/technology/global/cn/pub/articles/bodewig_ant1.6.html">http://www.oracle.com/technology/global/cn/pub/articles/bodewig_ant1.6.html</A><BR><BR>为大型项目提供的 Ant 1.6 新特性</SPAN><BR><SPAN class=italicbodycopy>作者：Stefan Bodewig</SPAN> </P>
<P><SPAN class=boldbodycopy>了解 Ant 1.6 的新特性以及它们如何影响您组织编译过程的方式。</SPAN> </P>
<P><SPAN class=bodycopy>虽然 Ant 版本的 1.5.x 系列在任务级方面有很大的改善，但它没有改变人们使用 Ant 的方式。而 Ant 1.6 却有所不同。它增加了几个新特性，以支持大型或非常复杂的编译情况。但是，要充分利用它们的功能，用户可能需要稍微调整它们的编译过程。</SPAN> </P>
<P><SPAN class=bodycopy>本文重点介绍了其中的三种新特性 — &lt;macrodef&gt;、&lt;import&gt;、&lt;subant&gt; 任务，表明使用它们可以有什么收获，以及它们如何影响您组织编译设置的方式。</SPAN> </P>
<P><SPAN class=parahead1>宏</SPAN> </P>
<P><SPAN class=bodycopy>大多数编译工程师迟早会面临必须执行相同的任务组合但在几个地方配置稍微有点不同的情况。一个常见的例子是创建一个web 应用程序存档，对于开发系统、测试系统和生产系统有着不同的配置。</SPAN> </P>
<P><SPAN class=bodycopy>让我们假设 web 应用程序拥有依赖于目标系统的不同的 web 部署描述符，并为开发环境使用了一个不同的 JSP 集合以及一个不同的资料库集合。配置信息将放在属性中，创建 web 存档的任务看起来将类似于</SPAN> </P>
<P><PRE>  &lt;target name="war" depends="jar"&gt;
    &lt;war destfile="${war.name}"
         webxml="${web.xml}"&gt;
      &lt;lib refid="support-libraries"/&gt;
      &lt;lib file="${jar.name}"/&gt;
      &lt;fileset dir="${jsps}"/&gt;
    &lt;/war&gt;
&lt;/target&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>其中 support-libraries 是引用一个在其它位置定义的 </SPAN><TT>&lt;fileset&gt;</TT><SPAN class=bodycopy> ，该引用指向您的应用程序所需的附加资料库的一个公共集合。</SPAN> </P>
<P><SPAN class=bodycopy>如果您只想一次创建一个 web 存档，那么您只需要正确地设置属性。比如说，您可以从一个您的目标专有的属性文件中加载它们。</SPAN> </P>
<P><SPAN class=boldbodycopy>利用 Ant 1.5 创建存档</SPAN> </P>
<P><SPAN class=bodycopy>现在，假定您想为测试系统和生产系统同时创建存档，以确保您真正为两个系统打包了相同的应用程序。利用 Ant 1.5，您可能使用 &lt;antcall&gt; 来调用拥有不同属性设置的 "war" 目标，类似：</SPAN> </P>
<P><PRE>  &lt;target name="production-wars"&gt;
    &lt;antcall target="war"&gt;
      &lt;param name="war.name" value="${staging.war.name}"/&gt;
      &lt;param name="web.xml" value="${staging.web.xml}"/&gt;
    &lt;/antcall&gt;
    &lt;antcall target="war"&gt;
      &lt;param name="war.name" value="${production.war.name}"/&gt;
      &lt;param name="web.xml" value="${production.web.xml}"/&gt;
    &lt;/antcall&gt;
&lt;/target&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>当然，这假定两个目标系统都将使用相同的 jar 和 JSP。</SPAN> </P>
<P><SPAN class=bodycopy>但这种方法有一个主要缺点 — 就是速度慢。</SPAN><TT>&lt;antcall&gt;</TT><SPAN class=bodycopy> 重新分析编译文件，并为每一次调用重新运行调用的目标所依赖的所有目标。在上面的例子中，"jar" 目标将被运行两次。我们希望这对第二次调用没有影响，因为 "war" 目标依赖于它。</SPAN> </P>
<P><SPAN class=boldbodycopy>利用 Ant 1.6 创建存档</SPAN> </P>
<P><SPAN class=bodycopy>使用 Ant 1.6，您可以忘掉用 </SPAN><TT>&lt;antcall&gt;</TT><SPAN class=bodycopy> 来实现宏的方法，相反您可以通过参数化现有的任务来创建一个新的任务。因而上面的例子将变为：</SPAN> </P>
<P><PRE>  &lt;macrodef name="makewar"&gt;
    &lt;attribute name="webxml"/&gt;
    &lt;attribute name="destfile"/&gt;
    &lt;sequential&gt;
      &lt;war destfile="@{destfile}"
           webxml="@{webxml}"&gt;
        &lt;lib refid="support-libraries"/&gt;
        &lt;lib file="${jar.name}"/&gt;
        &lt;fileset dir="${jsps}"/&gt;
      &lt;/war&gt;
    &lt;/sequential&gt;
  &lt;/macrodef&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>这定义了一个名称为 makewar 的任务，该任务可以和任何其它的任务一样使用。该任务有两个必需的属性，webxml 和 destfile。要使属性可选，我们必需在任务定义中提供一个默认值。这个示例假定 </SPAN><TT>${jar.name}</TT><SPAN class=bodycopy> 和 </SPAN><TT>${jsps}</TT><SPAN class=bodycopy> 在编译期间为常量，从而它们仍然作为属性指定。注意，属性在使用任务时展开而不是在定义宏的地方展开。</SPAN> </P>
<P><SPAN class=bodycopy>所用任务的特性几乎完全和属性一样，它们通过 </SPAN><TT>@{}</TT><SPAN class=bodycopy> 而不是 </SPAN><TT>${}</TT> 展开<SPAN class=bodycopy>。与属性不同，它们是可变的，也就是说，它们的值可以（并将）随着每一次调用而改变。它们也只在您的宏定义程序块内部可用。这意味着如果您的宏定义还包含了另一个定义了宏的任务，那么您内部的宏将看不到包含的宏的属性。</SPAN> </P>
<P><SPAN class=bodycopy>于是新的 production-wars 目标将类似于：</SPAN> </P>
<P><PRE>  &lt;target name="production-wars"&gt;
    &lt;makewar destfile="${staging.war.name}"
             webxml="${staging.web.xml}"/&gt;
    &lt;makewar destfile="${production.war.name}"
             webxml="${production.web.xml}"/&gt;
&lt;/target&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>这个新的代码段不仅执行得快一些，而且也更易读，因为属性名称提供了更多的信息。</SPAN> </P>
<P><SPAN class=bodycopy>宏任务还可以定义嵌套的元素。<TT>&lt;makewar&gt;</TT><SPAN class=bodycopy> 定义中的 </SPAN><TT>&lt;war&gt; </TT>任务的嵌套 </SPAN><TT>&lt;fileset&gt;</TT><SPAN class=bodycopy> 可以是这种嵌套元素的一种。可能开发目标需要一些额外的文件或想从不同的位置中挑选 JSP 或资源。以下代码段将一个可选的嵌套 &lt;morefiles&gt; 元素添加到了 &lt;makewar&gt; 任务中</SPAN> </P>
<P><PRE>  &lt;macrodef name="makewar"&gt;
    &lt;attribute name="webxml"/&gt;
    &lt;attribute name="destfile"/&gt;
    &lt;element name="morefiles" optional="true"/&gt;
    &lt;sequential&gt;
      &lt;war destfile="@{destfile}"
           webxml="@{webxml}"&gt;
        &lt;lib refid="support-libraries"/&gt;
        &lt;lib file="${jar.name}"/&gt;
        &lt;fileset dir="${jsps}"/&gt;
        &lt;morefiles/&gt;
      &lt;/war&gt;
    &lt;/sequential&gt;
  &lt;/macrodef&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>调用将类似于：</SPAN> </P>
<P><PRE>  &lt;makewar destfile="${development.war.name}"
           webxml="${development.web.xml}"&gt;
    &lt;morefiles&gt;
      &lt;fileset dir="${development.resources}"/&gt;
      &lt;lib refid="development-support-libraries"/&gt;
    &lt;/morefiles&gt;
  &lt;/makewar&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>这就像 &lt;morefiles&gt; 的嵌套元素直接在 &lt;war&gt; 任务内部使用的效果一样。</SPAN> </P>
<P><SPAN class=bodycopy>即使迄今为止的示例仅显示了包装单个任务的 &lt;macrodef&gt;，但它不限于此。</SPAN> </P>
<P><SPAN class=bodycopy>下面的宏不仅将创建 web 存档，还将确保包含最终存档的目录在试图写入之前存在。在一个实际的编译文件中，您可能在调用任务之前使用一个设置目标来完成这个操作。</SPAN> </P>
<P><PRE>  &lt;macrodef name="makewar"&gt;
    &lt;attribute name="webxml"/&gt;
    &lt;attribute name="destfile"/&gt;
    &lt;element name="morefiles" optional="true"/&gt;
    &lt;sequential&gt;
      &lt;dirname property="@{destfile}.parent"
               file="@{destfile}"/&gt;
      &lt;mkdir dir="${@{destfile}.parent}"/&gt;
      &lt;war destfile="@{destfile}"
           webxml="@{webxml}"&gt;
        &lt;lib refid="support-libraries"/&gt;
        &lt;lib file="${jar.name}"/&gt;
        &lt;fileset dir="${jsps}"/&gt;
        &lt;morefiles/&gt;
      &lt;/war&gt;
    &lt;/sequential&gt;
  &lt;/macrodef&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>这里注意两件事情：</SPAN> </P>
<P><SPAN class=bodycopy>首先，特性在属性展开之前展开，因此结构 ${@{destfile}.parent} 将展开一个名称包含了 destfile 特性的值和 ".parent" 后缀的属性。这意味着您可以将特性展开嵌入到属性展开中，而不是将属性展开嵌入特性展开中。</SPAN> </P>
<P><SPAN class=bodycopy>其次，这个宏定义了属性，该属性的名称基于一个特性的值，因为 Ant 中的属性是全局的并且不可改变。第一次尝试使用</SPAN> </P>
<P><PRE>      &lt;dirname property="parent"
               file="@{destfile}"/&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>相反将不会在 "production-wars" 目标中的第二次 &lt;makewar&gt; 调用产生期望的结果。第一次调用将定义一个新的名称为 parent 的属性，该属性指向父目录 ${staging.war.name}。第二次调用将查看这个属性但不会修改它的值。</SPAN> </P>
<P><SPAN class=bodycopy>预期 Ant 未来的版本将支持某些类型的限定范围的属性，这种属性只在宏执行期间定义。在此之前，使用特性的名称来构建属性名称是一种变通办法，潜在的副作用是要创建大量的属性。</SPAN> </P>
<P>
<TABLE cellPadding=5 width="30%" align=right bgColor=#cccccc border=1 vspace="5" hspace="5">
<TBODY>
<TR>
<TD><SPAN class=boldbodycopy>提示：</SPAN><SPAN class=bodycopy>如果您查看您的编译文件时发现使用了 &lt;antcall&gt; 代替宏，那么强烈建议您考虑使用 macrodef 将其转换成真正的宏。性能影响可能非常显著，并且还可能产生更易读和更易于维护的编译文件。</SPAN> </TD></TR></TBODY></TABLE><SPAN class=parahead1>导入</SPAN> </P>
<P><SPAN class=bodycopy>将一个编译文件分成多个文件有几个原因。 </SPAN>
<OL>
<LI><SPAN class=bodycopy>文件可能变得太大，需要分成几个单独的部分，以便更易于维护。 </SPAN>
<LI><SPAN class=bodycopy>您有某个功能集是多个编译文件公用的，您想共享它。</SPAN> </LI></OL>
<P></P>
<P><SPAN class=boldbodycopy>共享公用功能/在 Ant 1.6 之前包含文件</SPAN> </P>
<P><SPAN class=bodycopy>在 Ant 1.6 之前，您唯一的选择是实体包含的 XML 方法，类似于：</SPAN> </P>
<P><PRE>  &lt;!DOCTYPE project [
      &lt;!ENTITY common SYSTEM "file:./common.xml"&gt;
  ]&gt;
  
  &lt;project name="test" default="test" basedir="."&gt;
  
    &lt;target name="setup"&gt;
      ...
&lt;/target&gt;
  
    &amp;common;
  
    ...
  
&lt;/project&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>摘自 Ant 常见问题解答。</SPAN> </P>
<P><SPAN class=bodycopy>这种方法有两个主要的缺点。您不能使用 Ant 属性指向您想包含的文件，因此被迫在您的编译文件中对位置进行硬编码。您想包含的文件只是一个 XML 文件的一部分，它可能没有一个根元素，因而使用支持 XML 的工具进行维护更加困难。</SPAN> </P>
<P><SPAN class=boldbodycopy>共享公用功能/使用 Ant 1.6 包含文件</SPAN> </P>
<P><SPAN class=bodycopy>Ant 1.6 自带了一个名称为 import 的新任务，您现在可以使用它。上面的示例将变为</SPAN> </P>
<P><PRE>  &lt;project name="test" default="test" basedir="."&gt;
  
    &lt;target name="setup"&gt;
      ...
&lt;/target&gt;
  
    &lt;import file="common.xml"/&gt;
  
    ...
  
&lt;/project&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>因为它是一个任务，因此您可以使用 Ant 所有的特性来指定文件位置。主要的差异是被导入的文件本身必须是一个有效的 Ant 编译文件，因而必须有一个名称为 project 的根元素。如果您想从实体包含转换到导入，那么您必须在导入的文件的内容首尾放上 &lt;project&gt; 标记；然后 Ant 将在读取文件时再次划分它们。</SPAN> </P>
<P><SPAN class=bodycopy>注意文件名称由 Ant 任务根据编译文件的位置（而不是指定的基本目录）确定。如果您没有设置项目的 basedir 属性或将其设为 "."，那么您将不会注意到任何差异。如果您需要根据基本目录解析一个文件，那么您可以使用一个属性作为变通办法，类似于：</SPAN> </P>
<P><PRE>  &lt;property name="common.location" location="common.xml"/&gt;
  &lt;import file="${common.location}"/&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>属性 common.location 将包含文件 common.xml 的绝对路径，并已根据导入项目的基本目录解析。</SPAN> </P>
<P><SPAN class=bodycopy>使用 Ant 1.6，所有的任务都可能放在目标之外或之内，除了两个例外。</SPAN><TT>&lt;import&gt;</TT><SPAN class=bodycopy> 一定不能嵌入到目标中，</SPAN><TT>&lt;antcall&gt;</TT><SPAN class=bodycopy> 一定不能在目标外使用（否则它将创建一个无限循环）。</SPAN> </P>
<P><SPAN class=bodycopy>而 &lt;import&gt; 可做的不仅仅是导入另一个文件。</SPAN> </P>
<P><SPAN class=bodycopy>首先，它定义了名称为 ant.file.NAME 的特殊属性，其中 NAME 替换为每一个导入文件的 &lt;project&gt; 标记的名称属性。这个属性包含了导入文件的绝对路径，导入文件可用来根据它自己的位置（而不是导入文件的基本目录）定位文件和资源。</SPAN> </P>
<P><SPAN class=bodycopy>这意味着 </SPAN><TT>&lt;project&gt;</TT><SPAN class=bodycopy> 的名称属性在 &lt;import&gt; 任务环境中变得更加重要。它还用来为在被导入的编译文件中定义的目标提供别名。如果导入了以下文件</SPAN> </P>
<P><PRE>&lt;project name="share"&gt;
    &lt;target name="setup"&gt;
      &lt;mkdir dir="${dest}"/&gt;
&lt;/target&gt;
&lt;/project&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>导入编译文件可以查看作为 "setup" 或 "share.setup" 的目标。后者在目标覆盖的上下文中变得非常重要。</SPAN> </P>
<P><SPAN class=bodycopy>让我们假定有一个包含了多个独立的组件（每个组件拥有它自己的编译文件）的编译系统。这些编译文件几乎相同，因此我们决定将公用功能转移到一个共享和已导入的文件中。为了简单起见，我们只介绍 Java 文件的编译和创建结果的一个 JAR 存档。共享的文件将类似于</SPAN> </P>
<P><PRE>  &lt;project name="share"&gt;
    &lt;target name="setup" depends="set-properties"&gt;
      &lt;mkdir dir="${dest}/classes"/&gt;
      &lt;mkdir dir="${dest}/lib"/&gt;
&lt;/target&gt;
    &lt;target name="compile" depends="setup"&gt;
      &lt;javac srcdir="${src}" destdir="${dest}/classes"&gt;
        &lt;classpath refid="compile-classpath"/&gt;
&lt;/javac&gt;
&lt;/target&gt;
    &lt;target name="jar" depends="compile"&gt;
      &lt;jar destfile="${dest}/lib/${jar.name}" basedir="${dest}/classes"/&gt;
&lt;/target&gt;
&lt;/project&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>这个文件不会作为一个独立的 Ant 编译文件进行工作，因为它没有定义 "setup" 所依赖的 "set-properties" 目标。</SPAN> </P>
<P><SPAN class=bodycopy>组件 A 的编译文件可能类似于</SPAN> </P>
<P><PRE>  &lt;project name="A" default="jar"&gt;
    &lt;target name="set-properties"&gt;
      &lt;property name="dest" location="../dest/A"/&gt;
      &lt;property name="src" location="src"/&gt;
      &lt;property name="jar.name" value="module-A.jar"/&gt;
      &lt;path id="compile-classpath"/&gt;
&lt;/target&gt;
    &lt;import file="../share.xml"/&gt;
&lt;/project&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>它仅设置适当的环境，然后将全部的编译逻辑交给被导入的文件负责。注意该编译文件创建了一个空的路径作为编译 CLASSPATH，因为它是自包含的。模块 B 依赖于 A，它的编译文件将类似于</SPAN> </P>
<P><PRE>  &lt;project name="B" default="jar"&gt;
    &lt;target name="set-properties"&gt;
      &lt;property name="dest" location="../dest/B"/&gt;
      &lt;property name="src" location="src"/&gt;
      &lt;property name="jar.name" value="module-B.jar"/&gt;
      &lt;path id="compile-classpath"&gt;
        &lt;pathelement location="../dest/A/module-A.jar"/&gt;
      &lt;/path&gt;
&lt;/target&gt;
    &lt;import file="../share.xml"/&gt;
&lt;/project&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>您将注意到该编译文件与 A 的编译文件几乎一样，因此似乎有可能将大多数的 set-properties 目标也推送到 shared.xml 中。实际上，我们可以假定有一个对 dest 和 src 目标一致的命名惯例，以实现这一目的。</SPAN> </P>
<P><PRE>  &lt;project name="share"&gt;
    &lt;target name="set-properties"&gt;
      &lt;property name="dest" location="../dest/${ant.project.name}"/&gt;
      &lt;property name="src" location="src"/&gt;
      &lt;property name="jar.name" value="module-${ant.project.name}.jar"/&gt;
&lt;/target&gt;

    ... contents of first example above ...
&lt;/project&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>ant.project.name 是一个内置的属性，它包含了最外面的 &lt;project&gt; 标记的名称属性的值。因此，如果模块 A 的编译文件导入了 share.xml，那么它将拥有值 A。</SPAN> </P>
<P><SPAN class=bodycopy>注意，所有的文件都与导入编译文件的基本目录相关，因此 scr 属性的实际值依赖于导入文件。</SPAN> </P>
<P><SPAN class=bodycopy>为此，A 的编译文件将简单地变为</SPAN> </P>
<P><PRE>&lt;project name="A" default="jar"&gt;
    &lt;path id="compile-classpath"/&gt;
    &lt;import file="../share.xml"/&gt;
&lt;/project&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>B 的编译文件将变为</SPAN> </P>
<P><PRE>  &lt;project name="B" default="jar"&gt;
    &lt;path id="compile-classpath"&gt;
      &lt;pathelement location="../dest/A/module-A.jar"/&gt;
    &lt;/path&gt;
    &lt;import file="../share.xml"/&gt;
&lt;/project&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>现在假定 B 增加了一些 RMI 接口，需要在编译类之后但在创建 jar 之前运行 &lt;rmic&gt;。这就是目标覆盖能派上用场的地方。如果我们在导入编译文件中定义了一个目标，该目标与被导入的编译文件中的一个目标名称相同，那么将使用导入编译文件中的目标。例如，B 可以使用：</SPAN> </P>
<P><PRE>  &lt;project name="B" default="jar"&gt;
    &lt;path id="compile-classpath"&gt;
      &lt;pathelement location="../dest/A/module-A.jar"/&gt;
    &lt;/path&gt;
    &lt;import file="../share.xml"/&gt;

    &lt;target name="compile" depends="setup"&gt;
      &lt;javac srcdir="${src}" destdir="${dest}/classes"&gt;
        &lt;classpath refid="compile-classpath"/&gt;
&lt;/javac&gt;
      &lt;rmic base="${dest}/classes" includes="**/Remote*.class"/&gt;
&lt;/target&gt;
&lt;/project&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>在上面的示例中将使用 "compile" 目标，而不是 share.xml 中的目标；然而，不幸的是，这只是从共享那里复制 &lt;javac&gt; 任务。一种更好的解决方案是：</SPAN> </P>
<P><PRE>  &lt;project name="B" default="jar"&gt;
    &lt;path id="compile-classpath"&gt;
      &lt;pathelement location="../dest/A/module-A.jar"/&gt;
    &lt;/path&gt;
    &lt;import file="../share.xml"/&gt;

    &lt;target name="compile" depends="share.compile"&gt;
      &lt;rmic base="${dest}/classes" includes="**/Remote*.class"/&gt;
&lt;/target&gt;
&lt;/project&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>这只是使 B 的 "compile" 在原来的 "compile" 目标使用之后运行 &lt;rmic&gt;。</SPAN> </P>
<P><SPAN class=bodycopy>如果我们想在编译之前生成一些 Java 源代码（例如通过 XDoclet)，我们可以使用类似下面的方法：</SPAN> </P>
<P><PRE>    &lt;import file="../share.xml"/&gt;

    &lt;target name="compile" depends="setup,xdoclet,share.compile"/&gt;
    &lt;target name="xdoclet"&gt;
       .. details of XDoclet invocation omitted ..
&lt;/target&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>因此您可以完全覆盖一个目标或通过在原始目标之前或之后运行任务来增强它。</SPAN> </P>
<P><SPAN class=bodycopy>这里要注意一个危险。目标覆盖机制使导入编译文件依赖于在导入文件中使用的名称属性。如果任何人修改了导入文件的名称属性，那么导入编译文件将被破坏。Ant 开发社区目前正在讨论在 Ant 的一个未来的版本中为此提供一个解决方案。</SPAN> </P>
<P>
<TABLE cellPadding=5 width="30%" align=right bgColor=#cccccc border=1 vspace="5" hspace="5">
<TBODY>
<TR>
<TD><SPAN class=boldbodycopy>提示：</SPAN><SPAN class=bodycopy>如果您在编译文件中发现了非常常见的结构，那么值得尝试将文件重构为一个（一些）共享文件，并在必要时使用目标覆盖。这可以使您的编译系统更加一致，并让您能够重用编译逻辑。</SPAN> </TD></TR></TBODY></TABLE><SPAN class=parahead1>Subant</SPAN> </P>
<P><SPAN class=bodycopy>在某种意义上，subant 是两种任务合二为一，因为它了解操作的两种模式。</SPAN> </P>
<P><SPAN class=bodycopy>如果您使用 &lt;subant&gt; 的 genericantfile 属性，那么它的工作方式和 &lt;antcall&gt; 一样，调用包含任务的同一个编译文件中的目标。与 &lt;antcall&gt; 不同，&lt;subant&gt; 获取目录的列表或集合，并将为每一个目录调用一次目标，以设定项目的基本目录。如果您想在任意数量的目录中执行完全一样的操作，那么这非常有用。</SPAN> </P>
<P><SPAN class=bodycopy>第二种模式不使用 genericantfile 属性，而获取一个编译文件的列表和集合进行迭代，以在每一个编译文件中调用目标。这种工作方式类似于在一个循环中使用 &lt;ant&gt; 任务。</SPAN> </P>
<P><SPAN class=bodycopy>第二种形式的典型情景是几个能够独立编译的模块的一个编译系统，但是该系统需要一个主编译文件来一次性编译所有的模块。</SPAN> </P>
<TABLE cellPadding=5 width="30%" align=right bgColor=#cccccc border=1 vspace="5" hspace="5">
<TBODY>
<TR>
<TD>
<CENTER><SPAN class=parahead1>接下来的步骤</SPAN> </CENTER>
<P></P>
<P><SPAN class=bodycopy>使用以下资源了解关于 Ant 的更多信息，并开始编译和部署 Java 项目。</SPAN> 
<P><SPAN class=boldbodycopy>Ant 业界趋势</SPAN><BR><SPAN class=bodycopy>对这个跨平台编译工具的形成进行<A href="http://oracle.com/global/cn/oramag/oracle/02-jul/o42industry.html"><SPAN class=bodylink>幕后观察</SPAN></A>。</SPAN></P>
<P><SPAN class=boldbodycopy>阅读关于 JDeveloper 中的 Ant 集成的更多信息</SPAN> <BR><SPAN class=bodycopy>Oracle JDeveloper 中的 Ant <A href="http://helponline.oracle.com/jdeveloper/help/state?oldNavSetId=jdeveloper&amp;oldNavId=1&amp;navId=1&amp;vtTopicFile=&amp;navSetId=jdeveloper&amp;kwValue=212&amp;tpValue=1&amp;accesstype=topics&amp;action=&amp;locale=&amp;keywordInput=Ant&amp;event=update&amp;source=&amp;value=1&amp;size=&amp;topicid=213" target=_blank><SPAN class=bodylink>集成</SPAN></A>是通过在 JDeveloper 项目中添加一个 Ant 编译文件或通过从一个现有的 JDeveloper 项目中创建一个新的 Ant 编译文件来实现的。</SPAN></P>
<P><SPAN class=boldbodycopy>下载 Oracle JDeveloper 10g</SPAN><BR><SPAN class=bodycopy><A href="http://www.oracle.com/technology/global/cn/software/products/jdev/index.html" target=_blank><SPAN class=bodylink>Oracle JDeveloper 10<I>g</I></SPAN></A> 是一个集成开发环境，它提供了对建模、开发、调试、优化和部署 Java 应用程序及 Web 服务的端到端支持。</SPAN> </P>
<P><SPAN class=boldbodycopy>测试驱动：将 Ant 用于编译</SPAN><BR><SPAN class=bodycopy>这个 <A href="http://www.oracle.com/technology/products/jdev/collateral/papers/10g/reviewer/viewlets/reviewer_ant_viewlet_swf.html" target=_blank><SPAN class=bodylink>viewlet</SPAN></A> 演示了已拥有 Ant 项目的用户如何能够在 JDeveloper 内部使用这些项目。</SPAN></P>
<P><SPAN class=boldbodycopy>Ant 入门第 1 部分</SPAN><BR><SPAN class=bodycopy><A href="http://oracle.com/global/cn/oramag/oracle/02-nov/o62odev_ant.html" target=_blank><SPAN class=bodylink>这里</SPAN></A>开始将这个非常有用的工具用于构建和部署 Java 项目。本文介绍了您可能在 Java 开发过程期间执行的一些基本的 Ant 任务。</SPAN> </P>
<P><SPAN class=boldbodycopy>Ant 入门第 2 部分</SPAN><BR><SPAN class=bodycopy><A href="http://oracle.com/global/cn/oramag/oracle/03-jan/o13ant.html" target=_blank><SPAN class=bodylink>这里</SPAN></A>是我们的系列中的第 2 部分，这个系列介绍用于构建和部署 Java 项目的一个非常有用的工具。本文讨论在 Ant 的两个任务程序包（核心的任务程序包和可选的任务程序包）中提供的一些 Ant 的更高级的特性。</SPAN></P>
<P><SPAN class=boldbodycopy>在 Linux 上创建 Java 应用程序的命令行方法</SPAN><BR><SPAN class=bodycopy><A href="" target=_blank><SPAN class=bodylink>本文</SPAN></A>可用作使用 Ant 在 Linux 上开发和部署 Java 客户端应用程序的一个不错的上机操作指南。</SPAN> </P>
<P><SPAN class=boldbodycopy>阅读关于 Ant 的更多信息</SPAN><BR><SPAN class=bodycopy>访问<A href="http://ant.apache.org/" target=_blank><SPAN class=bodylink>官方 Apache Ant 站点</SPAN></A>，获取更多的项目详细信息。</SPAN> </P>
<P><SPAN class=boldbodycopy>相关文章与下载</SPAN> </P>
<P><A href="http://radio.weblogs.com/0132383/stories/2004/03/16/antDeploymentToOc4j.html" target=_blank><SPAN class=bodylink>Blog：到 OC4J 的 Ant 部署</SPAN></A></P>
<P><A href="http://radio.weblogs.com/0132383/stories/2004/03/16/antDeploymentToOc4j.html" target=_blank><SPAN class=bodylink>Blog： 如何从一个 JDeveloper 项目文件中将属性动态检索到一个 ant 编译文件中？</SPAN></A></P>
<P><A href="http://www.oracle.com/technology/products/jdev/collateral/papers/10g/reviewer/viewlets/reviewer_CVS_viewlet_swf.html" target=_blank a><SPAN class=bodylink>Viewlet：将 CVS 用于软件配置</SPAN></A></P></TD></TR></TBODY></TABLE>
<P><SPAN class=boldbodycopy>在 Ant 1.6 之前构建主编译文件</SPAN> </P>
<P><SPAN class=bodycopy>在导入部分中讨论的例子使用了这样一个主编译文件。</SPAN> </P>
<P><PRE>  &lt;target name="build-all"&gt;
    &lt;ant dir="module-A" target="jar"/&gt;
    &lt;ant dir="module-B" target="jar"/&gt;
&lt;/target&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>在 Ant 1.6 之前的 Ant 中。</SPAN> </P>
<P><SPAN class=boldbodycopy>使用 Ant 1.6 构建主编译文件</SPAN> </P>
<P><SPAN class=bodycopy>在 Ant 1.6 中使用 &lt;subant&gt;，这可以重写为</SPAN> </P>
<P><PRE> 
  &lt;target name="build-all"&gt;
    &lt;subant target="jar"&gt;
      &lt;filelist dir="."&gt;
        &lt;file name="module-A/build.xml"/&gt;
        &lt;file name="module-B/build.xml"/&gt;
      &lt;/filelist&gt;
    &lt;/subant&gt;
&lt;/target&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>这看起来并没有很大的改善，因为您仍然必须单独指定每一个子编译文件。相反如果您转用 &lt;fileset&gt;，那么情况将有所改观。</SPAN> </P>
<P><PRE>  &lt;target name="build-all"&gt;
    &lt;subant target="jar"&gt;
      &lt;fileset dir="." includes="module-*/build.xml"/&gt;
    &lt;/subant&gt;
&lt;/target&gt;
</PRE>
<P></P>
<P><SPAN class=bodycopy>这将自动发现所有模块的编译文件。如果您增加了一个模块 C，主编译文件中的目标不需要修改。</SPAN> </P>
<P><SPAN class=bodycopy>但小心。与 &lt;filelist&gt; 或 &lt;path&gt;（也被 &lt;subant&gt; 支持）不同，&lt;fileset&gt; 是无序的。在我们的例子中，模块 B 依赖于模块 A，因此我们需要确保首先编译模块 A，而使用 &lt;fileset&gt; 没有办法这么做。</SPAN> </P>
<P><SPAN class=bodycopy>如果编译完全彼此独立或者它们对于一个给定的操作彼此独立，那么 &lt;fileset&gt; 仍然有用。模块 B 的文档目标可能完全不依赖于模块 A，同样还有从您的 SCM 系统中更新源代码的目标。</SPAN> </P>
<P><SPAN class=bodycopy>如果您想将编译文件的自动发现与根据编译的相互依赖性对编译进行排序结合在一起，那么您将必须编写一个定制的 Ant 任务。基本的想法是编写一个使用 &lt;fileset&gt; 的任务（让我们目前称之为 &lt;buildlist&gt;），确定依赖关系并计算 &lt;subant&gt; 必须使用的顺序。然后它创建一个以正确的顺序包含编译文件的 &lt;path&gt;，然后将对这个路径的一个引用放到项目中。调用将类似于</SPAN> </P>
<P><PRE>  &lt;target name="build-all"&gt;
    &lt;buildlist reference="my-build-path"&gt;
      &lt;fileset dir="." includes="module-*/build.xml"/&gt;
    &lt;/buildlist&gt;
    &lt;subant target="jar"&gt;
      &lt;buildpath refid="my-build-path"/&gt;
    &lt;/subant&gt;
&lt;/target&gt;</PRE>
<P><SPAN class=bodycopy>这个假想的 buildlist 任务已经在 Ant 用户邮件列表和 bug 跟踪系统中进行了讨论。很有可能 Ant 的一个将来的版本中将包含这样的一个任务。</SPAN> </P>
<P><SPAN class=bodycopy>在 Ant 1.6 中已经增加了大量的新特性。这些新功能中的许多功能使得编译模板易于创建、构造和定制。特别是 &lt;import&gt; 和 &lt;target&gt; 进行了覆盖。&lt;import&gt;、&lt;macrodef&gt; 和 &lt;subant&gt; 特性很有可能使得 Ant 编译可高度重用。&lt;scriptdef&gt;（本文中未讨论）对于需要一些脚本但不想用 Java 编写定制任务的人而言可能非常有吸引力。</SPAN> </P><img src ="http://www.blogjava.net/kapok/aggbug/6106.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-06-14 12:22 <a href="http://www.blogjava.net/kapok/archive/2005/06/14/6106.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Appfuse实践(一)——配置安装</title><link>http://www.blogjava.net/kapok/archive/2005/06/14/6105.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 14 Jun 2005 04:09:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/06/14/6105.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/6105.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/06/14/6105.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/6105.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/6105.html</trackback:ping><description><![CDATA[<DIV class=postcolor><A href="http://bbs.mbig.cn/topic_20008.html">http://bbs.mbig.cn/topic_20008.html</A><BR>我使用的是webwork+spring+hibernate，其他的可以参考相关tutorial。<BR><BR>一、系统环境准备<BR>　　从http://java.sun.com下载，我使用1.4.2_05.<BR>　　从latest Tomcat release 下载tomcat，目前的版本是5.0.28。最好不要用LE版本否则你要添加DBCP和JavaMail的JAR包.<BR>　　下载最近的Appfuse版本,我使用的是 AppFuse 1.6。<BR>　　下载mysql。<BR><BR>　　将会使用 Ant, XDoclet, Spring, Hibernate (or iBATIS), JUnit, Cactus, StrutsTestCase, Canoo's WebTest, Struts Menu, Display Tag Library, OSCache, JSTL and Struts (or Spring MVC)这些组件。<BR><BR><BR><BR>二、开始安装配置Appfuse<BR>　　 download appfuse 并且解压<BR>　　 需要启动Mysql服务器<BR>　　 建立自己的应用程序数据库默认字符集是UTF8，这个问题很关键，因为Appfuse支持多语言否则程序无法正常运行。所以Mysql服务器的默认的字符集也应该是UTF8，大家使用mysql怎么更改到UTF8字符集我不知道，我是通过命名行参数启动的：<BR>　　 mysqld --default-character-set=utf8<BR><BR>　　此时可以用ant建立自己的应用了：<BR>ant new -Dapp.name=myApp -Ddb.name=mydb<BR><BR>如果需要改变数据库可以修改build.properties文件中这部分的参数：<BR><!--c1--></DIV>
<TABLE cellSpacing=1 cellPadding=3 width="95%" align=center border=0>
<TBODY>
<TR>
<TD><B>代码</B> </TD></TR>
<TR>
<TD id=CODE><!--ec1-->#database.jar=${postgresql.jar}<BR>#database.type=postgresql<BR>#database.name=myApp<BR>#database.host=localhost<BR>#database URL for creating other databases (doesn't work with pgsql)<BR>#database.admin.url=jdbc:${database.type}://${database.host}/template1<BR>#database.admin.username=postgres<BR>#database.admin.password=postgres<BR><BR>#hibernate.dialect=net.sf.hibernate.dialect.PostgreSQLDialect<BR>#database.driver_class=org.postgresql.Driver<BR>#database.url=jdbc:${database.type}://${database.host}/${database.name}<!--c2--></TD></TR></TBODY></TABLE>
<DIV class=postcolor><!--ec2--><BR><BR>建立数据库database, 在tomcat上发布应用<BR><BR>运行<BR>ant setup<BR><BR>出错了，无法自动建立数据库mydb。出错信息如下：<BR>BUILD FAILED: D:\myfile\myApp\build.xml:830: org.dbunit.dataset.NoSuchTableException: app_user<BR><BR>检查执行过程，把自动执行的创建app_user的sql语句放到mysql control center中执行<BR><!--sql--></DIV>
<TABLE cellSpacing=1 cellPadding=3 width="95%" align=center border=0>
<TBODY>
<TR>
<TD><B>SQL代码</B> </TD></TR>
<TR>
<TD id=CODE><!--sql1-->create table app_user (<BR>username varchar(20) not null,<BR>version integer not null,<BR>password varchar(255),<BR>first_name varchar(50),<BR>last_name varchar(50),<BR>address varchar(150),<BR>city varchar(50),<BR>province varchar(100),<BR>country varchar(100),<BR>postal_code varchar(15),<BR>email varchar(255) not null unique,<BR>phone_number varchar(255),<BR>website varchar(255),<BR>password_hint varchar(255),<BR>primary key (username)<BR>); <!--sql2--></TD></TR></TBODY></TABLE>
<DIV class=postcolor><!--sql3-->执行失败，错误提示：<BR>[root@localhost:3306] 错误 1071: Specified key was too long. Max key length is 500<BR>作如下修改，成功了，<BR>email varchar(255) not null unique改成<BR>email varchar(100) not null unique,<BR><BR>经过测试，长度超过166（包括）就出错了！<BR>不知道是不是UTF8字符集造成的原因，一个UTF8字符被认为占用了三个字节（500/3&lt;167）？？<BR>就是说声明为 not null unique的字段长度不能超过166。<BR><BR><BR>由于sql语句根据POJO的tag自动创建：<BR><BR>找到org.appfuse.model.User的源程序<BR><BR>修改<BR>@hibernate.column name="email" not-null="true" unique="true"<BR><BR>改成：<BR>@hibernate.column name="email" not-null="true" length="166" unique="true"<BR><BR>运行<BR>ant setup-db<BR><BR>成功了！ok!<BR><BR><BR><BR>启动tomcat 5.0.25<BR>运行<BR>ant setup<BR><BR>出错了<BR><BR>BUILD FAILED: D:\edu\edu\build.xml:33: Please copy junit.jar into C:\ant/lib<BR><BR>完成拷贝<BR><BR>成功了！excellent！<BR>打开浏览器FireFox run一下：<BR><BR>输入：http://127.0.0.1:8080/myApp<BR><BR>没有反应？？怎么回事？？<BR><BR>命令行下面启动tomcat，看看有什么错误提示：<BR><BR>unregistering logger Catalina:type=Logger,path=/myApp,host=localhost<BR><BR>打开C:\Tomcat\conf\Catalina\localhost\myAqpp.xml<BR><BR>修改：注释掉 logger信息<BR><!--c1--></DIV>
<TABLE cellSpacing=1 cellPadding=3 width="95%" align=center border=0>
<TBODY>
<TR>
<TD><B>代码</B> </TD></TR>
<TR>
<TD id=CODE><!--ec1-->&lt;!-- Logger className="org.apache.catalina.logger.FileLogger" prefix="myApp_log." suffix=".txt" timestamp="true"/ --&gt;<!--c2--></TD></TR></TBODY></TABLE>
<DIV class=postcolor><!--ec2--><BR><BR>重新启动tomcat<BR><BR>还是不对<BR><BR>更换版本tomcat 5.0.5.28<BR>错误信息变了<BR>Application 没有启动？？<BR><BR>运行任务<BR><BR>ant install<BR><BR><BR>没有成功！出错信息如下：<BR><BR>BUILD FAILED: D:\myfile\myApp\build.xml:1221: java.io.IOException: Server returned HTTP response code: 401 for URL: <A href="http://localhost:8080/manager/deploy?path=%2FmyApp" target=_blank><FONT color=#000000>http://localhost:8080/manager/deploy?path=%2FmyApp</FONT></A><BR><BR><BR>直接在浏览器中输入http://localhost:8080/manager/deploy?path=%2FmyApp<BR><BR>？？出现窗口要求输入用户名和密码<BR>查看myApp\tomcat.properties 管理员用户密码为admin,admin<BR><BR>修改 tomcat\conf\tomcat-users.xml 增加管理员admin<BR><BR>增加两个角色<BR><BR><!--c1--></DIV>
<TABLE cellSpacing=1 cellPadding=3 width="95%" align=center border=0>
<TBODY>
<TR>
<TD><B>代码</B> </TD></TR>
<TR>
<TD id=CODE><!--ec1-->&lt;role rolename="admin"/&gt;<BR>&nbsp;&lt;role rolename="manager"/&gt;<!--c2--></TD></TR></TBODY></TABLE>
<DIV class=postcolor><!--ec2--><BR><BR>增加admin用户<BR><!--c1--></DIV>
<TABLE cellSpacing=1 cellPadding=3 width="95%" align=center border=0>
<TBODY>
<TR>
<TD><B>代码</B> </TD></TR>
<TR>
<TD id=CODE><!--ec1-->&lt;user username="admin" password="admin" roles="role1,tomcat,admin,manager"/&gt;<!--c2--></TD></TR></TBODY></TABLE>
<DIV class=postcolor><!--ec2--><BR><BR>删除tomcat下面myApp目录和conf\Catalina\localhost下的myApp.xml文件，重新启动tomcat<BR><BR>运行<BR><BR>ant install<BR><BR>成功了！！<BR><BR><BR><BR>但是tomcat报错：<BR>2004-11-14 19:59:49 org.apache.catalina.core.StandardHostDeployer install<BR>信息: Processing Context configuration file URL file:/C:/Tomcat/conf/Catalina/localhost/myApp.xml<BR>2004-11-14 19:59:49 org.apache.catalina.core.StandardHostDeployer install<BR>信息: Installing web application from URL jar:file:/C:/Tomcat/webapps/myApp.war!<BR>/<BR>2004-11-14 20:00:04 org.apache.catalina.core.StandardContext listenerStart<BR>严重: Skipped installing application listeners due to previous error(s)<BR>2004-11-14 20:00:04 org.apache.catalina.core.StandardContext start<BR>严重: Error listenerStart<BR>2004-11-14 20:00:04 org.apache.catalina.core.StandardContext start<BR>严重: Context startup failed due to previous errors<BR>2004-11-14 20:00:04 org.apache.catalina.logger.LoggerBase stop<BR>信息: unregistering logger Catalina:type=Logger,path=/myApp,host=localhost<BR><BR>为什么呢？？<BR><BR>tomcat版本不对？？<BR><BR>运行struts安装程序，正常运行<BR><BR>重新运行ant install-webwork<BR><BR>BUILD FAILED: D:\myfile\myApp\build.xml:1391: Basedir D:\myfile\myApp\extras\webwork does not exist<BR><BR>嗯~~<BR><BR>更改extras\viewgen目录名称为webwork<BR><BR>重新运行ant install-webwork<BR><BR>再运行ant install<BR><BR>打开firefox，输入 <A href="http://127.0.0.1:8080/myApp。成功了！" target=_blank><FONT color=#000000>http://127.0.0.1:8080/myApp。成功了！</FONT></A><BR><BR><BR><BR>输入用户名mraible<BR>密码tomcat<BR><BR>OK！！进入主界面（界面没有上传，不好意思了^_^） </DIV><img src ="http://www.blogjava.net/kapok/aggbug/6105.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-06-14 12:09 <a href="http://www.blogjava.net/kapok/archive/2005/06/14/6105.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Servlet 2.3过滤器编程</title><link>http://www.blogjava.net/kapok/archive/2005/04/15/3334.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Fri, 15 Apr 2005 15:50:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/04/15/3334.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/3334.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/04/15/3334.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/3334.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/3334.html</trackback:ping><description><![CDATA[<FONT face=宋体>2005年3月17日&nbsp;&nbsp;作者：l_walker&nbsp;&nbsp;Matrix-与Java共舞<BR><A href="http://www.matrix.org.cn/article/1266.html">http://www.matrix.org.cn/article/1266.html</A><BR><BR>Jason&nbsp;Hunter通过对一些自由而又实用的过滤器的研究以对新的servlet过滤器模型进行深入探讨。你将知道这些过滤器是如何工作以及你能用他们做什么。最后，Jason介绍了他自己为简化文件上传而做的多路请求过滤器。<BR><BR>&nbsp;<BR><BR>&nbsp;<BR><BR>在"Servlet&nbsp;2.3:&nbsp;New&nbsp;Features&nbsp;Exposed,"中，我介绍了Servlet&nbsp;API&nbsp;2.3中的变化并给出了一个简单的servlet过滤器模型。在随后的文章中，我将对servlet过滤器进行深入的挖掘，而你看到的这些servlet过滤器都是能从Web上免费下载的。对每一个过滤器，我将检视它是做什么的，如何工作的，以及你能从哪里得到它。<BR><BR>&nbsp;<BR><BR>你可以在两种情况下使用本文：学习过滤器的功用，或者作为你写过滤器时的辅助。我将从几个简单的例子开始然后继续更多高级的过滤器。最后，我将向你介绍我为了支持多路请求而写的一个文件上传过滤器。<BR><BR>&nbsp;<BR><BR>Servlet&nbsp;过滤器<BR><BR>也许你还不熟悉情况，一个过滤器是一个可以传送请求或修改响应的对象。过滤器并不是servlet，他们并不实际创建一个请求。他们是请求到达一个servlet前的预处理程序，和/或响应离开servlet后的后处理程序。就像你将在后面的例子中看到的，一个过滤器能够：<BR><BR>·在一个servlet被调用前截获该调用<BR><BR>·在一个servlet被调用前检查请求<BR><BR>·修改在实际请求中提供了可定制请求对象的请求头和请求数据<BR><BR>·修改在实际响应中提供了可定制响应对象的响应头和响应数据<BR><BR>·在一个servlet被调用之后截获该调用<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;你可以一个过滤器以作用于一个或一组servlet，零个或多个过滤器能过滤一个或多个servlet。一个过滤器实现java.servlet.Filter接口并定义它的三个方法：<BR><BR>1．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;init(FilterConfig&nbsp;config)&nbsp;throws&nbsp;ServletException:在过滤器执行service前被调用，以设置过滤器的配置对象。<BR><BR>2．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;destroy();在过滤器执行service后被调用。<BR><BR>3．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Void&nbsp;doFilter(ServletRequest&nbsp;req,ServletResponse&nbsp;res,FilterChain&nbsp;chain)&nbsp;throws&nbsp;IOException,ServletException;执行实际的过滤工作。<BR><BR>&nbsp;<BR><BR>服务器调用一次init(FilterConfig)以为服务准备过滤器，然后在请求需要使用过滤器的任何时候调用doFilter()。FilterConfig接口检索过滤器名、初始化参数以及活动的servlet上下文。服务器调用destory()以指出过滤器已结束服务。过滤器的生命周期和servelt的生命周期非常相似&nbsp;——在Servlet&nbsp;API&nbsp;2.3&nbsp;最终发布稿2号&nbsp;中最近改变的。先前得用setFilterConfig(FilterConfig)方法来设置生命周期。<BR><BR>&nbsp;<BR><BR>在doFilter()方法中，每个过滤器都接受当前的请求和响应，而FilterChain包含的过滤器则仍然必须被处理。doFilter()方法中，过滤器可以对请求和响应做它想做的一切。（就如我将在后面讨论的那样，通过调用他们的方法收集数据，或者给对象添加新的行为。）过滤器调用<BR><BR>chain.doFilter()将控制权传送给下一个过滤器。当这个调用返回后，过滤器可以在它的doFilter()方法的最后对响应做些其他的工作；例如，它能记录响应的信息。如果过滤器想要终止请求的处理或或得对响应的完全控制，则他可以不调用下一个过滤器。<BR><BR>&nbsp;<BR><BR>循序渐进<BR><BR>如果想要真正理解过滤器，则应该看它们在实际中的应用。我们将看到的第一个过滤器是简单而有用的，它记录了所有请求的持续时间。在Tomcat&nbsp;4.0发布中被命名为ExampleFilter。代码如下：<BR><BR></FONT>
<TABLE style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>
<TBODY>
<TR>
<TD style="WORD-WRAP: break-word">import&nbsp;java.io.*;<BR><BR>import&nbsp;javax.servlet.*;<BR><BR>import&nbsp;javax.servlet.http.*;<BR><BR>&nbsp;<BR><BR>public&nbsp;class&nbsp;TimerFilter&nbsp;implements&nbsp;Filter&nbsp;{<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;private&nbsp;FilterConfig&nbsp;config&nbsp;=&nbsp;null;<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;void&nbsp;init(FilterConfig&nbsp;config)&nbsp;throws&nbsp;ServletException&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;this.config&nbsp;=&nbsp;config;<BR><BR>&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;void&nbsp;destroy()&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;config&nbsp;=&nbsp;null;<BR><BR>&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;void&nbsp;doFilter(ServletRequest&nbsp;request,&nbsp;ServletResponse&nbsp;response,<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FilterChain&nbsp;chain)&nbsp;throws&nbsp;IOException,&nbsp;ServletException&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;long&nbsp;before&nbsp;=&nbsp;System.currentTimeMillis();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;chain.doFilter(request,&nbsp;response);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;long&nbsp;after&nbsp;=&nbsp;System.currentTimeMillis();<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;name&nbsp;=&nbsp;"";<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(request&nbsp;instanceof&nbsp;HttpServletRequest)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name&nbsp;=&nbsp;((HttpServletRequest)request).getRequestURI();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;config.getServletContext().log(name&nbsp;+&nbsp;":&nbsp;"&nbsp;+&nbsp;(after&nbsp;-&nbsp;before)&nbsp;+&nbsp;"ms");<BR><BR>&nbsp;&nbsp;}<BR><BR>}</TD></TR></TBODY></TABLE><BR>&nbsp;<BR><BR>当服务器调用init()时，过滤器用config变量来保存配置类的引用，这将在后面的doFilter()方法中被使用以更改ServletContext。当调用doFilter()时，过滤器计算请求发生到该请求执行完毕之间的时间。该过滤器很好的演示了请求之前和之后的处理。注意doFilter()方法的参数并不是HTTP对象，因此要调用HTTP专用的getRequestURI()方法时必须将request转化为HttpServletRequest类型。<BR><BR>&nbsp;<BR><BR>使用此过滤器，你还必须在web.xml文件中用&lt;filter&gt;标签部署它，见下：<BR><BR>&nbsp;&nbsp;&nbsp; 
<TABLE style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>
<TBODY>
<TR>
<TD style="WORD-WRAP: break-word">&nbsp;&lt;filter&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;filter-name&gt;timerFilter&lt;/filter-name&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;filter-class&gt;TimerFilter&lt;/filter-class&gt;<BR><BR>&lt;/filter&gt;</TD></TR></TBODY></TABLE><BR><BR>&nbsp;<BR><BR>这将通知服务器一个叫timerFiter的过滤器是从TimerFiter类实现的。你可以使用确定的URL模式或使用&lt;filter-mapping&gt;标签命名的servelt&nbsp;来注册一个过滤器，如：<BR><BR>
<TABLE style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>
<TBODY>
<TR>
<TD style="WORD-WRAP: break-word">&lt;filter-mapping&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;filter-name&gt;timerFilter&lt;/filter-name&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;url-pattern&gt;/*&lt;/url-pattern&gt;<BR><BR>&lt;/filter-mapping&gt;</TD></TR></TBODY></TABLE><BR><BR>&nbsp;<BR><BR>这种配置使过滤器操作所有对服务器的请求（静态或动态），正是我们需要的计时过滤器。如果你连接一个简单的页面，记录输出可能如下：<BR><BR>2001-05-25&nbsp;00:14:11&nbsp;/timer/index.html:&nbsp;10ms<BR><BR>&nbsp;<BR><BR>在Tomcat&nbsp;4.0&nbsp;beta&nbsp;5中，你可以在server_root/logs/下找到该记录文件。<BR><BR>&nbsp;<BR><BR>此过滤器的WAR文件从此下载：<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;<IMG src="http://www.matrix.org.cn/article/images/small/url.gif" align=absMiddle><A href="http://www.javaworld.com/jw-06-2001/Filters/timer.war" target=_blank>http://www.javaworld.com/jw-06-2001/Filters/timer.war</A><BR><BR>&nbsp;<BR><BR>谁在你的网站上？他们在做什么？<BR><BR>我们下一个过滤器是由OpenSymphony成员写的clickstream过滤器。这个过滤器跟踪用户请求（比如：点击）和请求队列（比如：点击流）以向网络管理员显示谁在她的网站上以及每个用户正在访问那个页面。这是个使用LGPL的开源库。<BR><BR>&nbsp;<BR><BR>在clickstream包中你将发现一个捕获请求信息的ClickstreamFilter类，一个像操作结构一样的Clickstream类以保存数据，以及一个保存会话和上下文事件的ClickstreamLogger类以将所有东西组合在一起。还有个BotChecker类用来确定客户端是否是一个机器人（简单的逻辑，像“他们是否是从robots.txt来的请求?”）。该包中提供了一个clickstreams.jsp摘要页面和一个viewstream.jsp详细页面来查看数据。<BR><BR>&nbsp;<BR><BR>我们先看ClickstreamFilter类。所有的这些例子都做了些轻微的修改以格式化并修改了些可移植性问题，这我将在后面将到。<BR>
<TABLE style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>
<TBODY>
<TR>
<TD style="WORD-WRAP: break-word"><BR>import&nbsp;java.io.IOException;<BR><BR>import&nbsp;javax.servlet.*;<BR><BR>import&nbsp;javax.servlet.http.*;<BR><BR>&nbsp;<BR><BR>public&nbsp;class&nbsp;ClickstreamFilter&nbsp;implements&nbsp;Filter&nbsp;{<BR><BR>&nbsp;&nbsp;protected&nbsp;FilterConfig&nbsp;filterConfig;<BR><BR>&nbsp;&nbsp;private&nbsp;final&nbsp;static&nbsp;String&nbsp;FILTER_APPLIED&nbsp;=&nbsp;"_clickstream_filter_applied";<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;void&nbsp;init(FilterConfig&nbsp;config)&nbsp;throws&nbsp;ServletException&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;this.filterConfig&nbsp;=&nbsp;filterConfig;<BR><BR>&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;void&nbsp;doFilter(ServletRequest&nbsp;request,&nbsp;ServletResponse&nbsp;response,<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FilterChain&nbsp;chain)&nbsp;throws&nbsp;IOException,&nbsp;ServletException&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;确保该过滤器在每次请求中只被使用一次<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(request.getAttribute(FILTER_APPLIED)&nbsp;==&nbsp;null)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request.setAttribute(FILTER_APPLIED,&nbsp;Boolean.TRUE);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpSession&nbsp;session&nbsp;=&nbsp;((HttpServletRequest)request).getSession();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Clickstream&nbsp;stream&nbsp;=&nbsp;(Clickstream)session.getAttribute("clickstream");<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stream.addRequest(((HttpServletRequest)request));<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;传递请求<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;chain.doFilter(request,&nbsp;response);<BR><BR>&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;void&nbsp;destroy()&nbsp;{&nbsp;}<BR><BR>}</TD></TR></TBODY></TABLE><BR><BR>&nbsp;<BR><BR>doFilter()方法取得用户的session，从中获取Clickstream，并将当前请求数据加到Clickstream中。其中使用了一个特殊的FILTER_APPLIED标记属性来标注此过滤器是否已经被当前请求使用（可能会在请求调度中发生）并且忽略所有其他的过滤器行为。你可能疑惑过滤器是怎么知道当前session中有clickstream属性。那是因为ClickstreamLogger在会话一开始时就已经设置了它。ClickstreamLogger代码：<BR><BR>
<TABLE style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>
<TBODY>
<TR>
<TD style="WORD-WRAP: break-word">import&nbsp;java.util.*;<BR><BR>import&nbsp;javax.servlet.*;<BR><BR>import&nbsp;javax.servlet.http.*;<BR><BR>&nbsp;<BR><BR>public&nbsp;class&nbsp;ClickstreamLogger&nbsp;implements&nbsp;ServletContextListener,<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpSessionListener&nbsp;{<BR><BR>&nbsp;&nbsp;Map&nbsp;clickstreams&nbsp;=&nbsp;new&nbsp;HashMap();<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;ClickstreamLogger()&nbsp;{&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;void&nbsp;contextInitialized(ServletContextEvent&nbsp;sce)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;sce.getServletContext().setAttribute("clickstreams",&nbsp;clickstreams);<BR><BR>&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;void&nbsp;contextDestroyed(ServletContextEvent&nbsp;sce)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;sce.getServletContext().setAttribute("clickstreams",&nbsp;null);<BR><BR>&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;void&nbsp;sessionCreated(HttpSessionEvent&nbsp;hse)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;HttpSession&nbsp;session&nbsp;=&nbsp;hse.getSession();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;Clickstream&nbsp;clickstream&nbsp;=&nbsp;new&nbsp;Clickstream();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;session.setAttribute("clickstream",&nbsp;clickstream);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;clickstreams.put(session.getId(),&nbsp;clickstream);<BR><BR>&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;void&nbsp;sessionDestroyed(HttpSessionEvent&nbsp;hse)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;HttpSession&nbsp;session&nbsp;=&nbsp;hse.getSession();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;Clickstream&nbsp;stream&nbsp;=&nbsp;(Clickstream)session.getAttribute("clickstream");<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;clickstreams.remove(session.getId());<BR><BR>&nbsp;&nbsp;}<BR><BR>}<BR><BR>&nbsp;</TD></TR></TBODY></TABLE><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger（记录器）获取应用事件并将使用他们将所有东西帮定在一起。当context创建中，logger在context中放置了一个共享的流map。这使得clickstream.jsp页面知道当前活动的是哪个流。而在context销毁中，logger则移除此map。当一个新访问者创建一个新的会话时，logger将一个新的Clickstream实例放入此会话中并将此Clickstream加入到中心流map中。在会话销毁时，由logger从中心map中移除这个流。<BR><BR>&nbsp;<BR><BR>下面的web.xml部署描述片段将所有东西写在一块：<BR><BR>&nbsp;&nbsp;&nbsp; 
<TABLE style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>
<TBODY>
<TR>
<TD style="WORD-WRAP: break-word">&nbsp;&lt;filter&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;filter-name&gt;clickstreamFilter&lt;/filter-name&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;filter-class&gt;ClickstreamFilter&lt;/filter-class&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/filter&gt;</TD></TR></TBODY></TABLE><BR><BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp; 
<TABLE style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>
<TBODY>
<TR>
<TD style="WORD-WRAP: break-word">&nbsp;&lt;filter-mapping&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;filter-name&gt;clickstreamFilter&lt;/filter-name&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/filter-mapping&gt;<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;filter-mapping&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;filter-name&gt;clickstreamFilter&lt;/filter-name&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;url-pattern&gt;*.html&lt;/url-pattern&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/filter-mapping&gt;<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;listener&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;listener-class&gt;ClickstreamLogger&lt;/listener-class&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/listener&gt;</TD></TR></TBODY></TABLE><BR>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;这注册了ClickstreamFilter并设置其处理*.jsp和*.html来的请求。这也将ClickstreamLogger注册为一个监听器以在应用事件发生时接受他们。<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;两个JSP页面从会话中取clickstream数据和context对象并使用HTML界面来显示当前状态。下面的clickstream.jsp文件显示了个大概：<BR><BR>
<TABLE style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>
<TBODY>
<TR>
<TD style="WORD-WRAP: break-word">&lt;%@&nbsp;page&nbsp;import="java.util.*"&nbsp;%&gt;<BR><BR>&lt;%@&nbsp;page&nbsp;import="Clickstream"&nbsp;%&gt;<BR><BR>&lt;%<BR><BR>Map&nbsp;clickstreams&nbsp;=&nbsp;(Map)application.getAttribute("clickstreams");<BR><BR>String&nbsp;showbots&nbsp;=&nbsp;"false";<BR><BR>&nbsp;<BR><BR>if&nbsp;(request.getParameter("showbots")&nbsp;!=&nbsp;null)&nbsp;{<BR><BR>&nbsp;&nbsp;if&nbsp;(request.getParameter("showbots").equals("true"))<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;showbots&nbsp;=&nbsp;"true";<BR><BR>&nbsp;&nbsp;else&nbsp;if&nbsp;(request.getParameter("showbots").equals("both"))<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;showbots&nbsp;=&nbsp;"both";<BR><BR>}<BR><BR>%&gt;<BR><BR>&nbsp;<BR><BR>&lt;font&nbsp;face="Verdana"&nbsp;size="-1"&gt;<BR><BR>&lt;h1&gt;All&nbsp;Clickstreams&lt;/h1&gt;<BR><BR>&nbsp;<BR><BR>&lt;a&nbsp;href="clickstreams.jsp?showbots=false"&gt;No&nbsp;Bots&lt;/a&gt;&nbsp;|<BR><BR>&lt;a&nbsp;href="clickstreams.jsp?showbots=true"&gt;All&nbsp;Bots&lt;/a&gt;&nbsp;|<BR><BR>&lt;a&nbsp;href="clickstreams.jsp?showbots=both"&gt;Both&lt;/a&gt;&nbsp;&lt;p&gt;<BR><BR>&nbsp;<BR><BR>&lt;%&nbsp;if&nbsp;(clickstreams.keySet().size()&nbsp;==&nbsp;0)&nbsp;{&nbsp;%&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;No&nbsp;clickstreams&nbsp;in&nbsp;progress<BR><BR>&lt;%&nbsp;}&nbsp;%&gt;<BR><BR>&nbsp;<BR><BR>&lt;%<BR><BR>Iterator&nbsp;it&nbsp;=&nbsp;clickstreams.keySet().iterator();<BR><BR>int&nbsp;count&nbsp;=&nbsp;0;<BR><BR>while&nbsp;(it.hasNext())&nbsp;{<BR><BR>&nbsp;&nbsp;String&nbsp;key&nbsp;=&nbsp;(String)it.next();<BR><BR>&nbsp;&nbsp;Clickstream&nbsp;stream&nbsp;=&nbsp;(Clickstream)clickstreams.get(key);<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;if&nbsp;(showbots.equals("false")&nbsp;&amp;&amp;&nbsp;stream.isBot())&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;continue;<BR><BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;else&nbsp;if&nbsp;(showbots.equals("true")&nbsp;&amp;&amp;&nbsp;!stream.isBot())&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;continue;<BR><BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;count++;<BR><BR>&nbsp;&nbsp;try&nbsp;{<BR><BR>%&gt;<BR><BR>&nbsp;<BR><BR>&lt;%=&nbsp;count&nbsp;%&gt;.&nbsp;<BR><BR>&lt;a&nbsp;href="viewstream.jsp?sid=&lt;%=&nbsp;key&nbsp;%&gt;"&gt;&lt;b&gt;<BR><BR>&lt;%=&nbsp;(stream.getHostname()&nbsp;!=&nbsp;null&nbsp;&amp;&amp;&nbsp;!stream.getHostname().equals("")&nbsp;?<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stream.getHostname()&nbsp;:&nbsp;"Stream")&nbsp;%&gt;<BR><BR>&lt;/b&gt;&lt;/a&gt;&nbsp;&lt;font&nbsp;size="-1"&gt;&lt;%=&nbsp;stream.getStream().size()&nbsp;%&gt;&nbsp;reqs&lt;/font&gt;&lt;br&gt;<BR><BR>&nbsp;<BR><BR>&lt;%<BR><BR>&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;catch&nbsp;(Exception&nbsp;e)&nbsp;{<BR><BR>%&gt;<BR><BR>&nbsp;&nbsp;An&nbsp;error&nbsp;occurred&nbsp;-&nbsp;&lt;%=&nbsp;e&nbsp;%&gt;&lt;br&gt;<BR><BR>&lt;%<BR><BR>&nbsp;&nbsp;}<BR><BR>}<BR><BR>%&gt;</TD></TR></TBODY></TABLE><BR><BR><BR>这个包很容易从OpenSymphony下载并安装。将Java文件编译并放在<BR><BR>WEB-INF/classes下，将JSP文件放到Web应用路径下，按帮助修改web.xml文件。为防止在这些工作前的争论，你可以从<BR><BR><IMG src="http://www.matrix.org.cn/article/images/small/url.gif" align=absMiddle><A href="http://www.javaworld.com/jw-06-2001/Filters/clickstream.war" target=_blank>http://www.javaworld.com/jw-06-2001/Filters/clickstream.war</A>处找到打好包的WAR文件。<BR><BR>&nbsp;<BR><BR>为能让此过滤器能在Tomcat&nbsp;4.0&nbsp;beta&nbsp;5下工作，我发现我不得不做一些轻微的改动。我做的改动显示了一些在servlet和过滤器的可移植性中通常容易犯的错误，所以我将他们列在下面：<BR><BR>·我不得不将在JSP中添加一个额外的导入语句：&lt;%@&nbsp;page&nbsp;import=”Clickstream”&nbsp;%&gt;。在Java中你并不需要导入在同一包下的类，而在服务器上JSP被编译到默认包中，你并不需要这句导入行。但在像Tomcat这样的服务器上，JSP被编译到一个自定义的包中，你不得不明确地从默认包中导入类。<BR><BR>·我不得不将&lt;listener&gt;元素移动到web.xml文件中的&lt;filter&gt;和&lt;filter-mapping&gt;元素之后，就像部署描述DTD要求的那样。并不是所有服务器对元素都要求固定的顺序。但Tomcat必须要。<BR><BR>·我不得不将web.xml中的映射由/*.html和/*.jsp改成正确的*.html和*.jsp。一些服务器会忽略开头的/，但Tomcat强硬的规定开头不能有/。<BR><BR>·最后，我得将ClickstreamFilter类升级到最新的生命周期API，将setFilterConfig()改成新的init()和destory()方法。<BR><BR>&nbsp;<BR><BR>可下载的WAR文件已经包含了这些修改并能通过服务器在包外运行，虽然我并没有广泛的进行测试。<BR><BR>&nbsp;<BR><BR>压缩响应<BR><BR>第三个过滤器是自动压缩响应输出流，以提高带宽利用率并提供一个很好的包装响应对象的示例。这个过滤器是由来自SUN的Amy&nbsp;Roh编写的，他为Tomcat&nbsp;4.0&nbsp;的“examples”Web程序做出过贡献。你将从webapps/examples/WEB-INF/classes/compressionFilters下找到原始代码。这里的例子代码以及WAR下的都已经为了更清晰和更简单而编辑过了。<BR><BR>&nbsp;<BR><BR>CompressionFilter类的策略是检查请求头以判定客户端是否支持压缩，如果支持，则将响应对象用自定义的响应来打包，它的getOutputStream()和getWriter()方法已经被定义为可以利用压缩过的输出流。使用过滤器允许如此简单而有效的解决问题。<BR><BR>&nbsp;<BR><BR>我们将从init()开始看代码：<BR><BR>&nbsp;&nbsp; 
<TABLE style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>
<TBODY>
<TR>
<TD style="WORD-WRAP: break-word">public&nbsp;void&nbsp;init(FilterConfig&nbsp;filterConfig)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;config&nbsp;=&nbsp;filterConfig;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;compressionThreshold&nbsp;=&nbsp;0;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(filterConfig&nbsp;!=&nbsp;null)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;str&nbsp;=&nbsp;filterConfig.getInitParameter("compressionThreshold");<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(str&nbsp;!=&nbsp;null)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;compressionThreshold&nbsp;=&nbsp;Integer.parseInt(str);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;compressionThreshold&nbsp;=&nbsp;0;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;}</TD></TR></TBODY></TABLE><BR>&nbsp;<BR><BR>注意在检索请求头前必须把request转化为HttpServletRequest，就想在第一个例子里那样。过滤器使用wrapper类CompressResponseWrapper，一个从<BR><BR>HttpServletResponseWrapper类继承下来的自定义类。这个wrapper的代码相对比较简单：<BR><BR>
<TABLE style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>
<TBODY>
<TR>
<TD style="WORD-WRAP: break-word">public&nbsp;class&nbsp;CompressionResponseWrapper&nbsp;extends&nbsp;HttpServletResponseWrapper&nbsp;{<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;protected&nbsp;ServletOutputStream&nbsp;stream&nbsp;=&nbsp;null;<BR><BR>&nbsp;&nbsp;protected&nbsp;PrintWriter&nbsp;writer&nbsp;=&nbsp;null;<BR><BR>&nbsp;&nbsp;protected&nbsp;int&nbsp;threshold&nbsp;=&nbsp;0;<BR><BR>&nbsp;&nbsp;protected&nbsp;HttpServletResponse&nbsp;origResponse&nbsp;=&nbsp;null;<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;CompressionResponseWrapper(HttpServletResponse&nbsp;response)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;super(response);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;origResponse&nbsp;=&nbsp;response;<BR><BR>&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;void&nbsp;setCompressionThreshold(int&nbsp;threshold)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;this.threshold&nbsp;=&nbsp;threshold;<BR><BR>&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;ServletOutputStream&nbsp;createOutputStream()&nbsp;throws&nbsp;IOException&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;(new&nbsp;CompressionResponseStream(origResponse));<BR><BR>&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;ServletOutputStream&nbsp;getOutputStream()&nbsp;throws&nbsp;IOException&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(writer&nbsp;!=&nbsp;null)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;IllegalStateException("getWriter()&nbsp;has&nbsp;already&nbsp;been&nbsp;"&nbsp;+<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"called&nbsp;for&nbsp;this&nbsp;response");<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(stream&nbsp;==&nbsp;null)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stream&nbsp;=&nbsp;createOutputStream();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;((CompressionResponseStream)&nbsp;stream).setCommit(true);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;((CompressionResponseStream)&nbsp;stream).setBuffer(threshold);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;stream;<BR><BR>&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;public&nbsp;PrintWriter&nbsp;getWriter()&nbsp;throws&nbsp;IOException&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(writer&nbsp;!=&nbsp;null)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;writer;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(stream&nbsp;!=&nbsp;null)&nbsp;{<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;new&nbsp;IllegalStateException("getOutputStream()&nbsp;has&nbsp;already&nbsp;"&nbsp;+<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"been&nbsp;called&nbsp;for&nbsp;this&nbsp;response");<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;stream&nbsp;=&nbsp;createOutputStream();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;((CompressionResponseStream)&nbsp;stream).setCommit(true);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;((CompressionResponseStream)&nbsp;stream).setBuffer(threshold);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;writer&nbsp;=&nbsp;new&nbsp;PrintWriter(stream);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;writer;<BR><BR>&nbsp;&nbsp;}<BR><BR>}</TD></TR></TBODY></TABLE><BR><BR>&nbsp;<BR><BR>所有调用getOutputStream()&nbsp;或者getWriter()都返回一个使用<BR><BR>CompressResponseStream类的对象。CompressionResponseStrteam类没有显示在这个例子中，因为它继承于ServletOutputStream并使用java.util.zip.GZIPOutputStream类来压缩流。<BR><BR>&nbsp;<BR><BR>Tomcat的”examples”Web程序中已经预先配置了这个压缩过滤器并加载了一个示例servlet。示例servlet响应/CompressionTestURL（确定先前的路径是/examples）。使用我制作的有用的WAR文件，你可以用/servlet/compressionTest(再次提醒，别忘了适当的前导路径)访问此测试servlet。你可以使用如下的web.xml片段来配置这个测试：<BR>
<TABLE style="TABLE-LAYOUT: fixed" cellSpacing=0 borderColorDark=#ffffff cellPadding=4 width="98%" align=center bgColor=#e6e6e6 borderColorLight=#009ace border=1>
<TBODY>
<TR>
<TD style="WORD-WRAP: break-word"><BR>&lt;filter&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;filter-name&gt;compressionFilter&lt;/filter-name&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;filter-class&gt;CompressionFilter&lt;/filter-class&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;init-param&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-name&gt;compressionThreshold&lt;/param-name&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;param-value&gt;10&lt;/param-value&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/init-param&gt;<BR><BR>&lt;/filter&gt;<BR><BR>&nbsp;<BR><BR>&lt;filter-mapping&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;filter-name&gt;compressionFilter&lt;/filter-name&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-name&gt;compressionTest&lt;/servlet-name&gt;<BR><BR>&lt;/filter-mapping&gt;<BR><BR>&nbsp;<BR><BR>&lt;servlet&gt;<BR><BR>&nbsp;&nbsp;&lt;servlet-name&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;compressionTest<BR><BR>&nbsp;&nbsp;&lt;/servlet-name&gt;<BR><BR>&nbsp;&nbsp;&lt;servlet-class&gt;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;CompressionTestServlet<BR><BR>&nbsp;&nbsp;&lt;/servlet-class&gt;<BR><BR>&lt;/servlet&gt;</TD></TR></TBODY></TABLE><BR>&nbsp;<BR><BR>CompressionTestServlet（这里没有显示）输出压缩是否可用，如果可用，则输出压缩响应成功！&nbsp;<BR>&nbsp;<BR><BR>&nbsp;<BR><BR>//~~~~~~~~~~CSDN这里到底有多少字数限制？<BR><BR>请各位斧正了：）<BR><BR>来自:csdn<img src ="http://www.blogjava.net/kapok/aggbug/3334.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-04-15 23:50 <a href="http://www.blogjava.net/kapok/archive/2005/04/15/3334.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>struts-menu</title><link>http://www.blogjava.net/kapok/archive/2005/04/08/3003.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Fri, 08 Apr 2005 08:46:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/04/08/3003.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/3003.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/04/08/3003.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/3003.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/3003.html</trackback:ping><description><![CDATA[<A href="http://struts-menu.sourceforge.net/">http://struts-menu.sourceforge.net/</A><BR><BR><A href="https://appfuse.dev.java.net/">https://appfuse.dev.java.net/</A><img src ="http://www.blogjava.net/kapok/aggbug/3003.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-04-08 16:46 <a href="http://www.blogjava.net/kapok/archive/2005/04/08/3003.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WebWork开工大吉</title><link>http://www.blogjava.net/kapok/archive/2005/03/27/2500.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sun, 27 Mar 2005 05:18:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/27/2500.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/2500.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/27/2500.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/2500.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/2500.html</trackback:ping><description><![CDATA[<P>今天开始开工.<BR>在webwork.property中加入<BR>webwork.configuration.xml.reload=true之后,原来正常的action突然间不能正确执行, 后来发现原来是定义该action的xml还处于打开状态的缘故(也可能是其他原因?), 可能重建config对象的时候必须获得对该文件的独享访问??<BR><BR>PS:刚刚做了一次重新测试,以上原因排除,为什么尚未找到,反正现在好了.<BR><BR>2.<BR><BR>&lt;action name="hello" class="helloworld.HelloWorldAction"&gt;<BR>&nbsp;&nbsp;&nbsp;&lt;result name="success" type="dispatcher"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param name="location"&gt;/greetings.jsp&lt;/param&gt;<BR>&nbsp;&nbsp;&nbsp;&lt;/result&gt;<BR>&nbsp;&nbsp;&lt;/action&gt;<BR><BR>这段代码有另外一种调用方式:<BR><A href="http://localhost:8080/webwork_test/hello/hello!noHello.action">http://localhost:8080/webwork_test/hello/hello!noHello.action</A><BR><BR>这样就是调用hello这个action指定类的noHello方法了.<BR>如果要带参数的话,还没找到如何配置.呵呵.<BR><BR>3.<BR>明确指定调用那个方法的配置文件写法:<BR><BR>&lt;!-- action指定了调用action哪个方法--&gt;<BR>&nbsp;&nbsp;&lt;action name="nohello" class="helloworld.HelloWorldAction" method="noHello"&gt;<BR>&nbsp;&nbsp;&nbsp;&lt;result name="success" type="dispatcher"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&lt;param name="location"&gt;/greetings.jsp&lt;/param&gt;<BR>&nbsp;&nbsp;&nbsp;&lt;/result&gt;<BR>&nbsp;&nbsp;&lt;/action&gt;<BR><BR><BR>4.<BR>指定全局的资源文件方法:<BR><A href="http://wiki.opensymphony.com/display/WW/webwork.properties">http://wiki.opensymphony.com/display/WW/webwork.properties</A><BR>### Load custom <SPAN class=java-keyword>default</SPAN> resource bundles<BR>#webwork.custom.i18n.resources=testmessages,testmessages2<BR><BR>如果ApplicationResources的资源文件放在src/resources文件夹里面,在页面上面写<BR>&lt;ww:i18n name="'resources.ApplicationResources'"&gt;<BR>&nbsp;&lt;ww:text name="'delete'"&gt;<BR>&nbsp;&lt;/ww:text&gt;<BR>&lt;/ww:i18n&gt;<BR>可以正确的找到中文字符集. 如果写成&lt;ww:i18n name="ApplicationResources'"&gt;,则会提示:&nbsp; Could not find the bundle 'ApplicationResources'.以上操作并没有设置webwork.custom.i18n.resources属性.如果指定webwork.custom.i18n.resources=resources.ApplicationResources而不指定i18n标签的name是不允许的.i18n标签的name属性是必须的选项,并且要写全路径.<BR><BR>但是如果仅仅写&lt;ww:text name="'add'"/&gt;页面上仅仅是显示add, 不会显示对应的中文. 原因正在找.<BR>(这个问题在tomcat重新启动之后消失,在资源文件中配置了webwork.custom.i18n.resources=resources.ApplicationResources属性之后,页面上的&lt;ww:text name="'add'"/&gt;标签生效了.注意配置属性的时候前后左右不要写多余的空格).<BR><BR>另外一个国际化的参考链接:<BR><A href="http://wiki.opensymphony.com/display/WW/Internationalization">http://wiki.opensymphony.com/display/WW/Internationalization</A><BR>基本上涉及到资源文件的修改都需要重新启动服务器.<BR>如果不想重启的话可以参考这个链接:<BR><A href="http://forum.javaeye.com/viewtopic.php?t=6417&amp;highlight=locale">http://forum.javaeye.com/viewtopic.php?t=6417&amp;highlight=locale</A><BR><BR>5.<BR>上面的试验表示多语言的第一步是成功的,接下来的问题是中文显示成?.以及手工控制多语言的问题.<BR><BR>webwork.i18n.encoding=UTF-8<BR>测试页面中的文字如下:<BR><BR>中国<BR>&lt;p&gt;&lt;/p&gt;<BR>&lt;ww:text name="'add'"/&gt;<BR><BR>在这个页面里面没有指定页面的编码方式,只是页面本身的编码方式在IDE中指定,当在ie中选择UTF-8编码时,中文"中国"显示正常.<BR>(原来把页面设置成IDE里面的UFT-8,结果如果用myeclipse的jsp编辑器打开,默认时ISO编码).<BR><BR>&lt;%@ page contentType='text/html;charset=UTF-8'%&gt;<BR>&nbsp;&lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&gt;<BR>页面里面添加以上两句,把jsp的文件属性设置成UTF-8,再用myeclipse打开该jsp文件输入中文"中国",这个时候可以正确保存中文,浏览器可以正确的选择UFT-8编码.另外从ApplicationResources文件中选择出来的中文显示也正常了(该资源文件为UFT-8编码,利用Java ResourceBuddle Editor插件编辑).<BR><BR>中文问题解决,没有使用filter. 注意: 我的webwork版本为2.1.7.有些老版本的估计还是得用filter来处理.以后有空可以试一下.<BR><BR>6.<BR>继续上面的试验, 所有条件不变,在helloworld.HelloWorldAction所在包中新建与该action名字想同的资源文件,本例中叫做HelloWorldAction_en.properties和HelloWorld_zh_CN.properties,然后在里面加两个跟全局resource相同key的资源文件,页面上会优先从这里来取国际化文字,也就是说即使指定了全局resource文件,如果在action执行的包内存在同名的key也会覆盖同名全局资源文件中的key定义.<BR>同样,如果虽然action包中存在资源文件,但是没有定义请求的key的国际化文字,则会到全局资源文件里面进行查找.<BR><BR>结论: webwork可以支持很好的全局资源文件, 并且每个action可以根据需要覆盖某些key的定义. 在资源文件的搜索路径中是action的资源文件优先,其次是全局资源文件<BR><BR>原文<BR><A href="http://wiki.opensymphony.com/display/WW/Internationalization">http://wiki.opensymphony.com/display/WW/Internationalization</A><BR>WebWork supports internationalization (in short, i18n) in two different places: the UI tags and the action/field error messages.</P>
<P>UI Tags <BR>Validation Examples </P>
<P>Resource bundles are searched in the following order:</P>
<P>1.) ActionClass.properties <BR>2.)BaseClass.properties (all the way to Object.properties) <BR>3.) Interface.properties (every interface and sub-interface) <BR>4.) package.properties (every of every base class, all the way to java/lang/package.properties)<BR><BR>7.<BR><A href="http://forum.javaeye.com/viewtopic.php?t=11073&amp;start=0">http://forum.javaeye.com/viewtopic.php?t=11073&amp;start=0</A><BR><A href="http://www.opensymphony.com/webwork/wikidocs/Transparent%20web-app%20I18N.html">http://www.opensymphony.com/webwork/wikidocs/Transparent%20web-app%20I18N.html</A><BR><BR><A href="http://localhost:8080/webwork_test/hello/hello.action?set_locale=zh_CN">http://localhost:8080/webwork_test/hello/hello.action?set_locale=zh_CN</A><BR><A href="http://localhost:8080/webwork_test/hello/hello.action?set_locale=en_US">http://localhost:8080/webwork_test/hello/hello.action?set_locale=en_US</A><BR><BR>测试通过,自定义语种问题解决.<BR><BR><BR>相关链接:<BR><A href="http://forum.javaeye.com/viewtopic.php?t=11073&amp;start=0">http://forum.javaeye.com/viewtopic.php?t=11073&amp;start=0</A><BR><BR><BR><BR>8.<BR>今天发现5.8.4版本的myeclipse的tag属性面板挺好用的.good.<BR><BR><BR>9.<BR>有关webwork的javascript验证能力参看以下帖子:<BR><A href="http://www.hibernate.org.cn/viewtopic.php?t=9126&amp;postdays=0&amp;postorder=asc&amp;start=0">http://www.hibernate.org.cn/viewtopic.php?t=9126&amp;postdays=0&amp;postorder=asc&amp;start=0</A><BR><BR><A href="http://forum.javaeye.com/allbloglist.php?page=15">http://forum.javaeye.com/allbloglist.php?page=15</A><BR><BR><A href="http://scud.blogdriver.com/scud/index.html">http://scud.blogdriver.com/scud/index.html</A></P><img src ="http://www.blogjava.net/kapok/aggbug/2500.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-27 13:18 <a href="http://www.blogjava.net/kapok/archive/2005/03/27/2500.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>AppFuse中集成ValueList的问题</title><link>http://www.blogjava.net/kapok/archive/2005/03/18/2211.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Fri, 18 Mar 2005 15:40:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/18/2211.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/2211.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/18/2211.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/2211.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/2211.html</trackback:ping><description><![CDATA[<A href="http://rabbit8.blogchina.com/blog/article_144619.1140620.html">http://rabbit8.blogchina.com/blog/article_144619.1140620.html</A><BR><BR>如果按照我上一篇笔记所说的方法，可以在Tocmat下集成ValueList，但使用AppFuse的ant setup会有问题，我今天找到了原因，解决方法见内 
<P>在执行 ant setup的时候，在compile-module的时候，总是说找不到valuelist的类，后来发现AppFuse的jar都添加到了builde.xml中，于是我也把valuelist.jar添加过去了，发现问题解决了，方法如下：</P>
<P>AppFuse执行Setup后自动将valuelist.jar复制到Tomcat下：</P>
<P>在appfuse\lib\lib.properties中添加如下内容：</P>
<P>#<BR># ValueList<BR>#<BR>valuelist.version=0.1.7<BR>valuelist.dir=${lib.dir}<BR>valuelist.jar=${valuelist.dir}/valuelist.jar</P>
<P><BR>将properties.xml中添加：<BR>&lt;!-- Web --&gt;<BR>&lt;path id="web.compile.classpath"&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${dist.dir}/${webapp.name}-dao.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${dist.dir}/${webapp.name}-service.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${struts.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${strutsmenu.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${displaytag.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${jakarta-oro.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${commons-digester.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${commons-logging.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${commons-beanutils.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${commons-collections.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${commons-fileupload.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${commons-lang.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${commons-validator.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${servletapi.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;pathelement location="${valuelist.jar}"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //这是我添加的<BR>&nbsp;&nbsp;&nbsp; &lt;fileset dir="${javamail.dir}" includes="*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;fileset dir="${spring.dir}" includes="*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;fileset dir="${jstl.dir}/lib" includes="jstl.jar"/&gt;<BR>&lt;/path&gt;</P>
<P>在builde.xml中添加：<BR>&lt;war destfile="${webapp.dist}/${webapp.war}"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; webxml="${webapp.target}/WEB-INF/web.xml" compress="true"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fileset dir="${webapp.target}" excludes="**/web.xml,**/*-resources.xml"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;metainf dir="${webapp.dist}" includes="context.xml"/&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;classes dir="${build.dir}/web/classes"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;exclude name="**/database.properties"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/classes&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${dist.dir}/${webapp.name}-dao.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${dist.dir}/${webapp.name}-service.jar"/&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;webinf dir="${struts.dir}" includes="*.xml"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;webinf dir="web/WEB-INF" includes="*-resources.xml"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${clickstream.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib dir="${struts.dir}" includes="*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib dir="${jstl.dir}/lib"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="jstl.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="standard.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/lib&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib dir="${javamail.dir}" includes="*.jar"/&gt;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${log4j.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${strutsmenu.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${valuelist.jar}"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //这是我添加的内容<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib dir="${displaytag.dir}" includes="*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${hibernate.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib dir="${hibernate.dir}/lib"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="odmg*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="dom4j*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="cglib*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="ehcache*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;include name="oscache*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/lib&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib dir="${spring.dir}" includes="*.jar"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${sitemesh.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib dir="${velocity.dir}" includes="*.jar"/&gt;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;lib file="${urlrewrite.jar}"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/war&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/target&gt;</P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 兔八哥</P><img src ="http://www.blogjava.net/kapok/aggbug/2211.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-18 23:40 <a href="http://www.blogjava.net/kapok/archive/2005/03/18/2211.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Aceqi</title><link>http://www.blogjava.net/kapok/archive/2005/03/18/2190.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Fri, 18 Mar 2005 05:41:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/18/2190.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/2190.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/18/2190.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/2190.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/2190.html</trackback:ping><description><![CDATA[<A href="http://raibledesigns.com/wiki/Wiki.jsp?page=AppFuseSecurity">http://raibledesigns.com/wiki/Wiki.jsp?page=AppFuseSecurity</A><img src ="http://www.blogjava.net/kapok/aggbug/2190.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-18 13:41 <a href="http://www.blogjava.net/kapok/archive/2005/03/18/2190.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>再谈URLEncoder</title><link>http://www.blogjava.net/kapok/archive/2005/03/09/1864.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 08 Mar 2005 16:44:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/09/1864.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/1864.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/09/1864.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/1864.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/1864.html</trackback:ping><description><![CDATA[<TABLE width="96%" align=center>
<TBODY>
<TR>
<TD align=middle><SPAN class=article_title><A href="http://rabbit8.blogchina.com/blog/article_144619.859489.html">http://rabbit8.blogchina.com/blog/article_144619.859489.html</A><BR>再谈URLEncoder</SPAN></TD></TR>
<TR>
<TD align=right>2005年 02月16日</TD></TR>
<TR>
<TD class=article_content>有个朋友说在百度上提交的数据进行编码后不是我说的那样，我试了一下，找到原因如下 
<P><STRONG>关于URLEncoder的解析问题&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </STRONG></P>
<P><STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </STRONG>在<A href="http://rabbit8.blogchina.com/blog/article_144619.789425.html" rel=nofollow><FONT color=#002c99>http://rabbit8.blogchina.com/blog/article_144619.789425.html</FONT></A>后，有个朋友留言，说在百度试验的结果和我文章中说的不一致，我做了个实验，证实JDK的帮助没错，原因如下：</P>
<P><BR>我的试验代码如下：<BR><STRONG>public</STRONG> <STRONG>static</STRONG> <STRONG>void</STRONG> main(String[] args) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; URLEncoder urle = <STRONG>null</STRONG>;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //得到默认：%A8%B9<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("默认：" +&nbsp;&nbsp; urle.encode("<FONT size=+0>ü</FONT>"));&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <STRONG>try</STRONG> {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //得到GBK：%A8%B9<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("GBK：" +&nbsp; urle.encode("<FONT size=+0>ü</FONT>", "GBK"));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <STRONG>catch</STRONG> (UnsupportedEncodingException e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <STRONG>try</STRONG> {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //得到UTF-8：%C3%BC<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("UTF-8：" +&nbsp; urle.encode("<FONT size=+0>ü</FONT>", "UTF-8"));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <STRONG>catch</STRONG> (UnsupportedEncodingException e) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp; }<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果用UltraEdit来查看"<FONT size=+0>ü</FONT>"的ASCII的话，得到的结果如图：<BR>&nbsp;<IMG src="http://blog.blogchina.com/upload/2005-02-16/2005021608542558587.png" border=0><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可见，UltraEdit使用的是操作系统默认的编码方式(实际上，MS采用的也不是GBK，而是另一种编码，但效果和GBK差不多)，所以它显示的ASCII的编码为A8 B9,就是第一和第二种情况的结果。而第三种情况才是JDK帮助中所声明的情况。<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我查看了百度，提交了一下，结果和我预期的是一样的！</P>
<P><IMG src="http://blog.blogchina.com/upload/2005-02-16/2005021608531794233.png" border=0></P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果你查看页面的源文件，会看到百度的charset为gb2312,而帮助中明确提到例子使用的是<STRONG>UTF-8</STRONG>编码，所以出现了不一致的问题，也正是因为这个原因，所以JDK中决定要废弃<STRONG>public static String</STRONG> encode(<STRONG>String</STRONG>&nbsp;s)方法，因为这个方法的编码的字符集依赖于程序运行的系统的默认的字符集！<BR>&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 兔八哥<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;2005-2-15 17：41<BR>&nbsp;<BR></P></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/1864.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-09 00:44 <a href="http://www.blogjava.net/kapok/archive/2005/03/09/1864.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DisplayTag、Tomcat中编码的问题</title><link>http://www.blogjava.net/kapok/archive/2005/03/09/1863.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 08 Mar 2005 16:41:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/09/1863.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/1863.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/09/1863.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/1863.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/1863.html</trackback:ping><description><![CDATA[<TABLE width="96%" align=center>
<TBODY>
<TR>
<TD align=middle><SPAN class=article_title>DisplayTag、Tomcat中编码的问题</SPAN></TD></TR>
<TR>
<TD align=right>2005年 02月04日</TD></TR>
<TR>
<TD class=article_content>对DisplayTag、Tomcat中编码问题的总结 
<P align=center>关于<FONT size=+0>AppFuse</FONT>中使用中文编码的问题</P><FONT size=+0>&nbsp;<BR></FONT>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这<FONT size=+0>2</FONT>个月一直被<FONT size=+0>DisplayTag</FONT>折腾，如果<FONT size=+0>DisplayTag</FONT>的链接或者排序的列中有汉字，那么排序或者翻页都会查询不到结果或报错（如果你处理的好，可能会显示没有可以显示的数据）。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我一直以为这个问题是因为<FONT size=+0>DisplayTag</FONT>的开发者们没有考虑到中文的问题，因为项目进度很紧，所以就把排序的功能给屏蔽了，虽然经过了大量的测试，但都没有发现翻页时候也存在这个问题（<FONT size=+0>AppFuse</FONT>自动生成的页面的默认显示<FONT size=+0>25</FONT>行，如果超过<FONT size=+0>25</FONT>行会自动分页），直到前天，测试人员才发现这个问题，按照我的理解要修改<FONT size=+0>DisplayTag</FONT>的源码，后来又想通过自己写过滤器试试，幸运的是在我动手干之前，先和倦兔聊了几句，结果倦兔说他也遇到过这个问题，指出如果在<FONT size=+0>server.xml</FONT>中添加<FONT size=+0>URIEncoding="GBK"</FONT>就可以解决问题，我试了一下，果然可以，但这是为什么呢？</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在<FONT size=+0>AppFuse</FONT>中有一个<FONT size=+0>Spring</FONT>的过滤器，对请求的编码进行转换，这种做法很通用，即使是在以前，我们自己写这个过滤器也不麻烦，而且这个代码在网上随处可见，难道是<FONT size=+0>Spring</FONT>的过滤器没有起作用吗<FONT size=+0>?</FONT>也不是，如果没起作用，那么我在<FONT size=+0>Struts</FONT>中的<FONT size=+0>Action</FONT>中通过<FONT size=+0>request.getParmeter</FONT>接收到参数应该是乱码？而且我记得这种做法在<FONT size=+0>Tomcat4.1.24</FONT>中没有任何问题，那是为什么？和倦兔讨论了一下，我们都没有找到答案。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 于是，我就通过<FONT size=+0>google</FONT>搜索"<FONT size=+0>URIEncoding</FONT>"<FONT size=+0>,</FONT>找到一堆文章，我读了几篇文章，其中有一篇提到：<FONT size=+0>Tomcat5</FONT>中对<FONT size=+0>Post</FONT>和<FONT size=+0>Get</FONT>请求不再采用相同的处理策略了，而在<FONT size=+0>Tomcat4</FONT>中采用的处理策略是相同的。于是我想："我的提交的请求明明是<FONT size=+0>Post</FONT>，我又没有用<FONT size=+0>Get</FONT>请求，那<FONT size=+0>Spring</FONT>的过滤器就应该起作用，为什么结果会这样呢？"，我的一个<FONT size=+0>jsp</FONT>中的<FONT size=+0>Form</FONT>的代码如下：</P>
<P>&nbsp;&nbsp;&nbsp; &lt;<STRONG>html</STRONG>:<STRONG>form</STRONG> action="editGj" method="post" styleId="gjForm"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; focus="mc" onsubmit="return validateGjForm(this)"&gt;<BR>&nbsp;&nbsp;&nbsp; 看，我的代码明明是使用的是Post方法，为什么结果不正确呢？<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我的跳转到下一页的代码是：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;<STRONG>a href</STRONG>="?d-449687-p=2&amp;<STRONG>mc=%D7%A8%D2%B5&amp;</STRONG>;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; excelname=%D7%A8%D2%B5%C3%FB%B3%C6.xls"&gt;下一页&lt;<STRONG>/a</STRONG>&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 加粗的部分就是我的翻页的链接。如果你立刻就看出了我的问题，那么你就可以不用往下看了，呵呵。如果你没有看出问题，请继续，Come On Baby！！！<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我采用的真的是Post方法吗？我的form的提交采用的是Post方法，这是没错的，那么链接<A>采用的是Post方法吗？</A></P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <STRONG>不是！！！！它采用的是Get方法进行提交的，这就是问题的答案了!<BR></STRONG><STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Post和Get提交有什么区别吗？</STRONG></P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 简单的说：Post是将地址传送一次，将form的数据单独提交，而Get则是将地址和参数一起传送，传送的不止是form的数据，但传送的数据的长度有字节限制，另外还有安全问题。如果感兴趣，你可以用google搜一下，会找到很多资料。</P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当我想到这个区别时，我又检查了一下我的显示列表的页面，发现这个页面没有form，只有<A>，这说明我的提交的确是Get的，于是，Tomcat5对我的Get请求执行的是另外的处理方式，和Post的处理方式不再一样了！<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Tomcat5对Get请求是怎么处理的？<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 默认情况下，Tomcat对请求采用的默认编码是ISO-8859-1，我们的JSP页面上声明的<BR><!--page language="java" errorPage="/error.jsp" <br />pageEncoding="GBK" <br />contentType="text/html;charset=GBK"--></A><A><!--/span><span> page language=</span><span>"java"</span><span> errorPage=</span><span>"/error.jsp"</span><span> <p>&nbsp;</p></span><p style="margin: 0cm 0cm 0pt; text-indent: 21pt; text-align: left" align="left"><span>pageEncoding=</span><span>"GBK"</span><span> </span><span>contentType=</span><span>"text/html;charset=GBK"</span><span> </span><span--><BR>和<BR>&nbsp;&nbsp;&nbsp;&nbsp; &lt;<STRONG>meta</STRONG> http-equiv="Content-Type" content="text/html; charset=GBK"/&gt;<BR>只在显示汉字时有用，而且这2种写法的作用在理论上应该时一致的，但是由于各个软件厂商对Web标准都按照自己的理解进行了修改，所以，常常这2种写法作用就不一致了，使我们在开发Web程序时备感混乱。如果你想了解详情，请查看《网站重构》一书。 
<P>&nbsp;</P>
<P style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 21pt; TEXT-ALIGN: left" align=left>这样我们提交的汉字被认为是ISO-8859-1的编码，所以在程序中接收时显示乱码，如果使用过滤器，在过滤器中调用<FONT size=+0>request.setCharacterEncoding("GBK")</FONT>的话，那么<FONT size=+0>Post</FONT>上来的汉字将被认为是<FONT size=+0>GBK</FONT>编码，而<FONT size=+0>Tomcat5</FONT>对于<FONT size=+0>Get</FONT>请求上来的编码并不根据过滤器的设定辨认编码方式，默认的依然是<FONT size=+0>ISO-8859-1</FONT>，<FONT size=+0>Tomcat5</FONT>中处理<FONT size=+0>Get</FONT>请求的源代码如下：<BR><BR><FONT size=+0>&nbsp;<IMG alt=tomcat5中处理Get请求的函数 src="http://blog.blogchina.com/upload/2005-02-04/20050204141356862294.png" border=0><BR></FONT></P>
<P align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上面的代码中，<FONT size=+0>enc</FONT>代表<FONT size=+0>Tomcat5</FONT>中<FONT size=+0>server.xml</FONT>中<FONT size=+0>Connector</FONT>部分的<FONT size=+0>URIEnocodeing</FONT>项设定的值，可以看出，如果没有设定<FONT size=+0>URIEncoding</FONT>项的话，那么<FONT size=+0>Tomcat5</FONT>也并没有使用默认的<FONT size=+0>ISO-8859-1</FONT>编码，而是一段<FONT size=+0>fast conversion</FONT>，所以，即使你的页面使用默认的编码方式进行编码，然后使用<FONT size=+0>ISO-8859-1</FONT>进行解码，得到的结果也不对，这可能是<FONT size=+0>Tomcat5</FONT>的一个<FONT size=+0>Bug</FONT>。如果想绕过这个<FONT size=+0>Bug</FONT>，那么只有在<FONT size=+0>server.xml</FONT>的<FONT size=+0>Connector</FONT>部分设定<FONT size=+0>URIEncoding</FONT>的值，根据编码方式指定自己的值，在我的程序中我使用的是<FONT size=+0>GBK</FONT>，所以我修改后的部分如下：</P>&nbsp;&nbsp;&nbsp; acceptCount="100"<BR>&nbsp;&nbsp;&nbsp; connectionTimeout="20000"<BR>&nbsp;&nbsp;&nbsp; disableUploadTimeout="true"<BR>&nbsp;&nbsp;&nbsp; port="80"<BR>&nbsp;&nbsp;&nbsp; redirectPort="8443"<BR>&nbsp;&nbsp;&nbsp; enableLookups="false"<BR>&nbsp;&nbsp;&nbsp; minSpareThreads="25"<BR>&nbsp;&nbsp;&nbsp; maxSpareThreads="75"<BR>&nbsp;&nbsp;&nbsp; maxThreads="150"<BR>&nbsp;&nbsp;&nbsp; maxPostSize="0"<BR>&nbsp;&nbsp;&nbsp; URIEncoding="GBK"&gt;<BR>
<P align=left>&nbsp;</P><FONT size=+0>&nbsp;<BR></FONT>
<P align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个问题我参考了这篇文章：</P>
<P align=left><A href="http://www.javaworld.com.tw/jute/post/view?age=0&amp;bid=9&amp;ppg=1&amp;sty=1&amp;id=44042&amp;tpg=1" rel=nofollow><FONT color=#002c99 size=+0>http://www.javaworld.com.tw/jute/post/view?age=0&amp;bid=9&amp;ppg=1&amp;sty=1&amp;id=44042&amp;tpg=1</FONT></A></P></A><A href="http://www.javaworld.com.tw/jute/post/view?age=0&amp;bid=9&amp;ppg=1&amp;sty=1&amp;id=44042&amp;tpg=1" rel=nofollow><FONT color=#002c99 size=+0>http://www.javaworld.com.tw/jute/post/view?age=0&amp;bid=9&amp;ppg=1&amp;sty=1&amp;id=44042&amp;tpg=1</FONT></A><FONT size=+0><BR></FONT>
<P align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 另外，对于编码解码的问题，我还有一句要说，理论上，如果编码的<FONT size=+0>charset</FONT>和解码的<FONT size=+0>charset</FONT>是一致的话，那么就应该没有乱码的问题，但是，对于某些特殊的字符来说，如果采用的<FONT size=+0>charset</FONT>不对，则可能在解码的时候不能显示，所以要选择好字符集，我推荐在处理简体汉字的时候使用<FONT size=+0>GBK</FONT>。网上也有推荐<FONT size=+0>UTF-8</FONT>的，具体使用哪种依情况而定。</P><FONT size=+0>&nbsp;<BR></FONT>
<P align=left><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT>以上是我的总结，若有错误，请指正，谢谢！</P><FONT size=+0>&nbsp;<BR></FONT>
<P align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 明天，我就放假了，祝我的朋友们春节快乐，万事胜意！</P><FONT size=+0>&nbsp;<BR></FONT>
<P align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 兔八哥</P>
<P align=left><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2005</FONT>年<FONT size=+0>2</FONT>月<FONT size=+0>4</FONT>日<FONT size=+0>14:04</FONT></P><FONT size=+0>&nbsp;<BR></FONT><FONT size=+0>&nbsp;<BR></FONT>
<P align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 另外，我昨晚看了看<FONT size=+0>Tomcat5</FONT>自带的<FONT size=+0>Servlet Example</FONT>中过滤器的实现，其中就有关于处理编码的过滤器<FONT size=+0>SetCharacterEncodingFilter</FONT>，还有另外一个是处理用户输入的过滤器，叫<FONT size=+0>HTMLFilter</FONT>，用于将用户输入的字符转化为<FONT size=+0>HTML</FONT>格式的字符，如将"<FONT size=+0>&gt;</FONT>"转换为"<FONT size=+0>&gt;</FONT>"，这个过滤器稍加修改就可以用在自己的项目上了，呵呵！</P></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/1863.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-09 00:41 <a href="http://www.blogjava.net/kapok/archive/2005/03/09/1863.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在AppFuse中集成ValueList的方法</title><link>http://www.blogjava.net/kapok/archive/2005/03/09/1860.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 08 Mar 2005 16:29:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/09/1860.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/1860.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/09/1860.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/1860.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/1860.html</trackback:ping><description><![CDATA[<TABLE width="96%" align=center>
<TBODY>
<TR>
<TD align=middle><SPAN class=article_title><A href="http://rabbit8.blogchina.com/blog/article_144619.1057775.html">http://rabbit8.blogchina.com/blog/article_144619.1057775.html</A><BR>在AppFuse中集成ValueList的方法</SPAN></TD></TR>
<TR>
<TD align=right>2005年 03月08日</TD></TR>
<TR>
<TD class=article_content>怎样在AppFuse中集成ValueList 
<P>1.在web.xml中添加,/WEB-INF/classes/standardJspApplicationContext.xml。<BR>&lt;context-param&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;/WEB-INF/applicationContext-*.xml,/WEB-INF/classes/standardJspApplicationContext.xml&lt;/param-value&gt;<BR>&lt;/context-param&gt;<BR>&nbsp;&nbsp;&nbsp; <BR>2.把valuelist.tld拷贝到WEB-INF下。<BR>3.在taglibs.jsp中添加valuelist.tld。<BR>4.将standardJspApplicationContext.xml、i18n.properties、microsoftLook.properties、simpleLook.properties、classicLook.properties文件复制到/WEB－INF/classes下。<BR>5.在applicationContext-resources.xml的&lt;beans&gt;&lt;/beans&gt;中添加：<BR>&nbsp; &lt;bean id="resourceI18nBundle" class="org.springframework.context.support.ResourceBundleMessageSource"&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;property name="basename"&gt;&lt;value&gt;i18n&lt;/value&gt;&lt;/property&gt;<BR>&nbsp; &lt;/bean&gt;<BR>&nbsp; 和<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp; &lt;bean id="valueListHandler" singleton="true" <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="net.mlw.vlh.DefaultValueListHandlerImpl"&gt;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; &lt;property name="config.adapters"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;map&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;entry key="userlist"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean class="net.mlw.vlh.adapter.hibernate.Hibernate20Adapter"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="sessionFactory"&gt;&lt;ref bean="sessionFactory"/&gt;&lt;/property&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="defaultNumberPerPage"&gt;&lt;value&gt;2&lt;/value&gt;&lt;/property&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="defaultSortColumn"&gt;&lt;value&gt;lastName&lt;/value&gt;&lt;/property&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="defaultSortDirection"&gt;&lt;value&gt;asc&lt;/value&gt;&lt;/property&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="hsql"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FROM org.appfuse.model.User AS vo<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /~name: WHERE vo.lastName LIKE {name} ~/<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /~sortColumn: ORDER BY vo.[sortColumn] [sortDirection]~/<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/value&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/entry&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!--entry key="players2"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean class="net.mlw.vlh.adapter.hibernate.Hibernate20Adapter"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="sessionFactory"&gt;&lt;ref bean="mySessionFactory"/&gt;&lt;/property&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="defaultNumberPerPage"&gt;&lt;value&gt;20&lt;/value&gt;&lt;/property&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="defaultSortColumn"&gt;&lt;value&gt;lastName&lt;/value&gt;&lt;/property&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="defaultSortDirection"&gt;&lt;value&gt;asc&lt;/value&gt;&lt;/property&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="namedQuery"&gt;&lt;value&gt;playerList&lt;/value&gt;&lt;/property&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/entry--&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/map&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/property&gt;<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp; &lt;/bean&gt;<BR>&nbsp; <BR>&nbsp; 上面配置中的"sessionFactory"为原来配置好的。<BR>&nbsp; "defaultNumberPerPage"为每页显示的记录行数。<BR>&nbsp; "defaultSortColumn"排序列。<BR>&nbsp; "org.appfuse.model.User"为POJO的名字。<BR>&nbsp; <BR>6.将相关的样式表的内容合并到原有的样式表中。<BR>7.将valuelist.jar添加到类路径下。</P>
<P>&nbsp;</P>
<P><BR>8:如果为行添加onclick事件，注意要对"&amp;"进行转义－document.location='editUser.html?username=&lt;c:out value="${User.username}"/&gt;\&amp;from=list';：<BR>&lt;vlh:root value="valueList" configName="classicLook" url="?" includeParameters="*" &gt;</P>
<P><BR>&lt;table width="600" border="0" cellspacing="0" cellpadding="0"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td align="left" nowrap="true"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;c:out value="${list.valueListInfo.totalNumberOfEntries}"/&gt; Total <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; - Page (&lt;c:out value="${list.valueListInfo.pagingPage}"/&gt; of &lt;c:out value="${list.valueListInfo.totalNumberOfPages}"/&gt;)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td align="right"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:paging pages="5"&gt;&lt;c:out value="${page}"/&gt;&amp;nbsp;&lt;/vlh:paging&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td colspan="2"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Export to: &amp;nbsp;&amp;nbsp;<BR>&nbsp;&nbsp;&nbsp; &lt;vlh:filter url="example-export-format.jsp?format=excel&amp;"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;img src="images/export_excel.png" border="0"&gt; Excel<BR>&nbsp;&nbsp;&nbsp; &lt;/vlh:filter&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;vlh:filter url="example-export-format.jsp?format=csv&amp;"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;img src="images/export_csv.png" border="0"&gt; CSV<BR>&nbsp;&nbsp;&nbsp; &lt;/vlh:filter&gt;<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; &lt;table width="450" class="classicLook" cellspacing="0" cellpadding="0"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:row bean="User"&gt;<BR>&nbsp;&lt;vlh:attribute name="onclick"&gt;<BR>&nbsp;&nbsp; document.location='editUser.html?username=&lt;c:out value="${User.username}"/&gt;\&amp;from=list';<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/vlh:attribute&gt;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:column title="username"&nbsp;&nbsp; property="username"&nbsp;&nbsp; sortable="desc" /&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:column title="firstName"&nbsp;&nbsp; property="firstName"&nbsp;&nbsp; sortable="desc" /&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:column title="lastname"&nbsp;&nbsp; property="lastName"&nbsp;&nbsp; sortable="desc" /&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:column title="email"&nbsp;&nbsp;&nbsp;&nbsp; property="email"&nbsp;&nbsp;&nbsp;&nbsp; sortable="desc" /&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/vlh:row&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/table&gt;<BR>&nbsp;&lt;/td&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/table&gt;<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp; &lt;/vlh:root&gt;</P>
<P><BR>======================================================================<BR>为ValueList添加高亮<BR>1.添加vlh:attribute：&lt;vlh:attribute name="onmouseout"&gt;javascript:mouseout(this);&lt;/vlh:attribute&gt;。<BR>添加完attribute后的内容如下：</P>
<P>&nbsp; &lt;vlh:root value="list" url="?" includeParameters="*" &gt;<BR>&nbsp;&nbsp;&nbsp; &lt;vlh:retrieve name="nbaPlayers" /&gt;<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; &lt;table width="650" class="classicLook" cellspacing="0" cellpadding="0"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:row bean="player"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:attribute name="onmouseover"&gt;javascript:toggle(this);&lt;/vlh:attribute&gt;<BR>&nbsp;&lt;vlh:attribute name="onmouseout"&gt;javascript:mouseout(this);&lt;/vlh:attribute&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:attribute name="id"&gt;&lt;%=("player-"+playerRowNumber)%&gt;&lt;/vlh:attribute&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:attribute name="align" value="center" /&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:column title="playerid"&nbsp;&nbsp; property="playerid"&nbsp;&nbsp; sortable="desc"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:column title="teamname"&nbsp;&nbsp; property="teamname"&nbsp;&nbsp; sortable="desc" /&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:column title="firstname"&nbsp; property="firstname"&nbsp; sortable="desc"&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:attribute name="width" value="150"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/vlh:column&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:column title="lastname"&nbsp;&nbsp; property="lastname"&nbsp;&nbsp; sortable="desc" attributes="width='150'"/&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:column title="status"&nbsp;&nbsp;&nbsp;&nbsp; property="status"&nbsp;&nbsp;&nbsp;&nbsp; sortable="desc" /&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;vlh:column title="pos"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; property="pos"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sortable="desc" /&gt;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/vlh:row&gt;<BR>&nbsp;&nbsp;&nbsp; &lt;/table&gt;<BR>&nbsp; &lt;/vlh:root&gt;</P>
<P><BR>2.添加js函数，添加完函数后的内容如下：<BR>&lt;script&gt;<BR>&nbsp; var lastId;<BR>&nbsp; var lastStyle;<BR>&nbsp; var previousClass = null;<BR>&nbsp; function toggle(object)<BR>&nbsp; {<BR>&nbsp; if( lastId != undefined )<BR>&nbsp; {<BR>&nbsp; document.getElementById(lastId).className = lastStyle;<BR>&nbsp; }<BR>&nbsp; lastId = object.id;<BR>&nbsp; lastStyle = object.className;<BR>&nbsp; previousClass=this.className;<BR>&nbsp; object.className = "selected";<BR>&nbsp; }</P>
<P>&nbsp; function mouseout(object){<BR>&nbsp;&nbsp; object.className = previousClass;<BR>&nbsp; }<BR>&lt;/script&gt;</P>
<P>&nbsp;</P>
<P>&nbsp;</P></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/1860.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-09 00:29 <a href="http://www.blogjava.net/kapok/archive/2005/03/09/1860.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DT编译成功了！！！</title><link>http://www.blogjava.net/kapok/archive/2005/03/09/1858.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 08 Mar 2005 16:28:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/09/1858.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/1858.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/09/1858.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/1858.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/1858.html</trackback:ping><description><![CDATA[<TABLE width="96%" align=center>
<TBODY>
<TR>
<TD align=middle><SPAN class=article_title>DT编译成功了！！！</SPAN></TD></TR>
<TR>
<TD align=right>2005年 03月01日</TD></TR>
<TR>
<TD class=article_content>
<P>DT编译成功了，Struts Menu从数据库取数据组建菜单的试验成功了......</P>
<P>这几天又开始做技术实验了，被折磨的有点情绪化，不过一切还算顺利，多亏朋友们的帮忙！</P>
<P>昨晚在家读DisplayTag的源代码，发现DisplatyTag（以下简称DT）翻页时候不是从缓存中读取的数据，它又重新向数据库发出请求，和我调试程序时发现的一样，它的动作大致是这样的：</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 向数据库发送请求，一次读取所有数据的所有字段（我强调"所有"字段，并不是select *,而是读取你的语句中指定的全部的字段，我强调的是相对于lazy-load方式的部分读取），然后根据pagesize(一页显示数据的行数)和当前页号读取数据的子集，当翻到其他页时，又获取所有数据的所有字段，再过滤出显示的子集......，这样的效率不是差，而是很差，我昨天看了一下DT的JIRA的文字，发现这个问题早在2004年的5月份就有人提出了，而那个人就是ValueList的作者，而且你还会发现很多开发者提供的改进方案和参考实现，而且DT的开发组也同意添加提高分页效率的特性，但到现在也还没有实现，有些让人失望！据说ValueList对这方面做的很好，但ValueList的文档太少了，而且集成到AppFuse中还要自己改写模板，目前有些骑虎难下，明天开始折腾ValueList，希望一切顺利，菩萨保佑，呵呵</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 今天试了一下从数据库中读取数据来生成Struts Menu的菜单，结果发现如果一页中放入2个Struts Menu的菜单，则会有一个有问题，我看了一下源码，发现是在一个js文件中的函数引起的，Struts Menu首先初始化菜单，然后扩展，在扩展的时候，把点击的菜单写到Cookies中，如果同一个页面有2个的声明，则第二个菜单的折叠的部分不能展开，我的一个同事折腾了将近一周，就是因为这个问题。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 昨晚终于在倦兔的帮助下，使用Maven编译DT成功了，这样我可以放心的修改DT，然后编译、打包。但我要权衡一下和改用ValueList的工作量，目前暂不打算修改DT了。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Maven把所有下载的jar文件放在Window的Documents and Settings/你的用户名/.mave目录下，你可以把其他人的这个目录下的文件都拷贝过来（注意要保持原来的目录结构）直接用就行了。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Matt Raible关于DT、ValueList、DataGrid的简短介绍：<A href="http://raibledesigns.com/comments/rd/sunsets/there_s_a_new_sorting" rel=nofollow><FONT color=#002c99>http://raibledesigns.com/comments/rd/sunsets/there_s_a_new_sorting</FONT></A></P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 兔八哥</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2005-3-1 18:21</P>
<P>&nbsp;</P></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/1858.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-09 00:28 <a href="http://www.blogjava.net/kapok/archive/2005/03/09/1858.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ValueList的实验做完了，但是……</title><link>http://www.blogjava.net/kapok/archive/2005/03/09/1859.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 08 Mar 2005 16:28:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/09/1859.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/1859.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/09/1859.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/1859.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/1859.html</trackback:ping><description><![CDATA[<TABLE width="96%" align=center>
<TBODY>
<TR>
<TD align=middle><SPAN class=article_title>ValueList的实验做完了，但是……</SPAN></TD></TR>
<TR>
<TD align=right>2005年 03月07日</TD></TR>
<TR>
<TD class=article_content>
<P>今天，同事终于把ValueList的实验做完了，ValueList0.1.7新添了许多新功能，但是，今天看到"王者之剑"的留言，又读了一下源码，发现ValueList也是取回数据库的全部数据，只是把需要返回页面的数据返回了，并不是我想的使用Hibernate的分页方法进行查询数据库的</P>
<P>&nbsp;&nbsp;&nbsp; 今天，同事终于把ValueList的实验做完了，ValueList0.1.7新添了许多新功能，但是，今天看到"王者之剑"在我的Blog上的留言，有点不太相信ValueList也是取回所有的数据。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 刚才，我抽空看了一下ValueList的Hibernate20Adapter的源码，结果发现果然是把所有的数据都取回来了，但比DT稍强一些的是，它没有把所有的Object都放入request中，只是把当前页要显示的Object放入了，但这样也同样耗费数据库资源，同样需要循环，不同的是，耗费的网络资源少些，因为，它传回的对象只有一页的对象。</P>
<P>&nbsp;&nbsp;&nbsp; 但结果并不是我想像的，我以为，它会自己像Hibernate那样，根据不同的数据库采用分页算法，使用Criteria Query设定返回在指定范围的数据。<BR>实验结果有点让我失望，但毕竟比DT要强一些，不知道DT的1.1是不是也是这样解决的，如果是的话，那不用等它的正式版本了，在JIRA上已经有参考实现了。</P>
<P>&nbsp;&nbsp;&nbsp; 但学习ValueList还是有所收获的，看到了另一种显示实现的方式，而且ValueList的确比DT强，界面虽然不漂亮(0.1.7已经很有DT的意思了，通过自己定制样式表，可以作出漂亮的外观)，但比较灵活,这个工具值得继续关注。<BR>&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp; 使用ValueList需要注意的一点是，如果使用MVC框架（不知道其他的框架是否这样），从一个Controller转发到一个View上，如果ValueList的URL属性为""或者"？"，翻页图标的URL为上一个操作的URL，这和ValueList中提到的刷新按钮有点相似，我又看了一下DT的用法，也需要指定URL，对于解决重复提交的问题，可以使用Struts的token来解决，我问了一下倦兔，倦兔说WebWork和Spring都有类似的解决方案，在此也感谢倦兔推荐ValueList!^_^</P>
<P>&nbsp;&nbsp;&nbsp; 刚才和天天加班的段兄（SkyHero）聊了一下，感觉实在不行要自己改代码了，比较郁闷（本来想偷懒的，呵呵。<BR>祝段兄的加班生活早点结束，你的女朋友还在茫茫人海中等你去找她，不要把大好时光都浪费在计算机上！^_^</P>
<P>&nbsp;&nbsp;&nbsp; 昨天，和朋友开车去天津，在滨江路上转了一天，我在94年取过一次，发现十年后再看天津已经不是以前的那个城市了，看来是我的看法变了！不过昨天天津的气温有15度，很是舒服，好久没有这样放松了，希望每隔2个月，能出去转转！</P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 兔八哥</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2005-3-7 19:38</P></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/1859.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-09 00:28 <a href="http://www.blogjava.net/kapok/archive/2005/03/09/1859.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>AppFuse最新版本的帮助</title><link>http://www.blogjava.net/kapok/archive/2005/03/09/1857.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 08 Mar 2005 16:27:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/09/1857.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/1857.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/09/1857.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/1857.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/1857.html</trackback:ping><description><![CDATA[<A href="http://rabbit8.blogchina.com/blog/article_144619.988706.html">http://rabbit8.blogchina.com/blog/article_144619.988706.html</A><BR><BR><BR><A href="http://blog.blogchina.com/upload/2005-02-28/20050228111036159066.rar" rel=nofollow><FONT color=#002c99>http://blog.blogchina.com/upload/2005-02-28/20050228111036159066.rar</FONT></A><img src ="http://www.blogjava.net/kapok/aggbug/1857.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-09 00:27 <a href="http://www.blogjava.net/kapok/archive/2005/03/09/1857.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>URLEncode的问题解决了</title><link>http://www.blogjava.net/kapok/archive/2005/03/09/1856.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 08 Mar 2005 16:19:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/09/1856.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/1856.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/09/1856.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/1856.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/1856.html</trackback:ping><description><![CDATA[<TABLE width="96%" align=center>
<TBODY>
<TR>
<TD align=middle><SPAN class=article_title><A href="http://rabbit8.blogchina.com/blog/article_144619.790247.html">http://rabbit8.blogchina.com/blog/article_144619.790247.html</A><BR>URLEncode的问题解决了</SPAN></TD></TR>
<TR>
<TD align=right>2005年 02月02日</TD></TR>
<TR>
<TD class=article_content>URLEncode的问题解决了，倦兔的解决方法竟然如此简单！！！ 
<P>和倦兔聊了一下，他告诉我的解决方法竟然如此简单：在Tomcat的server.xml的Connector部分添加URIEncoding="GBK"，我的server.xml添加后内容如下：</P>
<P>&lt;Connector&nbsp; URIEncoding="GBK" port="8080"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; maxThreads="150" minSpareThreads="25" maxSpareThreads="75"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; enableLookups="false" redirectPort="8443" acceptCount="100"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; debug="0" connectionTimeout="20000" <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; disableUploadTimeout="true" /&gt;</P>
<P>&nbsp;</P>
<P>不用读DisplayTag的源码了，也可以安心过个年了，谢谢倦兔！！！</P></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/1856.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-09 00:19 <a href="http://www.blogjava.net/kapok/archive/2005/03/09/1856.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中的URLEncoder和URLDecoder类 </title><link>http://www.blogjava.net/kapok/archive/2005/03/09/1855.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 08 Mar 2005 16:18:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/09/1855.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/1855.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/09/1855.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/1855.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/1855.html</trackback:ping><description><![CDATA[<TABLE width="96%" align=center>
<TBODY>
<TR>
<TD align=middle><SPAN class=article_title><A href="http://rabbit8.blogchina.com/blog/article_144619.789425.html">http://rabbit8.blogchina.com/blog/article_144619.789425.html</A><BR>Java中的URLEncoder和URLDecoder类</SPAN></TD></TR>
<TR>
<TD align=right>2005年 02月02日</TD></TR>
<TR>
<TD class=article_content>为了解决DisplayTag的问题，可能要使用这2个类了，本篇继续分析URL编码的问题 
<P><STRONG>以下内容是我翻译的JDK的帮助。</STRONG></P>
<P><STRONG>URLEncoder类：</STRONG></P>
<P>用于HTML的form中数据编码的类。</P>
<P>这个类包含将字符串转换为application/x-www-form-urlencoded MIME 格式的静态方法.</P>
<P>如果想了解HTML的编码细则，请参考HTML规范。</P>
<P>编码规则如下：</P>
<P>字符"a"-"z"，"A"-"Z"，"0"-"9"，"."，"-"，"*"，和"_" 都不被编码，维持原值，</P>
<P>空格"&nbsp;"被转换为加号"+"。 </P>
<P>所有其他的字符都被认为是不安全的，首先都根据指定的编码scheme被转换为1个或者多个字节。[凭什么认为其他的字符都是不安全的？看来这些规范的制订者中没有中国人呀！]</P>
<P>然后每个字节都被表示成"%xy"格式的由3个字符组成的字符串，xy是字节的2位16进制的表达（xy is the two-digit hexadecimal representation of the byte），推荐的编码scheme为UTF-8,然而，出于兼容性的考虑，如果没有制定编码的scheme，那么将使用当前操作系统的编码的scheme。</P>
<P>如：如果编码scheme是UTF-8，</P>
<P>"The string <A href="mailto:ü@foo-bar"><FONT color=#002c99>ü@foo-bar</FONT></A>"将被转换为"The+string+%C3%BC%40foo-bar" 。</P>
<P>因为载UTF-8中字符ü被编码成2个字节C3 (十六进制) 和BC (十六进制)， 字符@被编码成一个字节40 (十六进制)。</P>
<P>起始于:JDK1.0</P>
<P>&nbsp;</P>
<P>这个类共有2个重载方法：</P>
<P><STRONG>public</STRONG> <STRONG>static</STRONG> <STRONG>String</STRONG> encode(<STRONG>String</STRONG>&nbsp;s,&nbsp; <STRONG>String</STRONG>&nbsp;enc)&nbsp; <STRONG>throws</STRONG> UnsupportedEncodingException。起始于：JDK1.4</P>
<P>和即将被废弃的方法：<STRONG>public static String</STRONG> encode(<STRONG>String</STRONG>&nbsp;s)。（因为这个方法的编码的字符集依赖于程序运行的系统的默认的字符集）。</P>
<P>第一个方法的作用是：根据指定的encode scheme 将一个字符串翻译成application/x-www-form-urlencoded格式。</P>
<P>注意: W3C推荐UTF-8。</P>
<P><STRONG>参数:</STRONG></P>
<P>s - 将要被翻译的字符串。</P>
<P>enc - 编码用的character。</P>
<P>&nbsp;返回:翻译后的字符串。</P>
<P>抛出异常: UnsupportedEncodingException - 如果不支持制定的编码</P>
<P>起始于:1.4</P>
<P>另请参考:URLDecoder.decode(java.lang.String, java.lang.String)</P>
<P>&nbsp;</P>
<P>类URLDecoder的作用和URLEncoder的作用相反，方法类似，这里就不再赘述了。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果你想知道你的字符串被编码后的值是什么样，你可以打开<A href="http://www.baidu.com/" rel=nofollow><FONT color=#002c99>www.baidu.com</FONT></A>，然后输入你要编码后的数值，然后提交，你可以在地址栏看到你被编码后的字符串，这个方法是Jason告诉我的，呵呵！</P>
<P>如果想解决DisplayTag的问题，就要修改源代码了，下一步就是读源代码，头疼ing......</P>
<P>&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;兔八哥</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2005-2-2下午16:30</P></TD></TR>
<TR>
<TD align=right><FONT color=#9c3316>ltf_ty 于16 : 01 发表 已被浏览249次</FONT> <A href="http://rabbit8.blogchina.com/blog/article_144619.789425.html"><FONT color=#002c99>评论(3)</FONT></A> / <A href="http://rabbit8.blogchina.com/blog/refer.789425.html"><FONT color=#002c99>引用(0)</FONT></A> <A href="javascript:d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://blogmark.blogchina.com/jsp/key/quickaddkey.jsp?k='+encodeURI(d.title)+'&amp;u='+encodeURI(d.location.href)+'&amp;c='+encodeURI(t),'keyit','scrollbars=no,width=500,height=430,status=no,resizable=yes'));keyit.focus();"><FONT color=#002c99>加入博采中心</FONT></A></TD></TR></TBODY></TABLE>&nbsp;&nbsp;&nbsp;&nbsp;<IMG src="http://rabbit8.blogchina.com/blog/imgs/comment_icon1.gif"> <SPAN class=categoryinfo>[<A href="http://rabbit8.blogchina.com/blog/article.php?id=789425&amp;parentId=346365&amp;blogId=144619"><FONT color=#002c99>回复</FONT></A>]</SPAN> 
<TABLE>
<TBODY>
<TR>
<TD>&nbsp;&nbsp;&nbsp;</TD>
<TD>
<P>在百度提交:The string ü@foo-bar<BR>并不是您说的结果.<BR><BR></P></TD></TR></TBODY></TABLE><SPAN class=commentposterinfo>&nbsp;&nbsp;&nbsp;&nbsp;匿名网友 | 2005年 02月05日 16 : 58 </SPAN><BR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;<IMG src="http://rabbit8.blogchina.com/blog/imgs/comment_icon1.gif"> 回复 <SPAN class=categoryinfo>[<A href="http://rabbit8.blogchina.com/blog/article.php?id=789425&amp;parentId=370282&amp;blogId=144619"><FONT color=#002c99>回复</FONT></A>]</SPAN> 
<TABLE>
<TBODY>
<TR>
<TD>&nbsp;&nbsp;&nbsp;</TD>
<TD>
<P>请看：http://rabbit8.blogchina.com/blog/article_144619.859489.html </P></TD></TR></TBODY></TABLE><SPAN class=commentposterinfo>&nbsp;&nbsp;&nbsp;&nbsp;兔八哥 | 2005年 02月16日 08 : 51 </SPAN><BR><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;<IMG src="http://rabbit8.blogchina.com/blog/imgs/comment_icon1.gif"> <SPAN class=categoryinfo>[<A href="http://rabbit8.blogchina.com/blog/article.php?id=789425&amp;parentId=451911&amp;blogId=144619"><FONT color=#002c99>回复</FONT></A>]</SPAN> 
<TABLE>
<TBODY>
<TR>
<TD>&nbsp;&nbsp;&nbsp;</TD>
<TD>
<P>不错不错，附上实现代码<BR>static String HEX_DIGITS = &nbsp;"0123456789ABCDEF";<BR>&nbsp; &nbsp;protected static String urlEncode( byte[] rs )<BR>&nbsp; &nbsp;{<BR>&nbsp; &nbsp; &nbsp; &nbsp;StringBuffer result = new StringBuffer();<BR><BR>&nbsp; &nbsp; &nbsp; &nbsp;// Does the URLEncoding. &nbsp;We could use the java.net one, but<BR>&nbsp; &nbsp; &nbsp; &nbsp;// it does not eat byte[]s.<BR><BR>&nbsp; &nbsp; &nbsp; &nbsp;for( int i = 0; i &lt; rs.length; i++ )<BR>&nbsp; &nbsp; &nbsp; &nbsp;{<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;char c = (char) rs[i];<BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;switch( c )<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;case '_':<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;case '.':<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;case '*':<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;case '-':<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;result.append( c );<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break;<BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;case ' ':<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;result.append( '+' );<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break;<BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;default:<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if( (c &gt;= 'a' &amp;&amp; c &lt;= 'z') ││<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(c &gt;= 'A' &amp;&amp; c &lt;= 'Z') ││<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(c &gt;= '0' &amp;&amp; c &lt;= '9') )<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;result.append( c );<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;else<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;result.append( '%' );<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;result.append( HEX_DIGITS.charAt( (c &amp; 0xF0) &gt;&gt; 4 ) );<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;result.append( HEX_DIGITS.charAt( c &amp; 0x0F ) );<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}<BR><BR>&nbsp; &nbsp; &nbsp; &nbsp;} // for<BR><BR>&nbsp; &nbsp; &nbsp; &nbsp;return result.toString();<BR>&nbsp; &nbsp;} </P></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/1855.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-09 00:18 <a href="http://www.blogjava.net/kapok/archive/2005/03/09/1855.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于DisplayTag：新的郁闷开始了……</title><link>http://www.blogjava.net/kapok/archive/2005/03/09/1854.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 08 Mar 2005 16:17:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/09/1854.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/1854.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/09/1854.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/1854.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/1854.html</trackback:ping><description><![CDATA[<TABLE width="96%" align=center>
<TBODY>
<TR>
<TD align=middle><SPAN class=article_title><A href="http://rabbit8.blogchina.com/blog/article_144619.789350.html">http://rabbit8.blogchina.com/blog/article_144619.789350.html</A><BR>关于DisplayTag：新的郁闷开始了……</SPAN></TD></TR>
<TR>
<TD align=right>2005年 02月02日</TD></TR>
<TR>
<TD class=article_content>DisplayTag的默认的URL为上一次的URL，而上一次的URL中包含被编码后的汉字，所以，将编码后的汉字再次进行编码，肯定查不到结果，所以也不能翻页，看来要修改DisplayTag了，本来想年后再弄的...... 
<P>DisplayTag的默认的URL默认为上一次的URL，如果上一次的URL包含中文的话，则会被进行URLEncode，所以在翻页的时候，又会把进行了URLEncode后的数据再次进行URLEncode，所以翻页就没有数据，因为这部分功能被封装在DisplayTag中，于是，我又开始郁闷了......</P>
<P>如汉字"专业"被编码后为"%D7%A8%D2%B5"，于是DisplayTag就将这个编码作为连接的关键字，如果再次提交，则这个编码还会被编码，所以查询结果一定就不对了！！！</P>
<P>下面的内容是我分析这个问题的由来：</P>
<P>---------------------------------------------------------------------------------------------------</P>
<P>刚才到网上查找了些资料，找到HTML4.0.1的规范中关于URLEncode的部分，我把我关心的内容翻译了一下：</P>
<P><A href="http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1" rel=nofollow><FONT color=#002c99>http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1</FONT></A></P>
<P>其中相关内容如下在<A name=h-17.13.3>17.13.3</A> <A name=form-data-processing>Processing form data</A>：</P>
<P>提交时，HTML的规范处理如下：</P>
<P>1.标识<A href="http://blog.blogchina.com/mcp/#successful-controls" rel=nofollow><FONT color=#002c99>successful controls</FONT></A><A name=h-17.13.3.1>&nbsp;</A>（概念可以在上文中查找）。</P>
<P>2.构建form的数据集。</P>
<P>3.根据form的enctype的设置，对form的数据集进行Encode。</P>
<P>4.提交已经Encode的数据集。</P>
<P>&nbsp;</P>
<P>HTML规范中指出Content type和Languange code是不区分大小写的。</P>
<P>详情参见:<A href="http://www.w3.org/TR/html401/types.html#type-content-type" rel=nofollow><FONT color=#002c99>http://www.w3.org/TR/html401/types.html#type-content-type</FONT></A></P>
<P>"&amp;" 表示"&amp;"。关于charset的详细内容见:<U>http://www.w3.org/TR/html401/charset.html#entities</U></P>
<P>&nbsp;</P>
<P>有一个小发现：Frame中的target的值的列表原来是在HTML的规范中制定的，呵呵：</P>
<H5><A href="http://www.w3.org/TR/html401/types.html#type-content-type" rel=nofollow><FONT color=#002c99>http://www.w3.org/TR/html401/types.html#type-content-type</FONT></A></H5>
<H5><A name=h-6.16>6.16</A> <A name=type-frame-target>Frame target names</A></H5>
<P>下面的<A name=idx-target_frame>target的名字是规范中声明的有特殊含义的保留</A>字。</P>
<P><STRONG>_blank</STRONG>&nbsp; 在一个没有指定名字的新窗口中打开页面。(new, unnamed window)</P>
<P><STRONG>_self</STRONG>&nbsp; 在同一个窗口中打开。（load the document in the same frame as the element that refers to this target）</P>
<P><STRONG>_parent</STRONG>&nbsp; 在当前窗口的父窗口中打开，如果当前窗口没有父窗口，那么就等同于<STRONG>_self</STRONG></P>
<P><STRONG>_top</STRONG>&nbsp; 在最开始的窗口中转载，如果当前框架没有parent，那就等于<STRONG>_self</STRONG>。</P>
<P>&nbsp;</P>
<H5>关键部分在这里了：<A href="http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1" rel=nofollow><FONT color=#002c99>http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1</FONT></A></H5>
<P>form的默认的content type是：<A name=didx-applicationx-www-form-urlencoded><STRONG>application/x-www-form-urlencoded</STRONG></A></P>
<P>form提交content type的数据必须用下列规则进行编码（encode）：</P>
<P>空格被封装为"+"，其他的保留字封装后的值在 <A href="http://blog.blogchina.com/references.html#ref-RFC1738" rel=nofollow><FONT color=#002c99>[RFC1738]</FONT></A>中可以查到。RF1738的规范：<A href="http://www.ietf.org/rfc/rfc1738.txt" rel=nofollow><FONT color=#002c99>http://www.ietf.org/rfc/rfc1738.txt</FONT></A>。</P>
<P>其他的非英文字符和非数字的字符都被编码为<STRONG>"<FONT size=+0>%HH</FONT>"，</STRONG></P>
<P><STRONG>HH</STRONG>是将字符的ASCII的编码转换为<STRONG>16进制</STRONG>后的字符。行尾是"CR LF"&nbsp; (如：<FONT size=+0>`%0D%0A'</FONT>)。</P>
<P>控件的名字和数值之间使用<STRONG>"="</STRONG>分隔，多个控件之间用<STRONG>"&amp;"</STRONG>分隔。</P>
<P><STRONG>------------------------------------------------------------------------------------------------------------------</STRONG></P><STRONG><STRONG>
<P><STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 兔八哥</STRONG></P>
<P><STRONG>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2005-2-2下午</STRONG></P>
<P>&nbsp;</P></STRONG></STRONG></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/1854.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-09 00:17 <a href="http://www.blogjava.net/kapok/archive/2005/03/09/1854.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>记事贴2：Struts的Validator并不好用！</title><link>http://www.blogjava.net/kapok/archive/2005/03/09/1853.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 08 Mar 2005 16:16:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/09/1853.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/1853.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/09/1853.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/1853.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/1853.html</trackback:ping><description><![CDATA[<TABLE width="96%" align=center>
<TBODY>
<TR>
<TD align=middle><SPAN class=article_title><A href="http://rabbit8.blogchina.com/blog/article_144619.782661.html">http://rabbit8.blogchina.com/blog/article_144619.782661.html</A><BR>记事贴2：Struts的Validator并不好用！</SPAN></TD></TR>
<TR>
<TD align=right>2005年 02月01日</TD></TR>
<TR>
<TD class=article_content>使用正则表达式，使email字段中不能输入汉字。最近用AppFuse开发一个BS的系统，用的是Struts的MVC部分，使用Validator进行验证，结果发现Validator的验证EMail并不好，EMail中可以输入汉字，然后到服务器端验证，我配置了客户端验证，也可以验证Email的格式，但如果输入的是正确的格式，但是包含汉字它却验证不出来，但到了后台又管用了，不知道为什么，时间紧，我也没时间去研究它，找到一个方法可以解决这个问题，虽不完美，却也凑合：<BR><BR>使用正则表达式，将原代码<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;html:text property="email" styleId="email" size="50"/&gt;<BR>注释，换成<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;input type="text" name="email" value='&lt;c:out value="${userForm.email}"/&gt;' onkeyup="value=value.replace(/[\u4E00-\u9FA5]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[\u4E00-\u9FA5]/g,''))"<BR>&nbsp;/&gt;<BR>就解决了问题，用户如果输入汉字，则自动删除汉字，而且如果使用向左的箭头向前移动使光标前移，则根本移动不了，光标始终在行尾，只能删除后面的字符，再重新写，其实最好是在EMail的自动生成的脚本中提示，目前先这样实现吧，将来再说！</TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/1853.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-09 00:16 <a href="http://www.blogjava.net/kapok/archive/2005/03/09/1853.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用AppFuse进行开发的总结</title><link>http://www.blogjava.net/kapok/archive/2005/03/08/1852.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 08 Mar 2005 15:53:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/08/1852.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/1852.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/08/1852.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/1852.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/1852.html</trackback:ping><description><![CDATA[<SPAN class=article_title>使用AppFuse进行开发的总结<BR><A href="http://rabbit8.blogchina.com/blog/article_144619.796348.html">http://rabbit8.blogchina.com/blog/article_144619.796348.html</A></SPAN><BR><BR>AppFuse是一个集成了当前最流行的<FONT size=+0>Web</FONT>应用框架的一个更高层次的<FONT size=+0>Web</FONT>开发框架，也可以说是一个<FONT size=+0>Web</FONT>开发基础平台，它与之所集成的各种框架相比，它提供了一部分所有<FONT size=+0>Web</FONT>系统开发过程中都需要开发的一些功能，如登陆、用户密码加密，用户管理、更具不同的用户可以展现不同的菜单，可以自动生成<FONT size=+0>40</FONT>％<FONT size=+0>-60%</FONT>左右的代码，自带了默认的一些在<FONT size=+0>CSS</FONT>中设定的样式，使用这些样式能很快的改变整个系统的外观，还有自动化测试的功能。
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 它最大的价值就是为我们提供了一个<FONT size=+0>Web</FONT>开发的新的方式和思路，尽管这些技术在国外都已进很流行了，但在国内能够将<FONT size=+0>Hibernate</FONT>、<FONT size=+0>Struts</FONT>、<FONT size=+0>Spring</FONT>、<FONT size=+0>DBUnit</FONT>、<FONT size=+0>Ant</FONT>、<FONT size=+0>Log4J</FONT>、<FONT size=+0>Struts Menu</FONT>、<FONT size=+0>Xdoclet</FONT>、<FONT size=+0>SiteMesh</FONT>、<FONT size=+0>Velocity</FONT>、<FONT size=+0>JUnit</FONT>、<FONT size=+0>JSTL</FONT>、<FONT size=+0>WebWork</FONT>这些技术集成到一个框架中的还不多见，所以即使不使用它的全部功能，它也给我们提供了一个很好的借鉴、学习的机会。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AppFuse的作者 matt raible是当今开源世界一个比较活跃的开发者，它是AppFuse、Struts Menu的作者，也是XDoclet、DisplayTag等一些著名开源项目的积极参与者，《Hibernate In Action》的作者就在感谢的名单里面提到他，XDoclet的下载版本中所带的Hibernate标签部分的例子就是他写的，他还是2004年Apache技术年会的主讲人之一。（这些都是我这2个多月来搜集到的，呵呵）</P><FONT size=+0>&nbsp;<BR></FONT>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT>通过关注<FONT size=+0>AppFuse</FONT>，我们可以看到目前国外的主流开发都使用了哪些技术，开发方式是什么样的，可能达到什么样的结果，而在以前，是很少能够看到这样完整的例子的。</P>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AppFuse</FONT>的另一个启示是：我们可以依靠开源软件的功能降低开发成本，而且可以阅读开源软件的代码提高所在团队的整体实力。</P>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT></P>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT>但是通过<FONT size=+0>2</FONT>个月的实际学习和使用，我也遇到一系列的问题，因为<FONT size=+0>AppFuse</FONT>是将其他的一些类库或者框架集成在一起的，集成的技术众多，而且有一些技术在国内甚至很少有人知道，资料也比较少，所以虽然作者经过了一些测试，但都是基于英文编码的，而对于中文编码来说，还潜在的存在着一些问题，虽然不是<FONT size=+0>AppFuse</FONT>的问题，但却降低了开发速度，下面是我在开发过程中遇到过的问题，有些解决了，有些还没有解决：</P>
<P>一．<FONT size=+0>Struts</FONT></P>
<P><FONT size=+0>1．&nbsp; AppFuse</FONT>中默认的<FONT size=+0>MVC</FONT>框架是<FONT size=+0>Struts</FONT>，而且使用的是<FONT size=+0>LookupDispatchAction</FONT>，并且使用的是按钮（<FONT size=+0>button</FONT>），在<FONT size=+0>XP</FONT>下用<FONT size=+0>IE</FONT>浏览效果还可以，但如果在<FONT size=+0>2000</FONT>或者<FONT size=+0>98</FONT>下，就使外观很难看，而且当时我还遇到一个问题：如果按钮显示中文，则在<FONT size=+0>DisplayTag</FONT>中翻页失灵，而且报错，后来我把<FONT size=+0>BaseAction</FONT>的相关方法改变了，才可以使用，因为国内的客户都比较重视界面，所以后来我将那些按钮都改成图片了，当然也要添加一些方法了，有点麻烦！</P><FONT size=+0>2．&nbsp; Struts</FONT>中的标签如今推荐使用的只有<FONT size=+0>html</FONT>部分的标签了，其他的标签或者可以使用<FONT size=+0>JSTL</FONT>替代，或者已经不推荐使用了，而且<FONT size=+0>AppFuse</FONT>中推荐使用<FONT size=+0>JSTL</FONT>，而<FONT size=+0>JSTL</FONT>和<FONT size=+0>struts</FONT>的标签的联合使用时，需要的不是<FONT size=+0>标签<FONT size=+0>&lt;html:标签&gt;</FONT>，而是<FONT size=+0>标签&lt;html-el:标签<FONT size=+0>&gt;</FONT>，这个问题曾经困扰了我整整<FONT size=+0>2</FONT>天。</FONT></FONT><FONT size=+0><FONT size=+0> 
<P><FONT size=+0>3．&nbsp; Struts</FONT>的<FONT size=+0>Validation</FONT>的校验规则并不完善，比如如果使用客户端的<FONT size=+0>javascript</FONT>校验，则在邮箱中输入汉字根本校验不出来，到了服务器端报错。</P>
<P><FONT size=+0>4．&nbsp; </FONT>最严重的问题是<FONT size=+0>AppFuse</FONT>生成的<FONT size=+0>Struts</FONT>的<FONT size=+0>validation.xml</FONT>文件中有许多多余的"<FONT size=+0>.</FONT>"，如果你去掉了，常常在执行<FONT size=+0>ant</FONT>的<FONT size=+0>deploy</FONT>任务时又恢复原样。这样是提交表单的时候经常会报<FONT size=+0>javascript</FONT>的脚本错误或者缺少对象或者缺少<FONT size=+0>value</FONT>，所以我会手工的修改这个文件，然后把修改后的文件备份，当重新生成有错误的文件时，我会用备份的没有错误的文件去覆盖。</P>
<P><FONT size=+0>5．&nbsp; Struts</FONT>的<FONT size=+0>validatioin</FONT>对于使用同一个<FONT size=+0>FormBean</FONT>的<FONT size=+0>Action</FONT>的校验方式比较复杂。（待解决）。</P>
<P>二．<FONT size=+0>Hibernate</FONT></P>
<P><FONT size=+0>1．&nbsp; Hibernate</FONT>是现在受到越来越多的人推崇的一个<FONT size=+0>ORM</FONT>工具（框架、类库），它将我们从繁琐的使用<FONT size=+0>JDBC</FONT>的开发过程中解放出来，但同时也带来了新的问题，如学习曲线，执行效率，数据库设计优化，还有最重要的灵活性。<FONT size=+0>Hibernate</FONT>不是一个很容易上手的东西，要完全驾驭它还需要读很多资料，但好的资料却很少。</P>
<P><FONT size=+0>2．&nbsp; </FONT>使用<FONT size=+0>Xdoclet</FONT>可以很方便的生成<FONT size=+0>Hibernate</FONT>中的持久类的配置文件（<FONT size=+0>*.hbm.xml</FONT>）<FONT size=+0>,</FONT>但对一些特殊的映射却无能为力，如使用序列的<FONT size=+0>id</FONT>生成规则，序列的名字没有地方写，所以也只好先利用它生成主要的内容，然后手工修改。</P>
<P><FONT size=+0>3．&nbsp; </FONT>同样还是<FONT size=+0>id</FONT>的生成策略问题，如果使用序列、<FONT size=+0>hilo</FONT>等需要一些数据库机制支持的策略时，<FONT size=+0>schemaExport</FONT>并不能自动生成序列或者保存当前<FONT size=+0>id</FONT>的表，这项工作仍然要手工解决。</P>
<P><FONT size=+0>4．&nbsp; Hibernate</FONT>中提供了几种关联，一对一、一对多、多对多，但对于怎样调整效率却没有一个很明确的提示，还要根据情况判定，这就带来和一些弹性的设计。</P>
<P><FONT size=+0>5．&nbsp; Hibernate</FONT>中可以选择的操作数据库的方式有<FONT size=+0>3</FONT>种，其中<FONT size=+0>HQL</FONT>功能最强大，但有些功能使用标准查询可能会更方便，但会有一些限制，所以虽然它很灵活，但易用性不如<FONT size=+0>JDBC</FONT>好。</P>
<P>三．<FONT size=+0>Spring</FONT></P>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT>在<FONT size=+0>AppFuse</FONT>的过程中，<FONT size=+0>Spring</FONT>完全隐藏在幕后，除了一些配置外，几乎感觉不到它的存在，所以我在使用它的过程中并没有遇到什么麻烦，这里只是简单的介绍一下它在<FONT size=+0>AppFuse</FONT>中起到的作用。</P>
<P><FONT size=+0>1．&nbsp; Spring</FONT>在<FONT size=+0>AppFuse</FONT>中起到的主要作用是对<FONT size=+0>Hibernate</FONT>的<FONT size=+0>Session</FONT>和事务的管理，利用<FONT size=+0>Spring</FONT>封装的<FONT size=+0>Hibernate</FONT>模板类，我们大大地减少了实现<FONT size=+0>DAO</FONT>的代码行数。</P>
<P><FONT size=+0>2．&nbsp; Spring</FONT>还起到了连接映射文件和类之间的关联，及接口和实现类之间的关联，这些都依赖于<FONT size=+0>Spring</FONT>的<FONT size=+0>IoC</FONT>的机制的实现。</P>
<P><FONT size=+0>3．&nbsp; </FONT>对于字符进行编码和解码部分用到了<FONT size=+0>Spring</FONT>自带的<FONT size=+0>Filter</FONT>，只需要在配置文件中配置就好了。</P><FONT size=+0>&nbsp;<BR></FONT>
<P>四．<FONT size=+0>SiteMesh</FONT></P>
<P><FONT size=+0>SiteMesh</FONT>是一个基于<FONT size=+0>Decorator</FONT>模式的技术，它可以修饰返回的网页文件，它的工作方式受到越来越多的人的推崇，这点从<FONT size=+0>Manning</FONT>出版的一些技术书籍中可以看出来。</P>
<P>我在使用<FONT size=+0>SiteMesh</FONT>的过程中并不顺利，我参考了《<FONT size=+0>Java Open Source Programming</FONT>》，这本书中说<FONT size=+0>SiteMesh</FONT>在默认的情况下不对下载文件进行装饰，但我在下载文件时发现，我的文件内容被丢弃了，取而代之的是<FONT size=+0>SiteMesh</FONT>的模板的内容，后来我通过修改<FONT size=+0>SiteMesh</FONT>的配置文件解决了这个问题，但感觉还有一些不太清楚的地方需要学习。</P><FONT size=+0>&nbsp;<BR></FONT>
<P>五．<FONT size=+0>DisplayTag</FONT></P>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DisplayTag</FONT>是一个优秀的显示内容的标签，从<FONT size=+0>SourceForge</FONT>的访问量来看，它是很活跃的项目，仅次于<FONT size=+0>Ant</FONT>、<FONT size=+0>Hibernate</FONT>、<FONT size=+0>Xdoclet</FONT>等几个著名的项目，我总结，它的主要功能有<FONT size=+0>4</FONT>项：显示、分页、排序、将显示的数据写入指定类型的文件中，然后下载。</P>
<P><FONT size=+0>1．&nbsp; </FONT>据我使用的情况看，我只使用了分页和显示的功能，因为当时我没有很好的解决中文编码的问题，所以排序会有问题，直到昨天，我在朋友的帮助下解决了这个问题，至此我可以放心使用的功能又增加了排序<FONT size=+0>(</FONT>我昨天简单的测试了一下是可以的<FONT size=+0>)</FONT>。</P><FONT size=+0>&nbsp;<BR></FONT>
<P><FONT size=+0>2．&nbsp; </FONT>但对于将显示的内容生成到一个指定格式的文件中的功能却有着很多缺陷，如：</P>
<P>（1）&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 生成的文件中只有显示的数据，那些没有显示在界面上的的数据，则不会被写到文件中。</P>
<P>（2）&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果修改了<FONT size=+0>DisplayTag</FONT>的显示的内容，比如添加一列，在这列中的内容不是字符，而是<FONT size=+0>HTML</FONT>的标签，则生成的文件只有这些<FONT size=+0>HTML</FONT>标签，而没有数据。</P>
<P>（3）&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 即使<FONT size=+0>DisplayTag</FONT>中没有我们定制的<FONT size=+0>HTML</FONT>脚本，生成的文件偶尔也有问题，比如：它会把"<FONT size=+0>007</FONT>"生成为"<FONT size=+0>7</FONT>"，把字符串自动的转换为整型值。有时候还生成空白内容的文件。</P>
<P>（4）&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <FONT size=+0>DisplayTag</FONT>生成的<FONT size=+0>Excel</FONT>文件兼容性不好，有时在<FONT size=+0>Excel2003</FONT>中不能正常打开，或者在<FONT size=+0>XP</FONT>下打开报错。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 后来，我看了作者写的《Spring Live》，书中说如果想实现稳定的Excel，推荐使用POI，于是我使用POI生成Excel，稳定性和兼容性都不错。</P>
<P>六．<FONT size=+0>DBUnit</FONT></P>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DBUnit</FONT>是一个可以被<FONT size=+0>Ant</FONT>集成的向数据库中添加数据和备份数据的一个类库，配置很方便，因为<FONT size=+0>AppFuse</FONT>已经集成好了，所以使用也很容易。</P>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT>但是如果你使用<FONT size=+0>EditPlus</FONT>之类的工具手工修改了<FONT size=+0>AppFuse</FONT>生成的内容，则执行<FONT size=+0>Ant</FONT>的<FONT size=+0>setup</FONT>、<FONT size=+0>setup-db</FONT>或者<FONT size=+0>deploy</FONT>的任务时，常常报错，说无效的格式，这是因为这个被手工修改的文件再次被<FONT size=+0>AppFuse</FONT>执行后，它的第一行的文件声明的前几个字母是无效的，是因为本地的字符集编码的原因而引起了乱码，如果把这几个无效的字母去掉，问题就解决了。</P><FONT size=+0>&nbsp;<BR></FONT>
<P>七．<FONT size=+0>Struts Menu</FONT></P>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Struts Menu</FONT>也是<FONT size=+0>AppFuse</FONT>的作者开发的一个开源软件，它可以根据配置文件读取当前用户可以使用的功能菜单，这个功能是我一直以来都想要的，我也找到了一些代码，但实现的都不如这个完善，没什么好说的，使用简单，配置容易，很好的解决了我的问题。</P>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT>问题是我只使用了<FONT size=+0>AppFuse</FONT>提供的<FONT size=+0>2</FONT>个角色，对于多个角色的实验我还没有做。</P><FONT size=+0>&nbsp;<BR></FONT>
<P>八．<FONT size=+0>XDoclet</FONT></P>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT>在<FONT size=+0>AppFuse</FONT>中，使用<FONT size=+0>Xdoclet</FONT>生成了几乎一切的配置文件：<FONT size=+0>Struts-config.xml</FONT>、<FONT size=+0>web.xml</FONT>、<FONT size=+0>validation.xml</FONT>、<FONT size=+0>*.hbm.xml</FONT>等文件，如果使用<FONT size=+0>AppGen</FONT>的话，还会生成更多的文件，这一切都是使用<FONT size=+0>Xdoclet</FONT>实现的。</P>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT>问题是我在<FONT size=+0>Struts</FONT>部分提到的，生成的<FONT size=+0>Validation.xml</FONT>文件中会多生成一个"<FONT size=+0>.</FONT>"，另外在生成资源文件时也会多生成一个"<FONT size=+0>.</FONT>"，目前我没有很好的阅读这段代码，不知道是不是<FONT size=+0>Xdoclet</FONT>的问题。</P><FONT size=+0>&nbsp;<BR></FONT>
<P>九．<FONT size=+0>Ant</FONT></P>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Ant</FONT>并没有什么问题，但在执行作者写的<FONT size=+0>Ant</FONT>任务的时候，有一些任务不能正常执行，比如，运行模拟对象测试的任务，作者也在1.7版本的修复列表中提到以前版本有些ant任务不能执行，在1.7中修改了一些ant任务，使他们能够正常的执行了。</P>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT>实际上，我们如果使用<FONT size=+0>AppGen</FONT>进行开发的话，使用的任务一般不超过<FONT size=+0>8</FONT>个。</P><FONT size=+0>&nbsp;<BR></FONT>
<P>十．<FONT size=+0>JSTL</FONT></P>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JSTL</FONT>是个好东西，我常用的有<FONT size=+0>和<FONT size=+0>部分的标签，但是如果使用<FONT size=+0>JSTL</FONT>进行逻辑判断，我并没有感觉比使用<FONT size=+0>JSP</FONT>的代码块优雅多少。另外，熟悉<FONT size=+0>JSTL</FONT>也需要一段时间，我就经历了面对着JSP页面不知道该怎么写JSTL语法的困境。当然，<FONT size=+0>AppFuse</FONT>中使用的基本都是<FONT size=+0>JSTL</FONT>，包括向<FONT size=+0>DisplayTag</FONT>传递显示的数据，使用的都是<FONT size=+0>JSTL</FONT>语法，这方面的资料挺多，我参考的是电子工业出版社出的《<FONT size=+0>JSP2.0</FONT>技术》，说的很详细。</P><FONT size=+0>&nbsp;<BR></FONT>
<P>十一<FONT size=+0>.Tomcat</FONT></P>
<P align=left><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT>你也许会说："<FONT size=+0>Tomcat</FONT>就不用说了吧？"，是的，<FONT size=+0>Tomcat</FONT>一般都会使用，但是―――――――――――――<FONT size=+0>Tomcat5</FONT>和<FONT size=+0>Tomcat4.X</FONT>对于中文编码使用了不同的机制，这个问题困扰了我好久，我解决了页面上写入汉字显示乱码的问题，我也曾经以为<FONT size=+0>DisplayTag</FONT>对汉字不能排序，也不能正常分页是因为<FONT size=+0>DisplayTag</FONT>的开发者都是老外，是因为他们没有考虑中文的关系的原因。</P>
<P align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 直到昨天，我才知道这一切都是因为<FONT size=+0>Tomcat5</FONT>对汉字编码的实现的方式和<FONT size=+0>Tomcat4</FONT>不一样的原因，如果感兴趣，可以看看这个帖子：<A href="http://www.javaworld.com.tw/jute/post/view?bid=9&amp;id=44042&amp;sty=1&amp;tpg=1&amp;age=0" rel=nofollow><FONT size=+0>http://www.javaworld.com.tw/jute/post/view?bid=9&amp;id=44042&amp;sty=1&amp;tpg=1&amp;age=0</FONT></A></P>
<P align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 再次感谢倦兔！:D</P><FONT size=+0>&nbsp;<BR></FONT>
<P align=left>十二<FONT size=+0>.JavaScript</FONT></P>
<P align=left><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JavaScript</FONT>简单易学，但想运用自如就不太容易了。<FONT size=+0>AppFuse</FONT>中嵌入了几个<FONT size=+0>js</FONT>文件，里面包含了许多函数，值得我们好好的研究一下，比如，如果有一个必填字段没有填写，<FONT size=+0>AppFuse</FONT>会自动的聚焦在那个<FONT size=+0>input</FONT>上，类似的小技巧有很多，你可以自己去翻看。</P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT>但<FONT size=+0>AppFuse</FONT>自带的<FONT size=+0>JavaScript</FONT>脚本有一个<FONT size=+0>Bug</FONT>，就是当<FONT size=+0>DisplatyTag</FONT>中没有可以显示的数据时，你用鼠标单击，它会报<FONT size=+0>JavaScript</FONT>错误，你仔细研究一下<STRONG>function</STRONG> highlightTableRows(tableId) 就知道了：我的解决办法是在location.href = <STRONG>link</STRONG>.getAttribute("href");前面添加一行判断：<STRONG>if</STRONG> (<STRONG>link</STRONG> != null)。<BR><FONT size=+0>&nbsp;<BR></FONT>
<P align=left>十三<FONT size=+0>.</FONT>资源文件国际化</P>
<P align=left><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT>对于<FONT size=+0>Struts</FONT>和<FONT size=+0>DisplayTag</FONT>都涉及到资源文件国际化<FONT size=+0>AppFuse1.6.1</FONT>很好的解决了<FONT size=+0>Struts</FONT>资源映射文件国际化的问题，你只需要在对应本国语言的资源文件中写入汉字，<FONT size=+0>Ant</FONT>中有一项执行<FONT size=+0>native2ascii</FONT>的任务，<FONT size=+0>AppFuse</FONT>自动的为你进行了资源文件的编码转换，而对于<FONT size=+0>DisplayTag</FONT>的资源文件问题，还要自己执行<FONT size=+0>native2ascii</FONT>命令，为了避免每次都输入一串命令，我用Delphi写了个小工具，可视化的选择资源文件，点击按钮自动执行该命令，底层依赖于<FONT size=+0>JDK</FONT>。</P><FONT size=+0>&nbsp;<BR></FONT><FONT size=+0>&nbsp;<BR></FONT>
<P align=left>经过<FONT size=+0>2</FONT>个多月的学习，我感觉这个框架非常不错，它为我以后的项目开发指出了一个新的方向，但如果想很熟练的使用这个框架进行开发，至少要对以下几种技术比较熟练：<FONT size=+0>Struts(</FONT>或者<FONT size=+0>WebWork</FONT>、<FONT size=+0>Spring</FONT>及其他的已经整合进来的<FONT size=+0>MVC</FONT>框架<FONT size=+0>)</FONT>、<FONT size=+0>Hibernate</FONT>（或者<FONT size=+0>ibatis</FONT>）、<FONT size=+0>JSTL</FONT>，当然其他的技术至少也要知道一点，否则遇到问题都不知道出在哪里。</P><FONT size=+0>&nbsp;<BR></FONT><FONT size=+0>&nbsp;<BR></FONT>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT>目前我还没有解决的问题有：</P>
<P><FONT size=+0>1．&nbsp; </FONT>如何在翻页的时候才读取下面的数据？</P>
<P><FONT size=+0>2．&nbsp; </FONT>怎样对使用同一个<FONT size=+0>FormBean</FONT>的多个<FONT size=+0>Form</FONT>进行客户端校验？</P>
<P><FONT size=+0>3．&nbsp; </FONT>怎样优化<FONT size=+0>Hibernate</FONT>的效率？《<FONT size=+0>Hibernate In Action</FONT>》中提供了多种策略，有些时候应该使用<FONT size=+0>lazy</FONT>，有些时候应该使用<FONT size=+0>outer-join</FONT>。</P>
<P><FONT size=+0>4．&nbsp; </FONT>在什么时机生成导出文件？目前我是在查询的<FONT size=+0>Action</FONT>中同时生成了导出文件，否则，到了下一页，我就不知道查询条件了，当然，如果把拼装后的<FONT size=+0>HQL</FONT>存储在<FONT size=+0>Session</FONT>或者<FONT size=+0>Hidden</FONT>中也可以解决这个问题，但是这样就破坏了<FONT size=+0>DAO</FONT>的封装，要把<FONT size=+0>DAO</FONT>封装后的<FONT size=+0>HQL</FONT>发送给<FONT size=+0>Action</FONT>，然后发送的到<FONT size=+0>Web</FONT>界面层，所以目前我还在犹豫生成导出文件的时机选择在哪里？</P>
<P><FONT size=+0>5．&nbsp; </FONT>什么时候应该自己获取数据库连接，执行<FONT size=+0>native SQL</FONT>？具体需要注意些什么？</P>
<P><FONT size=+0>6．&nbsp; SiteMesh</FONT>的模板优化？</P>
<P><FONT size=+0>7．&nbsp; DisplayTag</FONT>的底层实现？</P><FONT size=+0>&nbsp;<BR></FONT><FONT size=+0>&nbsp;<BR></FONT>
<P>每个问题都比较棘手，要一个一个解决！<FONT size=+0>&nbsp;&nbsp;&nbsp; </FONT></P><FONT size=+0>&nbsp;<BR></FONT>
<P><FONT size=+0>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT>这个框架的优点是：如果熟悉了开发流程，可以大幅度的提高开发速度，如果业务不是很复杂，使用<FONT size=+0>AppGen</FONT>可以生成<FONT size=+0>60%</FONT>左右的代码，而且程序可维护性好，因为作者使用了多个设计模式对各个层面进行了封装，所以不同的模块代码风格出奇的一致，有利于开发人员快速上手，有利于接收其他开发人员遗留的代码。</P><FONT size=+0>&nbsp;<BR></FONT>
<P>兔八哥<BR></P>
<P><FONT size=+0><BR><BR><BR><BR></P>
<P>native2ascii在Ant中有任务，可以写 &nbsp; &nbsp; &nbsp; &nbsp;<BR>&lt;target name="makeresource"&gt;<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;delete file="${resource.dir}/ApplicationResources_zh_CN.properties"/&gt;<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;native2ascii src="${resource.dir}" dest="${resource.dir}" includes="ApplicationResources_cn.properties" encoding="GBK"&gt;<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&lt;mapper refid="resourcemapper"/&gt;<BR>&lt;mapper id="resourcemapper" type="glob" from="*_cn.properties" to="*_zh_CN.properties"/&gt;<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &lt;/native2ascii&gt;<BR>&nbsp; &nbsp; &nbsp; &nbsp;&lt;/target&gt; </P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </FONT></P></FONT></FONT></FONT></FONT><img src ="http://www.blogjava.net/kapok/aggbug/1852.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-08 23:53 <a href="http://www.blogjava.net/kapok/archive/2005/03/08/1852.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从AppFuse开始 </title><link>http://www.blogjava.net/kapok/archive/2005/03/08/1851.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 08 Mar 2005 15:46:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/03/08/1851.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/1851.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/03/08/1851.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/1851.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/1851.html</trackback:ping><description><![CDATA[<H2><A href="http://blog.csdn.net/flydreamgy/archive/2004/12/13/214931.aspx">http://blog.csdn.net/flydreamgy/archive/2004/12/13/214931.aspx</A></H2>
<H2><A id=viewpost.ascx_TitleUrl href="http://blog.csdn.net/flydreamgy/archive/2004/12/13/214931.aspx">从AppFuse开始</A> </H2>很早以前接触过AppFuse,一直没有什么兴趣,今天在csdn上看到了新闻,就花了时间到官方网站出看了一下.呵呵,版本改进很快.包涵的东西越来越多了,很好的把众多的OpenSource结合在了一起,从明天开始花时间详细全面的学习一下.不停的学习才是进步.哈哈<BR>这是官方的介绍.<BR>AppFuse is an application for "kickstarting" webapp development. <A class=external href="https://appfuse.dev.java.net/servlets/ProjectDocumentList">Download</A><IMG class=outlink alt="" src="http://raibledesigns.com/wiki/images/out.png">, extract and execute <B>ant new -Dapp.name=yourApp -Ddb.name=database</B> to instantly be up and running with a Tomcat/MySQL app. Uses Ant, XDoclet, Spring, Hibernate (or iBATIS), JUnit, Cactus, StrutsTestCase, Canoo's WebTest, Struts Menu, Display Tag Library, OSCache, JSTL and Struts (or Spring MVC). The Spring Framework has greatly enhanced AppFuse since February 2004. It's used throughout for its Hibernate/iBATIS support, declarative transactions, dependency binding and layer decoupling. This clean and simple framework has greatly reduced the complexity of AppFuse, and also eliminated many lines of code. In short, for J2EE - it's the best thing since sliced bread. 
<P>Features include Container Managed Authentication (CMA), Remember Me, Self Registration, Password Hint and GZip Compression. <I>The fuse to start your apps</I>. <BR><BR>作者的介绍:<BR><A href="http://today.java.net/pub/a/today/2004/07/15/thefuse.html">http://today.java.net/pub/a/today/2004/07/15/thefuse.html</A><BR>官方网站:<BR><A href="http://raibledesigns.com/wiki/Wiki.jsp?page=AppFuse">http://raibledesigns.com/wiki/Wiki.jsp?page=AppFuse</A><BR><A href="http://appfuse.dev.java.net/">http://appfuse.dev.java.net/<BR></A><A href="http://jroller.com/page/raible">http://jroller.com/page/raible</A></P><img src ="http://www.blogjava.net/kapok/aggbug/1851.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-03-08 23:46 <a href="http://www.blogjava.net/kapok/archive/2005/03/08/1851.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>