﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-思想比知识更重要 成长比成功更重要-随笔分类-Struts</title><link>http://www.blogjava.net/renyangok/category/17027.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 13 Mar 2007 07:39:43 GMT</lastBuildDate><pubDate>Tue, 13 Mar 2007 07:39:43 GMT</pubDate><ttl>60</ttl><item><title>struts中action的线程安全问题</title><link>http://www.blogjava.net/renyangok/archive/2007/02/13/99700.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 13 Feb 2007 07:31:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/02/13/99700.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/99700.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/02/13/99700.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/99700.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/99700.html</trackback:ping><description><![CDATA[
		<p>        线程安全就是你可以在多线程环境下使用它，而不需要你来对它进行特殊的处理。action都是继承至servlet的，由于servlet就是线程不安全的（指多个线程共享一个servlet对象，所以），所以不要再action中定义类变量和实例变量，否则其他线程改变了这些值，可本线程还在使用呢。</p>
<img src ="http://www.blogjava.net/renyangok/aggbug/99700.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-02-13 15:31 <a href="http://www.blogjava.net/renyangok/archive/2007/02/13/99700.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts的扩展(PlugIn,ActionServlet,RequestProcess)</title><link>http://www.blogjava.net/renyangok/archive/2007/02/13/99646.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 13 Feb 2007 03:24:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/02/13/99646.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/99646.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/02/13/99646.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/99646.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/99646.html</trackback:ping><description><![CDATA[
		<div>简介：</div>
		<div>　　我看到很多项目中，开发者实现了自己的MVC框架，并不是因为他们想做同Struts根本不同的东西，而是因为他们并没有意识到如何扩展Struts。开发自己的MVC框架可以获得全部的控制权，但是这也意味着需要很多资源来实现它（人力物力），在紧张的日程安排下，有时候这是不可能的。 </div>
		<div>　　Struts不仅仅是一个强大的框架，同时它也是可扩展的。你可以以三种方式来扩展Struts。</div>
		<div>　　1、PlugIn：如果你想在application startup或shutdown的时候做一些业务逻辑的话，那就创建你自己的PlugIn类。</div>
		<div>　　2、RequestProcessor：如果你想在请求被处理的过程中某个时刻做一些业务逻辑的话，那么创建你自己的RequestProcessor类。比如说，在每次请求执行之前，你可以扩展RequestProcessor来检查用户是否登陆了以及他是否有权限去执行某个特定的action。</div>
		<div>　　3、ActionServlet：如果你想在application startup和shutdown的时候以及请求被处理的时候做某些业务逻辑，你也可以扩张ActionServlet类。不过你应当在PlugIn和RequestProcessor都不能解决你的需求的时候来使用ActionServlet。</div>
		<div>　　在这篇文章中，我们将使用一个Struts应用的示例来示范如何使用这三种方式来扩展Struts。示例程序的代码可以从<a href="http://www.onjava.com/onjava/2004/11/10/examples/sample1.zip">http://www.onjava.com/onjava/2004/11/10/examples/sample1.zip</a>下载。两个扩展Struts成功的范例是Struts自身的Validation和Tiles框架。</div>
		<div>　　我们假设你已经比较熟悉Struts框架并且知道如何使用它创建一个简单的应用。如果你想知道更多关于Struts的内容，请参考官方主页。</div>
		<div>    第一：PlugIn<br />　<br />　　PlugIn是一个接口，你可以创建一个实现该接口的类，当application startup或shutdown的时候做些事情。</div>
		<div>　　比方说，我创建了一个使用Hibernate作为持久层的web应用，我想当应用启动的时候就初始化Hibernate，这样子当我的web应用受到第一个请求的时候，Hibernate就已经是配置好的并且可用的。同时我们想当application关闭的时候关闭Hibernate。我们可以用一个Hibernate PlugIn来实现这个需求，通过如下的两步：</div>
		<div>　　1、创建一个类实现了PlugIn接口：</div>
		<div>public class HibernatePlugIn implements PlugIn{<br />　private String configFile;<br />　// This method will be called at application shutdown time<br />　public void destroy() {<br />　　System.out.println("Entering HibernatePlugIn.destroy()");<br />　　//Put hibernate cleanup code here<br />　　System.out.println("Exiting HibernatePlugIn.destroy()");<br />　}<br />　//This method will be called at application startup time<br />　public void init(ActionServlet actionServlet, ModuleConfig config)<br />　　throws ServletException {<br />　　　System.out.println("Entering HibernatePlugIn.init()");<br />　　　System.out.println("value of init parameter " +<br />　　　getConfigFile());<br />　　　System.out.println("Exiting HibernatePlugIn.init()");<br />　　}<br />　public String getConfigFile() {<br />　　return name;<br />　}<br />　public void setConfigFile(String string) {<br />　　configFile = string;<br />　}<br />} </div>
		<div>　　实现PlugIn接口的类必须完成两个方法：init()和destroy()。当application startup的时候init()方法被调用，当shutdown的时候destroy()方法被调用。Struts还允许给你的PlugIn类传递初始化参数。为了传递参数，你必须在PlugIn类中为每一个参数创建JavaBean式的setter方法。在我们的HibernatePlugIn类中，我会把configFile的name作为参数传进去，而不是硬编码到程序中。</div>
		<div>　　2、在struts-config.xml中添加如下的代码来通告Struts有新的PlugIn：</div>
		<div>＜struts-config＞<br />　...<br />　＜!-- Message Resources --＞<br />　＜message-resources parameter= "sample1.resources.ApplicationResources"/＞</div>
		<div>　＜!-- Declare your plugins --＞<br />　＜plug-in className="com.sample.util.HibernatePlugIn"＞<br />　　＜set-property property="configFile" value="/hibernate.cfg.xml"/＞<br />　＜/plug-in＞<br />＜/struts-config＞ </div>
		<div>　　属性className是实现了PlugIn接口的类的全限定名。对于每一个初始化参数，可以使用＜set-property＞元素传递参数。在我们的例子中，我要把config文件的名字传进去，所以使用了一个带有配置文件路径的＜set-property＞。</div>
		<div>　　Struts的Tiles和Validator框架都使用PlugIn来读取配置文件进行初始化。另外两件PlugIn可以帮你做到的事情是：</div>
		<div>　　·如果你的application依赖于某些配置文件，那么你可以在PlugIn类中检查它们是否可用，如果不可用的话抛出一个ServletException，这样就可以使ActionServlet变为不可用。</div>
		<div>　　·PlugIn接口的init()方法是你可以改变ModuleConfig的最后机会，ModuleConfig是一组静态配置信息的集合，用来描述基于Struts模块。Struts将会在所有PlugIn处理完后释放ModuleConfig。</div>
		<div>
				<br />　　第二：Request是如何被处理的</div>
		<div>　　ActionServlet是Struts框架中唯一的Servlet，它负责处理所有request。无论何时接收到一个request，它都会先尝试为当前的request寻找一个sub-application。一旦一个sub-application被找到，ActionServlet就会为那个sub-application创建一个RequestProcessor对象，调用这个对象的process()方法并把HttpServletRequest和HttpServletResponse对象传入。</div>
		<div>　　RequestProcessor.process()就是大部分request被处理的地方。process()方法使用了Template Method模式实现，其中有很多独立的方法来执行请求处理的每一步骤，这些方法将会在process方法中依次被调用。比如，将会有一个独立的方法用来寻找当前request对应的ActionForm类，一个方法来检查当前用户是否有执行action mapping所必须的权限。这些给与我们极大的灵活性。在发布的Struts包中有一个RequestProcessor类提供了请求处理每一步骤的默认实现。这就意味着你可以仅仅重写你感兴趣的方法，其它的使用默认的实现。举例来说，默认地Struts调用request.isUserInRole()来检查用户是否有权限执行当前的ActionMapping；这时如果你想通过查询数据库来实现，你所要做的就是重写processRoles()方法，通过查询出的用户是否拥有必须的权限来返回true或false。</div>
		<div>　　首先我们将会看到缺省情况下，process()方法是如何实现的，然后我将会详细解释默认的RequestProcessor类中的每一个方法，这样你就可以决定哪一部分是你想要改变的。</div>
		<div>public void process(HttpServletRequest request,HttpServletResponse response)<br />throws IOException, ServletException {<br />　// Wrap multipart requests with a special wrapper<br />　request = processMultipart(request);<br />　// Identify the path component we will<br />　// use to select a mapping<br />　String path = processPath(request, response);<br />　if (path == null) {<br />　　return;<br />　}<br />　if (log.isDebugEnabled()) {<br />　　log.debug("Processing a '" + request.getMethod() + "' for path '" + path + "'");<br />　}<br />　// Select a Locale for the current user if requested<br />　processLocale(request, response);<br />　// Set the content type and no-caching headers<br />　// if requested<br />　processContent(request, response);<br />　processNoCache(request, response);<br />　// General purpose preprocessing hook<br />　if (!processPreprocess(request, response)) {<br />　　return;<br />　}<br />　// Identify the mapping for this request<br />　ActionMapping mapping =<br />　processMapping(request, response, path);<br />　if (mapping == null) {<br />　　return;<br />　}<br />　// Check for any role required to perform this action<br />　if (!processRoles(request, response, mapping)) {<br />　　return;<br />　}<br />　// Process any ActionForm bean related to this request<br />　ActionForm form = processActionForm(request, response, mapping);<br />　processPopulate(request, response, form, mapping);<br />　if (!processValidate(request, response, form, mapping)) {<br />　　return;<br />}<br />// Process a forward or include specified by this mapping<br />if (!processForward(request, response, mapping)) {<br />　return;<br />}<br />if (!processInclude(request, response, mapping)) {<br />　return;<br />}<br />// Create or acquire the Action instance to<br />// process this request<br />Action action =<br />processActionCreate(request, response, mapping);<br />if (action == null) {<br />　return;<br />}<br />// Call the Action instance itself<br />ActionForward forward = processActionPerform(request, response,action, form, mapping);<br />// Process the returned ActionForward instance<br />processForwardConfig(request, response, forward);<br />} </div>
		<div>　　1、processMutipart()：在这个方法中，Struts将会读取request来检查request的contentType是否是multipart/form-data。如果是的话，将会解析request并且将之包装到HttpServletRequest中。当你创建了一个HTML FORM用来提交数据，那么request的contentType默认就是application/x-www-form-urlencoded。但是如果你的form使用了file类型的input控件允许用户上传文件的话，你就必须将contentType改为multipart/form-data。如果是这样的情况，你就不能再通过getParameter()来获取用户提交的数据；你必须将request作为一个InputStream来读取，并且自己解析它来获得参数值。</div>
		<div>　　2、processPath()：在这个方法中，Struts将会读取request的URI，来确定路径元素，这个元素是用来获取ActionMappint元素。</div>
		<div>　　3、processLocale()：在这个方法中，Struts将会为当前request取得Locale，如果配置过的话，还可以将这个对象作为HttpSession中org.apache.struts.action.LOCALE属性的值而保存。作为这个方法的副作用，HttpSession将会被创建，如果你不想创建的话，你可以在ControllerConfig中将locale属性设为false，在struts-config.xml中象如下这样：</div>
		<div>＜controller＞<br />　＜set-property property="locale" value="false"/＞<br />＜/controller＞ </div>
		<div>　　4、processContent()：通过调用response.setContentType()来为response设置contentType。这个方法首先会尝试从struts-config.xml配置中得到contentType。缺省情况下使用text/html。如果想覆盖它，可以象如下这样：</div>
		<div>＜controller＞<br />＜set-property property="contentType" value="text/plain"/＞<br />＜/controller＞ </div>
		<div>　　5、processNoCache()：如果配置是no-cache，Struts将会为每个response设置下面三个headers：</div>
		<div>requested in struts config.xml<br />response.setHeader("Pragma", "No-cache");<br />response.setHeader("Cache-Control", "no-cache");<br />response.setDateHeader("Expires", 1); </div>
		<div>　　如果你想设置no-cache header，在struts-config.xml中加入下面信息：</div>
		<div>＜controller＞<br />＜set-property property="noCache" value="true"/＞<br />＜/controller＞  </div>
		<div>　　6、processPreprocess()：这个方法为预处理提供一个hook，可以在子类中覆盖它。它的缺省实现没有作任何事情，总是返回true。返回false的话将会终止当前请求的处理。</div>
		<div>　　7、processMapping()：这个方法将会用路径信息得到一个ActionMapping对象。也就是struts-config.xml文件中的＜action＞元素：</div>
		<div>＜action path="/newcontact" type="com.sample.NewContactAction" name="newContactForm" scope="request"＞<br />＜forward name="sucess" path="/sucessPage.do"/＞<br />＜forward name="failure" path="/failurePage.do"/＞<br />＜/action＞ </div>
		<div>　　ActionMapping元素包含了Action类的名称和处理请求使用的ActionForm等等信息。它还包含当前ActionMapping配置的ActionForwards信息。</div>
		<div>　　8、processRoles()：Struts web应用提供了一个授权方案。也就是说，一旦一个用户登入了容器，struts的processRoles()方法将会通过调用request.isUserInRole()，来检查他是否有必须的角色来运行一个给定的ActionMapping。</div>
		<div>＜action path="/addUser" roles="administrator"/＞  </div>
		<div>　　假设你有一个AddUserAction并且你只想让administrator能够增加新的user。你所要做的就是给你的AddUserAction元素增加一个role属性，这个属性的值为administrator。这样，在运行AddUserAction之前，这个方法会确保用户拥有administraotr的角色。</div>
		<div>　　9、processActionForm()：每一个ActionMapping都一个相应的ActionForm类。当Struts处理一个ActionMapping的时候，它将会从＜action＞元素的name属性中找出对应的ActionForm类的名称。</div>
		<div>＜form-bean name="newContactForm" type="org.apache.struts.action.DynaActionForm"＞<br />　＜form-property name="firstName" type="java.lang.String"/＞<br />　＜form-property name="lastName" type="java.lang.String"/＞<br />＜/form-bean＞ </div>
		<div>　　在我们的例子中，它会先在request scope中检查是否有一个org.apache.struts.action.DynaActionForm类的对象存在。如果有它将会使用这个对象，如果没有它将会创建一个新的对象并把它设置在request scope。</div>
		<div>　　10、processPopulate()：在这个方法中，Struts将会用相匹配的request参数装配ActionForm的实例变量。</div>
		<div>　　11、processValidate()：Struts将会调用你的ActionForm类的validate方法。如果你从validate()返回ActionErrors，它将会将user重定向到＜action＞元素的input属性指定的页面。</div>
		<div>　　12、processForward()和processInclude()：在这些方法中，Struts将会检查＜action＞元素的forward或include属性，如果找到了，将会把forward或include请求放置到配置的页面中。</div>
		<div>＜action forward="/Login.jsp" path="/loginInput"/＞<br />＜action include="/Login.jsp" path="/loginInput"/＞ </div>
		<div>　　你可以从这些方法的名字上猜测它们的不同：processForward()最终调用RequestDispatcher.forward()，而processInclude()调用RequestDispatcher.include()。如果你同时配置了forward和include属性，它将会总是调用forward，因为forward先被处理。</div>
		<div>　　13、processActionCreate()：这个方法从＜action＞元素的type属性中获取获得Action类的名字并且创建返回它的实例。在我们的例子中，它将会创建一个com.sample.NewContactAction类的实例。</div>
		<div>　　14、processActionPerform()：这个方法调用你的Action类的excute()方法，你的业务逻辑也就是在excute方法中。</div>
		<div>　　15、processForwardConfig()：你的Action类的excute()方法将会返回一个ActionForward对象，这个对象将指出哪个页面是显示给用户的页面。因此，Struts将会为那个页面创建一个RequestDispatcher，并且调用RequestDispatcher.forward()。</div>
		<div>　　上面的列表说明了默认的RequestProcessor实现在处理请求时每一步作的工作，以及执行的顺序。正如你所看到的，RequestProcessor是非常灵活的，允许你通过设置＜controller＞元素的属性来配置它。举例来说，如果你的应用准备生成XML内容来代替HTML，你就可以通过设置controller元素的属性来通知Struts这些情况。</div>
		<div>　　第三：创建你自己的RequestProcessor</div>
		<div>　　通过上面，我们了解到了RequestProcessor的默认实现是如何工作的。现在我们要演示一个例子来说明如何定制你自己的RequestProcessor。为了展示创建用户定制的RequestProcessor，我们将会让我们的示例实现下面两个业务需求：</div>
		<div>　　·我们想创建一个ContactImageAction类，它将生成图片而不是平常的HTML页面。</div>
		<div>　　·在每个请求处理之前，我们都想通过检查session中的userName属性来确定用户是否已经登陆。如果那个属性没有找到，我们会把用户重定向到登陆页面。</div>
		<div>　　我们将分两步实现这些业务需求。</div>
		<div>　　1、创建你的CustomRequestProcessor类，它将继承自RequestProcessor类，如下：</div>
		<div>public class CustomRequestProcessor<br />extends RequestProcessor {<br />　protected boolean processPreprocess (<br />　　HttpServletRequest request,HttpServletResponse response) {<br />　　　HttpSession session = request.getSession(false);<br />　　　//If user is trying to access login page<br />　　　// then don't check<br />　　　if( request.getServletPath().equals("/loginInput.do")<br />|| request.getServletPath().equals("/login.do") )<br />　　　　return true;<br />　　　//Check if userName attribute is there is session.<br />　　　//If so, it means user has allready logged in<br />　　　if( session != null &amp;&amp; session.getAttribute("userName") != null)<br />　　　　return true;<br />　　　else{<br />　　　　try{<br />　　　　　//If no redirect user to login Page<br />　　　　　request.getRequestDispatcher("/Login.jsp").forward(request,response);<br />　　　　}catch(Exception ex){<br />　　　　}<br />　　　}<br />　　　return false;<br />　　}</div>
		<div>　protected void processContent(HttpServletRequest request,<br />HttpServletResponse response) {<br />　　//Check if user is requesting ContactImageAction<br />　　// if yes then set image/gif as content type<br />　　if( request.getServletPath().equals("/contactimage.do")){<br />　　　response.setContentType("image/gif");<br />　　　return;<br />　　}<br />　　　super.processContent(request, response);<br />　}　<br />} </div>
		<div>　　在CustomRequestProcessor类的processPreprocess方法中，我们检查session的userName属性，如果没有找到，就将用户重定向到登陆页面。</div>
		<div>　　对于生成图片作为输出的需求，我们必须覆盖processContent方法，首先检查请求是否是/contactimage路径。如果是的话，我们就会将contentType设置为image/gif；否则设置为text/html。</div>
		<div>　　2、在你的struts-config.xml文件的＜action-mappint＞元素之后加入下面的文字，告诉Struts CustomRequestProcessor应当被用作RequestProcessor类：</div>
		<div>＜controller＞<br />＜set-property property="processorClass"value="com.sample.util.CustomRequestProcessor"/＞<br />＜/controller＞ </div>
		<div>　　请注意，当你只有很少的action类需要生成非text/html类型的输出时，你覆写processContent()方法是OK的。如果不是这样子的话，你应该创建一个Struts的子应用来处理请求生成图片的Action，并为它们将contentType设置为image/gif。</div>
		<div>　　Struts的Tiles框架就是使用它自己的RequestProcessor来装饰Struts的输出。</div>
		<div>　　第四：ActionServlet</div>
		<div>　　如果你查看你的Struts web应用的web.xml，你会看到这样的文字：</div>
		<div>＜web-app ＞<br />　＜servlet＞<br />　　＜servlet-name＞action=＜/servlet-name＞<br />　　＜servlet-class＞org.apache.struts.action.ActionServlet＜/servlet-class＞<br />　　＜!-- All your init-params go here--＞<br />　＜/servlet＞<br />　＜servlet-mapping＞<br />　　＜servlet-name＞action＜/servlet-name＞<br />　　＜url-pattern＞*.do＜/url-pattern＞<br />　＜/servlet-mapping＞<br />＜/web-app ＞ </div>
		<div>　　这意味着ActionServlet负责处理你所有Struts的请求。你可以创建一个ActionServlet的子类，当应用启动，关闭，每个请求的时候做一些特定的事情。但是在继承ActionServlet类之前，你应该尽量创建一个PlugIn或RequestProcessor去解决你的问题。在Servlet1.1之前，Tiles框架是基于ActionServlet来修饰生成的响应。但是从1.1之后，它开始使用TilesRequestProcessor类。</div>
		<div>　　总结</div>
		<div>　　决定开发你自己的MVC框架是一个非常大的决定，你必须要考虑开发和维护框架代码所花费的时间和资源。Struts是一个非常强大和稳定的框架，你可以修改它来满足你绝大多数的业务需求。</div>
		<div>　　但另一方面，也不要草率地做出扩展Struts的决定。如果你在RequestProcessor中写了一些性能比较低的代码，它将会在每次请求时执行，因而降低你整个应用的效率。而且还是有一些情况，开发自己的MVC框架要比扩展Struts好。<br /></div>
<img src ="http://www.blogjava.net/renyangok/aggbug/99646.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-02-13 11:24 <a href="http://www.blogjava.net/renyangok/archive/2007/02/13/99646.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts中多行数据的提交</title><link>http://www.blogjava.net/renyangok/archive/2007/02/13/99648.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 13 Feb 2007 03:24:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/02/13/99648.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/99648.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/02/13/99648.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/99648.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/99648.html</trackback:ping><description><![CDATA[Struts中ActionForm类型对象的javaBean属性何时初使化？<br />Struts中涉及到多行提交问题时如何处理？<br />（此方法可以处理多行相同数据的提交，但表单验证就需要在formbean中实现，不能用动态form的方法了）<br /><p><br /></p><p>在Strust 中，我们可能经常要在 ActionForm 中使用其他 JavaBean 作为属性类型，这里就存在着如何使用好这些属性与 HTML Form 之间的数据交换。 <br /><br />如在注册书籍界面中，我们通常会将书籍的各种信息封装成一个单独的新的Class ，如BookBean ，包含以下属性：书籍编号 (bookId) ，书籍名称(bookName) ，出版社(company) ，作者(author) ，版本(state) 等等，这样条理也比较清晰，以下是这个 ActionForm 的部分代码： <br />public class RegisterBookForm extends ActionForm { <br />        private String currentUsrId; <br />        private Date currentDate; <br />        private BookBean book = new BookBean(); <br /><br />        ……….. <br />        public BookBean getBook() {<br />                  return book;<br />        }<br />} <br />其中BookBean为：<br />public class BookBean {<br />        private String bookId = null;<br />        private String bookName = null;<br />        private String company = null;<br />        private String author = null;<br />        private int state;<br />        public String getBookId() {<br />                 ........<br />        }<br />        public void setBookId(String bookId) {<br />                 .........<br />        }<br />        。。。。<br />}<br />在RegisterBookForm中我们需要将BookBean实例化（<font color="#ff0000">在reset 函数中需要重新实例化</font>），这主要因为 Struts 的机制：如我们将 HTML 的 Form 元素（如 bookId ）值传给 ActionForm ，Struts 需要执行的操作是 getBook().setBookId(String bookId) ，如果这时返回的book 对象为空的话，那么赋值如何进行，而且 Struts 也不会知道如何去实例化BookBean 。有时这些 JavaBean 的类型有可能是接口，实例化更是未知，所以关于 JavaBean 类型在 ActionForm 中的实例化，你需要自行完成，而且必须完成。至于在实际操作中判断 JavaBean 是否被进行过相关操作（不再是初始状态），你需要自行判断，其实也很简单，如可在BooBean 类中编写一个函数检验一下即可。 <br /><br />ActionForm 创建完毕后，我们需要在 Jsp Form 中引用这些 JavaBean 类型的属性值，那就很简单啦，只需采用“ Form 的属性名称”+“ . ”+“ JavaBean 中的属性名称”结构构成的名称付给相关元素即可。如： <br />&lt;html:text property="book.bookId"/&gt; <br />通过这种方式我们就可以处理好 ActionForm 中的 JavaBean 类型的属性值了。 <br /><br />实际的情况可能更复杂些，我们有时可能要提交多行数据到后台，而且行内的数据是相关的，如我们需要执行借书操作，要提交的是多本书的信息，这些行内的数据，如bookId ，bookName，company等都是和bookId相关的。这时我们构建的 ActionForm 中可能需要一个列表数据（ArrayList ）类型来处理这种情况。以下是这个 ActionForm 的部分代码： <br />public class BorrowBookForm extends ActionForm {   <br />           private ArrayList books = new BorrowArrayList (); <br />           ….. <br />} <br /><br />在以上代码中，我们同样处理了ArrayList 类型数据的初始化。Struts 在给ArrayList 中的对象赋值时，当然需要先获取ArrayList 数据，然后在获取ArrayList 中某一个对象（通过 Index ），最后给对象赋值。在这里想我们可以更详细点分析浏览器端传过来数据，字段值形式如下：books[0].bookName= StrutsInAction，Struts 获取 ActionForm 中的ArrayList 数据后，通过Index（此时为0）来获取ArrayList 中封装的 JavaBean 对象，然而此时ArrayList 是空的（尽管初始化了，但是没有数据），是无法取得封装的对象的，因此我们要在Struts 获取 ArrayList 中封装的对象的时候给它创建一个，这样就保证对象的获取和赋值的成功进行，我们新建一个 BorrowArrayList 类，继承 ArrayList ，只要重写 get(int index) 方法即可，其实很简单，代码如下： <br />import java.util.ArrayList;</p><br /><p>/**<br /> * &lt;strong&gt;BorrowArrayList&lt;/strong&gt;<br /> * &lt;p&gt;书籍借阅时所有书籍列表&lt;/p&gt;<br /> *  <br /> * @author  RenZW<br /> * @version 1.0 2005.09.2<br /> * @since   1.0<br /> */<br />public class BorrowArrayList extends ArrayList {<br />    /*<br />     * (non-Javadoc)<br />     * <br />     * @see java.util.List#get(int)<br />     */<br />    public Object get(int index) {<br />        while (index &gt;= size()) {<br />            add(new BookBean());<br />        }<br />        return super.get(index);<br />    }<br />}这样我们就完成可以多行数据提交的 ActionForm 设计，这里还有点提醒，如果从浏览器端传过来的行索引是跳跃是的，如缺少中间行，且首尾行index 相差很大的话，这种方式可能不太适合。这种多行提交形式比较适合对固定行的数据处理。<br /><br />下面我们就看看如何在 Jsp 中使用这个 ActionForm ，其实只需要执行一个循环即可： <br />&lt;logic:iterate id="<font color="#ff0000">books</font>"  name=" BorrowBookForm"  property="<font color="#ff0000">books</font> " indexId="index"&gt; <br />      &lt;html:text name="<font color="#ff0000">books</font>" property="bookId" indexed="true" /&gt; <br />      &lt;html:text name="<font color="#ff0000">books</font>" property="bookName" indexed="true"/&gt; <br />      .........<br />&lt;/logic:iterate&gt; <br /><br />这里我们再分析一下，代码中出现的“<font color="#ff0000">books</font>”都是ActionForm 中的ArrayList 数据类型变量名称，请确保一致，这也是方便提交到后台的数据接收。<font color="#0000ff">indexed="true" 可以确保生成的 html 元素的名称唯一，不要缺少这个声明</font>。<br /><br />通过以上步骤，我们完成了多行数据的提交所有环节，这样 Struts 就会完成其他所有的事情，我们的代码逻辑和实现也简单多了。 </p><img src ="http://www.blogjava.net/renyangok/aggbug/99648.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-02-13 11:24 <a href="http://www.blogjava.net/renyangok/archive/2007/02/13/99648.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts的替代者JSF，Tapestry</title><link>http://www.blogjava.net/renyangok/archive/2007/02/12/99377.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Mon, 12 Feb 2007 02:14:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/02/12/99377.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/99377.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/02/12/99377.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/99377.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/99377.html</trackback:ping><description><![CDATA[在2000年初诞生的Struts以及其他类似的 MVC(Model View Controler)框架主要都是以操作为中心，且都是无状态的开发模型，是基于Action(action-based)和请求驱动(request-driven)的开发模型；而现在，基于组件(component-based)和事件驱动(event- driven)的开发模型逐渐流行起来，在很多时候都成了Struts的有利竞争者，这其中来自jcp的jsr127-JavaServer Faces(JSF)以及来自apache的Tapestry是其中的佼佼者。<br /><br />没用过JSF和Tepestry，所以对基于组件和事件驱动还只是感性认识，以后有机会尝试了之后再来详细介绍<br /><img src ="http://www.blogjava.net/renyangok/aggbug/99377.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-02-12 10:14 <a href="http://www.blogjava.net/renyangok/archive/2007/02/12/99377.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>struts学习笔记（不断更新）</title><link>http://www.blogjava.net/renyangok/archive/2007/01/23/95539.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 23 Jan 2007 06:42:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/01/23/95539.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/95539.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/01/23/95539.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/95539.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/95539.html</trackback:ping><description><![CDATA[
		<p>一、使用struts表单验证而不用struts标签的方法：<br />使用struts标签时：<br />&lt;html:javascript formName="loginForm" dynamicJavascript="true" staticJavascript="false" /&gt;</p>
		<p>&lt;html:form action="<strong>login</strong>" onsubmit="return validateLoginForm(this);"&gt;<br /> &lt;input type="text" name="account.accountId"/&gt;<br /> &lt;input type="password" name="account.password"/&gt;<br /> &lt;INPUT type="submit" value="提交"&gt;<br /> &lt;input type="reset" value="清空"&gt;<br />&lt;/html:form&gt;</p>
		<p>改成html标签后：<br />&lt;html:javascript formName="loginForm" dynamicJavascript="true" staticJavascript="false" /&gt;</p>
		<p>&lt;form action="<strong>login.do</strong>" <strong>name="loginForm"</strong> onsubmit="return validateLoginForm(this);"&gt;<br />&lt;input type="text" name="account.accountId" size="16" style="width:120;height:23"/&gt;<br />&lt;input type="password" name="account.password" size="16" style="width:120;height:23" /&gt;<br />&lt;INPUT type="submit" value="提交"&gt;<br />&lt;input type="reset" value="清空"&gt;<br />&lt;/form&gt;<br /><br />二、动态表单知识<br />  &lt;form-bean&gt;的&lt;form-property&gt;子元素用来设置动态ActionForm的属性。&lt;form-property&gt;元素的name属性指定属性名，type指定属性类型，可以把动态ActionForm的属性设为以下Java类型：</p>
		<p style="TEXT-INDENT: 23.25pt">· java.lang.BigDecimal</p>
		<p style="TEXT-INDENT: 23.25pt">· java.lang.BigInteger</p>
		<p style="TEXT-INDENT: 23.25pt">· java.lang.Boolean</p>
		<p style="TEXT-INDENT: 23.25pt">· java.lang.Byte</p>
		<p style="TEXT-INDENT: 23.25pt">· java.lang.Character</p>
		<p style="TEXT-INDENT: 23.25pt">· java.lang.Class</p>
		<p style="TEXT-INDENT: 23.25pt">· java.lang.Double</p>
		<p style="TEXT-INDENT: 23.25pt">· java.lang.Float</p>
		<p style="TEXT-INDENT: 23.25pt">· java.lang.Integer</p>
		<p style="TEXT-INDENT: 23.25pt">· java.lang.Long</p>
		<p style="TEXT-INDENT: 23.25pt">· java.lang.Short</p>
		<p style="TEXT-INDENT: 23.25pt">· java.lang.String</p>
		<p style="TEXT-INDENT: 23.25pt">· java.sql.Data</p>
		<p style="TEXT-INDENT: 23.25pt">· java.sql.Time</p>
		<p style="TEXT-INDENT: 23.25pt">· java.sql.Timestamp<br /><br />      如果表单的字段值为Java基本类型，在配置时应该用响应的包装类型来代替，例如int类型的包装类型为Integer。如果属性为Java基本类型，应该把属性设置为相应的Java包装类型。<br />      DynaActionForm基类提供了initialize()方法，它把表单的所有属性恢复为默认值。表单属性默认值由&lt;form-bean&gt;的&lt;form-property&gt;子元素的initial属性来决定。如果没有设置initial属性，则表单属性的默认值由其Java类型来自动决定，例如对象类型的默认值为null，整数类型的默认值为0，boolean类型的默认值为false，date类型的默认值为当天。<br />2、动态form的赋值（从action到页面）<br />比如修改项目信息页，要把project，projectInfo传入dform中，其中projectInfo是从project中取出的，代码如下：<br />dform.set("project", project);//project不可能为null<br />if(project.getProjectInfo() != null){<br />   dform.set("projectInfo", project.getProjectInfo());<br />}<br />原因：如果不加if(project.getProjectInfo() != null)判断，则传入dform中的projectInfo是null，在页面用&lt;html:text property="projectInfo.***"&gt;&lt;/html:text&gt;就会出错，因为取的projectInfo是null的，再取projectInfo.***肯定不对；反之，加入判断，struts会自动为formbean没设的对象new一个，就不会出错了<br /><br /><br /><br />三、用到spring框架后action都是继承org.springframework.web.struts.DispatchActionSupport，DispatchActionSupport类里有<br />1）Log log，所以在action中可直接写：log.info("Get a Bean named:Object");<br />2）protected void onInit(){};//创建action对象时先执行此方法。<br />3）protected void onDestroy(){};//回收action对象时先执行此方法<br /></p>
		<p>四、Tiles框架用法见《精通struts》第十六章。<br />详细步骤：P397<br />当编译Index.jsp时如果错误信息为： javax.servlet.jsp.JspException: ServletException in '/Layout.jsp': ServletException in</p>
		<p>'common/CatalogsBar.jsp': null<br />其中Layout.jsp为模板,common/CatalogsBar.jsp为Index.jsp其中一部分，这个信息表名不是'common/CatalogsBar.jsp'找不到，而是它内部</p>
		<p>有空引用异常，单独编译该文件得到具体错误信息：<br />java.lang.NullPointerException at _common._CatalogsBar_2e_jsp._jspService(CatalogsBar.jsp:11) [/common/CatalogsBar.jsp] 表名</p>
		<p>在第11行有空引用。</p>
		<p>五、国际化<br />1.开发和编译代码时指定字符集为UTF-8。JBuilder和Eclipse都可以在项目属性中设置。<br />2.使用过滤器，如果所有请求都经过一个Servlet控制分配器，那么使用Servlet的filter执行语句，将所有来自浏览器的请求（request）转换为UTF-8，因为浏览器发过来的请求包根据浏览器所在的操作系统编码，可能是各种形式编码。关键一句：<br />request.setCharacterEncoding("UTF-8")。<br />3.在JSP头部声明：&lt;%@ page contentType="text/html;charset= UTF-8" %&gt;。<br />在Jsp的html代码中，声明UTF-8:<br />&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"&gt;<br />4.设定数据库连接方式是UTF-8。例如连接MYSQL时配置URL如下：<br />jdbc:mysql://localhost:3306/test?useUnicode=true&amp;amp;characterEncoding=UTF-8<br />注意，上述写法是JBoss的mysql-ds.xml写法，多亏网友提示，在tomcat中&amp;amp;要写成&amp;即可。一般其他数据库都可以通过管理设置设定UTF-8<br />其他和外界交互时能够设定编码时就设定UTF-8，例如读取文件，操作XML等。<br /> <br />六、表单验证：<br />1、验证的配置是在validation.xml中,验证规则的配置是在validator-rules.xml与validator-rules-user.xml中.<br /><br />遇到错误信息如下：<br />javax.servlet.jsp.JspException: No form found under 'employeeForm' in locale 'zh_CN'<br />at org.apache.struts.taglib.html.JavascriptValidatorTag.renderJavascript(JavascriptValidatorTag.java:384)<br />at org.apache.struts.taglib.html.JavascriptValidatorTag.doStartTag(JavascriptValidatorTag.java:349)<br /><br />原因是：<br />&lt;html:javascript formName="employeeForm" cdata="false" dynamicJavascript="true" staticJavascript="false"/&gt;<br />在validation.xml中缺少了相应的配置<br />&lt;form name="employeeForm"&gt;<br />          &lt;field property="name" depends="required"&gt;<br />           &lt;arg0 key="employeeForm.name"/&gt;<br />          &lt;/field&gt;<br /> &lt;/form&gt; <br /><br />七、action中转url的方式：<br />1。转到.jsp页面：<br />return mapping.findForward("***");//***指struts-config中定义好的forward项。<br />forward项有两个选项，默认都为false，说明如下：<br />contextRelative——是否是相对目录。（如现页面为business/input.jsp，转到action后forward项到business/result.jsp，如果contextRelative为false，则转到链接business/business/result.jsp，所以此选项最好勾上）<br />redirect——是否重定向，即是否包含request信息。<br />2。返回上页显示出错信息：<br />this.saveErrors(request,errors);<br />return this.***(mapping,form,request,response);//***指本action的一个方法，为了转到上个页面显示出错信息<br />3。转到本action的其他方法：<br />ActionForward forward = new ActionForward();<br />forward.setPath(mapping.getPath() + ".do?method=****&amp;***);<br />forward.setRedirect(true););//用此方法则间接跳转，即不带request信息，不加此<br />//方法会保留request信息<br />return forward;<br /><br /><br />八、处理日期类型<br /><span id="ArticleContent1_ArticleContent1_lblContent"><font size="4"><font color="#a52a2a">struts使用日期包括将string自动转化为日期fill到form中，以及将form中的日期按照指定格式显示在html的textfield中。首先讲第一种情况的解决方法：<br /><br />创建如下类：<br /><br /><b>import</b> java.util.*;<br /><b>import</b> org.apache.commons.beanutils.*;<br /><b>import</b> java.text.SimpleDateFormat;<br /><br /><b>public</b> <b>class</b> DateConvert <b>implements</b> Converter<br />{<br />  <b>static</b> SimpleDateFormat df = <b>new</b> SimpleDateFormat("yyyy/MM/dd");<br />  <b>public</b> DateConvert()<br />  {<br /><br />  }<br />  <b>public</b> Object convert(Class type, Object value)<br />  {<br />    <br />    <b>if</b>(value==null)<b>return</b> null;<br />    <b>if</b>(((String)value).trim().length()==0) <b>return</b> null;<br /><br />    <b>if</b>(value <b>instanceof</b> String)<br />    {<br />      <b>try</b><br />      {<br />        <b>return</b> df.parse((String)value);<br />      }<br />      <b>catch</b> (Exception ex)<br />      {<br />        <b>throw</b> <b>new</b> ConversionException("输入的日期类型不合乎yyyy/MM/dd"<br />          + value.getClass());<br />      }<br /><br />    }<br />    <b>else</b><br />    {<br />      <b>throw</b> <b>new</b> ConversionException("输入的不是字符类型"+value.getClass());<br />    }<br /><br />  }<br />}<br /><br />然后在你的系统某出使用如下（如web的init方法）<br />ConvertUtils.register(new DateConvert(),java.util.Date.class);<br />参数用于表示DateConvert类负责处理java.util.Date类型的转化<br /><br />第二种情况是如何显示form中日期类型到html:text中，我用的办法是修改struts的代码，重新生成一个新的struts.jar<br />org.apache.struts.taglib.html.BaseFieldTag的doStartTag的方法<br />找到 if (value != null) {results.append(ResponseUtils.filter(value))代码行下面的内容，需要修改此处代码，以便输出日期类型<br />如下：<br /><b>if</b> (value != null) {<br />            results.append(ResponseUtils.filter(value));<br />        } <b>else</b> <b>if</b> (redisplay || !"password".equals(type)) {<br />            Object value = RequestUtils.lookup(pageContext, name, property, null);<br />            <i>//System.out.println("lijz "+value);</i></font><br /></font><font size="4"><font color="#a52a2a">            <b>if</b>(value <b>instanceof</b> java.util.Date)<br />            {<br />               <i>//System.out.println("date="+value);</i></font><br /></font><font size="4"><font color="#a52a2a">               <b>if</b> (value == null)<br />                   value = "";<br />        <b>else</b><br />        {<br />          java.util.Date d= (java.util.Date)value;<br />          <b>try</b><br />           {<br /><br />            results.append(ResponseUtils.filter(df.format(d)));<br />          }<br />          <b>catch</b>(Exception ex)<br />          {<br />            System.out.println("form error:"+ex.getMessage());<br />          }<br />          }<br />            }<br />           <b>else</b><br />           {<br />               <b>if</b> (value == null)<br />                value = "";<br />                results.append(ResponseUtils.filter(value.toString()));<br />           }<br />           <br />       }<br />        results.append("\"");<br />        results.append(prepareEventHandlers());<br />        results.append(prepareStyles());<br />        results.append(getElementClose());<br /><br />        <i>// Print this field to our output writer</i></font><br /></font><font size="4"><font color="#a52a2a">        ResponseUtils.write(pageContext, results.toString());<br /><br />        <i>// Continue processing this page</i></font><br /></font><font color="#a52a2a" size="4">        <b>return</b> (EVAL_BODY_TAG);      <br /> <br /> 重新编译struts成struts.jar.放到你需要的项目中 </font></span></p>
<img src ="http://www.blogjava.net/renyangok/aggbug/95539.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-01-23 14:42 <a href="http://www.blogjava.net/renyangok/archive/2007/01/23/95539.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>struts框架bean,html,logic标签手册</title><link>http://www.blogjava.net/renyangok/archive/2007/01/14/93753.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Sun, 14 Jan 2007 06:12:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/01/14/93753.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/93753.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/01/14/93753.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/93753.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/93753.html</trackback:ping><description><![CDATA[
		<div>（转自solol.org）<br />目录：<br />bean:cookie<br />bean:define <br />bean:header <br />bean:include <br />bean:message <br />bean:page <br />bean:parameter <br />bean:resource <br />bean:size <br />bean:struts <br />bean:write <br />html:base <br />html:cancel <br />html:select <br />html:checkbox <br />html:radio <br />html:multibox <br />html:link <br />html:errors <br />html:javascript <br />html:xhtml <br />logic:iterate <br />logic:present <br />logic:messagesPresent <br />logic:empty <br />logic:match <br />logic:equal <br />logic:forward <br />总结 <br />参考资料 <br />关于作者<br /><br />正文：<a href="http://www.solol.org/technologic/java/j-struts/#d3e77"><br /></a></div>
		<div>Struts是Apache Jakarta的一个著名的开源项目，目标是为构建基于Java的web应用程序提供一个框架。</div>
		<p>Struts提供了非常多的标签，依据功能和使用习惯的不同被分到了五个标签库中:</p>
		<ul>
				<li>Bean Tags:该标签库包含的标签可以用来创建bean、访问bean和访问bean的属性。同时提供了依据cookies、headers和parameters的值创建相关bean的能力。 
</li>
				<li>HTML Tags:该标签库包含的标签可以用来创建Struts输入表单。 
</li>
				<li>Logic Tags:该标签库包含的标签可以用来进行逻辑判断、集合迭代和流程控制。 
</li>
				<li>Nested Tags:该标签库建立在前三个标签库的基础上，具有前三个标签库的所有功能，只是允许标签间的嵌套。 
</li>
				<li>Tiles Tags:该标签库包含的标签可以用来创建tiles样式的页面。 </li>
		</ul>
		<p>这篇指南主要介绍前三个标签库中的标签。如果您对后两类标签也感兴趣可以查阅<a href="http://www.solol.org/technologic/java/j-struts/#resource">参考资料</a>中的Struts的用户指南。 </p>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e77">bean:cookie</a>
		</h2>
		<p>cookie最早是由Netscape公司提出来的，用来存储客户的少量状态信息。如果您对cookie的具体细节感兴趣可以查阅<a href="http://www.solol.org/technologic/java/j-struts/#resource">参考资料</a>中的cookie spec。 </p>
		<p>bean:cookie标签取回请求中名称为name的cookie的值。如果没有指定multiple属性则依据刚取回的值创建一个Cookie类型的bean。如果指定了multiple属性则依据刚取回的值创建一个Cookie[]类型的数组。然后用id属性值将Cookie或Cookie[]绑定到page作用域中(这种绑定是为了其它标签能够使用该值)，并创建对应的scripting变量(这种变量是为了JSP脚本能够使用该值)。</p>
		<p>下面的代码片段示例了如何使用bean:cookie标签读取名为JSESSIONID的cookie的值，并且使用了两种方式进行了输出：</p>
		<pre>&lt;logic:present cookie="JSESSIONID"&gt;
  &lt;bean:cookie id="jSession" name="JSESSIONID"/&gt;
  &lt;!-- 其它标签通过绑定到page作用域中的属性使用该值 --&gt;
  这个cookie的名称是&lt;bean:write name="jSession" property="name"/&gt;，
  值为&lt;bean:write name="jSession" property="value"/&gt;。&lt;br/&gt;
  &lt;!-- JSP脚本通过scripting变量使用该值 --&gt;
  &lt;%
    String name = jSession.getName();
    String value = jSession.getValue();
    out.println("这个cookie的名称是"+name+"，值为"+value+"。
    &lt;br/&gt;");	
  %&gt;
&lt;/logic:present&gt;
</pre>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e98">bean:define</a>
		</h2>
		<p>bean:define标签在toScope(如果没有指定值就使用page作用域)指定的作用域中创建一个新属性，同时创建一个scripting变量。我们可以通过id值使用它们。新创建的属性可以由其它标签使用，而新创建的scripting变量可以由JSP脚本使用。</p>
		<p>我们可以使用三种方式为新创建的属性和scripting变量赋值： </p>
		<ul>
				<li>通过该标签的name、property和scope取回值，并且保持类型的一致性，除非取回的值为Java的原始类型，这时会使用适合的包装器类对这些值进行包装。 
</li>
				<li>通过该标签的value指定值，这时新创建的属性和scripting变量的类型为java.lang.String。 
</li>
				<li>通过在该标签的体中嵌入值，这时新创建的属性和scripting变量的类型为java.lang.String。 </li>
		</ul>
		<p>
		</p>
		<p>下面的代码片段示例了如何使用bean:define标签创建新属性values和新scripting变量values，它将listForm中persons的值取出来赋给values:</p>
		<pre>&lt;bean:define id="values" name="listForm" property = "persons" 
	type="java.util.List"/&gt; 
</pre>
		<p>下面给出ListForm的代码片段以帮助您更好的理解，其中Person是一个只有id和name两个属性的简单bean：</p>
		<pre>public class ListForm extends ActionForm {
  private List&lt;Person&gt; persons = null;
  public List&lt;Person&gt; getPersons() {
    return persons;
  }
  public void setPersons(List&lt;Person&gt; persons) {
    this.persons = persons;
  }
  public void reset(ActionMapping mapping, 
      HttpServletRequest request) {
    persons = null;
  }
} 
</pre>
		<p>下面的代码片段示例了logic:iterate标签如何使用bean:define标签创建的新属性values:</p>
		<pre>&lt;logic:iterate id="person" name="values"&gt;
  &lt;bean:write name="person" property="id"/&gt;&lt;br/&gt;
&lt;/logic:iterate&gt;
</pre>
		<p>下面的代码片段示例了JSP脚本如何使用bean:define标签创建的新scripting变量values:</p>
		<pre>&lt;%
  Person p = new Person();
  for(int i=0;i&lt;values.size();i++){
    p = (Person)values.get(i);
    out.println(p.getId());
    out.println("&lt;br/&gt;");
  }		
%&gt;
</pre>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e148">bean:header</a>
		</h2>
		<p>bean:header标签取回请求中名称为name的header的值。如果没有指定multiple属性则依据刚取回的值创建一个String类型的bean。如果指定了multiple属性则依据刚取回的值创建一个String[]类型的数组。然后用id属性值将String或String[]绑定到page作用域中(这种绑定是为了其它标签能够使用该值)，并创建对应的scripting变量(这种变量是为了JSP脚本能够使用该值)。</p>
		<p>下面是我的浏览器发送的header的内容，这些内容和浏览器有关，因此您的浏览器发送的内容可能和下面列出的不同。不过这没有关系，因为要理解bean:header标签您只要对这些内容有一个大概的认识就足够了。</p>
		<pre>accept: */* 
accept-language: zh-cn 
accept-encoding: gzip, deflate 
user-agent: Mozilla/4.0 (compatible; MSIE 6.0; //应该在同一行
  Windows NT 5.1; SV1; .NET CLR 1.1.4322) 
host: localhost:8080 
connection: Keep-Alive 
</pre>
		<p>下面的代码片段示例了如何使用bean:header标签读取名为User-Agent的header的值，并且使用了两种方式进行了输出：</p>
		<pre>&lt;logic:present header="User-Agent"&gt;
  &lt;!-- 其它标签通过绑定到page作用域中的属性使用该值 --&gt;
  您的浏览器是&lt;bean:header id="userAgent" name="User-Agent"/&gt;
  &lt;bean:write name="userAgent"/&gt;。&lt;br/&gt;
  &lt;!-- JSP脚本通过scripting变量使用该值 --&gt;
  &lt;%
    out.println("您的浏览器是"+userAgent+"。&lt;br/&gt;");	
  %&gt;
&lt;/logic:present&gt;
</pre>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e169">bean:include</a>
		</h2>
		<p>bean:include标签对指定url(由forward、href或page确定)处的资源做一个请求，将响应数据作为一个String类型的bean绑定到page作用域，同时创建一个scripting变量。我们可以通过id值访问它们。</p>
		<p>下面的代码片段示例了bean:include标签的用法，其中include.txt文件包含要include的内容，然后将这些内容输出:</p>
		<pre>&lt;bean:include id="value" page="/include.txt"/&gt;
&lt;!-- 其它标签通过绑定到page作用域中的属性使用该值 --&gt;
&lt;bean:write name="value"/&gt;&lt;br/&gt;
&lt;!-- JSP脚本通过scripting变量使用该值 --&gt;
&lt;%
  out.println(value);	
%&gt;
</pre>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e184">bean:message</a>
		</h2>
		<p>bean:message标签用来从指定的locale中取回国际化的消息并输出，在这个过程中我们还可以传递五个以内的参数。message key可以通过key直接指定，也可以通过name和property间接的指定。</p>
		<p>bean:message标签有两种指定message key的方式，一是通过key属性直接指定；二是通过name和property属性间接的指定，其中message key是在message resources文件中定义的。</p>
		<p>我们可以在struts-config.xml文件中使用&lt;message-resources&gt;来设置message resources文件。</p>
		<p>为了介绍该标签我使用了三个message resources文件，三个文件的名字分别为Resources.properties、Resources_en.properties和Resources_zh.properties。在struts-config.xml文件中的设置(这里不用设置三个，struts会依据locale自动找到对应的文件)如下：</p>
		<pre>&lt;message-resources parameter="Resources" /&gt;
</pre>
		<p>三个message resources文件中定义的message key为：</p>
		<pre>&lt;!-- Resources.properties --&gt;
resource=Resources.properties.
from=Resources.properties.
&lt;!-- Resources_en.properties --&gt;
from=Resources_en.properties.
&lt;!-- Resources_zh.properties 
  因为文件的编码被限制为ISO8859所以要有汉字必须用jdk的native2ascii提前转换 
--&gt;
from=Resources_zh.properties.
</pre>
		<p>下面的代码片段示例了bean:message标签的用法:</p>
		<pre>&lt;bean:message key="from"/&gt;&lt;br/&gt;
&lt;bean:message key="resource"/&gt;&lt;br/&gt;
&lt;html:link action="/locale?language=en"&gt;English&lt;/html:link&gt;
&lt;html:link action="/locale?language=zh"&gt;Chinese&lt;/html:link&gt;
</pre>
		<p>上面的代码中含有改变locale的两个html:link标签，要使它们工作您的struts-config.xml文件中必须含有下面定义的form和action:</p>
		<pre>&lt;form-bean name="localeForm" 
    type="org.apache.struts.action.DynaActionForm"&gt;
  &lt;form-property name="language" type="java.lang.String" /&gt;
  &lt;form-property name="country" type="java.lang.String" /&gt;
  &lt;!--action成功后要跳到那里--&gt;
  &lt;form-property name="page"  type="java.lang.String" 
    initial="/message.jsp"/&gt;
&lt;/form-bean&gt;
	
&lt;action path="/locale" type="org.apache.struts.actions.LocaleAction"
  name="localeForm" scope="request"&gt;
&lt;/action&gt;
</pre>
		<p>在不同的locale下我们得到了如下的两个结果：</p>
		<pre>在locale为zh时的结果：
Resources_zh.properties.
Resources.properties.
在locale为en时的结果：	
Resources_en.properties.
Resources.properties.
</pre>
		<p>让我们来看一下在locale为zh时如何得到的是上面的结果。因为locale为zh所以&lt;bean:message key="from"/&gt;&lt;br/&gt;先找Resources_zh.properties这个文件从中得到form键的值。而&lt;bean:message key="resource"/&gt;&lt;br/&gt;也会先找Resources_zh.properties这个文件但这次没有找到resource键，这时Struts会到Resources.properties这个文件中找，很幸运这里找到了。如果还没有找到，或message resource文件不存在就会抛出异常。当locale为en时类似，您可以自己试试。 </p>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e235">bean:page</a>
		</h2>
		<p>bean:page标签将页上下文中的application、config、request、response 或 session取出，然后用id属性值将它们绑定到page作用域中(这种绑定是为了其它标签能够使用该值)，并创建对应的scripting变量(这种变量是为了JSP脚本能够使用该值)。</p>
		<p>下面的代码片段示例了bean:page标签取出response，然后使用bean:write标签将response的characterEncoding和contentType属性输出：</p>
		<pre>&lt;bean:page id="res" property="response"/&gt;
&lt;!-- 其它标签通过绑定到page作用域中的属性使用该值 --&gt;
&lt;bean:write name="res" property="characterEncoding"/&gt;&lt;br/&gt;
&lt;bean:write name="res" property="contentType"/&gt;&lt;br/&gt;
&lt;!-- JSP脚本通过scripting变量使用该值 --&gt;
&lt;%
  String characterEncoding = res.getCharacterEncoding();
  String contentType = res.getContentType();
  out.println(characterEncoding+"&lt;br/&gt;");	
  out.println(contentType+"&lt;br/&gt;");	
%&gt;
</pre>
		<p>您可以用和上面类似的代码访问application、config、request 或 session中的任何一个对象。</p>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e253">bean:parameter</a>
		</h2>
		<p>bean:parameter标签取回请求中的参数值。如果没有指定multiple属性则依据刚取回的值创建一个String类型的bean。如果指定了multiple属性则依据刚取回的值创建一个String[]类型的数组。然后用id属性值将String或String[]绑定到page作用域中(这种绑定是为了其它标签能够使用该值)，并创建对应的scripting变量(这种变量是为了JSP脚本能够使用该值)。</p>
		<p>下面的两个代码片段使用相同的url传递参数，url的形式为http://127.0.0.1:8080/struts-demo/parameter.jsp?param=1&amp;param=2&amp;param=3。前面的代码片段中没有指定multiple属性，因此p是String类型而且仅仅读取了参数的第一个值。后面的代码片段中指定了multiple属性的值，因此ps是String[]类型的包含所有参数的值。</p>
		<pre>&lt;bean:parameter id="p" name="param"/&gt;
&lt;bean:write name="p"/&gt;
</pre>
		<pre>&lt;bean:parameter id="ps" multiple="true" name="param"/&gt;
&lt;logic:iterate id="p" name="ps"&gt;
  &lt;bean:write name="p"/&gt;&lt;br/&gt;
&lt;/logic:iterate&gt;
</pre>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e271">bean:resource</a>
		</h2>
		<p>bean:resource标签取回指定的web应用程序的资源，以InputStream或String的形式保存到page作用域中并且创建scripting变量。采用什么形式取决于标签的input属性，如果指定input则以InputStream的形式保存，如果没有指定input则以String的形式保存。</p>
		<p>下面的两个代码片段示例了bean:resource标签，其中resource.txt是要使用的资源文件。前面的代码片段中没有指定input属性，因此以String的形式处理资源文件，bean:write标签输出资源文件的内容。后面的代码片段中指定了input属性的值，因此以InputStream的形式使用资源文件，两个bean:write标签分别输出InputStream对象的实例名(如java.io.ByteArrayInputStream@16dadf9)和类名(如class java.io.ByteArrayInputStream)。 </p>
		<pre>&lt;bean:resource id="str" name="/resource.txt"/&gt;
&lt;!-- 其它标签通过绑定到page作用域中的属性使用该值 --&gt;
&lt;bean:write name="str"/&gt;&lt;br/&gt;
&lt;!-- JSP脚本通过scripting变量使用该值 --&gt;
&lt;%
  out.println(str+"&lt;br/&gt;");
%&gt;
</pre>
		<pre>&lt;bean:resource id="is" input="true" name="/resource.txt"/&gt;
&lt;!-- 其它标签通过绑定到page作用域中的属性使用该值 --&gt;
&lt;bean:write name="is"/&gt;&lt;br/&gt;
&lt;bean:write name="is" property="class"/&gt;
&lt;!-- JSP脚本通过scripting变量使用该值 --&gt;
&lt;%
  out.println(is+"&lt;br/&gt;");
  out.println(is.getClass()+"&lt;br/&gt;");
%&gt;
</pre>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e290">bean:size</a>
		</h2>
		<p>bean:size标签创建一个java.lang.Integer类型的bean，这个bean的值为该标签指定的Collection或Map中所含元素的个数。 这可以和logic:iterate标签配合使用，因为logic:iterate标签不能得到所叠代的集合的元素的个数，这有时候很不方便。 </p>
		<p>下面的代码片段示例了bean:size标签取出persons中还有元素的个数，其中listForm和persons的定义参见bean:define标签部分：</p>
		<pre>&lt;logic:notEmpty name="listForm" property = "persons"&gt;		
  &lt;bean:size id="size" name="listForm" property = "persons"/&gt;
  &lt;bean:write name="size"/&gt;		
&lt;/logic:notEmpty&gt;  
</pre>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e305">bean:struts</a>
		</h2>
		<p>bean:struts标签取回Struts的内部对象formBean、forward或mapping的值，然后用id绑定到page作用域中(这种绑定是为了其它标签能够使用该值)，并创建对应的scripting变量(这种变量是为了JSP脚本能够使用该值)。</p>
		<p>下面的代码片段示例了bean:struts标签取出listForm对象，让我们先来看一下listForm的定义在读代码：</p>
		<pre>&lt;!-- listForm的定义&lt;form-bean name="listForm" 
  type="org.solo.struts.form.ListForm" /&gt; --&gt;
&lt;bean:struts id="listFormBean" formBean="listForm"/&gt;
name:&lt;bean:write name="listFormBean" property="name"/&gt;&lt;br/&gt;
type:&lt;bean:write name="listFormBean" property="type"/&gt;&lt;br/&gt;
dynamic:&lt;bean:write name="listFormBean" property="dynamic"/&gt;&lt;br/&gt;
</pre>
		<p>上面代码运行的结果为：</p>
		<pre>name:listForm 
type:org.solo.struts.form.ListForm
dynamic:false 
</pre>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e326">bean:write</a>
		</h2>
		<p>bean:write标签将指定的bean的属性值写到当前的JspWriter中，并且可以对输出进行格式化。</p>
		<p>下面的代码片段示例了bean:write标签输出User-Agent：</p>
		<pre>&lt;logic:present header="User-Agent"&gt;
  &lt;bean:header id="header" name="User-Agent"/&gt;
  &lt;bean:write name="header"/&gt;
&lt;/logic:present&gt;
</pre>
		<p>下面的代码片段示例了bean:write标签格式化输出当前日期，其中now是在DataForm中定义的一个java.util.Date类型的域(值为new Date())，format.date.standard是在资源文件中的一个键(format.date.standard=yyyy-MM-dd)：</p>
		<pre>&lt;bean:define id="date" name="dataForm" property="now"/&gt;
&lt;br/&gt;&lt;bean:write name="date"/&gt;
&lt;br/&gt;&lt;bean:write name="date" format="MM/dd/yyyy"/&gt;
&lt;br/&gt;&lt;bean:write name="date" formatKey="format.date.standard"/&gt;
</pre>
		<p>上面代码运行的结果为：</p>
		<pre>Sun Jun 04 17:04:05 CST 2006
06/04/2006
2006-06-04
</pre>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e355">html:base</a>
		</h2>
		<p>虽然这里的标题是html:base标签，但是这里也是这篇指南要介绍的第一个Struts html标签。因此我想在这里从整体上简单的介绍一下，最后给出一个对照表。其中的绝大多数标签就不一一介绍了，要介绍的也不会在这里介绍，而是将其独立出来以显重要性。</p>
		<p>从用户处收集数据是动态web应用非常重要的一个方面，因此构建输入表单也就自然而然的成为struts框架的一个重要内容。Struts html标签库含有创建Struts输入表单的标签，和其它标签库(bean、logic、nested和tiles)中的标签一起协作就可以产生基于html的用户界面。</p>
		<p>下面的对照表会使您对Struts html标签库有一个整体的印象：</p>
		<p>
		</p>
		<div>图示 1. Struts HTML标签和HTML元素对照表</div>
		<img height="755" alt="Struts HTML标签和HTML元素对照表" src="http://www.solol.org/technologic/java/j-struts/strutshtmlvshtml.gif" width="679" />
		<p>
		</p>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e378">html:cancel</a>
		</h2>
		<p>html:cancel标签生成一个取消按钮。当点击该按钮后action servlet会绕过相应的form bean的validate()方法，同时将控制权交给相应的action。在该action中可以使用Action.isCancelled(HttpServletRequest)方法判断是否被取消了。如果返回true表示这个action被取消了，否则表示这个action没有被取消。</p>
		<p>请注意，如果您修改了html:cancel标签的property属性值，那么struts提供的cancel探测机制就失效了，您自己必须提供类似的机制。</p>
		<p>下面是可取消的action的配置文件，注意&lt;set-property property="cancellable" value="true"/&gt;这一行，如果不添加Struts会抛出org.apache.struts.action.InvalidCancelException异常。这是我在完成本指南的过程中发现的唯一向下不兼容的地方。</p>
		<pre>&lt;action path="/cancel" 
  type="org.solo.struts.action.CancelAction" name="cancelForm" 
    scope="request"&gt;
  &lt;set-property property="cancellable" value="true"/&gt;
  &lt;forward name="success" path="/cancel.jsp" /&gt;
&lt;/action&gt;
</pre>
		<p>下面是html:cancel标签的代码：</p>
		<pre>&lt;html:cancel&gt;取消&lt;/html:cancel&gt;
</pre>
		<p>下面是对应的action中的代码：</p>
		<pre>if(isCancelled(request)){			
  //action被取消时要做的事情写在这里
  return mapping.findForward("cancel");
}else{
  //action没有被取消时要做的事情写在这里
  return mapping.findForward("success");
}
</pre>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e409">html:select</a>
		</h2>
		<p>html:select标签生成一个select元素。是单选还是多选取决于该标签的multiple属性。如果指定了multiple="true"则为多选，此时对应的属性应该是一个数组。如果没有指定multiple="true"则为单选，此时对应的属性应该是标量。</p>
		<p>注意：为了正确的处理没有做任何的选择的情况，在ActionForm中的reset()方法中必须将标量属性设置为默认值而将数组的长度置为0。</p>
		<p>另外的一个重要问题就是struts如何生成option元素了，这个任务struts交给了html:option、html:options和html:optionsCollection三个标签。</p>
		<p>html:option标签</p>
		<p>html:option标签生成一个HTML的option元素。该标签必须嵌在html:select标签中。它的显示文本来自其标签体，也可以来自于资源文件。它的value属性用来指定什么值将要被提交。</p>
		<pre>&lt;html:option value="one"&gt;one&lt;/html:option&gt;
&lt;html:option value="two"&gt;two&lt;/html:option&gt;
</pre>
		<p>html:options标签</p>
		<p>html:options标签生成多个HTML的option元素。该标签必须嵌在html:select标签中。而且工作方式有些特殊，它的name与property属性和其它标签的name与property属性意义不一致，让我们具体看一下它的工作方式。</p>
		<ul>
				<li>指定collection属性 
</li>
				<li>没有指定collection属性 </li>
		</ul>
		<p>指定collection属性</p>
		<p>让我通过示例来介绍在指定collection属性时该标签的工作方式，首先要说明selectForm中的persons和listForm中的persons完全一致。请参见bean:define标签。</p>
		<p>下面的代码先利用bean:define标签将selectForm中的persons取到page作用域中，然后html:options标签再依据collection="personCollection"选中这个persons并将其中的每一个对象(Person类型)生成一个option元素。该标签的property="id"表示persons中的对象(Person类型)的id属性将作为option元素的value值。该标签的labelProperty="name"表示persons中的对象(Person类型)的name属性将作为option元素的label值。</p>
		<p>当这个select提交时所选择的值将被提交到selectForm(name="selectForm")中的person对象(这是在SelectForm中声明的一个Person类型的域专门用来接收提交的值)的id属性中(property="person.id")。</p>
		<pre>&lt;bean:define id="personCollection" name="selectForm" 
  property="persons"/&gt;
&lt;html:select name="selectForm" property="person.id" size="1"&gt;
  &lt;html:options collection="personCollection" property="id" 
    labelProperty="name"/&gt; 
&lt;/html:select&gt;
</pre>
		<p>没有指定collection属性</p>
		<p>让我通过示例来介绍没有指定collection属性时该标签的工作方式，先来看看ids和names的定义：</p>
		<pre>  private List&lt;String&gt; ids = null;
  private List&lt;String&gt; names = null;
</pre>
		<p>上面的代码来自SelectForm，其中ids是一个String的列表，names也是一个String的列表。我们暂时假定这两个列表含有相同数目的元素。有了这些让我们开始介绍下面的代码。html:options标签用ids中的第i个值作为option元素的value值同时使用names中相同位置的值(第i个值)作为option元素的label值。如果ids比names长那么多出的ids中的值将即作为option的value又作为option的label。如果ids比names短那么多出的names的值会被丢掉。</p>
		<p>当这个select提交时所选择的值将被提交到selectForm(name="selectForm")中的person对象(这是在SelectForm中声明的一个Person类型的域专门用来接收提交的值)的id属性中(property="person.id")。</p>
		<pre>&lt;html:select name="selectForm" property="person.id" size="1"&gt;
  &lt;html:options property="ids" labelProperty="names"/&gt; 
&lt;/html:select&gt;
</pre>
		<p>html:optionsCollection标签</p>
		<p>html:optionsCollection标签生成多个HTML的option元素。该标签必须嵌在html:select标签中。它的功能和html:options标签的相同，但是它的name与property属性和其它标签的name与property属性意义一致，理解起来比较自然。</p>
		<p>让我通过示例来介绍html:optionsCollection标签的用法。首先依据name="selectForm"和property="persons"取到selectForm中的persons列表，然后将列表中的对象(Person类型)的id属性作为option元素的value值(value="id")，将列表中的对象(Person类型)的name属性作为option元素的label值(label="name")。</p>
		<pre>&lt;html:select name="selectForm" property="person.id" size="1"&gt;
  &lt;html:optionsCollection name="selectForm" property="persons" 
  label="name" value="id"/&gt; 
&lt;/html:select&gt;
</pre>
		<p>下面是一个多选的示例，虽然示例中使用了html:options标签，但是html:option和html:optionsCollection也能够用来多选。而且您还必须意识到html:option、html:options和html:optionsCollection这三个标签可以同时使用。代码中的personIds是SelectForm中声明的一个String[]类型的数组用来接收提交的多个值。</p>
		<pre>&lt;html:select name="selectForm" property="personIds" 
    multiple="true" size="2"&gt;
  &lt;html:options property="ids" labelProperty="names"/&gt; 
&lt;/html:select&gt;
</pre>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e504">html:checkbox</a>
		</h2>
		<p>html:check标签生成一个checkbox。这里的value值可以是true，yes或on。如果您要提交其它的值(如某种形式的标识)应该考虑使用html:multibox标签。</p>
		<p>注意:为了正确的处理没有选中的checkbox您必须在reset()中设置对应的属性为false。</p>
		<p>下面的代码示例了html:checkbox标签的用法，其中CheckboxForm中声明了三个boolean类型的域，如下：</p>
		<pre>
				<!-- 略掉了get方法(也可以是isXXX形式)和set方法 -->	
  private boolean one = false;
  private boolean two = false;
  private boolean three = false;
</pre>
		<pre>&lt;html:checkbox name="checkboxForm" property="one"&gt;
  One
&lt;/html:checkbox&gt;
&lt;html:checkbox name="checkboxForm" property="two"&gt;
  Two
&lt;/html:checkbox&gt;
&lt;html:checkbox name="checkboxForm" property="three"&gt;
  Three
&lt;/html:checkbox&gt;
</pre>
		<p>如果选中后被提交则相应的属性的值为true。</p>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e531">html:radio</a>
		</h2>
		<p>html:radio标签生成一个radio。主要的用法有两种，下面我们通过代码来示例。</p>
		<p>下面的代码示例了html:radio标签的一般用法，如果被提交则选中的radio的value值将被提交到radioForm中的id中。</p>
		<pre>&lt;html:radio name="radioForm" property="id" value="00001"&gt;
  One
&lt;/html:radio&gt;
&lt;html:radio name="radioForm" property="id" value="00002"&gt;
  Two
&lt;/html:radio&gt;
</pre>
		<p>下面的代码示例了html:radio标签的典型用法，其中的persons和bean:define标签中的一致，您可以参考bean:define标签。我只介绍这个&lt;html:radio idName="person" property="id" value="id"&gt;，idName指定html:radio要使用的bean(这里为person)，value="id"表示person的id属性将作为radio元素的value值而property="id"表示提交时选中的radio的值将被提交给radioForm中的id属性。 </p>
		<pre>&lt;logic:notEmpty name="radioForm" property="persons"&gt;		
  &lt;logic:iterate id="person" name="radioForm" property="persons"&gt;
    &lt;html:radio idName="person" property="id" value="id"&gt;
      &lt;bean:write name="person" property="name"/&gt;
    &lt;/html:radio&gt; 
  &lt;/logic:iterate&gt;		
&lt;/logic:notEmpty&gt;
</pre>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e552">html:multibox</a>
		</h2>
		<p>html:multibox标签生成多个checkbox。当您要使用大量的checkbox时使用这个标签非常方便，可以使您避免在ActionForm中声明大量的boolean类型的变量，带之以一个数组就行了。</p>
		<p>注意:为了正确的处理没有选中的checkbox您必须在reset()中设置数组的长度为0。</p>
		<p>下面的代码示例了html:multibox标签的一般用法，如果被提交则选中的所有checkbox的value值将被提交到multiboxForm中的selectedItems中，这是一个String[]数组。</p>
		<pre>&lt;html:multibox name="multiboxForm" property="selectedItems" 
  value="00001"/&gt;
&lt;html:multibox name="multiboxForm" property="selectedItems" 
  value="00002"/&gt;
</pre>
		<p>下面的代码示例了html:multibox标签的典型用法：</p>
		<pre>&lt;logic:iterate id="person" name="multiboxForm" property="persons"&gt; 
  &lt;html:multibox property="selectedItems"&gt;
    &lt;bean:write name="person" property="id"/&gt;
  &lt;/html:multibox&gt; 
  &lt;bean:write name="person" property="name"/&gt; 
&lt;/logic:iterate&gt;
</pre>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e578">html:link</a>
		</h2>
		<p>html:link标签生成一个锚点(&lt;a&gt;)元素。我从html:link标签如何构造基本url和如何构造query参数两个方面来介绍这个标签。</p>
		<p>构造基本url是依据该标签的如下四个属性进行的，这四个属性一次只能出现一个。让我们看看它们： </p>
		<ul>
				<li>forward，指定一个全局ActionForward的名称。另外如果forward是module相关的则该forward必须指向一个action而不能是一个页面。 
</li>
				<li>action，指定一个Action的名称。 
</li>
				<li>href，struts会直接使用这个值而不会对其进行任何处理。 
</li>
				<li>page，指定一个页面的路径，必须以/开始。 </li>
		</ul>
		<p>
		</p>
		<p>构造query参数，下面列举了可能的形式，其中baseurl只是一个占位符： </p>
		<ul>
				<li>baseurl?p=00001(单参单值) 
</li>
				<li>baseurl?p1=00001&amp;p2=00002&amp;p3=00003(多参单值) 
</li>
				<li>baseurl?p=00001&amp;p=00002&amp;p=00003(单参多值) 
</li>
				<li>baseurl?p1=00001&amp;p2=00002&amp;p=00001&amp;p=00002&amp;p=00003(混合) </li>
		</ul>
		<p>
		</p>
		<p>下面我们以代码示例来说明每一种html:link的用法，首先让我们花点时间看看相关action中的代码：</p>
		<pre>&lt;!-- 下面所有代码的数据都是在这里构造的 --&gt;
public ActionForward execute(ActionMapping mapping,ActionForm form,
    HttpServletRequest request,HttpServletResponse response) {

  DataForm dataForm = (DataForm) form;

  dataForm.setParam("00001");

  HashMap paramSingleMap = new HashMap();
  paramSingleMap.put("p1","00001");
  paramSingleMap.put("p2","00002");
  paramSingleMap.put("p3","00003");		
  dataForm.setParamSingleMap(paramSingleMap);

  HashMap paramMultiMap = new HashMap();
  paramMultiMap.put("p",new String[]{"00001","00002","00003"});		
  dataForm.setParamMultiMap(paramMultiMap);

  HashMap paramSMMap = new HashMap();
  paramSMMap.put("p1","00001");
  paramSMMap.put("p2","00002");
  paramSMMap.put("p",new String[]{"00001","00002","00003"});		
  dataForm.setParamSMMap(paramSMMap);

  List&lt;Person&gt; persons = new ArrayList&lt;Person&gt;();		
  Person person1 = new Person();
  person1.setId("00001");
  person1.setName("赵辰");		
  Person person2 = new Person();
  person2.setId("00002");
  person2.setName("李为芳");		
  Person person3 = new Person();
  person3.setId("00003");
  person3.setName("王微");		
  persons.add(person1);
  persons.add(person2);
  persons.add(person3);		
  dataForm.setPersons(persons);

  return mapping.findForward("success");
}
</pre>
		<pre>&lt;!-- 这用来设置一个位置 --&gt;
&lt;html:link linkName="top"/&gt;
&lt;!-- 这用来定位到上面的那个位置 --&gt;
&lt;html:link page="/link.do" anchor="top"&gt;Go Top&lt;/html:link&gt;
</pre>
		<pre>&lt;html:link page="/link.do" paramId="p" paramName="dataForm" 
    paramProperty="param"&gt;
  单参单值
&lt;/html:link&gt;&lt;br/&gt;
&lt;html:link page="/link.do" name="dataForm" property="paramSingleMap"&gt;
  多参单值
&lt;/html:link&gt;&lt;br/&gt;
&lt;html:link page="/link.do" name="dataForm" property="paramMultiMap"&gt;
  单参多值
&lt;/html:link&gt;&lt;br/&gt;
&lt;html:link page="/link.do" name="dataForm" property="paramSMMap"&gt;
  混合
&lt;/html:link&gt;
</pre>
		<p>下面的代码示例了html:link标签的indexed属性和indexId属性的用法，这两个属性只有html:link标签嵌套在logic:iterate标签中时才可用。</p>
		<pre>&lt;logic:iterate id="person" name="dataForm" property="persons"&gt;
  &lt;html:link action="/link.do" paramId="person" paramName="person" 
	  paramProperty="id" indexed="true" indexId="number"&gt;
    person
  &lt;/html:link&gt;
  &lt;br/&gt;
&lt;/logic:iterate&gt;
&lt;!-- 下面是上面代码的运行结果(产生的html)
  &lt;a href="/struts-demo/link.do?person=00001&amp;number=0"&gt;赵辰&lt;/a&gt;&lt;br/&gt;
  &lt;a href="/struts-demo/link.do?person=00002&amp;number=1"&gt;李为芳&lt;/a&gt;&lt;br/&gt;
  &lt;a href="/struts-demo/link.do?person=00003&amp;number=2"&gt;王微&lt;/a&gt;&lt;br/&gt; 
  其中的number是由indexId="number"确定的，而该参数的值为元素在集合中的位置。
--&gt;
</pre>
		<p>html:rewrite标签和html:link标签类似只是不生成锚点(&lt;a&gt;)，而是简单的输出字符串。</p>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e645">html:errors</a>
		</h2>
		<p>html:errors标签和html:messages标签的功能相似，所以我们放到一起来介绍。</p>
		<p>html:errors标签将由name属性指定的ActionMessages、ActionErrors、String和String[]直接输出到页面中。</p>
		<p>html:messages标签将用由name属性(注意message属性值对它的影响)指定的ActionMessages、ActionErrors、String和String[]创建一个新的属性和scripting变量，使用id属性值作为名称。</p>
		<p>html:errors标签和html:messages标签的property属性是用来为errors和messages分类的。我们可以给这两个标签指定property属性，以便只显示某一类的错误或消息。</p>
		<p>在资源文件增加了如下的内容：</p>
		<pre># -- standard errors --
errors.header=&lt;ul&gt;
errors.prefix=&lt;li&gt;
errors.suffix=&lt;/li&gt;
errors.footer=&lt;/ul&gt;

error=error with none value .
error1=error1 with one value is {0} .
error2=error2 with two values are {0} , {1} .
error3=error3 with three values are {0} , {1} , {2} .
error4=error4 with four values are {0} , {1} , {2} ,{3} .
</pre>
		<p>下面的代码示例了actionErrors的构造：</p>
		<pre>public ActionErrors validate(ActionMapping mapping, 
    HttpServletRequest request) {
  ActionErrors actionErrors = new ActionErrors();
  actionErrors.add("property1",
  new ActionMessage("error"));
  actionErrors.add("property2",
  new ActionMessage("error1","value0"));
  actionErrors.add("property2",
  new ActionMessage("error2","value0","value1"));
  actionErrors.add("property3",
  new ActionMessage("error3","value0","value1","value2"));
  actionErrors.add("property3",
  new ActionMessage("error4","value0","value1","value2","value3"));

  actionErrors.add("property4",
  new ActionMessage("error1",new Object[]{"value0"}));
  actionErrors.add("property4",
  new ActionMessage("error2",new Object[]{"value0","value1"}));
  actionErrors.add("property4",
  new ActionMessage("error3",new Object[]{"value0","value1",
    "value2"}));
  actionErrors.add("property5",
  new ActionMessage("error4",new Object[]{"value0","value1",
    "value2","value3"}));

  actionErrors.add("notBundle",
  new ActionMessage("not a bundle key",false));
  return actionErrors;
}	
</pre>
		<p>errors标签代码示例：</p>
		<pre>&lt;html:errors/&gt;
&lt;br/&gt;	
&lt;html:errors property="property4"/&gt;
</pre>
		<p>messages标签代码示例：</p>
		<pre>&lt;logic:messagesPresent&gt;
  &lt;ul&gt;
  &lt;html:messages id="message"&gt;
    &lt;li&gt;&lt;bean:write name="message"/&gt;&lt;/li&gt;
  &lt;/html:messages&gt;
  &lt;/ul&gt;
&lt;/logic:messagesPresent&gt;
&lt;br/&gt;
&lt;logic:messagesPresent&gt;
  &lt;ul&gt;
  &lt;html:messages id="message" property="property4"&gt;
    &lt;li&gt;&lt;bean:write name="message"/&gt;&lt;/li&gt;
  &lt;/html:messages&gt;
&lt;/ul&gt;
&lt;/logic:messagesPresent&gt;
</pre>
		<p>
		</p>
		<div>图示 2. 上面的两段代码示例运行结果相同，如下所示：</div>
		<img height="368" alt="运行结果" src="http://www.solol.org/technologic/java/j-struts/errorsmessages.gif" width="553" />
		<p>
		</p>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e697">html:javascript</a>
		</h2>
		<p>html:javascript标签生成用于校验的javascript脚本代码。</p>
		<p>下面的代码片段示例了html:javascript标签的基本用法，其中formName属性的值是在validation.xml文件的&lt;formset&gt;中定义的form的名称。有一点值得注意的是在确定&lt;formset&gt;时要使用合适的language属性值。</p>
		<pre>&lt;html:javascript formName="dataForm"/&gt;
</pre>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e713">html:xhtml</a>
		</h2>
		<p>html:xhtml标签在页面中一出现就是告诉该页的所有其它的Struts html标签以XHTML1.0兼容的形式输出。这和将html:html标签的xhtml属性值指定为true有些类似。如果上述情况都没有出现，Struts html标签将以html4.01兼容的形式输出。</p>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e722">logic:iterate</a>
		</h2>
		<p>logic:iterate标签用来迭代集合，您可以使用如下方式来为这个标签指定其要叠代的集合：</p>
		<ul>
				<li>使用一个运行时表达式，这个表达式的值是一个集合。 
</li>
				<li>用name属性引用一个JSP Bean，这个JSP Bean本身就是一个集合。 
</li>
				<li>用name属性引用一个JSP Bean，这个JSP Bean的一个属性是一个集合，这时可以联合使用property来指定这个集合。 </li>
		</ul>
		<p>上面所提到的集合可以是：</p>
		<ul>
				<li>对象类型或原子类型的数组(Array)。 
</li>
				<li>java.util.Collection的实现，包括ArrayList，Vector。 
</li>
				<li>java.util.Enumeration的实现。 
</li>
				<li>java.util.Iterator的实现。 
</li>
				<li>java.util.Map的实现，包括HashMap，Hashtable和TreeMap。 </li>
		</ul>
		<p>如果您叠代的集合中含有null的值，这时需要采取一定的措施，因为这时logic:iterate不会在page作用域中创建对象。一般是使用&lt;logic:present&gt;标签或&lt;logic:notPresent&gt;标签来判断一下。</p>
		<p>下面是logic:iterate叠代ArrayList的示例的对象引用关系和部分代码：</p>
		<p>
		</p>
		<div>图示 3. logic:iterate中对象的引用关系</div>
		<img height="258" alt="logic:iterate中对象的引用关系" src="http://www.solol.org/technologic/java/j-struts/iterate.gif" width="614" />
		<p>
		</p>
		<p>图中的persons列表是在ListAction中填充的，在这里只是简单的加入了三个Person对象，在实际的应用中这些数据应该取自数据库。具体的代码如下：</p>
		<pre>public ActionForward execute(ActionMapping mapping,ActionForm form,
    HttpServletRequest request,	HttpServletResponse response) {

  ListForm listForm = (ListForm) form;
	
  List&lt;Person&gt; persons = new ArrayList&lt;Person&gt;();
	
  Person person1 = new Person();
  person1.setId("00001");
  person1.setName("赵辰");
	
  Person person2 = new Person();
  person2.setId("00002");
  person2.setName("李为芳");
	
  Person person3 = new Person();
  person3.setId("00003");
  person3.setName("王微");
	
  persons.add(person1);
  persons.add(person2);
  persons.add(person3);
	
  listForm.setPersons(persons);
	
  return mapping.findForward("success");
}
</pre>
		<p>标签输出的结果为：</p>
		<pre>00001--&gt;赵辰 
00002--&gt;李为芳 
00003--&gt;王微
</pre>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e791">logic:present</a>
		</h2>
		<p>如果指定的值出现该标签就会创建其标签体内的内容。该标签用于以下情况：</p>
		<ul>
				<li>检查具有指定名称的cookie是否出现。 
</li>
				<li>检查具有指定名称的header是否出现。 
</li>
				<li>检查具有指定名称的JSP Bean是否出现 或 检查具有指定名称的JSP Bean中的property属性是否出现。 
</li>
				<li>检查request中指定名称的参数是否出现。 
</li>
				<li>检查当前的认证用户是否与指定的安全角色相关联。 
</li>
				<li>检查当前认证的主体是否具有指定的名字。 </li>
		</ul>
		<p>下面的代码示例了logic:present标签检查具有指定名称User-Agent的header是否出现：</p>
		<pre>&lt;logic:present header="User-Agent"&gt;
  您的浏览器是&lt;bean:header id="userAgent" name="User-Agent"/&gt;
    &lt;bean:write name="userAgent"/&gt;。&lt;br/&gt;
&lt;/logic:present&gt;
</pre>
		<p>logic:notPresent标签的应用正好和logic:present标签相反。</p>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e832">logic:messagesPresent</a>
		</h2>
		<p>logic:messagesPresent标签用于以下情况：</p>
		<ul>
				<li>在request作用域中存在一个ActionMessages对象，标签的property属性和ActionMessages中的property对应。 
</li>
				<li>在request作用域中存在一个ActionErrors对象，标签的property属性和ActionErrors中的property对应。 
</li>
				<li>存在一个String对象，将其转换(构造)成ActionMessage然后在添加到ActionMessages中。 
</li>
				<li>存在一个String Array对象，将数组中的每一个String转换成一个ActionMessage，在将其添加到ActionMessages中。 </li>
		</ul>
		<p>标签的message属性值为true时将以Globals.MESSAGE_KEY为key在request作用域中查找Message，其它情况下，将name的值作为key查找，如果name 没有出现，默认值为Globals.ERROR_KEY。 </p>
		<p>下面的代码示例了logic:messagesPresent标签的典型用法：</p>
		<pre>&lt;logic:messagesPresent&gt;
  &lt;ul&gt;
  &lt;html:messages id="message"&gt;
    &lt;li&gt;&lt;bean:write name="message"/&gt;&lt;/li&gt;
  &lt;/html:messages&gt;
  &lt;/ul&gt;
&lt;/logic:messagesPresent&gt;	
</pre>
		<p>标签logic:messagesNotPresent的应用正好和logic:messagesPresent的应用相反。</p>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e869">logic:empty</a>
		</h2>
		<p>logic:empty标签是用来判断是否为空的。如果为空，该标签体中嵌入的内容就会被处理。该标签用于以下情况：</p>
		<ul>
				<li>当Java对象为null时 
</li>
				<li>当String对象为""时 
</li>
				<li>当java.util.Collection对象中的isEmpty()返回true时 
</li>
				<li>当java.util.Map对象中的isEmpty()返回true时 </li>
		</ul>
		<p>下面的代码示例了logic:empty标签判断集合persons是否为空：</p>
		<pre>&lt;logic:empty name="listForm" property = "persons"&gt;
  &lt;div&gt;集合persons为空!&lt;/div&gt;			
&lt;/logic:empty&gt; 	
</pre>
		<p>logic:notEmpty标签的应用正好和logic:empty标签相反。</p>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e902">logic:match</a>
		</h2>
		<p>logic:match标签用来处理子串匹配问题。</p>
		<p>如果指定的值匹配该标签就会创建其标签体内的内容。该标签用于以下情况： </p>
		<ul>
				<li>检查具有指定名称的cookie是否与该标签的value匹配。 
</li>
				<li>检查具有指定名称的header是否与该标签的value匹配。 
</li>
				<li>检查具有指定名称的JSP Bean是否与该标签的value匹配 或 检查具有指定名称的JSP Bean中的property属性值是否与该标签的value匹配。 
</li>
				<li>检查request中指定名称的参数值是否与该标签的value匹配。 </li>
		</ul>
		<p>
		</p>
		<p>下面的代码示例了logic:match标签的典型用法：</p>
		<pre>&lt;logic:present header="User-Agent"&gt;
  &lt;logic:match header="User-Agent" value="MSIE 6.0"&gt;
    MS IE 6.0
  &lt;/logic:match&gt;
&lt;/logic:present&gt; 	
</pre>
		<p>logic:notMatch标签的应用正好和logic:match标签相反。</p>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e939">logic:equal</a>
		</h2>
		<p>这里要介绍的不只是logic:equal(=)标签，而是要介绍一类标签，这类标签完成比较运算，包括：</p>
		<ul>
				<li>logic:equal(=) 
</li>
				<li>logic:notEqual(!=) 
</li>
				<li>logic:greaterEqual(&gt;=) 
</li>
				<li>logic:lessEqual(&lt;=) 
</li>
				<li>logic:graterThan(&gt;) 
</li>
				<li>logic:lessThan(&lt;) </li>
		</ul>
		<p>该类标签的用法类似，我们只介绍logic:equal标签，其它的留给您自己去完成。</p>
		<p>logic:equal是用来判断是否相等的。如果相等，该标签体中嵌入的内容就会被处理。该标签用于以下情况：</p>
		<ul>
				<li>比较由该标签的cookie属性指定的cookie的值是否与该标签的value属性值相等。 
</li>
				<li>比较由该标签的header属性指定的header的值是否与该标签的value属性值相等。 
</li>
				<li>比较由该标签的name属性指定的JSP Bean是否与该标签的value属性值相等(property属性不出现)或比较由该标签的name属性指定的JSP Bean中的property属性值是否与该标签的value属性值相等。 
</li>
				<li>比较由该标签的parameter属性指定的参数值(request中)是否与该标签的value属性值相等。 </li>
		</ul>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e991">logic:forward</a>
		</h2>
		<p>我觉得将forward和redirect这两个动作放到一起对比着介绍更加有利于理解，基于此原因也就将logic:forward和logic:redirect这两个标签也拿到这里一起介绍了。</p>
		<p>让我们看看这两个动作的区别： </p>
		<ul>
				<li>forward是在servlet内部执行，浏览器完全不会感知到这个动作，原始的url也不会改变，浏览器重新装载的话也只是对原始的请求进行简单的重复。 
</li>
				<li>redirect则分成两个步骤：第一步是web应用程序告诉浏览器第二个url，然后浏览器向第二个url发送请求。 </li>
		</ul>
		<p>
		</p>
		<p>redirect比forward慢，因为浏览器要做二次请求。还有就是要注意，在第一次的请求作用域(request作用域)内的bean对于第二次请求是不可见的。</p>
		<p>理解了上面描述的区别也就知道了什么时候该选用logic:forward标签什么时候该选用logic:redirect标签了。logic:forward标签完成PageContext.forward()或HttpServletResponse.sendRedirect()，如何选择由控制器决定。logic:redirect标签完成HttpServletResponse.sendRedirect()。</p>
		<p>在使用logic:redirect标签时我们可以向使用html:link一样构造baseurl和query参数。如果您感兴趣可以参考html:link标签。</p>
		<div class="go-top">
				<a title="回页首" href="http://www.solol.org/technologic/java/j-struts/#top">回页首</a>
		</div>
		<h2>
				<a name="d3e1021">总结</a>
		</h2>
		<p>这篇指南的背景是Struts-1.2.9，其中的所有的代码示例也都是在这个版本下调试通过的。目前Struts仍然在快速的发展中，但是从开发者应用的角度来讲还是比较稳定的。因此我有理由相信其中的代码示例也可以很好的运行在其它的版本之上。最后希望这篇指南能够让您心情愉快！</p>
		<div>
				<a name="resource">参考资料</a>
				<ul>
						<li>
								<a href="http://struts.apache.org/struts-doc-1.2.9/index.html">Struts的官方文档</a>
						</li>
						<li>
								<a href="http://struts.apache.org/struts-doc-1.2.9/userGuide/index.html">Struts的用户指南</a>
						</li>
						<li>
								<a href="http://wp.netscape.com/newsref/std/cookie_spec.html">cookie spec</a>，这是关于Cookie最的全面的说明。 
</li>
						<li>
								<a href="http://www.w3.org/TR/html4/index/elements.html">HTML元素</a>
						</li>
				</ul>
		</div>
		<div>
				<div>
						<a name="author">关于作者</a>
				</div>
				<a href="mailto:solo_l@eyou.com?cc=solo.l at hotmail.com">solo L</a> 一位有些理想主义的软件工程师，创建了<a href="http://www.solol.org/">solol.org</a>。他常常在<a href="http://www.solol.org/blogs/solol.html">这里</a>发表一些对技术的见解。 <br /></div>
<img src ="http://www.blogjava.net/renyangok/aggbug/93753.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-01-14 14:12 <a href="http://www.blogjava.net/renyangok/archive/2007/01/14/93753.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>