﻿<?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/rkind/category/4178.html</link><description>&lt;font size=5 align=right&gt;知人者智  自知者明
Fighting!!&lt;/font&gt;</description><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 07:18:14 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 07:18:14 GMT</pubDate><ttl>60</ttl><item><title>郁闷的Struts数据源</title><link>http://www.blogjava.net/rkind/archive/2006/10/31/78299.html</link><dc:creator>rkind</dc:creator><author>rkind</author><pubDate>Tue, 31 Oct 2006 07:49:00 GMT</pubDate><guid>http://www.blogjava.net/rkind/archive/2006/10/31/78299.html</guid><wfw:comment>http://www.blogjava.net/rkind/comments/78299.html</wfw:comment><comments>http://www.blogjava.net/rkind/archive/2006/10/31/78299.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rkind/comments/commentRss/78299.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rkind/services/trackbacks/78299.html</trackback:ping><description><![CDATA[今天在试验了一下在Struts-config中配置数据源，虽然说这种方法并不推荐，<br />操作步骤：<br />      首先导入几个需要的包：首先自己连接数据库用的包，因为我用的是Mysql所以用的是“mm.mysql-2.0.4-bin.jar”，还有“commons-dbcp-1.2.1.jar”这个是数据源中要用到的包，还有“commons-pool-1.2.jar”这个不太清楚<br />     其次在Struts-config.xml中加入如下代码
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">    </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">data-sources</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />        </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">data-source </span><span style="COLOR: #ff0000">key</span><span style="COLOR: #0000ff">="rki"</span><span style="COLOR: #ff0000"> type</span><span style="COLOR: #0000ff">="org.apache.commons.dbcp.BasicDataSource"</span><span style="COLOR: #0000ff">&gt;  //type代表类，rki表示当有多个数据源时相<br />//当于一个索引，id值</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">set-property </span><span style="COLOR: #ff0000">property</span><span style="COLOR: #0000ff">="driverClassName"</span><span style="COLOR: #ff0000"> value</span><span style="COLOR: #0000ff">="org.gjt.mm.mysql.Driver"</span><span style="COLOR: #ff0000"> </span><span style="COLOR: #0000ff">/&gt;//连接mysql所需的类库</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">set-property </span><span style="COLOR: #ff0000">property</span><span style="COLOR: #0000ff">="url"</span><span style="COLOR: #ff0000"> value</span><span style="COLOR: #0000ff">="jdbc:mysql://172.20.0.40:3306/test?useUnicode=true&amp;amp;characterEncoding=GBK"</span><span style="COLOR: #ff0000"> </span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">set-property </span><span style="COLOR: #ff0000">property</span><span style="COLOR: #0000ff">="username"</span><span style="COLOR: #ff0000"> value</span><span style="COLOR: #0000ff">="root"</span><span style="COLOR: #ff0000"> </span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">set-property </span><span style="COLOR: #ff0000">property</span><span style="COLOR: #0000ff">="password"</span><span style="COLOR: #ff0000"> value</span><span style="COLOR: #0000ff">="你的密码"</span><span style="COLOR: #ff0000"> </span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">set-property </span><span style="COLOR: #ff0000">property</span><span style="COLOR: #0000ff">="maxActive"</span><span style="COLOR: #ff0000"> value</span><span style="COLOR: #0000ff">="10"</span><span style="COLOR: #ff0000"> </span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">set-property </span><span style="COLOR: #ff0000">property</span><span style="COLOR: #0000ff">="maxWait"</span><span style="COLOR: #ff0000"> value</span><span style="COLOR: #0000ff">="500"</span><span style="COLOR: #ff0000"> </span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">set-property </span><span style="COLOR: #ff0000">property</span><span style="COLOR: #0000ff">="defaultAutoCommit"</span><span style="COLOR: #ff0000"> value</span><span style="COLOR: #0000ff">="false"</span><span style="COLOR: #ff0000"> </span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">set-property </span><span style="COLOR: #ff0000">property</span><span style="COLOR: #0000ff">="defaultReadOnly"</span><span style="COLOR: #ff0000"> value</span><span style="COLOR: #0000ff">="false"</span><span style="COLOR: #ff0000"> </span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />        </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">data-source</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />    </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">data-sources</span><span style="COLOR: #0000ff">&gt;</span></div>然后现在就可以在你的Action试验数据库的连接，简单引用的代码片断<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">datasource </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> getDataSource(request, </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">rki</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            conn </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> datasource.getConnection();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            Statement state </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> conn.createStatement();<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            System.out.println(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">hello</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            String sql </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">select * from notice where title='test'</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            ResultSet rs </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> state.executeQuery(sql);<br /><img id="Codehighlighter1_282_414_Open_Image" onclick="this.style.display='none'; Codehighlighter1_282_414_Open_Text.style.display='none'; Codehighlighter1_282_414_Closed_Image.style.display='inline'; Codehighlighter1_282_414_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_282_414_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_282_414_Closed_Text.style.display='none'; Codehighlighter1_282_414_Open_Image.style.display='inline'; Codehighlighter1_282_414_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" />            </span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000"> (rs.next()) </span><span id="Codehighlighter1_282_414_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/images/dot.gif" /></span><span id="Codehighlighter1_282_414_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />                request.getSession().setAttribute(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">cont</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">,<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />                        rs.getString(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">content</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">));<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />                System.out.println(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">database connect true</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">);<br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockEnd.gif" align="top" />            }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            rs.close();</span></div>在试验的过程中，老是不能连接，出“Initializing application data source”我仔细检查过以上的配置并没有问题，重启了几次服务器也不行，查了N多资料，最后居然自己好了，郁闷的要死<br />一点总结，<br />           可见在Struts中配置数据源要比别的连接方法要复杂，而且效率上来说也不一定要好，还不如自己通过自己编写简单的访问数据库的类，或者是直接采用Tomcat的数据源要方便 一些。<img src ="http://www.blogjava.net/rkind/aggbug/78299.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rkind/" target="_blank">rkind</a> 2006-10-31 15:49 <a href="http://www.blogjava.net/rkind/archive/2006/10/31/78299.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts中Cannot find bean XXX in any scope的问题</title><link>http://www.blogjava.net/rkind/archive/2006/10/30/78037.html</link><dc:creator>rkind</dc:creator><author>rkind</author><pubDate>Mon, 30 Oct 2006 06:07:00 GMT</pubDate><guid>http://www.blogjava.net/rkind/archive/2006/10/30/78037.html</guid><wfw:comment>http://www.blogjava.net/rkind/comments/78037.html</wfw:comment><comments>http://www.blogjava.net/rkind/archive/2006/10/30/78037.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rkind/comments/commentRss/78037.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rkind/services/trackbacks/78037.html</trackback:ping><description><![CDATA[
		<p>这就是在前面的简单应用中，在view中，通过&lt;bean:write name="myform" property="name"&gt;调用Actionform时出的问题，这个问题的直接原因很简单就是：找不到"myform"这个Bean，用网上的话说就是<strong>“</strong><strong><em>在Action里一般会request.setAttribute()一些对象，然后在转向的jsp文件里（用tag或request.getAttribute()方法）得到这些对象并显示出来。这个异常是说jsp要得到一个对象，但前面的Action里并没有将对象设置到request（也可以是session、servletContext）里。可能是名字错了，请检查jsp里的tag的一般是name属性，或getAttribute()方法的参数值；或者是Action逻辑有问题没有执行setAttribute()方法就先转向了。<br />还有另外一个可能，纯粹是jsp文件的问题，例如&lt;logic:iterate&gt;会指定一个id值，然后在循环里&lt;bean:write&gt;使用这个值作为name的值，如果这两个值不同，也会出现此异常。（都是一个道理，request里没有对应的对象。）</em>”。<br /></strong>      我对Actionform的机制不理解，心中的疑问：<br />      1）如果ActionForm是Struts自动封装到Request中，那么我可以直接在view.jsp中，能过&lt;bean:write&gt;调用这个ActionForm，如果假设这种情况成立时，那么Beanwrite中的bean名应该是ActionForm的类名，还是在Struts-config.xml里给ActionForm定义的类名？<br />      2）如果没有封装的话，那么是不是需要在Action中把Actionform通过request.setAttribute("","")放到request中<br />还是要放到Session中，<br />      我测试时候出的问题<br />       有一次就是在1）的情况下测试成功的，但是以后再怎么试都不行，那次是引用的Struts-config.xml里给ActionForm定义的类名。非常奇怪，<br />        还有就是2）的情况，测试中如果Action和Frombean在一个包下面，那么通过request.setAttribute("","")，就可以获取到Bean，如果不是的话，有时候行，有时候不行，<br />       <strong> 如果通过request.getSession.setAttribute()设置ActionForm的话，每次都可以</strong>，他们的Scope肯定在一个Request中，那么为什么Session就可以，而Request不行。(<strong>结论：因为是在Actionfoward中设置了“redirect='true'，这样的话一跳转时，Request中的内容都会被重置，所以不行，把这一句去掉就可以了</strong>”)</p>
<img src ="http://www.blogjava.net/rkind/aggbug/78037.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rkind/" target="_blank">rkind</a> 2006-10-30 14:07 <a href="http://www.blogjava.net/rkind/archive/2006/10/30/78037.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>超简单的struts小程序</title><link>http://www.blogjava.net/rkind/archive/2006/10/27/77602.html</link><dc:creator>rkind</dc:creator><author>rkind</author><pubDate>Fri, 27 Oct 2006 04:39:00 GMT</pubDate><guid>http://www.blogjava.net/rkind/archive/2006/10/27/77602.html</guid><wfw:comment>http://www.blogjava.net/rkind/comments/77602.html</wfw:comment><comments>http://www.blogjava.net/rkind/archive/2006/10/27/77602.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rkind/comments/commentRss/77602.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rkind/services/trackbacks/77602.html</trackback:ping><description><![CDATA[
		<font size="4">
				<strong>
						<hr />
1、简介</strong>
		</font>
		<br />            简单的页面输入并显示的功能，有两个页面，<br />            upnews.jsp 提供输入内容的界面，show.jsp显示已经输入的内容 
<hr /><br /><strong>2、源码<br /></strong>      <strong>struts-config</strong>，流程图<br /><p align="left"><img height="211" alt="Snap3.jpg" src="http://www.blogjava.net/images/blogjava_net/rkind/Snap3.jpg" width="410" border="0" /><br />源代码<br />Strust-config.xml</p><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">form-bean </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="upNewsForm"</span><span style="COLOR: #ff0000"> type</span><span style="COLOR: #0000ff">="com.rkind.struts.form.UpNewsForm"</span><span style="COLOR: #ff0000"> </span><span style="COLOR: #0000ff">/&gt;</span></div><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">        </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">action </span><span style="COLOR: #ff0000">attribute</span><span style="COLOR: #0000ff">="upNewsForm"</span><span style="COLOR: #ff0000"> input</span><span style="COLOR: #0000ff">="/upNews.jsp"</span><span style="COLOR: #ff0000"> name</span><span style="COLOR: #0000ff">="upNewsForm"</span><span style="COLOR: #ff0000"> path</span><span style="COLOR: #0000ff">="/upNews"</span><span style="COLOR: #ff0000"> scope</span><span style="COLOR: #0000ff">="request"</span><span style="COLOR: #ff0000"> type</span><span style="COLOR: #0000ff">="com.rkind.struts.action.UpNewsAction"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">forward </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="suc"</span><span style="COLOR: #ff0000"> path</span><span style="COLOR: #0000ff">="/show.jsp"</span><span style="COLOR: #ff0000"> redirect</span><span style="COLOR: #0000ff">="true"</span><span style="COLOR: #ff0000"> </span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />        </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">action</span><span style="COLOR: #0000ff">&gt;</span></div><hr /><br />upnews.jsp<br />     都是eclispe自动生成的
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">html:form </span><span style="COLOR: #ff0000">action</span><span style="COLOR: #0000ff">="/upNews"</span><span style="COLOR: #0000ff">&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            content : </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">html:text </span><span style="COLOR: #ff0000">property</span><span style="COLOR: #0000ff">="content"</span><span style="COLOR: #0000ff">/&gt;&lt;</span><span style="COLOR: #800000">html:errors </span><span style="COLOR: #ff0000">property</span><span style="COLOR: #0000ff">="content"</span><span style="COLOR: #0000ff">/&gt;&lt;</span><span style="COLOR: #800000">br</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />            </span><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">html:submit</span><span style="COLOR: #0000ff">/&gt;&lt;</span><span style="COLOR: #800000">html:cancel</span><span style="COLOR: #0000ff">/&gt;</span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />        </span><span style="COLOR: #0000ff">&lt;/</span><span style="COLOR: #800000">html:form</span><span style="COLOR: #0000ff">&gt;</span></div><hr />
show.jsp这个简单就一句话，
<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #0000ff">&lt;</span><span style="COLOR: #800000">bean:write </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="upNewsForm"</span><span style="COLOR: #ff0000"> property</span><span style="COLOR: #0000ff">="content"</span><span style="COLOR: #0000ff">/&gt;</span></div><hr />
模型的部分 formbean,自动生成，未做改动<br />Controller部分，核心啊<br /><div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /><span style="COLOR: #000000">UpNewsForm upNewsForm </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> (UpNewsForm) form;<br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" />        </span><span style="COLOR: #008000">//</span><span style="COLOR: #008000"> TODO Auto-generated method stub</span><span style="COLOR: #008000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/None.gif" align="top" /></span><span style="COLOR: #000000">        String te</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">upNewsForm.getContent();<br /><img id="Codehighlighter1_122_265_Open_Image" onclick="this.style.display='none'; Codehighlighter1_122_265_Open_Text.style.display='none'; Codehighlighter1_122_265_Closed_Image.style.display='inline'; Codehighlighter1_122_265_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_122_265_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_122_265_Closed_Text.style.display='none'; Codehighlighter1_122_265_Open_Image.style.display='inline'; Codehighlighter1_122_265_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">try</span><span id="Codehighlighter1_122_265_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/images/dot.gif" /></span><span id="Codehighlighter1_122_265_Open_Text"><span style="COLOR: #000000">{<br /><img id="Codehighlighter1_149_222_Open_Image" onclick="this.style.display='none'; Codehighlighter1_149_222_Open_Text.style.display='none'; Codehighlighter1_149_222_Closed_Image.style.display='inline'; Codehighlighter1_149_222_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top" /><img id="Codehighlighter1_149_222_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_149_222_Closed_Text.style.display='none'; Codehighlighter1_149_222_Open_Image.style.display='inline'; Codehighlighter1_149_222_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedSubBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(te.equals(</span><span style="COLOR: #000000">""</span><span style="COLOR: #000000">))</span><span id="Codehighlighter1_149_222_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/images/dot.gif" /></span><span id="Codehighlighter1_149_222_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> ActionForward(mapping.getInput());        <br /><img src="http://www.blogjava.net/images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top" />        }</span></span><span style="COLOR: #000000"><br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />        </span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000"> (mapping.findForward(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">suc</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">));<br /><img id="Codehighlighter1_284_332_Open_Image" onclick="this.style.display='none'; Codehighlighter1_284_332_Open_Text.style.display='none'; Codehighlighter1_284_332_Closed_Image.style.display='inline'; Codehighlighter1_284_332_Closed_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ExpandedBlockStart.gif" align="top" /><img id="Codehighlighter1_284_332_Closed_Image" style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_284_332_Closed_Text.style.display='none'; Codehighlighter1_284_332_Open_Image.style.display='inline'; Codehighlighter1_284_332_Open_Text.style.display='inline';" src="http://www.blogjava.net/images/OutliningIndicators/ContractedBlock.gif" align="top" />        }</span></span><span style="COLOR: #0000ff">catch</span><span style="COLOR: #000000">(Exception e)</span><span id="Codehighlighter1_284_332_Closed_Text" style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.blogjava.net/images/dot.gif" /></span><span id="Codehighlighter1_284_332_Open_Text"><span style="COLOR: #000000">{<br /><img src="http://www.blogjava.net/images/OutliningIndicators/InBlock.gif" align="top" />            </span><span style="COLOR: #0000ff">throw</span><span style="COLOR: #000000"> </span><span style="COLOR: #0000ff">new</span><span style="COLOR: #000000"> RuntimeException(e.getMessage());</span></span></div><br />好了，但是在测试的时候一直出问题，输入以后不能正常，原因有2<br />1、在链接时候，没有加“/”导致不能正常连接<br />2、<span style="COLOR: #800000">forward </span><span style="COLOR: #ff0000">name</span><span style="COLOR: #0000ff">="suc"</span><span style="COLOR: #ff0000"> path</span><span style="COLOR: #0000ff">="/show.jsp"</span><span style="COLOR: #ff0000"> redirect</span><span style="COLOR: #0000ff">="true"</span><span style="COLOR: #ff0000"> <font color="#000000">起初没有加redirect，不能跳转。</font></span><br /><img src ="http://www.blogjava.net/rkind/aggbug/77602.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rkind/" target="_blank">rkind</a> 2006-10-27 12:39 <a href="http://www.blogjava.net/rkind/archive/2006/10/27/77602.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JSP和Struts解决用户退出问题 [转]</title><link>http://www.blogjava.net/rkind/archive/2005/11/14/19726.html</link><dc:creator>rkind</dc:creator><author>rkind</author><pubDate>Mon, 14 Nov 2005 08:48:00 GMT</pubDate><guid>http://www.blogjava.net/rkind/archive/2005/11/14/19726.html</guid><wfw:comment>http://www.blogjava.net/rkind/comments/19726.html</wfw:comment><comments>http://www.blogjava.net/rkind/archive/2005/11/14/19726.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rkind/comments/commentRss/19726.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rkind/services/trackbacks/19726.html</trackback:ping><description><![CDATA[<P>　　在一个有密码保护的Web应用中，正确处理用户退出过程并不仅仅只需调用HttpSession的invalidate()方法。现在大部分浏览器上都有后退和前进按钮，允许用户后退或前进到一个页面。如果在用户在退出一个Web应用后按了后退按钮浏览器把缓存中的页面呈现给用户，这会使用户产生疑惑，他们会开始担心他们的个人数据是否安全。许多Web应用强迫用户退出时关闭整个浏览器，这样，用户就无法点击后退按钮了。还有一些使用javascript，但在某些客户端浏览器这却不一定起作用。这些解决方案都很笨拙且不能保证在任一情况下100%有效，同时，它也要求用户有一定的操作经验。 <BR><BR>　　这篇文章以示例阐述了正确解决用户退出问题的方案。作者Kevin Le首先描述了一个密码保护Web应用，然后以示例程序解释问题如何产生并讨论解决问题的方案。文章虽然是针对JSP页面进行阐述，但作者所阐述的概念很容易理解切能够为其他Web技术所采用。最后作者展示了如何用Jakarta Struts优雅地解决这一问题。 <BR><BR>　　大部分Web应用不会包含象银行账户或信用卡资料那样机密的信息，但一旦涉及到敏感数据，我们就需要提供一类密码保护机制。举例来说，一个工厂中工人通过Web访问他们的时间安排、进入他们的训练课程以及查看他们的薪金等等。此时应用SSL(<A class=bluekey href="http://www.yesky.com/key/4768/34768.html" target=_blank>Secure</A> Socket Layer)有点杀鸡用牛刀的感觉，但不可否认，我们又必须为这些应用提供密码保护，否则，工人（也就是Web应用的使用者）可以窥探到工厂中其他雇员的私人机密信息。<BR><BR>　　与上述情形相似的还有位处图书馆、医院等公共场所的计算机。在这些地方，许多用户共同使用几台计算机，此时保护用户的个人数据就显得至关重要。设计良好编写优秀的应用对用户专业知识的要求少之又少。<BR><BR>　　我们来看一下现实世界中一个完美的Web应用是如何表现的：一个用户通过浏览器访问一个页面。Web应用展现一个登陆页面要求用户输入有效的验证信息。用户输入了用户名和密码。此时我们假设用户提供的身份验证信息是正确的，经过了验证过程，Web应用允许用户浏览他有权访问的区域。用户想退出时，点击退出按钮，Web应用要求用户确认他是否则真的需要退出，如果用户确定退出，Session结束，Web应用重新定位到登陆页面。用户可以放心的离开而不用担心他的信息会泄露。另一个用户坐到了同一台电脑前，他点击后退按钮，Web应用不应该出现上一个用户访问过的任何一个页面。事实上，Web应用在第二个用户提供正确的验证信息之前应当一直停留在登陆页面上。<BR>通过示例程序，文章向您阐述了如何在一个Web应用中实现这一功能。<BR><BR>　　<B>JSP示例</B><BR><BR>　　为了更为有效地阐述实现方案，本文将从展示一个示例应用logoutSampleJSP1中碰到的问题开始。这个示例代表了许多没有正确解决退出过程的Web应用。logoutSampleJSP1包含了下述jsp页面：login.jsp, home.jsp, secure1.jsp, secure2.jsp, logout.jsp, loginAction.jsp, and logoutAction.jsp。其中页面home.jsp, secure1.jsp, secure2.jsp, 和logout.jsp是不允许未经认证的用户访问的，也就是说，这些页面包含了重要信息，在用户登陆之前或者退出之后都不应该出现在浏览器中。login.jsp包含了用于用户输入用户名和密码的form。logout.jsp页包含了要求用户确认是否退出的form。loginAction.jsp和logoutAction.jsp作为控制器分别包含了登陆和退出代码。<BR><BR>　　第二个示例应用logoutSampleJSP2展示了如何解决示例logoutSampleJSP1中的问题。然而，第二个应用自身也是有疑问的。在特定的情况下，退出问题还是会出现。<BR><BR>　　第三个示例应用logoutSampleJSP3在第二个示例上进行了改进，比较完善地解决了退出问题。<BR><BR>　　最后一个示例logoutSampleStruts展示了Struts如何优美地解决登陆问题。<BR><BR>　　注意：本文所附示例在最新版本的Microsoft Internet Explorer (IE), <A class=bluekey href="http://www.yesky.com/key/1353/36353.html" target=_blank>Netscape</A> Navigator, Mozilla, FireFox和Avant浏览器上测试通过。<BR><BR>　　Login action<BR><BR>　　Brian Pontarelli的经典文章《J2EE Security: Container Versus Custom》讨论了不同的J2EE认证途径。文章同时指出，HTTP协议和基于form的认证并未提供处理用户退出的机制。因此，解决途径便是引入自定义的安全实现机制。<BR><BR>　　自定义的安全认证机制普遍采用的方法是从form中获得用户输入的认证信息，然后到诸如LDAP (lightweight directory access protocol)或关系数据库的安全域中进行认证。如果用户提供的认证信息是有效的，登陆动作往HttpSession对象中注入某个对象。HttpSession存在着注入的对象则表示用户已经登陆。为了方便读者理解，本文所附的示例只往HttpSession中写入一个用户名以表明用户已经登陆。清单1是从loginAction.jsp页面中节选的一段代码以此阐述登陆动作：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#d8d8d1 border=1>
<TBODY>
<TR>
<TD>Listing 1 <BR>//...<BR>//initialize RequestDispatcher object; <A class=bluekey href="http://www.yesky.com/key/4740/34740.html" target=_blank>set</A> forward <A class=bluekey href="http://www.yesky.com/key/4152/34152.html" target=_blank>to</A> home page by default<BR>RequestDispatcher rd = request.getRequestDispatcher("home.jsp");<BR><BR>//Prepare connection and statement<BR>rs = stmt.executeQuery("select password from USER where userName = '" + userName + "'");<BR>if (rs.next()) { <BR>　//Query only returns 1 record in the result set; only 1 <BR>　password per userName which is also the primary key<BR>　if (rs.getString("password").equals(password)) { //If valid password<BR>　　session.setAttribute("User", userName); //Saves username string in the session object<BR>　}<BR>　else { //Password does <A class=bluekey href="http://www.yesky.com/key/1283/36283.html" target=_blank>not</A> match, i.e., invalid user password<BR>　　request.setAttribute("Error", "Invalid password."); <BR><BR>　　rd = request.getRequestDispatcher("login.jsp");<BR>　}<BR>} //No record in the result set, i.e., invalid username<BR>else {<BR><BR>　request.setAttribute("Error", "Invalid user name.");<BR>　rd = request.getRequestDispatcher("login.jsp");<BR>}<BR>}<BR><BR>//As a controller, loginAction.jsp finally either forwards to "login.jsp" or "home.jsp"<BR>rd.forward(request, response);<BR>//...</TD></TR></TBODY></TABLE><BR>　　本文所附示例均以关系型数据库作为安全域，但本文所阐述的观点对任何类型的安全域都是适用的。<BR><BR>　　Logout action <BR><BR>　　退出动作就包含了简单的删除用户名以及对用户的HttpSession对象调用invalidate()方法。清单2是从loginoutAction.jsp页面中节选的一段代码以此阐述退出动作：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#d8d8d1 border=1>
<TBODY>
<TR>
<TD>Listing 2 <BR>//...<BR>session.removeAttribute("User");<BR>session.invalidate();<BR>//...</TD></TR></TBODY></TABLE></P><STRONG>阻止未经认证访问受保护的JSP页面<BR><BR></STRONG>　　从form中获取用户提交的认证信息并经过验证后，登陆动作简单地往 HttpSession对象中写入一个用户名，退出动作则做相反的工作，它从用户的HttpSession对象中删除用户名并调用invalidate()方法销毁HttpSession。为了使登陆和退出动作真正发挥作用，所有受保护的JSP页面都应该首先验证HttpSession中是否包含了用户名以确认当前用户是否已经登陆。如果HttpSession中包含了用户名，也就是说用户已经登陆，Web应用则将剩余的JSP页发送给浏览器，否则，JSP页将跳转到登陆页login.jsp。页面home.jsp, secure1.jsp, secure2.jsp和logout.jsp均包含清单3中的代码段：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#d8d8d1 border=1>
<TBODY>
<TR>
<TD>Listing 3 <BR>//...<BR>String userName = (String) session.getAttribute("User");<BR>if (null == userName) {<BR>　request.setAttribute("Error", "Session has ended. Please login.");<BR>　RequestDispatcher rd = request.getRequestDispatcher("login.jsp");<BR>　rd.forward(request, response);<BR>}<BR>//...<BR>//Allow the rest of the dynamic content in this JSP to be served to the browser<BR>//...</TD></TR></TBODY></TABLE><BR>　　在这个代码段中，程序从HttpSession中减缩username字符串。如果字符串为空，Web应用则自动中止执行当前页面并跳转到登陆页，同时给出Session has ended. Please log in.的提示；如果不为空，Web应用则继续执行，也就是把剩余的页面提供给用户。<BR><BR>　　运行logoutSampleJSP1<BR><BR>　　运行logoutSampleJSP1将会出现如下几种情形：<BR><BR>　　1) 如果用户没有登陆，Web应用将会正确中止受保护页面home.jsp, secure1.jsp, secure2.jsp和logout.jsp的执行，也就是说，假如用户在浏览器地址栏中直接敲入受保护JSP页的地址试图访问，Web应用将自动跳转到登陆页并提示Session has ended.Please log in.<BR><BR>　　2) 同样的，当一个用户已经退出，Web应用也会正确中止受保护页面home.jsp, secure1.jsp, secure2.jsp和logout.jsp的执行<BR><BR>　　3) 用户退出后，如果点击浏览器上的后退按钮，Web应用将不能正确保护受保护的页面——在Session销毁后（用户退出）受保护的JSP页重新在浏览器中显示出来。然而，如果用户点击返回页面上的任何链接，Web应用将会跳转到登陆页面并提示Session has ended.Please log in.<BR><BR>　　<B>阻止浏览器缓存</B><BR><BR>　　上述问题的根源在于大部分浏览器都有一个后退按钮。当点击后退按钮时，默认情况下浏览器不是从Web服务器上重新获取页面，而是从浏览器缓存中载入页面。基于Java的Web应用并未限制这一功能，在基于PHP、ASP和.NET的Web应用中也同样存在这一问题。<BR><BR>　　在用户点击后退按钮后，浏览器到服务器再从服务器到浏览器这样通常意思上的HTTP回路并没有建立，仅仅只是用户，浏览器和缓存进行了交互。所以，即使包含了清单3上的代码来保护JSP页面，当点击后退按钮时，这些代码是不会执行的。<BR><BR>　　缓存的好坏，真是仁者见仁智者见智。缓存的确提供了一些便利，但通常只在使用静态的HTML页面或基于图形或影响的页面你才能感受到。而另一方面，Web应用通常是基于数据的，数据通常是频繁更改的。与从缓存中读取并显示过期的数据相比，提供最新的数据才是更重要的！<BR><BR>　　幸运的是，HTTP头信息“Expires”和“Cache-Control”为应用程序服务器提供了一个控制浏览器和代理服务器上缓存的机制。HTTP头信息Expires告诉代理服务器它的缓存页面何时将过期。HTTP1.1规范中新定义的头信息Cache-Control可以通知浏览器不缓存任何页面。当点击后退按钮时，浏览器重新访问服务器已获取页面。如下是使用Cache-Control的基本方法：<BR><BR>　　1) no-cache:强制缓存从服务器上获取新的页面<BR><BR>　　2) no-store: 在任何环境下缓存不保存任何页面<BR><BR>　　HTTP1.0规范中的Pragma:no-cache等同于HTTP1.1规范中的Cache-Control:no-cache，同样可以包含在头信息中。 <BR><BR>　　通过使用HTTP头信息的cache控制，第二个示例应用logoutSampleJSP2解决了logoutSampleJSP1的问题。logoutSampleJSP2与logoutSampleJSP1不同表现在如下代码段中，这一代码段加入进所有受保护的页面中:<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#d8d8d1 border=1>
<TBODY>
<TR>
<TD>//...<BR>response.setHeader("Cache-Control","no-cache"); //Forces caches to obtain a new copy of the page from the origin server<BR>response.setHeader("Cache-Control","no-store"); //Directs caches not to store the page under any circumstance<BR>response.setDateHeader("Expires", 0); //Causes the proxy cache to see the page as "stale"<BR>response.setHeader("Pragma","no-cache"); //HTTP 1.0 backward compatibility<BR>String userName = (String) session.getAttribute("User");<BR>if (null == userName) {<BR>　request.setAttribute("Error", "Session has ended. Please login.");<BR>　RequestDispatcher rd = request.getRequestDispatcher("login.jsp");<BR>　rd.forward(request, response);<BR>}<BR>//...</TD></TR></TBODY></TABLE><BR>　　通过设置头信息和检查HttpSession中的用户名确保了浏览器不缓存页面，同时，如果用户未登陆，受保护的JSP页面将不会发送到浏览器，取而代之的将是登陆页面login.jsp。<BR><BR>　　<B>运行logoutSampleJSP2</B><BR><BR>　　运行logoutSampleJSP2后将回看到如下结果：<BR><BR>　　1) 当用户退出后试图点击后退按钮，浏览器并不会显示受保护的页面，它只会现实登陆页login.jsp同时给出提示信息Session has ended. Please log in.<BR><BR>　　2) 然而，当按了后退按钮返回的页是处理用户提交数据的页面时，IE和Avant浏览器将弹出如下信息提示：<BR><BR>　　警告：页面已过期……（你肯定见过）<BR><BR>　　选择刷新后前一个JSP页面将重新显示在浏览器中。很显然，这不是我们所想看到的因为它违背了logout动作的目的。发生这一现象时，很可能是一个恶意用户在尝试获取其他用户的数据。然而，这个问题仅仅出现在后退按钮对应的是一个处理POST请求的页面。<BR><BR>　　<B>记录最后登陆时间</B> <BR><BR>　　上述问题之所以出现是因为浏览器将其缓存中的数据重新提交了。这本文的例子中，数据包含了用户名和密码。无论是否给出安全警告信息，浏览器此时起到了负面作用。<BR><BR>　　为了解决logoutSampleJSP2中出现的问题，logoutSampleJSP3的login.jsp在包含username和password的基础上还包含了一个称作lastLogon的隐藏表单域，此表单域动态的用一个long型值初始化。这个long型值是调用System.currentTimeMillis()获取到的自1970年1月1日以来的毫秒数。当login.jsp中的form提交时，loginAction.jsp首先将隐藏域中的值与用户数据库中的值进行比较。只有当lastLogon表单域中的值大于数据库中的值时Web应用才认为这是个有效的登陆。<BR><BR>　　为了验证登陆，数据库中lastLogon字段必须以表单中的lastLogon值进行更新。上例中，当浏览器重复提交数据时，表单中的lastLogon值不比数据库中的lastLogon值大，因此，loginAction转到login.jsp页面，并提示Session has ended.Please log in.清单5是loginAction中节选的代码段：<BR><BR>　　清单5<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#d8d8d1 border=1>
<TBODY>
<TR>
<TD>//...<BR>RequestDispatcher rd = request.getRequestDispatcher("home.jsp"); //Forward to homepage by default<BR>//...<BR>if (rs.getString("password").equals(password)) { <BR>　//If valid password<BR>　long lastLogonDB = rs.getLong("lastLogon");<BR>　if (lastLogonForm ＞ lastLogonDB) {<BR>　　session.setAttribute("User", userName); //Saves username string in the session object<BR>　　stmt.executeUpdate("update USER set lastLogon= " + lastLogonForm + " where userName = '" + userName + "'");<BR>　}<BR>　else {<BR>　　request.setAttribute("Error", "Session has ended. Please login.");<BR>　　rd = request.getRequestDispatcher("login.jsp"); }<BR>　}<BR>　else { //Password does not match, i.e., invalid user password<BR>　　request.setAttribute("Error", "Invalid password.");<BR>　　rd = request.getRequestDispatcher("login.jsp"); <BR>　}<BR>　//...<BR>　rd.forward(request, response);<BR>//...</TD></TR></TBODY></TABLE><BR>　　为了实现上述方法，你必须记录每个用户的最后登陆时间。对于采用关系型数据库安全域来说，这点可以可以通过在某个表中加上lastLogin字段轻松实现。LDAP以及其他的安全域需要稍微动下脑筋，但很显然是可以实现的。<BR><BR>　　表示最后登陆时间的方法有很多。示例logoutSampleJSP3利用了自1970年1月1日以来的毫秒数。这个方法在许多人在不同浏览器中用一个用户账号登陆时也是可行的。<BR><BR>　　<B>运行logoutSampleJSP3</B><BR><BR>　　运行示例logoutSampleJSP3将展示如何正确处理退出问题。一旦用户退出，点击浏览器上的后退按钮在任何情况下都不会是受保护的页面在浏览器上显示出来。这个示例展示了如何正确处理退出问题而不需要额外的培训。<BR><BR>　　为了使代码更简练有效，一些冗余的代码可以剔除掉。一种途径就是把清单4中的代码写到一个单独的JSP页中，通过标签＜jsp:include＞其他页面也可以引用。<BR><BR><BR>　　<B>Struts框架下的退出实现</B><BR><BR>　　与直接使用JSP或JSP/servlets相比，另一个可选的方案是使用Struts。为一个基于Struts的Web应用添加一个处理退出问题的框架可以优雅地不费气力的实现。这部分归功于Struts是采用MVC设计模式的因此将模型和视图清晰的分开。另外，Java是一个面向对象的语言，其支持继承，可以比JSP中的脚本更为容易地实现代码重用。在Struts中，清单4中的代码可以从JSP页面中移植到Action类的execute()方法中。<BR>此外，我们还可以定义一个继承Struts Action类的基本类，其execute()方法中包含了清单4中的代码。通过使用类继承机制，其他类可以继承基本类中的通用逻辑来设置HTTP头信息以及检索HttpSession对象中的username字符串。这个基本类是一个抽象类并定义了一个抽象方法executeAction()。所有继承自基类的子类都应实现exectuteAction()方法而不是覆盖它。清单6是基类的部分代码：<BR><BR>　　清单6<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#d8d8d1 border=1>
<TBODY>
<TR>
<TD>public abstract class BaseAction extends Action {<BR>　public ActionForward execute(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) <BR>　throws IOException, ServletException {<BR>　　response.setHeader("Cache-Control","no-cache"); <BR>　　//Forces caches to obtain a new copy of the page from the origin server<BR>　　response.setHeader("Cache-Control","no-store");<BR>　　//Directs caches not to store the page under any circumstance<BR>　　response.setDateHeader("Expires", 0); //Causes the proxy cache to see the page as "stale"<BR>　　response.setHeader("Pragma","no-cache"); //HTTP 1.0 backward compatibility <BR><BR>　　if (!this.userIsLoggedIn(request)) {<BR>　　　ActionErrors errors = new ActionErrors();<BR>　　　errors.add("error", new ActionError("logon.sessionEnded"));<BR>　　　this.saveErrors(request, errors);<BR>　　　return mapping.findForward("sessionEnded");<BR>　　}<BR>　　return executeAction(mapping, form, request, response);<BR>　}<BR><BR>　protected abstract ActionForward executeAction(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) <BR>　throws IOException, ServletException; <BR><BR>　private boolean userIsLoggedIn(HttpServletRequest request) {<BR>　　if (request.getSession().getAttribute("User") == null) {<BR>　　　return false;<BR>　　}<BR><BR>　　return true;<BR>　}<BR>}</TD></TR></TBODY></TABLE><BR>　　清单6中的代码与清单4中的很相像，仅仅只是用ActionMapping findForward替代了RequestDispatcher forward。清单6中，如果在HttpSession中未找到username字符串，ActionMapping对象将找到名为sessionEnded的forward元素并跳转到对应的path。如果找到了，子类将执行其实现了executeAction()方法的业务逻辑。因此，在配置文件struts-web.xml中为所有子类声明个一名为sessionEnded的forward元素是必须的。清单7以secure1 action阐明了这样一个声明：<BR><BR>　　清单7<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#d8d8d1 border=1>
<TBODY>
<TR>
<TD>＜action path="/secure1" <BR>type="com.kevinhle.logoutSampleStruts.Secure1Action" <BR>scope="request"＞<BR>＜forward name="success" path="/WEB-INF/jsps/secure1.jsp"/＞<BR>＜forward name="sessionEnded" path="/login.jsp"/＞<BR>＜/action＞</TD></TR></TBODY></TABLE><BR>　　继承自BaseAction类的子类Secure1Action实现了executeAction()方法而不是覆盖它。Secure1Action类不执行任何退出代码，如清单8：<BR><BR>
<TABLE borderColor=#ffcc66 width="90%" align=center bgColor=#d8d8d1 border=1>
<TBODY>
<TR>
<TD>public class Secure1Action extends BaseAction {<BR>　public ActionForward executeAction(ActionMapping mapping, ActionForm form,<BR>HttpServletRequest request, HttpServletResponse response)<BR>　throws IOException, ServletException {<BR><BR>　　HttpSession session = request.getSession(); <BR>　　return (mapping.findForward("success"));<BR>　}<BR>}</TD></TR></TBODY></TABLE><BR>　　只需要定义一个基类而不需要额外的代码工作，上述解决方案是优雅而有效的。不管怎样，将通用的行为方法写成一个继承StrutsAction的基类是许多Struts项目的共同经验，值得推荐。<BR><BR>　　<B>结论</B><BR><BR>　　本文阐述了解决退出问题的方案，尽管方案简单的令人惊讶，但却在所有情况下都能有效地工作。无论是对JSP还是Struts，所要做的不过是写一段不超过50行的代码以及一个记录用户最后登陆时间的方法。在Web应用中混合使用这些方案能够使拥护的私人数据不致泄露，同时，也能增加用户的经验。<img src ="http://www.blogjava.net/rkind/aggbug/19726.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rkind/" target="_blank">rkind</a> 2005-11-14 16:48 <a href="http://www.blogjava.net/rkind/archive/2005/11/14/19726.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用Struts的Token机制解决表单的重复提交  [转]　</title><link>http://www.blogjava.net/rkind/archive/2005/11/14/19685.html</link><dc:creator>rkind</dc:creator><author>rkind</author><pubDate>Mon, 14 Nov 2005 06:32:00 GMT</pubDate><guid>http://www.blogjava.net/rkind/archive/2005/11/14/19685.html</guid><wfw:comment>http://www.blogjava.net/rkind/comments/19685.html</wfw:comment><comments>http://www.blogjava.net/rkind/archive/2005/11/14/19685.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rkind/comments/commentRss/19685.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rkind/services/trackbacks/19685.html</trackback:ping><description><![CDATA[<SPAN id=ArticleContent1_ArticleContent1_lblContent>&nbsp;
<P>Struts的Token（令牌）机制能够很好的解决表单重复提交的问题，基本原理是：服务器端在处理到达的请求之前，会将<I>请求中包含的令牌值</I>与保存在当前用户会话中的令牌值进行比较，看是否匹配。在处理完该请求后，且在答复发送给客户端之前，将会产生一个新的令牌，该令牌除传给客户端以外，也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次提交的话，客户端传过来的令牌就和服务器端的令牌不一致，从而有效地防止了重复提交的发生。</P>
<P>这时其实也就是两点，第一：你需要在请求中有这个令牌值，请求中的令牌值如何保存，其实就和我们平时在页面中保存一些信息是一样的，通过隐藏字段来保存，保存的形式如： 〈input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae"〉，这个value是TokenProcessor类中的generateToken()获得的，是根据当前用户的session id和当前时间的long值来计算的。第二：在客户端提交后，我们要根据判断在请求中包含的值是否和服务器的令牌一致，因为服务器每次提交都会生成新的Token，所以，如果是重复提交，客户端的Token值和服务器端的Token值就会不一致。下面就以在数据库中插入一条数据来说明如何防止重复提交。</P>
<P>在Action中的add方法中，我们需要将Token值明确的要求保存在页面中，只需增加一条语句：saveToken(request);，如下所示： <BR><FONT color=#ff0000>public ActionForward add(ActionMapping mapping, ActionForm form, <BR>HttpServletRequest request, HttpServletResponse response)<BR>//前面的处理省略<BR>saveToken(request);<BR>return mapping.findForward("add");<BR>}</FONT>在Action的insert方法中，我们根据表单中的Token值与服务器端的Token值比较，如下所示：<BR><FONT color=#ff0000>public ActionForward insert(ActionMapping mapping, ActionForm form, <BR>HttpServletRequest request, HttpServletResponse response)<BR>if (isTokenValid(request, true)) {<BR>// 表单不是重复提交<BR>//这里是保存数据的代码<BR>} else {<BR>//表单重复提交<BR>saveToken(request);<BR>//其它的处理代码<BR>}<BR>}</FONT></P>
<P>其实使用起来很简单，举个最简单、最需要使用这个的例子：<BR>一般控制重复提交主要是用在对数据库操作的控制上，比如插入、更新、删除等，由于更新、删除一般都是通过id来操作（例如：updateXXXById, removeXXXById），所以这类操作控制的意义不是很大（不排除个别现象），重复提交的控制也就主要是在插入时的控制了。<BR><BR>先说一下，我们目前所做项目的情况：<BR>目前的项目是用Struts＋Spring＋Ibatis，页面用jstl，Struts复杂View层，Spring在Service层提供事务控制，Ibatis是用来代替JDBC，所有页面的访问都不是直接访问jsp，而是访问Structs的Action，再由Action来Forward到一个Jsp，所有针对数据库的操作，比如取数据或修改数据，都是在Action里面完成，所有的Action一般都继承BaseDispatchAction，这个是自己建立的类，目的是为所有的Action做一些统一的控制，在Struts层，对于一个功能，我们一般分为两个Action，一个Action里的功能是不需要调用Struts的验证功能的（常见的方法名称有add,edit,remove,view,list），另一个是需要调用Struts的验证功能的（常见的方法名称有insert,update）。<BR><BR>就拿论坛发贴来说吧，论坛发贴首先需要跳转到一个页面，你可以填写帖子的主题和内容，填写完后，单击“提交”，贴子就发表了，所以这里经过两个步骤：<BR>1、转到一个新增的页面，在Action里我们一般称为add，例如：<BR>public ActionForward add(ActionMapping mapping, ActionForm form,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpServletRequest request, HttpServletResponse response)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws Exception {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//这一句是输出调试信息，表示代码执行到这一段了<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.debug(":: action - subject add");&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//your code here<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //这里保存Token值<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;saveToken(request);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //跳转到add页面，在Structs-config.xml里面定义，例如，跳转到subjectAdd.jsp<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return mapping.findForward("add");<BR>&nbsp;&nbsp;&nbsp;&nbsp;}</P>
<P>2、在填写标题和内容后，选择 提交 ，会提交到insert方法，在insert方法里判断，是否重复提交了。<BR>public ActionForward insert(ActionMapping mapping, ActionForm form, <BR>HttpServletRequest request, HttpServletResponse response){<BR>if (isTokenValid(request, true)) {<BR>// 表单不是重复提交<BR>//这里是保存数据的代码<BR>} else {<BR>//表单重复提交<BR>saveToken(request);<BR>//其它的处理代码<BR>}<BR>}</P>
<P>下面更详细一点（注意，下面所有的代码使用全角括号）：<BR>1、你想发贴时，点击“我要发贴”链接的代码可以里这样的：<BR>〈html:link action="subject.do?method=add"〉我要发贴〈/html:link〉<BR>subject.do 和 method 这些在struct-config.xml如何定义我就不说了，点击链接后，会执行subject.do的add方法，代码如上面说的，跳转到subjectAdd.jsp页面。页面的代码大概如下：<BR>〈html:form action="subjectForm.do?method=insert"〉<BR>&nbsp;&nbsp;〈html:text property="title" /〉<BR>&nbsp;&nbsp;〈html:textarea property="content" /〉<BR>&nbsp;&nbsp;〈html:submit property="发表" /〉<BR>&nbsp;&nbsp;〈html:reset property="重填" /〉<BR>〈html:form〉<BR>如果你在add方法里加了“saveToken(request);”这一句，那在subjectAdd.jsp生成的页面上，会多一个隐藏字段，类似于这样〈input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae"〉，</P>
<P>2、点击发表后，表单提交到subjectForm.do里的insert方法后，你在insert方法里要将表单的数据插入到数据库中，如果没有进行重复提交的控制，那么每点击一次浏览器的刷新按钮，都会在数据库中插入一条相同的记录，增加下面的代码，你就可以控制用户的重复提交了。<BR>if (isTokenValid(request, true)) {<BR>// 表单不是重复提交<BR>//这里是保存数据的代码<BR>} else {<BR>//表单重复提交<BR>saveToken(request);<BR>//其它的处理代码<BR>}<BR>注意，你必须在add方法里使用了saveToken(request)，你才能在insert里判断，否则，你每次保存操作都是重复提交。<BR>记住一点，Struts在你每次访问Action的时候，都会产生一个令牌，保存在你的Session里面，如果你在Action里的函数里面，使用了saveToken(request);，那么这个令牌也会保存在这个Action所Forward到的jsp所生成的静态页面里。<BR>如果你在你Action的方法里使用了isTokenValid，那么Struts会将你从你的request里面去获取这个令牌值，然后和Session里的令牌值做比较，如果两者相等，就不是重复提交，如果不相等，就是重复提交了。<BR><BR>由于我们项目的所有Action都是继承自BaseDispatchAction这个类，所以我们基本上都是在这个类里面做了表单重复提交的控制，默认是控制add方法和insert方法，如果需要控制其它的方法，就自己手动写上面这些代码，否则是不需要手写的，控制的代码如下：<BR>public abstract class BaseDispatchAction extends BaseAction {<BR>protected ActionForward perform(ActionMapping mapping, ActionForm form,<BR>&nbsp;&nbsp;&nbsp;&nbsp;HttpServletRequest request, HttpServletResponse response)<BR>&nbsp;&nbsp;&nbsp;&nbsp;throws Exception {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String parameter = mapping.getParameter();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String name = request.getParameter(parameter);<BR>if (null == name) { //如果没有指定 method ，则默认为 list<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;name = "list";<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ("add".equals(name)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if ("add".equals(name)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;saveToken(request);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} else if ("insert".equals(name)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!isTokenValid(request, true)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resetToken(request);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;saveError(request, new ActionMessage("error.repeatSubmit"));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.error("重复提交！");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return mapping.findForward("error");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return dispatchMethod2(mapping, form, request, response, name);<BR>&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}</P></SPAN><img src ="http://www.blogjava.net/rkind/aggbug/19685.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rkind/" target="_blank">rkind</a> 2005-11-14 14:32 <a href="http://www.blogjava.net/rkind/archive/2005/11/14/19685.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts中下拉菜单的实现[原创]</title><link>http://www.blogjava.net/rkind/archive/2005/10/27/17073.html</link><dc:creator>rkind</dc:creator><author>rkind</author><pubDate>Thu, 27 Oct 2005 08:51:00 GMT</pubDate><guid>http://www.blogjava.net/rkind/archive/2005/10/27/17073.html</guid><wfw:comment>http://www.blogjava.net/rkind/comments/17073.html</wfw:comment><comments>http://www.blogjava.net/rkind/archive/2005/10/27/17073.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/rkind/comments/commentRss/17073.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rkind/services/trackbacks/17073.html</trackback:ping><description><![CDATA[<P>在jsp轻易实现下拉菜单，那么在struts中呢？<BR>大概的步骤如下：<BR>1首先定义一个bean，最少有两个属性，每个属性都有set和get的方法<BR>2定义一个业务逻辑类，重复从数据库中读取纪录为Bean赋值，将这些bean添加入一个Collection<BR>3将collection放入到Request中。<BR>4在JSP中用<?xml:namespace prefix = html /><html:options>的标签显示出来<BR>实例：<BR>要实现一个显示姓名返回Id值　的下拉菜单，<BR>１首先实现一个user的bean，该bean有两个属性,name,id,每个属性都有对应set和get的方法<BR>比如name就有，setname()和getname()的方法<BR>2定义一个逻辑类，GetUserList<BR>         select * from User;<BR>         Collection listuser;<BR>               while(rs.next()){<BR>                         user.setname(rs.getString(username));<BR>                           ....<BR>                        listuser.add(user);<BR>}<BR>3比如list.jsp做为要显示下拉菜单的页面，那么在/list　对应的action ListUser中应该<BR>request.setAttribute("userlist",user);<BR>4,在list.jsp中用<BR><html:select property="select_user">//actionForm中定义的变量<BR>   <html:options property="id" collection="listuser" Labelproperty="name"></html:options><BR></html:select><BR>即可<BR>不过这样显示的只是简单的菜单，要实现能动态得跳转得不知道用什么办法<IMG height=19 src="http://www.blogjava.net/Emoticons/angry_smile.gif" width=19 border=0></P></html:options><img src ="http://www.blogjava.net/rkind/aggbug/17073.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rkind/" target="_blank">rkind</a> 2005-10-27 16:51 <a href="http://www.blogjava.net/rkind/archive/2005/10/27/17073.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java框架研究——JSF与Struts的异同</title><link>http://www.blogjava.net/rkind/archive/2005/10/27/17030.html</link><dc:creator>rkind</dc:creator><author>rkind</author><pubDate>Thu, 27 Oct 2005 05:07:00 GMT</pubDate><guid>http://www.blogjava.net/rkind/archive/2005/10/27/17030.html</guid><wfw:comment>http://www.blogjava.net/rkind/comments/17030.html</wfw:comment><comments>http://www.blogjava.net/rkind/archive/2005/10/27/17030.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rkind/comments/commentRss/17030.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rkind/services/trackbacks/17030.html</trackback:ping><description><![CDATA[Struts和JSF/Tapestry都属于表现层框架，这两种分属不同性质的框架，后者是一种事件驱动型的组件模型，而Struts只是单纯的MVC模式框架，老外总是急吼吼说事件驱动型就比MVC模式框架好，何以见得，我们下面进行详细分析比较一下到底是怎么回事？<BR><BR>首先事件是指从客户端页面（浏览器）由用户操作触发的事件，Struts使用Action来接受浏览器表单提交的事件，这里使用了Command模式，每个继承Action的子类都必须实现一个方法execute。<BR><BR>在Struts中，实际是一个表单Form对应一个Action类(或DispatchAction)，换一句话说：在Struts中实际是一个表单只能对应一个事件，Struts这种事件方式称为application event，application event和Component event相比是一种粗粒度的事件。<BR><BR>Struts重要的表单对象ActionForm是一种对象，它代表了一种应用，这个对象中至少包含几个字段，这些字段是Jsp页面表单中的input字段，因为一个表单对应一个事件，所以，当我们需要将事件粒度细化到表单中这些字段时，也就是说，一个字段对应一个事件时，单纯使用Struts就不太可能，当然通过结合JavaScript也是可以转弯实现的。<BR><BR>而这种情况使用JSF就可以方便实现<BR><BR>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>&lt;h:inputText id="userId" value="#{login.userId}"&gt;
&lt;f:valueChangeListener type="logindemo.UserLoginChanged" /&gt;
&lt;/h:inputText&gt;</PRE></TD></TR></TBODY></TABLE></CENTER><BR><BR>#{login.userId}表示从名为login的JavaBean的getUserId获得的结果，这个功能使用struts也可以实现，name="login" property="userId" <BR><BR>关键是第二行，这里表示如果userId的值改变并且确定提交后，将触发调用类UserLoginChanged的processValueChanged(...)方法。 <BR><BR>JSF可以为组件提供两种事件：Value Changed和 Action. 前者我们已经在上节见识过用处，后者就相当于struts中表单提交Action机制，它的JSF写法如下： <BR><BR>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>&lt;h:commandButton id="login" commandName="login"&gt;
&lt;f:actionListener type=”logindemo.LoginActionListener” /&gt;
&lt;/h:commandButton&gt;</PRE></TD></TR></TBODY></TABLE></CENTER>从代码可以看出，这两种事件是通过Listerner这样观察者模式贴在具体组件字段上的，而Struts此类事件是原始的一种表单提交Submit触发机制。如果说前者比较语言化（编程语言习惯做法类似Swing编程）；后者是属于WEB化，因为它是来自Html表单，如果你起步是从Perl/PHP开始，反而容易接受Struts这种风格。<BR><BR>基本配置<BR><BR>Struts和JSF都是一种框架，JSF必须需要两种包JSF核心包、JSTL包（标签库），此外，JSF还将使用到Apache项目的一些commons包，这些Apache包只要部署在你的服务器中既可。<BR><BR>JSF包下载地址：http://java.sun.com/j2ee/javaserverfaces/download.html选择其中Reference Implementation。<BR><BR>JSTL包下载在http://jakarta.apache.org/site/downloads /downloads_taglibs-standard.cgi<BR><BR>所以，从JSF的驱动包组成看，其开源基因也占据很大的比重，JSF是一个SUN伙伴们工业标准和开源之间的一个混血儿。<BR><BR>上述两个地址下载的jar合并在一起就是JSF所需要的全部驱动包了。与Struts的驱动包一样，这些驱动包必须位于Web项目的WEB-INF/lib，和Struts一样的是也必须在web.xml中有如下配置：<BR><BR>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>&lt;web-app&gt;
　　&lt;servlet&gt;
　　&lt;servlet-name&gt;Faces Servlet&lt;/servlet-name&gt;
　　&lt;servlet-class&gt;javax.faces.webapp.FacesServlet&lt;/servlet-class&gt;
　　&lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
　　&lt;/servlet&gt; 
　　&lt;servlet-mapping&gt;
　　&lt;servlet-name&gt;Faces Servlet&lt;/servlet-name&gt;
　　&lt;url-pattern&gt;*.faces&lt;/url-pattern&gt;
　　&lt;/servlet-mapping&gt; 
    &lt;/web-app&gt;</PRE></TD></TR></TBODY></TABLE></CENTER><BR><BR>这里和Struts的web.xml配置何其相似，简直一模一样。 <BR><BR>正如Struts的struts-config.xml一样，JSF也有类似的faces-config.xml配置文件： <BR><BR>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>&lt;faces-config&gt;
　　&lt;navigation-rule&gt;
　　&lt;from-view-id&gt;/index.jsp&lt;/from-view-id&gt;
　　&lt;navigation-case&gt;
　  &lt;from-outcome&gt;login&lt;/from-outcome&gt;
　　&lt;to-view-id&gt;/welcome.jsp&lt;/to-view-id&gt;
　　&lt;/navigation-case&gt;
　　&lt;/navigation-rule&gt; 
　　&lt;managed-bean&gt; 
　　&lt;managed-bean-name&gt;user&lt;/managed-bean-name&gt;
　　&lt;managed-bean-class&gt;com.corejsf.UserBean&lt;/managed-bean-class&gt; 
　　&lt;managed-bean-scope&gt;session&lt;/managed-bean-scope&gt; 
　　&lt;/managed-bean&gt;
    &lt;/faces-config&gt;</PRE></TD></TR></TBODY></TABLE></CENTER>在Struts-config.xml中有ActionForm Action以及Jsp之间的流程关系，在faces-config.xml中，也有这样的流程，我们具体解释一下Navigation：<BR><BR>在index.jsp中有一个事件：<BR><BR><BR><BR><BR>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>&lt;h:commandButton label="Login" action="login" /&gt;</PRE></TD></TR></TBODY></TABLE></CENTER><BR><BR>Action的值必须匹配form-outcome值，上述Navigation配置表示：如果在index.jsp中有一个login事件，那么事件触发后下一个页面将是welcome.jsp <BR><BR>JSF有一个独立的事件发生和页面导航的流程安排，这个思路比struts要非常清晰。 <BR><BR>managed-bean类似Struts的ActionForm，正如可以在struts-config.xml中定义ActionForm的scope一样，这里也定义了managed-bean的scope为session。 <BR><BR>但是如果你只以为JSF的managed-bean就这点功能就错了，JSF融入了新的Ioc模式/依赖性注射等技术。 <BR><BR>Ioc模式 <BR><BR>对于Userbean这样一个managed-bean，其代码如下： <BR><BR>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public class UserBean {
　　private String name;
　　private String password;

　　// PROPERTY: name
　　public String getName() { return name; }
　　public void setName(String newValue) { name = newValue; }

　　// PROPERTY: password
　　public String getPassword() { return password; }
　　public void setPassword(String newValue) { password = newValue; }
    }

    &lt;managed-bean&gt;
　　&lt;managed-bean-name&gt;user&lt;/managed-bean-name&gt;
　　&lt;managed-bean-class&gt;com.corejsf.UserBean&lt;/managed-bean-class&gt;
　　&lt;managed-bean-scope&gt;session&lt;/managed-bean-scope&gt;

　　&lt;managed-property&gt;
　　&lt;property-name&gt;name&lt;/property-name&gt;
　　&lt;value&gt;me&lt;/value&gt;
　　&lt;/managed-property&gt;

　　&lt;managed-property&gt;
　　&lt;property-name&gt;password&lt;/property-name&gt;
　　&lt;value&gt;secret&lt;/value&gt;
　　&lt;/managed-property&gt;
    &lt;/managed-bean&gt;</PRE></TD></TR></TBODY></TABLE></CENTER>faces-config.xml这段配置其实是将"me"赋值给name，将secret赋值给password，这是采取Ioc模式中的Setter注射方式。<BR><BR>Backing Beans<BR><BR>对于一个web form，我们可以使用一个bean包含其涉及的所有组件，这个bean就称为Backing Bean， Backing Bean的优点是：一个单个类可以封装相关一系列功能的数据和逻辑。<BR><BR>说白了，就是一个Javabean里包含其他Javabean，互相调用，属于Facade模式或Adapter模式。<BR><BR>对于一个Backing Beans来说，其中包含了几个managed-bean，managed-bean一定是有scope的，那么这其中的几个managed-beans如何配置它们的scope呢？<BR><BR>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>&lt;managed-bean&gt;
　　...
　　&lt;managed-property&gt;
　　&lt;property-name&gt;visit&lt;/property-name&gt;
　　&lt;value&gt;#{sessionScope.visit}&lt;/value&gt;
　　&lt;/managed-property&gt;</PRE></TD></TR></TBODY></TABLE></CENTER><BR><BR>这里配置了一个Backing Beans中有一个setVisit方法，将这个visit赋值为session中的visit，这样以后在程序中我们只管访问visit对象，从中获取我们希望的数据（如用户登陆注册信息），而visit是保存在session还是application或request只需要配置既可。 <BR><BR>UI界面 <BR><BR>JSF和Struts一样，除了JavaBeans类之外，还有页面表现元素，都是是使用标签完成的，Struts也提供了struts-faces.tld标签库向JSF过渡。 <BR><BR>使用Struts标签库编程复杂页面时，一个最大问题是会大量使用logic标签，这个logic如同if语句，一旦写起来，搞的JSP页面象俄罗斯方块一样，但是使用JSF标签就简洁优美： <BR><BR>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>&lt;jia:navigatorItem name="inbox" label="InBox"
　　icon="/images/inbox.gif"
　　action="inbox"
　　disabled="#{!authenticationBean.inboxAuthorized}"/&gt;</PRE></TD></TR></TBODY></TABLE></CENTER><BR><BR>如果authenticationBean中inboxAuthorized返回是假，那么这一行标签就不用显示，多干净利索！ <BR><BR>先写到这里，我会继续对JSF深入比较下去，如果研究过Jdon框架的人，可能会发现，Jdon框架的jdonframework.xml中service配置和managed-bean一样都使用了依赖注射，看来对Javabean的依赖注射已经迅速地成为一种新技术象征，如果你还不了解Ioc模式，赶紧补课。 <BR><img src ="http://www.blogjava.net/rkind/aggbug/17030.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rkind/" target="_blank">rkind</a> 2005-10-27 13:07 <a href="http://www.blogjava.net/rkind/archive/2005/10/27/17030.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts常见错误汇总 </title><link>http://www.blogjava.net/rkind/archive/2005/10/27/17029.html</link><dc:creator>rkind</dc:creator><author>rkind</author><pubDate>Thu, 27 Oct 2005 05:06:00 GMT</pubDate><guid>http://www.blogjava.net/rkind/archive/2005/10/27/17029.html</guid><wfw:comment>http://www.blogjava.net/rkind/comments/17029.html</wfw:comment><comments>http://www.blogjava.net/rkind/archive/2005/10/27/17029.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/rkind/comments/commentRss/17029.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/rkind/services/trackbacks/17029.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width=448 border=0>
<TBODY>
<TR>
<TD colSpan=2 height=29><STRONG>Struts常见错误汇总</STRONG></TD></TR>
<TR>
<TD style="WORD-BREAK: break-all" colSpan=2>以下所说的struts-config.xml和ApplicationResources.properties等文件名是缺省时使用的，如果你使用了多模块，或指定了不同的资源文件名称，这些名字要做相应的修改。<BR><BR>　　1、“No&nbsp;bean&nbsp;found&nbsp;under&nbsp;attribute&nbsp;key&nbsp;XXX”<BR>　　在struts-config.xml里定义了一个ActionForm，但type属性指定的类不存在，type属性的值应该是Form类的全名。或者是，在Action的定义中，name或attribute属性指定的ActionForm不存在。<BR><BR>　　2、“Cannot&nbsp;find&nbsp;bean&nbsp;XXX&nbsp;in&nbsp;any&nbsp;scope”<BR>　　在Action里一般会request.setAttribute()一些对象，然后在转向的jsp文件里（用tag或request.getAttribute()方法）得到这些对象并显示出来。这个异常是说jsp要得到一个对象，但前面的Action里并没有将对象设置到request（也可以是session、servletContext）里。<BR>可能是名字错了，请检查jsp里的tag的一般是name属性，或getAttribute()方法的参数值；或者是Action逻辑有问题没有执行setAttribute()方法就先转向了。<BR>还有另外一个可能，纯粹是jsp文件的问题，例如&lt;logic:iterate&gt;会指定一个id值，然后在循环里&lt;bean:write&gt;使用这个值作为name的值，如果这两个值不同，也会出现此异常。（都是一个道理，request里没有对应的对象。）<BR><BR>　　3、“Missing&nbsp;message&nbsp;for&nbsp;key&nbsp;"XXX"”<BR>　　缺少所需的资源，检查ApplicationResources.properties文件里是否有jsp文件里需要的资源，例如：<BR>　　&lt;bean:message&nbsp;key="msg.name.prompt"/&gt;<BR>　　这行代码会找msg.name.prompt资源，如果AppliationResources.properties里没有这个资源就会出现本异常。在使用多模块时，要注意在模块的struts-config-xxx.xml里指定要使用的资源文件名称，否则当然什么资源也找不到，这也是一个很容易犯的错误。<BR><BR>　　4、“No&nbsp;getter&nbsp;method&nbsp;for&nbsp;property&nbsp;XXX&nbsp;of&nbsp;bean&nbsp;teacher”<BR>　　这条异常信息说得很明白，jsp里要取一个bean的属性出来，但这个bean并没有这个属性。你应该检查jsp中某个标签的property属性的值。例如下面代码中的cade应该改为code才对：<BR>　　&lt;bean:write&nbsp;name="teacher"&nbsp;property="cade"&nbsp;filter="true"/&gt;<BR><BR>　　5、“Cannot&nbsp;find&nbsp;ActionMappings&nbsp;or&nbsp;ActionFormBeans&nbsp;collection”<BR>　　待解决。<BR><BR>　　6、“Cannot&nbsp;retrieve&nbsp;mapping&nbsp;for&nbsp;action&nbsp;XXX”<BR>　　在.jsp的&lt;form&gt;标签里指定action='/XXX'，但这个Action并未在struts-config.xml里设置过。<BR><BR>　　7、HTTP&nbsp;Status&nbsp;404&nbsp;-&nbsp;/xxx/xxx.jsp<BR>　　Forward的path属性指向的jsp页面不存在，请检查路径和模块，对于同一模块中的Action转向，path中不应包含模块名；模块间转向，记住使用contextRelative="true"。<BR><BR>　　8、没有任何异常信息，显示空白页面<BR>　　可能是Action里使用的forward与struts-config.xml里定义的forward名称不匹配。<BR><BR>　　9、“The&nbsp;element&nbsp;type&nbsp;"XXX"&nbsp;must&nbsp;be&nbsp;terminated&nbsp;by&nbsp;the&nbsp;matching&nbsp;end-tag&nbsp;"XXX".”<BR>　　这个是struts-config.xml文件的格式错误，仔细检查它是否是良构的xml文件，关于xml文件的格式这里就不赘述了。<BR><BR>　　10、“Servlet.init()&nbsp;for&nbsp;servlet&nbsp;action&nbsp;threw&nbsp;exception”<BR>　　一般出现这种异常在后面会显示一个关于ActionServlet的异常堆栈信息，其中指出了异常具体出现在代码的哪一行。我曾经遇到的一次提示如下：<BR><BR>　　java.lang.NullPointerException<BR>　　　at&nbsp;org.apache.struts.action.ActionServlet.parseModuleConfigFile(ActionServlet.java:1003)<BR>　　　at&nbsp;org.apache.struts.action.ActionServlet.initModuleConfig(ActionServlet.java:955)<BR><BR>　　为解决问题，先下载struts的源码包，然后在ActionServlet.java的第1003行插入断点，并对各变量进行监视。很丢人，我竟然把struts-config.xml文件弄丢了，因此出现了上面的异常，应该是和CVS同步时不小心删除的。<BR><BR>　　11、“Resources&nbsp;not&nbsp;defined&nbsp;for&nbsp;Validator”<BR>　　这个是利用Validator插件做验证时可能出现的异常，这时你要检查validation.xml文件，看里面使用的资源是否确实有定义，form的名称是否正确，等等。</TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/rkind/aggbug/17029.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/rkind/" target="_blank">rkind</a> 2005-10-27 13:06 <a href="http://www.blogjava.net/rkind/archive/2005/10/27/17029.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>