<?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-SIMONE-随笔分类-JSP</title><link>http://www.blogjava.net/wangxinsh55/category/9224.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 18 Jan 2008 07:46:13 GMT</lastBuildDate><pubDate>Fri, 18 Jan 2008 07:46:13 GMT</pubDate><ttl>60</ttl><item><title>jsp中文件下载的实现</title><link>http://www.blogjava.net/wangxinsh55/archive/2008/01/17/176006.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Thu, 17 Jan 2008 09:10:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2008/01/17/176006.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/176006.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2008/01/17/176006.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/176006.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/176006.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jsp中实现文件下载的最简单的方式是在网页上做超级链接，如：&lt;a href="music/abc.mp3"&gt;点击下载&lt;/a&gt;。但是这样服务器上的目录资源会直接暴露给最终用户，会给网站带来一些不安全的因素。因此可以采用其它方式实现下载，可以采用：1、RequestDispatcher的方式进行；2、采用文件流输出的方式下载。</p>
<p>1、采用RequestDispatcher的方式进行</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jsp页面中添加如下代码：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;%<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.setContentType("application/x-download");//设置为下载application/x-download<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String filedownload = "/要下载的文件名";//即将下载的文件的相对路径<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String filedisplay = "最终要显示给用户的保存文件名";//下载文件时显示的文件保存名称<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; filenamedisplay = URLEncoder.encode(filedisplay,"UTF-8");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.addHeader("Content-Disposition","attachment;filename=" + filedisplay);<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RequestDispatcher dis = application.getRequestDispatcher(filedownload);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(dis!= null)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dis.forward(request,response);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.flushBuffer();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(Exception e)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finally<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
%&gt;</p>
<p>2、采用文件流输出的方式下载</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;%@page language="java" contentType="application/x-msdownload"&nbsp;&nbsp;&nbsp; pageEncoding="gb2312"%&gt;&lt;%<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //关于文件下载时采用文件流输出的方式处理：<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //<span style="color: red">加上response.reset()，并且<strong><em>所有的％&gt;后面不要换行，包括最后一个</em></strong>；<br />
</span><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.reset();//可以加也可以不加<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.setContentType("application/x-download");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String filedownload = "想办法找到要提供下载的文件的物理路径＋文件名";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String filedisplay = "给用户提供的下载文件名";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; filedisplay = URLEncoder.encode(filedisplay,"UTF-8");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.addHeader("Content-Disposition","attachment;filename=" + filedisplay);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OutputStream outp = null;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileInputStream in = null;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; outp = response.getOutputStream();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; in = new FileInputStream(filenamedownload);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte[] b = new byte[1024];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int i = 0;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while((i = in.read(b)) &gt; 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; outp.write(b, 0, i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; outp.flush();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(Exception e)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("Error!");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; finally<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(in != null)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; in.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; in = null;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(outp != null)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; outp.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; outp = null;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
%&gt;<br />
<br />
在wsad里面写JSP文件下载,总是出现这个异常,getOutputStream() has already been called for this response,输出流已经被调用了. </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;上网查半天终于明白一点,JSP早下载文件的时候用到了OutputStream,而在Application Server在处理编译jsp时对于％&gt;和&lt;％之间的内容一般是原样输出，而且默认是PrintWriter.</p>
<img src ="http://www.blogjava.net/wangxinsh55/aggbug/176006.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2008-01-17 17:10 <a href="http://www.blogjava.net/wangxinsh55/archive/2008/01/17/176006.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>应用HttpClient来对付各种顽固的WEB服务器</title><link>http://www.blogjava.net/wangxinsh55/archive/2007/10/09/151300.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Tue, 09 Oct 2007 03:12:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/10/09/151300.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/151300.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/10/09/151300.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/151300.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/151300.html</trackback:ping><description><![CDATA[一般的情况下我们都是使用IE或者Navigator浏览器来访问一个WEB服务器，用来浏览页面查看信息或者提交一些数据等等。所访问的这些页面有的仅仅是一些普通的页面，有的需要用户登录后方可使用，或者需要认证以及是一些通过加密方式传输，例如HTTPS。目前我们使用的浏览器处理这些情况都不会构成问题。不过你可能在某些时候需要通过程序来访问这样的一些页面，比如从别人的网页中&#8220;偷&#8221;一些数据；利用某些站点提供的页面来完成某种功能，例如说我们想知道某个手机号码的归属地而我们自己又没有这样的数据，因此只好借助其他公司已有的网站来完成这个功能，这个时候我们需要向网页提交手机号码并从返回的页面中解析出我们想要的数据来。如果对方仅仅是一个很简单的页面，那我们的程序会很简单，本文也就没有必要大张旗鼓的在这里浪费口舌。但是考虑到一些服务授权的问题，很多公司提供的页面往往并不是可以通过一个简单的URL就可以访问的，而必须经过注册然后登录后方可使用提供服务的页面，这个时候就涉及到 COOKIE问题的处理。我们知道目前流行的动态网页技术例如ASP、JSP无不是通过COOKIE来处理会话信息的。为了使我们的程序能使用别人所提供的服务页面，就要求程序首先登录后再访问服务页面，这过程就需要自行处理cookie，想想当你用java.net.HttpURLConnection 来完成这些功能时是多么恐怖的事情啊！况且这仅仅是我们所说的顽固的WEB服务器中的一个很常见的&#8220;顽固&#8221;！再有如通过HTTP来上传文件呢？不需要头疼，这些问题有了&#8220;它&#8221;就很容易解决了！
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>我们不可能列举所有可能的顽固，我们会针对几种最常见的问题进行处理。当然了，正如前面说到的，如果我们自己使用 java.net.HttpURLConnection来搞定这些问题是很恐怖的事情，因此在开始之前我们先要介绍一下一个开放源码的项目，这个项目就是 Apache开源组织中的httpclient，它隶属于Jakarta的commons项目，目前的版本是2.0RC2。commons下本来已经有一个net的子项目，但是又把httpclient单独提出来，可见http服务器的访问绝非易事。</p>
<p>Commons-httpclient项目就是专门设计来简化HTTP客户端与服务器进行各种通讯编程。通过它可以让原来很头疼的事情现在轻松的解决，例如你不再管是HTTP或者HTTPS的通讯方式，告诉它你想使用HTTPS方式，剩下的事情交给httpclient替你完成。本文会针对我们在编写HTTP客户端程序时经常碰到的几个问题进行分别介绍如何使用httpclient来解决它们，为了让读者更快的熟悉这个项目我们最开始先给出一个简单的例子来读取一个网页的内容，然后循序渐进解决掉前进中的所有问题。</p>
<p>1．&nbsp;读取网页(HTTP/HTTPS)内容</p>
<p>下面是我们给出的一个简单的例子用来访问某个页面</p>
<p align="left"><span lang="EN-US">/*</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span>* Created on 2003-12-14 by Liudong</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span>*/</span></p>
<p align="left"><strong><span lang="EN-US">package</span></strong><span lang="EN-US"> <span>http.demo;</span></p>
<p align="left"><span lang="EN-US">&nbsp;</span></p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>java.io.IOException;</span></p>
<p align="left"><span lang="EN-US">&nbsp;</span></p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>org.apache.commons.httpclient.*;</span></p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>org.apache.commons.httpclient.methods.*;</span></p>
<p align="left"><span lang="EN-US">/**</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span><span>*</span> </span><span>最简单的</span><span lang="EN-US">HTTP</span><span>客户端</span><span lang="EN-US">,</span><span>用来演示通过</span><span lang="EN-US">GET</span><span>或者</span><span lang="EN-US">POST</span><span>方式访问某个页面</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span><span>*</span> <strong><span>@author</span></strong> <span>Liudong</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span><span>*/</span></p>
<p align="left"><strong><span lang="EN-US">public</span></strong><span lang="EN-US"> <strong><span>class</span></strong> <span>SimpleClient</span> <span>{</span></p>
<p align="left"><span lang="EN-US">&nbsp;</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><strong><span>public</span></strong> <strong><span>static</span></strong> <strong><span>void</span></strong> <span>main(String[]</span> <span>args)</span> <strong><span>throws</span></strong> <span>IOException</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>{</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>HttpClient</span> <span>client</span> <span>=</span> <strong><span>new</span></strong> <span>HttpClient();</span><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>//</span><span>设置代理服务器地址和端口</span><span lang="EN-US"><span>&nbsp;</span><span>&nbsp;&nbsp;&nbsp; </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //client.getHostConfiguration().setProxy("proxy_host_addr",proxy_port);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span>//</span><span>使用</span><span lang="EN-US">GET</span><span>方法</span><span>，如果服务器需要通过</span><span lang="EN-US">HTTPS</span><span>连接，那只需要将下面</span><span lang="EN-US">URL</span><span>中的</span><span lang="EN-US">http</span><span>换成</span><span lang="EN-US">https</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>HttpMethod</span> <span>method</span> <span>=</span> <strong><span>new</span></strong> <span>GetMethod(</span><span>"http://java.sun.com"</span><span>);</span><span lang="EN-US"> </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>//</span><span>使用</span><span lang="EN-US">POST</span><span>方法</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>//HttpMethod method = new PostMethod("http://java.sun.com");</span><span lang="EN-US"> </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>client.executeMethod(method);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>//</span><span>打印服务器返回的状态</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>System.out.println(method.getStatusLine());</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>//</span><span>打印返回的信息</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>System.out.println(method.getResponseBodyAsString());</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>//</span><span>释放连接</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>method.releaseConnection();</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>}<br />
</span><span lang="EN-US">}</span></p>
<p align="left">&nbsp;</p>
<p align="left"><span lang="EN-US">在这个例子中首先创建一个HTTP客户端(HttpClient)的实例，然后选择提交的方法是GET或者POST，最后在HttpClient实例上执行提交的方法，最后从所选择的提交方法中读取服务器反馈回来的结果。这就是使用HttpClient的基本流程。其实用一行代码也就可以搞定整个请求的过程，非常的简单！</span></p>
<span lang="EN-US">
<p align="left"><br />
2．&nbsp;以GET或者POST方式向网页提交参数<br />
<br />
其实前面一个最简单的示例中我们已经介绍了如何使用GET或者POST方式来请求一个页面，本小节与之不同的是多了提交时设定页面所需的参数，我们知道如果是GET的请求方式，那么所有参数都直接放到页面的URL后面用问号与页面地址隔开，每个参数用&amp;隔开，例如：<a href="http://java.sun.com/?name=liudong&amp;mobile=123456">http://java.sun.com?name=liudong&amp;mobile=123456</a>，但是当使用POST方法时就会稍微有一点点麻烦。本小节的例子演示向如何查询手机号码所在的城市，代码如下：</p>
<p align="left">&nbsp;</p>
<p align="left"><span lang="EN-US">/*</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span>* Created on 2003-12-7 by Liudong</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span>*/</span></p>
<p align="left"><strong><span lang="EN-US">package</span></strong><span lang="EN-US"> <span>http.demo;</span></p>
<p align="left"><span lang="EN-US">&nbsp;</span></p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>java.io.IOException;</span></p>
<p align="left"><span lang="EN-US">&nbsp;</span></p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>org.apache.commons.httpclient.*;</span></p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>org.apache.commons.httpclient.methods.*;</span></p>
<p align="left"><span lang="EN-US">/**</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span><span>*</span> </span><span>提交参数演示</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span><span>*</span> </span><span>该程序连接到一个用于查询手机号码所属地的页面</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span><span>*</span> </span><span>以便查询号码段</span><span lang="EN-US">1330227</span><span>所在的省份以及城市</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span><span>*</span> <strong><span>@author</span></strong> <span>Liudong</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span><span>*/</span></p>
<p align="left"><strong><span lang="EN-US">public</span></strong><span lang="EN-US"> <strong><span>class</span></strong> <span>SimpleHttpClient</span> <span>{</span></p>
<p align="left"><span lang="EN-US">&nbsp;</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><strong><span>public</span></strong> <strong><span>static</span></strong> <strong><span>void</span></strong> <span>main(String[]</span> <span>args)</span> <strong><span>throws</span></strong> <span>IOException</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>{</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>HttpClient</span> <span>client</span> <span>=</span> <strong><span>new</span></strong> <span>HttpClient();</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>client.getHostConfiguration().setHost(</span><span>"www.imobile.com.cn"</span><span>,</span> <span>80,</span> <span>"http"</span><span>);</span></p>
<p align="left"><span lang="EN-US">&nbsp;</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>HttpMethod</span> <span>method</span> <span>=</span> <span>getPostMethod();</span><span>//</span><span>使用</span><span lang="EN-US">POST</span><span>方式提交数据</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>client.executeMethod(method);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>//</span><span>打印服务器返回的状态</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>System.out.println(method.getStatusLine());</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>//</span><span>打印</span><span>结果页面</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>String</span> <span>response</span> <span>=</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><strong><span>new</span></strong> <span>String(method.getResponseBodyAsString().getBytes(</span><span>"8859_1"</span><span>));</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>//</span><span>打印返回的信息</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>System.out.println(response);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>method.releaseConnection();</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>}</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>/**</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;</span><span>*</span> </span><span>使用</span><span lang="EN-US">GET</span><span>方式提交数据</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;</span><span>*</span> <strong><span>@return</span></strong></span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;</span><span>*/</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><strong><span>private</span></strong> <strong><span>static</span></strong> <span>HttpMethod</span> <span>getGetMethod(){</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><strong><span>return</span></strong> <strong><span>new</span></strong> <span>GetMethod(</span><span>"/simcard.php?simcard=1330227"</span><span>);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>}</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>/**</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;</span><span>*</span> </span><span>使用</span><span lang="EN-US">POST</span><span>方式提交数据</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;</span><span>*</span> <strong><span>@return</span></strong></span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;</span><span>*/</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><strong><span>private</span></strong> <strong><span>static</span></strong> <span>HttpMethod</span> <span>getPostMethod(){</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>PostMethod</span> <span>post</span> <span>=</span> <strong><span>new</span></strong> <span>PostMethod(</span><span>"/simcard.php"</span><span>);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>NameValuePair</span> <span>simcard</span> <span>=</span> <strong><span>new</span></strong> <span>NameValuePair(</span><span>"simcard"</span><span>,</span><span>"1330227"</span><span>);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>post.setRequestBody(</span><strong><span>new</span></strong> <span>NameValuePair[]</span> <span>{</span> <span>simcard});</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><strong><span>return</span></strong> <span>post;</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>}</span></p>
<p align="left"><span lang="EN-US">}</span></p>
<p><span lang="EN-US">在上面的例子中页面<a href="http://www.imobile.com.cn/simcard.php">http://www.imobile.com.cn/simcard.php</a>需要一个参数是simcard，这个参数值为手机号码段，即手机号码的前七位，服务器会返回提交的手机号码对应的省份、城市以及其他详细信息。GET的提交方法只需要在URL后加入参数信息，而POST则需要通过NameValuePair类来设置参数名称和它所对应的值</span></p>
<p><span lang="EN-US">3．&nbsp;处理页面重定向</span></p>
<p><span lang="EN-US">在JSP/Servlet 编程中response.sendRedirect方法就是使用HTTP协议中的重定向机制。它与JSP中的&lt;jsp:forward &#8230;&gt;的区别在于后者是在服务器中实现页面的跳转，也就是说应用容器加载了所要跳转的页面的内容并返回给客户端；而前者是返回一个状态码，这些状态码的可能值见下表，然后客户端读取需要跳转到的页面的URL并重新加载新的页面。就是这样一个过程，所以我们编程的时候就要通过 HttpMethod.getStatusCode()方法判断返回值是否为下表中的某个值来判断是否需要跳转。如果已经确认需要进行页面跳转了，那么可以通过读取HTTP头中的location属性来获取新的地址。</span></p>
<span lang="EN-US">
<p>
<table cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr>
            <td valign="top" width="67">
            <p><span>状态码</span></p>
            </td>
            <td valign="top" width="216">
            <p><span>对应</span><span lang="EN-US"><font face="Times New Roman">HttpServletResponse</font></span><span>的常量</span></p>
            </td>
            <td valign="top" width="261">
            <p><span>详细描述</span></p>
            </td>
        </tr>
        <tr>
            <td valign="top" width="67">
            <p><span lang="EN-US"><font face="Times New Roman">301</font></span></p>
            </td>
            <td valign="top" width="216">
            <p><span lang="EN-US"><font face="Times New Roman">SC_MOVED_PERMANENTLY</font></span></p>
            </td>
            <td valign="top" width="261">
            <p><span>页面已经永久移到另外一个新地址</span></p>
            </td>
        </tr>
        <tr>
            <td valign="top" width="67">
            <p><span lang="EN-US"><font face="Times New Roman">302</font></span></p>
            </td>
            <td valign="top" width="216">
            <p><span lang="EN-US"><font face="Times New Roman">SC_MOVED_TEMPORARILY</font></span></p>
            </td>
            <td valign="top" width="261">
            <p><span>页面暂时移动到另外一个新的地址</span></p>
            </td>
        </tr>
        <tr>
            <td valign="top" width="67">
            <p><span lang="EN-US"><font face="Times New Roman">303</font></span></p>
            </td>
            <td valign="top" width="216">
            <p><span lang="EN-US"><font face="Times New Roman">SC_SEE_OTHER</font></span></p>
            </td>
            <td valign="top" width="261">
            <p><span>客户端请求的地址必须通过另外的</span><span lang="EN-US"><font face="Times New Roman">URL</font></span><span>来访问</span></p>
            </td>
        </tr>
        <tr>
            <td valign="top" width="67">
            <p><span lang="EN-US"><font face="Times New Roman">307</font></span></p>
            </td>
            <td valign="top" width="216">
            <p><span lang="EN-US"><font face="Times New Roman">SC_TEMPORARY_REDIRECT</font></span></p>
            </td>
            <td valign="top" width="261">
            <p><span>同</span><span lang="EN-US"><font face="Times New Roman">SC_MOVED_TEMPORARILY</font></span></p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>下面的代码片段演示如何处理页面的重定向</p>
<p align="left"><span lang="EN-US">client.executeMethod(post);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>System.out.println(post.getStatusLine().toString());</span> </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>post.releaseConnection();</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>//</span><span>检查是否重定向</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>int</span></strong> <span>statuscode</span> <span>=</span> <span>post.getStatusCode();</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>if</span></strong> <span>((statuscode</span> <span>==</span> <span>HttpStatus.SC_MOVED_TEMPORARILY)</span> <span>||</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>(statuscode</span> <span>==</span> <span>HttpStatus.SC_MOVED_PERMANENTLY)</span> <span>||</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>(statuscode</span> <span>==</span> <span>HttpStatus.SC_SEE_OTHER)</span> <span>||</span></p>
<p align="left"><span lang="EN-US">(statuscode</span><span lang="EN-US"> <span>==</span> <span>HttpStatus.SC_TEMPORARY_REDIRECT))</span> <span>{</span></p>
<p align="left"><span lang="EN-US">//</span><span>读取新的</span><span lang="EN-US">URL</span><span>地址</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>Header</span> <span>header</span> <span>=</span> <span>post.getResponseHeader(</span><span>"location"</span><span>);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>if</span></strong> <span>(header</span> <span>!=</span> <strong><span>null</span></strong><span>)</span> <span>{</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span>String</span> <span>newuri</span> <span>=</span> <span>header.getValue();</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>if</span></strong> <span>((newuri</span> <span>==</span> <strong><span>null</span></strong><span>)</span> <span>||</span> <span>(newuri.equals(</span><span>""</span><span>)))</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>newuri</span> <span>=</span> <span>"/"</span><span>;</span> </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>GetMethod</span> <span>redirect</span> <span>=</span> <strong><span>new</span></strong> <span>GetMethod(newuri);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>client.executeMethod(redirect);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span>System.out.println(</span><span>"Redirect:"</span><span>+</span> <span>redirect.getStatusLine().toString());</span> </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>redirect.releaseConnection();</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>}</span> <strong><span>else</span></strong> </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>System.out.println(</span><span>"Invalid redirect"</span><span>);</span></p>
<p><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>}</span></p>
<p>我们可以自行编写两个JSP页面，其中一个页面用response.sendRedirect方法重定向到另外一个页面用来测试上面的例子。</p>
<p>4．&nbsp;模拟输入用户名和口令进行登录</p>
<p>本小节应该说是HTTP客户端编程中最常碰见的问题，很多网站的内容都只是对注册用户可见的，这种情况下就必须要求使用正确的用户名和口令登录成功后，方可浏览到想要的页面。因为HTTP协议是无状态的，也就是连接的有效期只限于当前请求，请求内容结束后连接就关闭了。在这种情况下为了保存用户的登录信息必须使用到Cookie机制。以JSP/Servlet为例，当浏览器请求一个JSP或者是Servlet的页面时，应用服务器会返回一个参数，名为jsessionid（因不同应用服务器而异），值是一个较长的唯一字符串的Cookie，这个字符串值也就是当前访问该站点的会话标识。浏览器在每访问该站点的其他页面时候都要带上jsessionid这样的Cookie信息，应用服务器根据读取这个会话标识来获取对应的会话信息。</p>
<p>对于需要用户登录的网站，一般在用户登录成功后会将用户资料保存在服务器的会话中，这样当访问到其他的页面时候，应用服务器根据浏览器送上的 Cookie中读取当前请求对应的会话标识以获得对应的会话信息，然后就可以判断用户资料是否存在于会话信息中，如果存在则允许访问页面，否则跳转到登录页面中要求用户输入帐号和口令进行登录。这就是一般使用JSP开发网站在处理用户登录的比较通用的方法。</p>
<p>这样一来，对于HTTP的客户端来讲，如果要访问一个受保护的页面时就必须模拟浏览器所做的工作，首先就是请求登录页面，然后读取Cookie值；再次请求登录页面并加入登录页所需的每个参数；最后就是请求最终所需的页面。当然在除第一次请求外其他的请求都需要附带上Cookie信息以便服务器能判断当前请求是否已经通过验证。说了这么多，可是如果你使用httpclient的话，你甚至连一行代码都无需增加，你只需要先传递登录信息执行登录过程，然后直接访问想要的页面，跟访问一个普通的页面没有任何区别，因为类HttpClient已经帮你做了所有该做的事情了，太棒了！下面的例子实现了这样一个访问的过程。<br />
</p>
<p align="left"><span lang="EN-US">/*</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span>* Created on 2003-12-7 by Liudong</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span>*/</span></p>
<p align="left"><strong><span lang="EN-US">package</span></strong><span lang="EN-US"> <span>http.demo;</span></p>
<p align="left"><span lang="EN-US">&nbsp;</span></p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>org.apache.commons.httpclient.*;</span></p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>org.apache.commons.httpclient.cookie.*;</span></p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>org.apache.commons.httpclient.methods.*;</span></p>
<p align="left"><span lang="EN-US">&nbsp;</span></p>
<p align="left"><span lang="EN-US">/**</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span><span>*</span> </span><span>用来演示登录表单的示例</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span><span>*</span> <strong><span>@author</span></strong> <span>Liudong</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span><span>*/</span></p>
<p align="left"><strong><span lang="EN-US">public</span></strong><span lang="EN-US"> <strong><span>class</span></strong> <span>FormLoginDemo</span> <span>{</span></p>
<p align="left"><span lang="EN-US">&nbsp;</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><strong><span>static</span></strong> <strong><span>final</span></strong> <span>String</span> <span>LOGON_SITE</span> <span>=</span> <span>"localhost"</span><span>;</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><strong><span>static</span></strong> <strong><span>final</span></strong> <strong><span>int</span></strong><span>&nbsp;&nbsp;&nbsp; </span><span>LOGON_PORT</span> <span>=</span> <span>8080;</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><strong><span>public</span></strong> <strong><span>static</span></strong> <strong><span>void</span></strong> <span>main(String[]</span> <span>args)</span> <strong><span>throws</span></strong> <span>Exception{</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>HttpClient</span> <span>client</span> <span>=</span> <strong><span>new</span></strong> <span>HttpClient();</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>client.getHostConfiguration().setHost(LOGON_SITE,</span> <span>LOGON_PORT);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>//</span><span>模拟登录页面</span><span lang="EN-US">login.jsp-&gt;main.jsp</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>PostMethod</span> <span>post</span> <span>=</span> <strong><span>new</span></strong> <span>PostMethod(</span><span>"/main.jsp"</span><span>);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>NameValuePair</span> <span>name</span> <span>=</span> <strong><span>new</span></strong> <span>NameValuePair(</span><span>"name"</span><span>,</span> <span>"ld"</span><span>);</span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>NameValuePair</span> <span>pass</span> <span>=</span> <strong><span>new</span></strong> <span>NameValuePair(</span><span>"password"</span><span>,</span> <span>"ld"</span><span>);</span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>post.setRequestBody(</span><strong><span>new</span></strong> <span>NameValuePair[]{name,pass});</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>int</span></strong> <span>status</span> <span>=</span> <span>client.executeMethod(post);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>System.out.println(post.getResponseBodyAsString());</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>post.releaseConnection();</span><span>&nbsp; </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>//</span><span>查看</span><span lang="EN-US">cookie</span><span>信息</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>CookieSpec</span> <span>cookiespec</span> <span>=</span> <span>CookiePolicy.getDefaultSpec();</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>Cookie[]</span> <span>cookies</span> <span>=</span> <span>cookiespec.match(LOGON_SITE,</span> <span>LOGON_PORT,</span> <span>"/"</span><span>,</span> <strong><span>false</span></strong><span>,</span> <span>client.getState().getCookies());</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>if</span></strong> <span>(cookies.length</span> <span>==</span> <span>0)</span> <span>{</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>System.out.println(</span><span>"None"</span><span>);</span><span>&nbsp;&nbsp;&nbsp; </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>}</span> <strong><span>else</span></strong> <span>{</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><strong><span>for</span></strong> <span>(</span><strong><span>int</span></strong> <span>i</span> <span>=</span> <span>0;</span> <span>i</span> <span>&lt;</span> <span>cookies.length;</span> <span>i++)</span> <span>{</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>System.out.println(cookies[i].toString());</span><span>&nbsp;&nbsp;&nbsp; </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>}</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>}</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>//</span><span>访问所需的页面</span><span lang="EN-US">main2.jsp</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>GetMethod</span> <span>get</span> <span>=</span> <strong><span>new</span></strong> <span>GetMethod(</span><span>"/main2.jsp"</span><span>);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>client.executeMethod(get);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>System.out.println(get.getResponseBodyAsString());</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp; </span><span>get.releaseConnection();</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>}</span></p>
<p><span lang="EN-US">}</span></p>
<p><span lang="EN-US">5．&nbsp;提交XML格式参数</span></p>
<p><span lang="EN-US">提交XML格式的参数很简单，仅仅是一个提交时候的ContentType问题，下面的例子演示从文件文件中读取XML信息并提交给服务器的过程，该过程可以用来测试Web服务。</span></p>
<span lang="EN-US">
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>java.io.File;</span></p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>java.io.FileInputStream;</span></p>
<p align="left"><span lang="EN-US">&nbsp;</span></p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>org.apache.commons.httpclient.HttpClient;</span></p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>org.apache.commons.httpclient.methods.EntityEnclosingMethod;</span></p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>org.apache.commons.httpclient.methods.PostMethod;</span></p>
<p align="left"><span lang="EN-US">&nbsp;</span></p>
<p align="left"><span lang="EN-US">/**</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span><span>*</span> </span><span>用来演示提交</span><span lang="EN-US">XML</span><span>格式数据的例子</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;</span><span>*/</span></p>
<p align="left"><strong><span lang="EN-US">public</span></strong><span lang="EN-US"> <strong><span>class</span></strong> <span>PostXMLClient</span> <span>{</span></p>
<p align="left"><span lang="EN-US">&nbsp;</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><strong><span>public</span></strong> <strong><span>static</span></strong> <strong><span>void</span></strong> <span>main(String[]</span> <span>args)</span> <strong><span>throws</span></strong> <span>Exception</span> <span>{</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>File</span> <span>input</span> <span>=</span> <strong><span>new</span></strong> <span>File(&#8220;test.xml&#8221;);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>PostMethod</span> <span>post</span> <span>=</span> <strong><span>new</span></strong> <span>PostMethod(&#8220;http://localhost:8080/httpclient/xml.jsp&#8221;);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>// </span><span>设置请求的内容直接从文件中读取</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>post.setRequestBody(</span><strong><span>new</span></strong> <span>FileInputStream(input));</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>if</span></strong> <span>(input.length()</span> <span>&lt;</span> <span>Integer.MAX_VALUE)</span> </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>post.setRequestContentLength(input.length());</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>else</span></strong><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>post.setRequestContentLength(EntityEnclosingMethod.CONTENT_LENGTH_CHUNKED);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>// </span><span>指定请求内容的类型</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>post.setRequestHeader(</span><span>"Content-type"</span><span>,</span> <span>"text/xml; charset=GBK"</span><span>);</span></strong></span><strong></strong></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;</span><span>HttpClient</span> <span>httpclient</span> <span>=</span> <strong><span>new</span></strong> <span>HttpClient();</span> </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>int</span></strong> <span>result</span> <span>=</span> <span>httpclient.executeMethod(post);</span> </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>System.out.println(</span><span>"Response status code: "</span> <span>+</span> <span>result);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>System.out.println(</span><span>"Response body: "</span><span>);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>System.out.println(post.getResponseBodyAsString());</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>post.releaseConnection();</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>}</span></p>
<p><span lang="EN-US">}</span></p>
<p><span lang="EN-US">6．&nbsp;通过HTTP上传文件</span></p>
<p><span lang="EN-US">httpclient使用了单独的一个HttpMethod子类来处理文件的上传，这个类就是MultipartPostMethod，该类已经封装了文件上传的细节，我们要做的仅仅是告诉它我们要上传文件的全路径即可，下面的代码片段演示如何使用这个类。</span></p>
<span lang="EN-US">
<p align="left"><span lang="EN-US">MultipartPostMethod</span><span lang="EN-US"> <span>filePost</span> <span>=</span> <strong><span>new</span></strong> <span>MultipartPostMethod(targetURL);</span></p>
<p align="left"><span lang="EN-US">filePost.addParameter(</span><span lang="EN-US">"fileName"</span><span lang="EN-US">,</span><span lang="EN-US"> <span>targetFilePath);</span></p>
<p align="left"><span lang="EN-US">HttpClient</span><span lang="EN-US"> <span>client</span> <span>=</span> <strong><span>new</span></strong> <span>HttpClient();</span></p>
<p align="left"><span lang="EN-US">//</span><span>由于要上传的文件可能比较大</span><span lang="EN-US">,</span><span>因此在此设置最大的连接超时时间</span></p>
<p align="left"><span lang="EN-US">client.getHttpConnectionManager().</span><span lang="EN-US"><span>getParams().setConnectionTimeout(5000);</span></p>
<p><strong><span lang="EN-US">int</span></strong><span lang="EN-US"> <span>status</span> <span>=</span> <span>client.executeMethod(filePost);</span><br />
</p>
</span>
<p>&nbsp;</p>
<p>上面代码中，targetFilePath即为要上传的文件所在的路径。</p>
<p>7．&nbsp;访问启用认证的页面</p>
<p>我们经常会碰到这样的页面，当访问它的时候会弹出一个浏览器的对话框要求输入用户名和密码后方可，这种用户认证的方式不同于我们在前面介绍的基于表单的用户身份验证。这是HTTP的认证策略，httpclient支持三种认证方式包括：基本、摘要以及NTLM认证。其中基本认证最简单、通用但也最不安全；摘要认证是在HTTP 1.1中加入的认证方式，而NTLM则是微软公司定义的而不是通用的规范，最新版本的NTLM是比摘要认证还要安全的一种方式。</p>
<p>下面例子是从httpclient的CVS服务器中下载的，它简单演示如何访问一个认证保护的页面：<br />
</p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>org.apache.commons.httpclient.HttpClient;</span></p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>org.apache.commons.httpclient.UsernamePasswordCredentials;</span></p>
<p align="left"><strong><span lang="EN-US">import</span></strong><span lang="EN-US"> <span>org.apache.commons.httpclient.methods.GetMethod;</span></p>
<p align="left"><span lang="EN-US">&nbsp;</span></p>
<p align="left"><strong><span lang="EN-US">public</span></strong><span lang="EN-US"> <strong><span>class</span></strong> <span>BasicAuthenticationExample</span> <span>{</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><strong><span>public</span></strong> <span>BasicAuthenticationExample()</span> <span>{</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>}</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp; </span><span>&nbsp;</span><strong><span>public</span></strong> <strong><span>static</span></strong> <strong><span>void</span></strong> <span>main(String[]</span> <span>args)</span> <strong><span>throws</span></strong> <span>Exception</span> <span>{</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>HttpClient</span> <span>client</span> <span>=</span> <strong><span>new</span></strong> <span>HttpClient();</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>client.getState().setCredentials(</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>"www.verisign.com"</span><span>,</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>"realm"</span><span>,</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>new</span></strong> <span>UsernamePasswordCredentials(</span><span>"username"</span><span>,</span> <span>"password"</span><span>)</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>GetMethod</span> <span>get</span> <span>=</span> <strong><span>new</span></strong> <span>GetMethod(</span><span>"https://www.verisign.com/products/index.html"</span><span>);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>get.setDoAuthentication(</span> <strong><span>true</span></strong> <span>);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>int</span></strong> <span>status</span> <span>=</span> <span>client.executeMethod(</span> <span>get</span> <span>);</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>System.out.println(status+</span><span>"\n"</span><span>+</span> <span>get.getResponseBodyAsString());</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>get.releaseConnection();</span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp;&nbsp; </span><span>}</span></p>
<p><span lang="EN-US">}</span></p>
<p><span lang="EN-US">8．&nbsp;多线程模式下使用httpclient</span></p>
<p><span lang="EN-US">多线程同时访问httpclient，例如同时从一个站点上下载多个文件。对于同一个HttpConnection同一个时间只能有一个线程访问，为了保证多线程工作环境下不产生冲突，httpclient使用了一个多线程连接管理器的类： MultiThreadedHttpConnectionManager，要使用这个类很简单，只需要在构造HttpClient实例的时候传入即可，代码如下：</span></p>
<span lang="EN-US">
<p align="left"><span lang="EN-US">MultiThreadedHttpConnectionManager</span><span lang="EN-US"> <span>connectionManager</span> <span>=</span> </span></p>
<p align="left"><span lang="EN-US"><span>&nbsp;&nbsp; </span><strong><span>new</span></strong> <span>MultiThreadedHttpConnectionManager();</span></p>
<p><span lang="EN-US"><span>HttpClient</span> <span>client</span> <span>=</span> <strong><span>new</span></strong> <span>HttpClient(connectionManager);</span></p>
<p><span lang="EN-US"><span>以后尽管访问client实例即可。</span></p>
<p><span lang="EN-US"><span>参考资料：</span></p>
<p><span lang="EN-US"><span>httpclient首页：&nbsp;&nbsp;&nbsp;&nbsp;<a href="http://jakarta.apache.org/commons/httpclient/">http://jakarta.apache.org/commons/httpclient/</a><br />
关于NTLM是如何工作：&nbsp;&nbsp;<a href="http://davenport.sourceforge.net/ntlm.html">http://davenport.sourceforge.net/ntlm.html</a> </span></p>
</span></span></td>
</tr>
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>
<img src ="http://www.blogjava.net/wangxinsh55/aggbug/151300.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-10-09 11:12 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/10/09/151300.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> JSTL教程 </title><link>http://www.blogjava.net/wangxinsh55/archive/2007/08/10/135808.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Fri, 10 Aug 2007 06:01:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/08/10/135808.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/135808.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/08/10/135808.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/135808.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/135808.html</trackback:ping><description><![CDATA[JSP 标准标记库（JSP Standard Tag Library，JSTL）是一个实现 Web 应用程序中常见的通用功能的定制标记库集，这些功能包括迭代和条件判断、数据管理格式化、XML 操作以及数据库访问。在 developerWorks 上其新系列的第一篇文章中，软件工程师 Mark Kolb 向您展示了如何使用 JSTL 标记来避免在 JSP 页面中使用脚本编制元素。您还将了解如何通过从表示层删除源代码来简化软件维护。最后，您将了解 JSTL 经过简化的表达式语言，它允许在不必使用功能齐全的编程语言的情况下对 JSTL 操作指定动态属性值。 <br>
<div class=postText><br>　　 JavaServer Pages（JSP）是用于 J2EE 平台的标准表示层技术。JSP 技术提供了用于执行计算（这些计算用来动态地生成页面内容）的脚本编制元素和操作。脚本编制元素允许在 JSP 页面中包括程序源代码，在为响应用户请求而呈现页面时可以执行这些源代码。操作将计算操作封装到很象 HTML 或 XML 标记的标记中，JSP 页面的模板文本通常包含这些标记。JSP 规范只将几种操作定义成了标准，但从 JSP 1.1 开始，开发人员已经能够以定制标记库的方式创建其自己的操作了。 <br><br>　　 JSP 标准标记库（JSTL）是 JSP 1.2 定制标记库集，这些标记库实现大量服务器端 Java 应用程序常用的基本功能。通过为典型表示层任务（如数据格式化和迭代或条件内容）提供标准实现，JSTL 使 JSP 作者可以专注于特定于应用程序的开发需求，而不是为这些通用操作&#8220;另起炉灶&#8221;。 <br><br>　　 当然，您可以使用 JSP 脚本编制元素（scriptlet、表达式和声明）来实现此类任务。例如，可以使用三个 scriptlet 实现条件内容，清单 1 中着重显示了这三个 scriptlet。但是，因为脚本编制元素依赖于在页面中嵌入程序源代码（通常是 Java 代码），所以对于使用这些脚本编制元素的 JSP 页面，其软件维护任务的复杂度大大增加了。例如，清单 1 中的 scriptlet 示例严格地依赖于花括号的正确匹配。如果不经意间引入了一个语法错误，则条件内容中的嵌套其它 scriptlet 可能会造成严重破坏，并且在 JSP 容器编译该页面时，要使所产生的错误信息有意义可能会很困难。 <br><br><ccid_code></ccid_code>　　清单 1. 通过 scriptlet 实现条件内容 &lt;% if (user.getRole() == "member")) { %&gt; <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>&lt;p&gt;Welcome, member!&lt;/p&gt;<br>&lt;% } else { %&gt;<br>    &lt;p&gt;Welcome, guest!&lt;/p&gt;<br>&lt;% } %&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br><ccid_code></ccid_code>　　修正此类问题通常需要相当丰富的编程经验。尽管通常会由十分精通页面布局和图形设计 的设计人员来开发和维护 JSP，但是同一页面中的脚本编制元素出现问题时，需要程序员的介入。这种状况将单个文件中代码的责任分担给多人，因而使得开发、调试和增强此类 JSP 页面成为很麻烦的任务。通过将常用功能包装到定制标记库的标准集合中，JSTL 使 JSP 作者可以减少对编制脚本元素的需求，甚至可以不需要它们，并避免了相关的维护成本。 <br><br>　　 <strong>JSTL 1.0</strong> <br><br><ccid_code></ccid_code>　 　JSTL 1.0 发布于 2002 年 6 月，由四个定制标记库（core、format、xml 和 sql）和一对通用标记库验证器（ScriptFreeTLV 和 PermittedTaglibsTLV）组成。core 标记库提供了定制操作，通过限制了作用域的变量管理数据，以及执行页面内容的迭代和条件操作。它还提供了用来生成和操作 URL 的标记。顾名思义，format 标记库定义了用来格式化数据（尤其是数字和日期）的操作。它还支持使用本地化资源束进行 JSP 页面的国际化。xml 库包含一些标记，这些标记用来操作通过 XML 表示的数据，而 sql 库定义了用来查询关系数据库的操作。 <br><br>　 　 两个 JSTL 标记库验证器允许开发人员在其 JSP 应用程序中强制使用编码标准。可以配置 ScriptFreeTLV 验证器以在 JSP 页面中禁用各种类型的 JSP 脚本元素 — scriptlet、表达式和声明。类似地，PermittedTaglibsTLV 验证器可以用来限制可能由应用程序的 JSP 页面访问的定制标记库集（包括 JSTL 标记库）。 <br><br>　　 尽管 JSTL 最终将会成为 J2EE 平台的必需组件，但目前只有少数应用程序服务器包括它。JSTL 1.0 的参考实现可作为 Apache 软件基金会（Apache Software Foundation）的 Jakarta Taglibs 项目（请参阅参考资料）的一部分而获得。可以将该参考实现中的定制标记库合并到任何支持 JSP 1.2 和 Servlet 2.3 规范的服务器，以添加对 JSTL 的支持。 <br><br>　　 <strong>表达式语言</strong> <br><br><ccid_code></ccid_code>　 　在 JSP 1.2 中，可以使用静态字符串或表达式（如果允许的话）指定 JSP 操作的属性。例如，在清单 2 中，对 &lt;jsp:setProperty&gt; 操作的 name 和 property 属性指定了静态值，而用表达式指定了其 value 属性。这个操作的效果是将请求参数的当前值赋予命名的 bean 特性。以这种形式使用的表达式被称为请求时属性值（request-time attribute value），这是构建到 JSP 规范中的用于动态指定属性值的唯一机制。 <br><br>　　 清单 2. 合并请求时属性值的 JSP 操作 <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>&lt;jsp:setProperty name="user" property="timezonePref"<br>                 value='&lt;%= request.getParameter("timezone") %&gt;'/&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br><ccid_code></ccid_code>　　因为请求时属性值是用表达式指定的，所以它们往往有和其它脚本元素一样的软件维护问 题。因此，JSTL 定制标记支持另一种用于指定动态属性值的机制。可以用简化的表达式语言（EL）而不使用完整的 JSP 表达式来指定 JSTL 操作的属性值。EL 提供了一些标识符、存取器和运算符，用来检索和操作驻留在 JSP 容器中的数据。EL 在某种程度上以 EcmaScript（请参阅参考资料）和 XML 路径语言（XML Path Language，XPath）为基础，因此页面设计人员和程序员都应该熟悉它的语法。EL 擅长寻找对象及其特性，然后对它们执行简单操作；它不是编程语言，甚至不是脚本编制语言。但是，与 JSTL 标记一起使用时，它就能使用简单而又方便的符号来表示复杂的行为。EL 表达式的格式是这样的：用美元符号（$）定界，内容包括在花括号（{}）中，如清单 3 所示。 <br><br>　　 清单 3. 说明 EL 表达式定界符的 JSTL 操作 <br><br>　　 &lt;c:out value="${user.firstName}"/&gt; <br><br><ccid_code></ccid_code>　 　此外，您可以将多个表达式与静态文本组合在一起以通过字符串并置来构造动态属性值，如清单 4 所示。单独的表达式由标识符、存取器、文字和运算符组成。标识符用来引用存储在数据中心中的数据对象。EL 有 11 个保留标识符，对应于 11 个 EL 隐式对象。假定所有其它标识符都引用限制了作用域的变量。存取器用来检索对象的特性或集合的元素。文字表示固定的值 — 数字、字符、字符串、布尔型或空值。运算符允许对数据和文字进行组合以及比较。 <br><br>　　 清单 4. 组合静态文本和多个 EL 表达式以指定动态属性值 <br><br>　　 &lt;c:out value="Hello ${user.firstName} ${user.lastName}"/&gt; <br><br>　　 <strong>限制了作用域的变量</strong> <br><br><ccid_code></ccid_code>　 　JSP API 通过 &lt;jsp:useBean&gt; 操作允许从 JSP 容器内的四个不同作用域中存储和检索数据。JSTL 通过提供用于指定和除去这些作用域中的对象的附加操作来扩展这一能力。此外，EL 提供将这些对象作为限制了作用域的变量进行检索的内置支持。特别地，任何出现在 EL 表达式中但不对应于任何 EL 隐式对象的标识符，都被自动假定为引用存储在四个 JSP 作用域的其中某个中的对象，这四个作用域是： <br><br>　　 <strong>&#183;</strong>页面作用域 <br><br>　　 <strong>&#183;</strong>请求作用域 <br><br>　　 <strong>&#183;</strong>会话作用域 <br><br>　　 <strong>&#183;</strong>应用程序作用域 <br><br><ccid_code></ccid_code>　 　您可能还记得，只有在为特定请求处理页面期间才能检索存储在该页面作用域中的对象。如果对象是存储在请求作用域中的，可以在处理所有参与处理某请求的页 面期间检索这些对象（譬如在对某个请求的处理中遇到了一个或多个 &lt;jsp:include&gt; 或 &lt;jsp:forward&gt; 操作）。如果对象是存储在会话作用域中的，则在与 Web 应用程序的交互式会话期间，可以由用户访问的任何页面检索它（即，直到与该用户交互相关联的 HttpSession 对象无效为止）。可以由任何用户从任何页面访问存储在应用程序作用域中的对象，直到卸载 Web 应用程序本身为止（通常是由于关闭 JSP 容器所致）。 <br><br>　　 通过将字符串映射为期望作用域中的对象来将对象存储到该作用域。然后，就可以通过提供相同字符串来从该作用域检索该对象。在作用域的映射中查找字符串，并 返回被映射的对象。在 Servlet API 中，将此类对象称为相应作用域的属性。但是，在 EL 的上下文中，也将与属性相关联的字符串看作变量的名称，该变量通过属性映射的方式获得特定的值。 <br><br>　　 在 EL 中，与隐式对象无关联的标识符被认为是存储在四个 JSP 作用域中的名称对象。首先对页面作用域检查是否存在这样的标识符，其次对请求作用域、然后对会话作用域、最后对应用程序作用域依次进行这样的检查，然后测 试该标识符的名称是否与存储在该作用域中的某个对象的名称匹配。第一个这样的匹配作为 EL 标识符的值被返回。通过这种方法，可以将 EL 标识符看作引用限制了作用域的变量。 <br><br>　　 从更技术的方面来说，没有映射到隐式对象的标识符是用 PageContext 实例的 findAttribute() 方法求值的，该实例表示对页面的处理，在该页面上，当前正在处理用于请求的表达式。标识符的名称作为参数传递给这个方法，然后该方法依次在四个作用域中搜 索具有相同名称的属性。并将所找到的第一个匹配项作为 findAttribute() 方法的值返回。如果未在这四个作用域中找到这样的属性，则返回 null。 <br><br>　　 最终，限制了作用域的变量是四个 JSP 作用域的属性，这些属性具有可以用作 EL 标识符的名称。只要对限制了作用域的变量赋予由字母数字组成的名称，就可以通过 JSP 中提供的用于设置属性的任何机制来创建它们。这包括内置的 &lt;jsp:useBean&gt; 操作，以及由 Servlet API 中的几个类定义的 setAttribute() 方法。此外，四个 JSTL 库中定义的许多定制标记本身就能够设置作为限制了作用域的变量使用的属性值。 <br><br>　　 <strong>隐式对象</strong> <br><br>　　 表 1 中列出了 11 个 EL 隐式对象的标识符。不要将这些对象与 JSP 隐式对象（一共只有九个）混淆，其中只有一个对象是它们所共有的。 <br><br>　　 表 1. EL 隐式对象 <br><br>　　 类别 　　　标识符　　　　　　　　　　　　 描述 <br><br>　　 JSP 　　　pageContext 　　PageContext 实例对应于当前页面的处理 <br><br>　　 作用域　　pageScope 　　　 与页面作用域属性的名称和值相关联的 Map 类 <br><br>　　　　　　　 requestScope 　　与请求作用域属性的名称和值相关联的 Map 类 <br><br>　　　　　　　 sessionScope 　　与会话作用域属性的名称和值相关联的 Map 类 <br><br>　　　　　　　 applicationScope 与应用程序作用域属性的名称和值相关联的 Map 类 <br><br>　　 请求参数　param　　　　　 按名称存储请求参数的主要值的 Map 类 <br><br>　　　　　　　 paramValues　 将请求参数的所有值作为 String 数组存储的 Map 类 <br><br>　　 请求头 　header 　　　　按名称存储请求头主要值的 Map 类 <br><br>　　　　　　　 headerValues　将请求头的所有值作为 String 数组存储的 Map 类 <br><br>　　 Cookie 　cookie　　　　 按名称存储请求附带的 cookie 的 Map 类 <br><br>　　 初始化参数 initParam 　　按名称存储 Web 应用程序上下文初始化参数的 Map 类 <br><br>　　 <ccid_code></ccid_code>　 　尽管 JSP 和 EL 隐式对象中只有一个公共对象（pageContext），但通过 EL 也可以访问其它 JSP 隐式对象。原因是 pageContext 拥有访问所有其它八个 JSP 隐式对象的特性。实际上，这是将它包括在 EL 隐式对象中的主要理由。 <br><br>　　 其余所有 EL 隐式对象都是映射，可以用来查找对应于名称的对象。前四个映射表示先前讨论的各种属性作用域。可以用它们来查找特定作用域中的标识符，而不用依赖于 EL 在缺省情况下使用的顺序查找过程。 <br><br>　 　 接下来的四个映射用来获取请求参数和请求头的值。因为 HTTP 协议允许请求参数和请求头具有多个值，所以它们各有一对映射。每对中的第一个映射返回请求参数或头的主要值，通常是恰巧在实际请求中首先指定的那个值。每 对中第二个映射允许检索参数或头的所有值。这些映射中的键是参数或头的名称，但这些值是 String 对象的数组，其中的每个元素都是单一参数值或头值。 <br><br>　　 cookie 隐式对象提供了对由请求设置的 cookie 名称的访问。这个对象将所有与请求相关联的 cookie 名称映射到表示那些 cookie 特性的 Cookie 对象。 <br><br>　　 最后一个 EL 隐式对象 initParam 是一个映射，它储存与 Web 应用程序相关联的所有上下文的初始化参数的名称和值。初始化参数是通过 web.xml 部署描述符文件指定的，该文件位于应用程序的 WEB-INF 目录中。 <br><br>　　 <strong>存取器</strong> <br><br><ccid_code></ccid_code>　 　因为 EL 标识符是作为隐式对象或限制了作用域的变量（通过属性来实现）解析的，因此有必要将它们转换成 Java 对象。EL 可以自动包装和解包其相应的 Java 类中的基本类型（例如，可以在后台将 int 强制转换成 Integer 类，反之亦可），但大多数的标识符将成为指向完整的 Java 对象的指针。 <br><br>　　 结果是，对这些对象的特性或（在对象是数组和集合的情况下）对其元素的访问通常是令人满意的。就为了实现这种用途，EL 提供了两种不同的存取器（点运算符（.）和方括号运算符（[]）），也支持通过 EL 操作特性和元素。 <br><br>　 　 点运算符通常用于访问对象的特性。例如，在表达式 ${user.firstName} 中，使用点运算符来访问 user 标识符所引用对象的名为 firstName 的特性。EL 使用 Java bean 约定访问对象特性，因此必须定义这个特性的 getter 方法（通常是名为 getFirstName() 的方法），以便表达式正确求值。当被访问的特性本身是对象时，可以递归地应用点运算符。例如，如果我们虚构的 user 对象有一个实现为 Java 对象的 address 特性，那么也可以用点运算符来访问这个对象的特性。例如，表达式 ${user.address.city} 将会返回这个地址对象嵌套的 city 特性。 <br><br>　　 方括号运算符用来检索数组和集合的元素。在数组和有序集合（也即，实现了 java.util.List 接口的集合）的情况下，把要检索的元素的下标放在方括号中。例如，表达式 ${urls[3]} 返回 urls 标识符所引用的数组或集合的第四个元素（和 Java 语言以及 JavaScript 中一样，EL 中的下标是从零开始的）。 <br><br>　 　 对于实现 java.util.Map 接口的集合，方括号运算符使用关联的键查找存储在映射中的值。在方括号中指定键，并将相应的值作为表达式的值返回。例如，表达式 ${commands["dir"]} 返回与 commands 标识符所引用的 Map 中的 "dir" 键相关联的值。 <br><br>　　 对于上述两种情况，都可允许表达式出现在方括号中。对嵌套表达式求值的结果将被作为下标或键，用来检索集合或数组的适当元素。和点运算符一样，方括号运算 符也可以递归应用。这使得 EL 能够从多维数组、嵌套集合或两者的任意组合中检索元素。此外，点运算符和方括号运算符还可以互操作。例如，如果数组的元素本身是对象，则可以使用方括号运 算符来检索该数组的元素，并结合点运算符来检索该元素的一个特性（例如 ${urls[3].protocol}）。 <br><br>　　 假定 EL 充当指定动态属性值的简化语言，EL 存取器有一个有趣的功能（与 Java 语言的存取器不同），那就是它们在应用于 null 时不抛出异常。如果应用 EL 存取器的对象（例如，${foo.bar} 和 ${foo["bar"]} 中的 foo 标识符）是 null，那么应用存取器的结果也是 null。事实证明，在大多数情况下，这是一个相当有用的行为，不久您就会了解这一点。 <br><br>　　 最后，点运算符和方括号运算符可能实现某种程度的互换。例如，也可以使用 ${user["firstName"]} 来检索 user 对象的 firstName 特性，正如可以用 ${commands.dir} 获取与 commands 映射中的 "dir" 键相关联的值一样。 <br><br>　　 <strong>运算符</strong> <br><br><ccid_code></ccid_code>　　EL 还可以通过使用标识符和存取器，遍历包含应用程序数据（通过限制了作用域的变量公开）或关于环境的信息（通过 EL 隐式对象）的对象层次结构。但是，只是访问这些数据，通常不足以实现许多 JSP 应用程序所需的表示逻辑。 <br><br>　　 最终，EL 还包括了几个用来操作和比较 EL 表达式所访问数据的运算符。表 2 中汇总了这些运算符。 <br><br>　　 表 2. EL 运算符 <br><br>　　 类别 　　　　运算符 <br><br>　　 算术运算符　　+、-、*、/（或 div）和 %（或 mod） <br><br>　　 关系运算符 　==（或 eq）、!=（或 ne）、&lt;&lt;/code&gt;（或 lt）、&gt;（或 gt）、&lt;=（或 le）和 &gt;=（或 ge） <br><br>　　 逻辑运算符　　&amp;&amp;（或 and）、||（或 or）和 !（或 not） <br><br>　　 验证运算符 　empty <br><br>　 　 算术运算符支持数值的加法、减法、乘法和除法。还提供了一个求余运算符。注：除法和求余运算符都有替代的、非符号的名称（为的是与 XPath 保持一致）。清单 5 中显示了一个演示算术运算符用法的示例表达式。对几个 EL 表达式应用算术运算符的结果是将该算术运算符应用于这些表达式返回的数值所得的结果。 <br><br>　　 清单 5. 利用算术运算符的 EL 表达式 <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>${item.price * (1 + taxRate[user.address.zipcode])}</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br>　　 关系运算符允许比较数字或文本数据。比较的结果作为布尔值返回。逻辑运算符允许合并布尔值，返回新的布尔值。因此，可以将 EL 逻辑运算符应用于嵌套的关系或逻辑运算符的结果，如清单 6 所示。 <br><br>　　 清单 6. 利用关系和逻辑运算符的 EL 表达式 <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>${(x &gt;= min) &amp;&amp; (x &lt;= max)}</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br>　　 最后一种 EL 运算符是 empty，它对于验证数据特别有用。empty 运算符采用单个表达式作为其变量（也即，${empty input}），并返回一个布尔值，该布尔值表示对表达式求值的结果是不是&#8220;空&#8221;值。求值结果为 null 的表达式被认为是空，即无元素的集合或数组。如果参数是对长度为零的 String 求值所得的结果，则 empty 运算符也将返回 true。 <br><br>　　 表 3 显示了 EL 运算符的优先级。正如清单 5 和 6 所示，可以用圆括号对表达式分组，高于普通的优先级规则。 <br><br>　　 表 3. EL 运算符优先级（自顶到底，从左到右） <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>[], . <br>() <br>unary -、not、!、empty <br>*、/、div、%、mod <br>+、binary - <br>() &lt;&lt;/code&gt;、&gt;、&lt;=、&gt;=、lt、gt、le、ge <br>==、!=、eq、ne <br>&amp;&amp;、and <br>||、or</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br>　　 <strong>文字</strong> <br><br>　　 在 EL 表达式中，数字、字符串、布尔值和 null 都可以被指定为文字值。字符串可以用单引号或双引号定界。布尔值被指定为 true 和 false。 <br><br>　　 <strong>Taglib 伪指令</strong> <br><br>　 　 正如我们先前讨论的，JSTL 1.0 包括四个定制标记库。为了演示 JSTL 标记和表达式语言的交互，我们将研究几个来自 JSTL core 库的标记。和使用任何 JSP 定制标记库一样，必须在您想要使用这个库标记的任何页面中包括 taglib 伪指令。清单 7 显示了用于这个特定库的伪指令。 <br><br>　　 清单 7. 用于 JSTL core 库 EL 版本的 taglib 伪指令 <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>&lt;%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br><ccid_code></ccid_code>　　实际上，对应于 JSTL core 库的 taglib 伪指令有两种，因为在 JSTL 1.0 中，EL 是可选的。所有四个 JSTL 1.0 定制标记库都有使用 JSP 表达式（而不是 EL）指定动态属性值的备用版本。因为这些备用库依赖于 JSP 的更传统的请求时属性值，所以它们被称为 RT 库，而那些使用表达式语言的则被称为 EL 库。开发人员用不同的 taglib 伪指令来区分每个库的这两个版本。清单 8 显示了使用 core 库的 RT 版本的伪指令。但是，由于现在我们讨论的重点是 EL，所以首先需要这些伪指令。 <br><br>　　 清单 8. 用于 JSTL core 库 RT 版本的 taglib 伪指令 <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>&lt;%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c_rt" %&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br>　　 <strong>变量标记</strong> <br><br><ccid_code></ccid_code>　　我们首先要考 虑的 JSTL 定制标记是 &lt;c:set&gt; 操作。正如已经说明的，限制了作用域的变量在 JSTL 中起关键作用，&lt;c:set&gt; 操作提供基于标记的机制来创建和设置限制了作用域的变量。清单 9 中显示了该操作的语法，其中 var 属性指定了限制了作用域的变量的名称，scope 属性表明了该变量驻留在哪个作用域中，value 属性指定了分配给该变量的值。如果指定变量已经存在，则简单地将所指明的值赋给它。如果不存在，则创建新的限制了作用域的变量，并用该值初始化这个变量。 <br><br>　　 清单 9. &lt;c:set&gt; 操作的语法 <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>&lt;c:set var="name" scope="scope" value="expression"/&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br><ccid_code></ccid_code>　　scope 属性是可选的，其缺省值是 page。 <br><br>　　 清单 10 中显示了 &lt;c:set&gt; 的两个示例。在第一个示例中，将会话作用域变量设置成 String 值。在第二个示例中，用表达式来设置数值：将页面作用域内名为 square 的变量赋值为名为 x 的请求参数的值的平方。 <br><br>　　 清单 10. &lt;c:set&gt; 操作示例 <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>&lt;c:set var="timezone" scope="session" value="CST"/&gt;<br>&lt;c:set var="square" value="${param['x'] * param['x']}"/&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br><ccid_code></ccid_code>　　您还可以将限制了作用域的变量的值指定为 &lt;c:set&gt; 操作的主体内容，而不是使用属性。使用这种方法，您可以重新编写清单 10 中的第一个示例，如清单 11 所示。此外，正如我们马上可以看到的，&lt;c:set&gt; 标记的主体内容本身也可以使用定制标记。&lt;c:set&gt; 主体内生成的所有内容都将作为一个 String 值赋给指定变量。 <br><br>　　 清单 11. 通过主体内容指定 &lt;c:set&gt; 操作的值 <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>&lt;c:set var="timezone" scope="session"&gt;CST&lt;/c:set&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br><ccid_code></ccid_code>　　JSTL core 库包含第二个用于管理限制了作用域的变量的标记 — &lt;c:remove&gt;。顾名思义，&lt;c:remove&gt; 操作是用来删除限制了作用域的变量的，它获取两个属性。var 属性指定待删除变量的名称，scope 属性是可选的，它表示待删除变量来自哪个作用域，缺省为 page，如清单 12 所示。 <br><br>　　 清单 12. &lt;c:remove&gt; 操作示例 <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>&lt;c:remove var="timezone" scope="session"/&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br>　　 <strong>输出</strong> <br><br><ccid_code></ccid_code>　　尽管 &lt;c:set&gt; 操作允许将表达式结果赋给限制了作用域的变量，但开发人员通常会希望只显示表达式的值，而不存储它。JSTL &lt;c:out&gt; 定制标记承担这一任务，其语法如清单 13 所示。该标记对由其 value 属性指定的表达式进行求值，然后打印结果。如果指定了可选属性 default，那么，在对 value 属性的表达式求值所得结果为 null 或空 String 的情况下，&lt;c:out&gt; 将打印其值。 <br><br>　　 清单 13. &lt;c:out&gt; 操作的语法 <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>&lt;c:out value="expression" default="expression" escapeXml="boolean"/&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br><ccid_code></ccid_code>　　escapeXml 属性也是可选的。它控制当用 &lt;c:out&gt; 标记输出诸如&#8220;&lt;&#8221;、&#8220;&gt;&#8221;和&#8220;&amp;&#8221;之类的字符（在 HTML 和 XML 中具有特殊意义）时是否应该进行转义。如果将 escapeXml 设置为 true，则会自动将这些字符转换成相应的 XML 实体（此处提到的字符分别转换成 &amp;lt;、&amp;gt; 和 &amp;amp;）。 <br><br>　　 例如，假定有一个名为 user 的会话作用域变量，它是一个类的实例，该类为用户定义了两个特性：username 和 company。每当用户访问站点时，这个对象被自动分配给会话，但直到用户实际登录后，才会设置这两个特性。假定是这种方案，请考虑清单 14 中的 JSP 片段。在用户登录之后，这个片段将显示单词&#8220;Hello&#8221;，其后是他／她的用户名和一个惊叹号。但是，在用户登录之前，由这个片段生成的内容则是短语 &#8220;Hello Guest!&#8221;。在这种情况下，因为 username 特性还有待初始化，所以 &lt;c:out&gt; 标记将转而打印出 default 属性的值（即字符串&#8220;Guest&#8221;）。 <br><br>　　 清单 14. 带缺省内容的 &lt;c:out&gt; 操作示例 Hello <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>&lt;c:out value="${user.username}" default=="Guest"/&gt;!</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br><ccid_code></ccid_code>　　接下来，考虑清单 15，它使用了 &lt;c:out&gt; 标记的 escapeXml 属性。如果在这种情况下已经将 company 特性设置成 Java String 值 "Flynn &amp; Sons"，那么，实际上该操作生成的内容将是 Flynn &amp; Sons。如果这个操作是生成 HTML 或 XML 内容的 JSP 页面的一部分，那么，这个字符串中间的&#8220;&amp;&#8221;符号最终可能被解释为 HTML 或 XML 控制字符，从而妨碍了对该内容的显示或解析。但是，如果将 escapeXml 属性值设置成 true，则所生成的内容将是 Flynn &amp;amp; Sons。浏览器或解析器不会因在解释时遇到这种内容而出问题。假定 HTML 和 XML 是 JSP 应用程序中最常见的内容类型，所以 escapeXml 属性的缺省值是 true 就不足为奇了。 <br><br>　　 清单 15. 禁用转义的 &lt;c:out&gt; 操作示例 <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>&lt;c:out value="${user.company}" escapeXml=="false"/&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br>　　 <strong>用缺省值设置变量</strong> <br><br><ccid_code></ccid_code>　　除了 简化动态数据的显示之外，当通过 &lt;c:set&gt; 设置变量值时，&lt;c:out&gt; 指定缺省值的能力也很有用。正如清单 11 所示，用来赋给限制了作用域的变量的值可以指定为 &lt;c:set&gt; 标记的主体内容，也可以通过其值属性来指定。通过将 &lt;c:out&gt; 操作嵌套在 &lt;c:set&gt; 标记的主体内容中，变量赋值就可以利用其缺省值能力。 <br><br>　　 清单 16 中说明了这种方法。外部 &lt;c:set&gt; 标记的行为非常简单：它根据其主体内容设置会话作用域 timezone 变量的值。但是，在这种情况下，主体内容是通过 &lt;c:out&gt; 操作生成的。这个嵌套操作的值属性是表达式 ${cookie['tzPref'].value}，它尝试通过 cookie 隐式对象返回名为 tzPref 的 cookie 值。（cookie 隐式对象将 cookie 名称映射到相应的 Cookie 实例，这意味着必须通过对象的 value 特性使用点运算符来检索储存在 cookie 中的实际数据。） <br><br>　　 清单 16. 合并 &lt;c:set&gt; 和 &lt;c:out&gt; 以提供缺省变量值 <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>&lt;c:set var="timezone" scope=="session"&gt;<br>   &lt;c:out value="${cookie['tzPref'].value}" default=="CST"/&gt;<br>&lt;/c:set&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br><ccid_code></ccid_code>　　但是，请考虑以下情况，用户是第一次尝试使用这段代码的 Web 应用程序。结果是，请求中没有提供名为 tzPref 的 cookie。这意味着使用隐式对象的查找将返回 null，在这种情况下整个表达式将返回 null。因为对 &lt;c:out&gt; 标记的 value 属性求值的结果是 null，所以 &lt;c:out&gt; 标记会转而输出对其 default 属性求值的结果。在这里是字符串 CST。因此，实际的结果是将 timezone 限制了作用域的变量设置成用户的 tzPref cookie 中存储的时区，或者，如果没有，则使用缺省时区 CST。 <br><br>　　 <strong>EL 和 JSP 2.0</strong> <br><br><ccid_code></ccid_code>　 　目前，表达式语言仅可用于指定 JSTL 定制标记中的动态属性值。但 JSTL 1.0 表达式语言的一个扩展已经被提出，会把它包括到 JSP 2.0 中去，眼下正在进行最后评审。这个扩展将允许开发人员通过自己的定制标记来使用 EL。页面作者将可以在目前允许使用 JSP 表达式的任何地方使用 EL 表达式，譬如将动态值插入模板文本中：&lt;p&gt;Your preferred time zone is ${timezone}&lt;/p&gt;。 <br><br>　　 这个 JSP 2.0 功能（就象 JSTL 本身一样）将支持页面作者进一步减少对 JSP 编制脚本元素的依赖，从而改进 JSP 应用程序的可维护性。 <br><br>　　 <strong>结束语</strong> <br><br><ccid_code></ccid_code>　 　EL（与四个 JSTL 定制标记库提供的操作结合起来）允许页面作者不使用脚本元素即可实现表示层逻辑。例如，对比本文开头清单 1 中的 JSP 代码和清单 17 中显示的通过 JSTL 实现的同样功能。（JSTL core 库中其余的标记，包括 &lt;c:choose&gt; 及其子标记，将在本系列的下一篇文章中讨论。）尽管显然执行了条件逻辑，但是 JSTL 版本中没有 Java 语言源代码，并且标记之间的关系（尤其是关于嵌套需求）对于任何精通 HTML 语法的人都应该是熟悉的。 <br><br>　　 清单 17. 合并 &lt;c:set&gt; 和 &lt;c:out&gt; 以提供缺省变量值 <br><br><ccid_nobr></ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=550 align=center borderColorLight=#000000 border=1>
    <tbody>
        <tr>
            <td class=code bgColor=#e6e6e6>
            <pre><ccid_code></ccid_code>&lt;c:choose&gt;&lt;c:when test="${user.role == 'member'}"&gt;<br>    &lt;p&gt;Welcome, member!&lt;/p&gt;<br>  &lt;/c:when&gt;&lt;c:otherwise&gt;<br>    &lt;p&gt;Welcome, guest!&lt;/p&gt;<br>  &lt;/c:otherwise&gt;&lt;/c:choose&gt;</pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br><ccid_code></ccid_code>　　通过提供大多数 Web 应用程序常用功能的标准实现，JSTL 有助于加速开发周期。与 EL 结合起来，JSTL 可以不需要对表示层程序编写代码，这极大地简化了 JSP 应用程序的维护。 <br><br>　　 <strong>参考资料</strong> <br><br>　　 <a href="http://java.sun.com/products/jsp/jstl/index.html" target=_blank><u><font color=#0000ff>Sun 的 JSP 标准标记库主页是了解关于 JSTL 的更多信息的良好起点。</font></u></a> <br><br>　　 <a href="http://jcp.org/aboutJava/communityprocess/final/jsr052/" target=_blank><u><font color=#0000ff>JSTL 1.0 规范是关于 EL 和四个 JSTL 标记库的最终权威文本。 </font></u></a><br><br>　　 <a href="http://jakarta.apache.org/taglibs/index.html" target=_blank><u><font color=#0000ff>Jakarta Taglibs 项目是 JSTL 1.0 参考实现的起源。</font></u></a> <br><br>　　 <a href="http://www.manning.com/bayern/index.html" target=_blank><u><font color=#0000ff>Shawn Bayern 所著的 JSTL in Action（Manning Publications Co.，2002 年）提供了对所有 JSTL 功能的精彩论述，作者是该参考实现的领导。</font></u></a> <br><br>　　 <a href="http://www.amazon.com/exec/obidos/tg/detail/-/0131001531/103-4207394-1320606?vi=glance" target=_blank><u><font color=#0000ff>David Geary 是 Java 技术方面很受欢迎的作者，他也写了一本关于 JSTL 的书，书名是 Core JSTL。</font></u></a> <br><br>　　 <a href="http://jsptags.com/index.jsp" target=_blank><u><font color=#0000ff>JSPTags.com 是 JSP 技术参考资料的目录，它尤其专注于定制标记库。</font></u></a> <br><br>　　 <a href="http://java.sun.com/webservices/docs/ea2/tutorial/doc/JSTL3.html" target=_blank><u><font color=#0000ff>Sun 的 Java Web Services Tutorial 中包含了对 JSTL 的讨论。 </font></u></a><br><br>　　 <a href="http://www-900.ibm.com/developerWorks/cn/cgi-bin/click.cgi?url=http://www7b.software.ibm.com/wsdd/library/tutorials/vajwebsph353/Part-I/JSP11Part-I.html&amp;origin=j" target=_blank><u><font color=#0000ff>&#8220;Using JSPs and custom tags within VisualAge for Java and WebSphere Studio&#8221;（WebSphere 开发者园地）是一篇 WBOnline 实用论文，它演示了 servlet、JSP 和定制标记库的使用。</font></u></a> <br><br>　　 <a href="http://www-900.ibm.com/developerworks/cn/java/j-taglib/index.shtml" target=_blank><u><font color=#0000ff>通过 Jeff Wilson 精彩的文章&#8220;使用定制标记控制 JSP 页面&#8221;（developerWorks，2002 年 1 月）了解关于定制标记库的一切。</font></u></a> <br><br>　　 <a href="http://www-900.ibm.com/developerworks/cn/java/j-jsptags/index.shtml" target=_blank><u><font color=#0000ff>Noel Bergman 的文章&#8220;JSP 标记库：着意设计的更好的可用性&#8221;（developerWorks，2001 年 12 月）向您展示了声明性标记是如何帮助提高 JSP 页面的可用性的。</font></u></a> <br><br>　　 <a href="http://www-900.ibm.com/developerworks/cn/java/j-qdjava/index.shtml" target=_blank><u><font color=#0000ff>有关 EcmaScript 的更多详细信息，请参阅 Sing Li 的&#8220;快速上手 Java 编程&#8221;（developerWorks，2001 年 7 月）。</font></u></a> <br><br>　　 <a href="http://www-900.ibm.com/developerWorks/cn/java/index.shtml" target=_blank><u><font color=#0000ff>在 developerWorks Java 技术专区可以找到多达数百篇的 Java 技术参考资料。</font></u></a><br></div>
&nbsp;<br>
<img src ="http://www.blogjava.net/wangxinsh55/aggbug/135808.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-08-10 14:01 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/08/10/135808.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>eclipse 启动参数</title><link>http://www.blogjava.net/wangxinsh55/archive/2007/07/06/128589.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Fri, 06 Jul 2007 07:04:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/07/06/128589.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/128589.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/07/06/128589.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/128589.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/128589.html</trackback:ping><description><![CDATA[		 其实，Eclipse是一个可以进行非常灵活配置的系统，除了以缺省的方式启动以外，还可以指定各种参数来定制启动方式。在参考了一些资料之后，我总结了一些比较常用的启动时Command Arguments，如果有不正确的地方希望大家予以指出。<br><br>-arch [processor architecture]<br>描述：指定所使用的处理器的类别<br>举例：eclipse -arch x86或eclipse -arch sparc<br><br>-application [id]<br>描述：指定要运行的应用，id为扩展org.eclipse.core.applications扩展点的插件id加扩展id<br>举例：例如有个插件id为edu.sdu.app，扩展id为myapp，则eclipse -application edu.sdu.app.myapp，就会执行你的扩展应用<br><br>-clean<br>描述：清空插件缓存内容<br>举例：eclipse -clean，有时插件显示不出来是因为Eclipse将插件进行了缓存以加速启动过程，若指定此参数则会清空缓存，从头加载<br><br>-configuration [cofigfile location]<br>描述：指定配置文件的位置，在启动时使用此目录下的配置文件config.ini来启动<br>举例：eclipse -configuration d:/eclipse/configuration<br><br>-data [workspace location]<br>描述：指定启动时的Workspace位置<br>举例：例如Workspace位置设在D:/myworkspace，则eclipse -data D:/myworkspace<br><br>-debug [option file]<br>描述：以Debug状态启动Eclipse，所有的Debug开关在.options文件中指定<br>举例：eclipse -debug d:/eclipse/.options<br><br>-dev [classpath entry]<br>描述：以开发状态启动Eclipse，这会添加所有指定的路径作为每个插件的Classpath<br>举例：例如eclipse -dev bin，会将产生在bin目录下的所有类加载到类路径中，这在开发插件时非常有用<br><br>-nosplash<br>描述：指定启动时不显示闪屏<br>举例：eclipse -nosplash<br><br>-vm [jre path]<br>描述：指定启动时所使用的Java虚拟机<br>举
例：例如要使用自己的Java虚拟机，则eclipse
-vmD:/j2sdk1.4.2_04/jre/bin/java.exe，这样还有一个好处，就是可以开启一个Console，能够显示控制台信息，
当然若使用eclipse -vm D:/j2sdk1.4.2_04/jre/bin/javaw.exe则不会再显示控制台<br><br>-vmargs [Java VM arguments]<br>描述：指定启动时要使用的Java虚拟机参数<br>举例：例如要指定使用的内存容量，则eclipse -vmargs "-Xms256m -Xmx1024m"<br>注：此参数一定要放在所有参数变量的最后面<br><br>
<div class="postText">
<p>如果你觉得你的Eclipse在启动的时候很慢（比如说超过20秒钟），也许你要调整一下你的Eclipse启动参数了，以下是一些``小贴士'':</p>
<p>1. 检查启动Eclipse的JVM设置。 在Help\About Eclipse SDK\Configuration
Detail里面，你可以看到启动Eclipse的JVM。 这个JVM和你在Eclipse中设置的Installed JDK是两回事情。
如果启动Eclipse的JVM还是JDK 1.4的话，那最好改为JDK 5，因为JDK 5的性能比1.4更好。</p>
<p><code>C:\eclipse\eclipse.exe -vm "C:\Program Files\Java\jdk1.5.0_08\ bin\javaw.exe"</code></p>
<p>2. 检查Eclipse所使用的heap的大小。 在C:\eclipse目录下有一个配置文件eclipse.ini，其中配置了Eclipse启动的默认heap大小</p>
<p><code>-vmargs<br>-Xms40M<br>-Xmx256M</code></p>
<p>所以你可以把默认值改为:</p>
<p><code>-vmargs<br>-Xms256M<br>-Xmx512M</code></p>
<p>当然，也可以这样做，把堆的大小改为256 - 512。</p>
<p><code>C:\eclipse\eclipse.exe -vm "C:\Program Files\Java\jdk1.5.0_08\ bin\javaw.exe" -vmargs -Xms256M -Xmx512M</code></p>
<p>3. 其他的启动参数。 如果你有一个双核的CPU，也许可以尝试这个参数:</p>
<p><code>-XX:+UseParallelGC</code></p>
<p>让GC可以更快的执行。（只是JDK 5里对GC新增加的参数）</p>
<p>【参考资料】<a href="http://swem.wm.edu/blogs/waynegraham/index.cfm/2006/9/7/Tweaking-Eclipse" class="externalLink" target="_blank">Tweaking Eclipse</a></p>
<p>
<!-- Tag links generated by Zoundry Blog Writer. Do not manually edit. http://www.zoundry.com -->
Technorati : <a href="http://technorati.com/tag/Eclipse" class="ztag" rel="tag">Eclipse</a> <br>Del.icio.us : <a href="http://del.icio.us/tag/Eclipse" class="ztag" rel="tag">Eclipse</a>
</p>
</div>
<br><img src ="http://www.blogjava.net/wangxinsh55/aggbug/128589.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-07-06 15:04 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/07/06/128589.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>servlet 相关的Listener应用</title><link>http://www.blogjava.net/wangxinsh55/archive/2007/06/25/126058.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Mon, 25 Jun 2007 03:38:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/06/25/126058.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/126058.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/06/25/126058.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/126058.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/126058.html</trackback:ping><description><![CDATA[<table cellSpacing=1 cellPadding=1 width="98%" border=0>
    <tbody>
        <tr>
            <td><span class=date>张利海 于 2004年11月22日 23:27 发表</span> </td>
        </tr>
        <tr>
            <td>关键词 : servlet listener timer 定时器</td>
        </tr>
        <tr>
            <td>
            <p>从作用域范围来说,Servlet的作用域有ServletContext,HttpSession,ServletRequest.<br><br>Context范围:<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>ServletContextListener:<br>对一个应用进行全局监听.随应用启动而启动,随应用消失而消失主要有两个方法:<br>contextDestroyed(ServletContextEvent&nbsp;event)&nbsp;<br>&nbsp;在应用关闭的时候调用<br>contextInitialized(ServletContextEvent&nbsp;event)&nbsp;<br>在应用启动的时候调用<br><br>这个监听器主要用于一些随着应用启动而要完成的工作,也就是很多人说的我想在容器<br>启动的时候干..........<br>一般来说对"全局变量"初始化,如<br>public&nbsp;void&nbsp;contextInitialized(ServletContextEvent&nbsp;event){<br>&nbsp;&nbsp;&nbsp;&nbsp;ServletContex&nbsp;sc&nbsp;=&nbsp;event.getServletContext();<br>&nbsp;&nbsp;&nbsp;&nbsp;sc.setAttribute(name,value);<br>}<br>以后你就可以在任何servlet中getServletContext().getAttribute(name);<br>我最喜欢用它来做守护性工作,就是在contextInitialized(ServletContextEvent&nbsp;event)&nbsp;<br>方法中实现一个Timer,然后就让应用在每次启动的时候让这个Timer工作:<br>public&nbsp;void&nbsp;contextInitialized(ServletContextEvent&nbsp;event){<br>&nbsp;&nbsp;&nbsp;&nbsp;timer&nbsp;=&nbsp;new&nbsp;Timer();<br>&nbsp;&nbsp;&nbsp;&nbsp;timer.schedule(new&nbsp;TimerTask(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;run(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//do&nbsp;any&nbsp;things<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;},0,时间间隔);<br>}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;有人说Timer只能规定从现在开始的多长时间后,每隔多久做一次事或在什么时间做<br>一次事,那我想在每月1号或每天12点做一项工作如何做呢?<br>你只要设一个间隔,然后每次判断一下当时是不是那个时间段就行了啊,比如每月一号做,那你<br>时间间隔设为天,即24小时一个循环,然后在run方法中判断当时日期new&nbsp;Date().getDate()==1<br>就行了啊.如果是每天的12点,那你时间间隔设为小时,然后在run中判断new&nbsp;Date().getHour()<br>==12,再做某事就行了.<br><br>ServletContextAttributeListener:<br><br>这个监听器主要监听ServletContex对象在setAttribute()和removeAttribute()的事件,注意<br>也就是一个"全局变量"在被Add(第一次set),replace(对已有的变量重新赋值)和remove的时候.<br>分别调用下面三个方法:<br>public&nbsp;void&nbsp;attributeAdded(ServletContextAttributeEvent&nbsp;scab)这个方法不仅可以知道<br>哪些全局变量被加进来,而且可获取容器在启动时自动设置了哪些context变量:<br><br>public&nbsp;void&nbsp;attributeAdded(ServletContextAttributeEvent&nbsp;scab){<br>&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(scab.getName());<br>}<br>&nbsp;&nbsp;public&nbsp;void&nbsp;attributeRemoved(ServletContextAttributeEvent&nbsp;scab)&nbsp;<br><br>&nbsp;&nbsp;public&nbsp;void&nbsp;attributeReplaced(ServletContextAttributeEvent&nbsp;scab)&nbsp;<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;<br><br><br><br><br><br><br><br><br>Session范围:<br>HttpSessionListener:<br>这个监听器主要监听一个Session对象被生成和销毁时发生的事件.对应有两个方法:<br>&nbsp;&nbsp;public&nbsp;void&nbsp;sessionCreated(HttpSessionEvent&nbsp;se)&nbsp;<br><br>&nbsp;&nbsp;public&nbsp;void&nbsp;sessionDestroyed(HttpSessionEvent&nbsp;se)<br><br>&nbsp;&nbsp;一般来说,一个session对象被create时,可以说明有一个新客端进入.可以用来粗略统计在线人<br>数,注意这不是精确的,因为这个客户端可能立即就关闭了,但sessionDestroyed方法却会按一定<br>的策略很久以后才会发生.<br><br>HttpSessionAttributeListener:<br>和ServletContextAttributeListener一样,它监听一个session对象的Attribut被Add(一个特定<br>名称的Attribute每一次被设置),replace(已有名称的Attribute的值被重设)和remove时的事件.<br>对就的有三个方法.<br>&nbsp;&nbsp;public&nbsp;void&nbsp;attributeAdded(HttpSessionBindingEvent&nbsp;se)&nbsp;<br><br>&nbsp;&nbsp;public&nbsp;void&nbsp;attributeRemoved(HttpSessionBindingEvent&nbsp;se)&nbsp;<br><br>&nbsp;&nbsp;public&nbsp;void&nbsp;attributeReplaced(HttpSessionBindingEvent&nbsp;se)&nbsp;<br><br>&nbsp;&nbsp;上面的几个监听器的方法,都是在监听应用逻辑中servlet逻辑中发生了什么事,一般的来说.<br>我们只要完成逻辑功能,比如session.setAttribute("aaa","111");我只要把一个名为aaa的变量<br>放在session中以便以后我能获取它,我并不关心当session.setAttribute("aaa","111");发生时<br>我还要干什么.(当然有些时候要利用的),但对于下面这个监听器,你应该好好发解一下:<br><br>HttpSessionBindingListener:<br>上面的监听器都是作为一个独立的Listener在容器中控制事件的.而HttpSessionBindingListener<br>对在一对象中监听该对象的状态,实现了该接口的对象如果被作为value被add到一个session中或从<br>session中remove,它就会知道自己已经作为一个session对象或已经从session删除,这对于一些非<br>纯JAVA对象,生命周期长于session的对象,以及其它需要释放资源或改变状态的对象非常重要.<br>比如:<br>session.setAttribute("abcd","1111");<br>以后session.removeAttribute("abcd");因为abcd是一个字符中,你从session中remove后,它就会<br>自动被垃圾回收器回收,而如果是一个connection:(只是举例,你千万不要加connection往session<br>中加入)<br>session.setAttribute("abcd",conn);<br>以后session.removeAttribute("abcd");这时这个conn被从session中remove了,你已经无法获取它<br>的句柄,所以你根本没法关闭它.而在没有remove之前你根本不知道什么时候要被remove,你又无法<br>close(),那么这个connection对象就死了.另外还有一些对象可以在被加入一个session时要锁定<br>还要被remove时要解锁,应因你在程序中无法判断什么时候被remove(),add还好操作,我可以先加锁<br>再add,但remove就后你就找不到它的句柄了,根本没法解锁,所以这些操作只能在对象自身中实现.<br>也就是在对象被add时或remove时通知对象自己回调相应的方法:<br><br>MyConn&nbsp;extends&nbsp;Connection&nbsp;implements&nbsp;HttpSessionBindingListener{<br>&nbsp;&nbsp;public&nbsp;void&nbsp;valueBound(HttpSessionBindingEvent&nbsp;se){<br>&nbsp;&nbsp;&nbsp;&nbsp;this.initXXX();<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;public&nbsp;void&nbsp;valueUnbound(HttpSessionBindingEvent&nbsp;se){<br><br>&nbsp;&nbsp;&nbsp;&nbsp;this.close();<br>&nbsp;&nbsp;}<br>}<br><br>session.setAttribute("aaa",new&nbsp;MyConn());<br>这时如果调用session.removeAttribute("aaa"),则触发valueUnbound方法,就会自动关闭自己.<br>而其它的需要改变状态的对象了是一样.<br><br>另外还有一个HttpSessionActivationListener监听器是实现分布式应用中session同步的.不作<br>多介绍,如果有要实现该功能的朋友可以和我联系.<br><br><br><br><br>在servlet2.4中,对于request范围已经实现对应的监听器:<br>ServletRequestListener，ServletRequestAttributeListener<br>但没有找到好的容器的支持所以没有做过多的测试.虽然从API可以掌握99%,但没有经过真正的<br>测试我是不会仅把API抄出来的.以后我会补齐这方面的内容 <br><br>原作者:Axman<br></p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/wangxinsh55/aggbug/126058.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wangxinsh55/" target="_blank">SIMONE</a> 2007-06-25 11:38 <a href="http://www.blogjava.net/wangxinsh55/archive/2007/06/25/126058.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转贴]垃圾收集器与Java编程 </title><link>http://www.blogjava.net/wangxinsh55/archive/2007/06/19/125071.html</link><dc:creator>SIMONE</dc:creator><author>SIMONE</author><pubDate>Tue, 19 Jun 2007 03:22:00 GMT</pubDate><guid>http://www.blogjava.net/wangxinsh55/archive/2007/06/19/125071.html</guid><wfw:comment>http://www.blogjava.net/wangxinsh55/comments/125071.html</wfw:comment><comments>http://www.blogjava.net/wangxinsh55/archive/2007/06/19/125071.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wangxinsh55/comments/commentRss/125071.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wangxinsh55/services/trackbacks/125071.html</trackback:ping><description><![CDATA[<p><a href="http://www-900.ibm.com/developerWorks/cn/java/l-JavaMemoryLeak2/index.shtml#author1"><u><font color=#0000ff>欧阳辰</font></u></a> (<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#121;&#101;&#101;&#107;&#101;&#101;&#64;&#115;&#105;&#110;&#97;&#46;&#99;&#111;&#109;"><u><font color=#0000ff>yeekee@sina.com</font></u></a>)<br><a href="http://www-900.ibm.com/developerWorks/cn/java/l-JavaMemoryLeak2/index.shtml#author2"><u><font color=#0000ff>周欣</font></u></a> (<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#122;&#104;&#111;&#117;&#120;&#105;&#110;&#64;&#115;&#101;&#105;&#46;&#112;&#107;&#117;&#46;&#101;&#100;&#117;&#46;&#99;&#110;"><u><font color=#0000ff>zhouxin@sei.pku.edu.cn</font></u></a>)<br></p>
<blockquote>垃圾收集器(Garbage Collector，GC)对Java程序员来说，基本 上是透明的，但是一个优秀的Java程序员必须了解GC的工作原理、如何优化GC的性能、如何与GC进行有限的交互，因为有一些应用程序对性能要求较高，例如嵌入式系统、实时系统等，只有全面提升内存的管理效率 ,才能提高整个应用程序的性能。本篇文章首先简单介绍GC的工作原理之后，然后再对GC的几个关键问题进行深入探讨，最后提出一些Java程序设计建议，从GC角度提高Java程序的性能。</blockquote>
<p><a name=1><span class=atitle2>一GC的基本原理</span></a></p>
<p>Java的内存管理实际上就是对象的管理，其中包括对象的分配和释放。</p>
<p>对于程序员来说，分配对象使用new关键字；释放对象时，只要将对象所有引用赋值为null，让程序不能够再访问到这个对象，我们称该对象为"不可达的"。GC将负责回收所有"不可达"对象的内存空间。</p>
<p>对于GC来说，当程序员创建对象时，GC就开始监控这个对象的地址、大小以及使用情况。通常，GC采用有向图的方式记录和管理堆(heap)中的所有对象(详见<a href="http://www-900.ibm.com/developerWorks/cn/java/l-JavaMemoryLeak2/index.shtml#resources"><u><font color=#0000ff>参考资料1</font></u></a> )。通过这种方式确定哪些对象是"可达的"，哪些对象是"不可达的"。当GC确定一些对象为"不可达"时，GC就有责任回收这些内存空间。但是，为了保证GC能够在不同平台实现的问题，Java规范对GC的很多行为都没有进行严格的规定。例如，对于采用什么类型的回收算法、什么时候进行回收等重要问题都没有明确的规定。因此，不同的JVM的实现者往往有不同的实现算法。这也给Java程序员的开发带来行多不确定性。本文研究了几个与GC工作相关的问题，努力减少这种不确定性给Java程序带来的负面影响。</p>
<p><a name=2><span class=atitle2>二 增量式GC( Incremental GC )</span></a></p>
<p>GC在JVM中通常是由一个或一组进程来实现的，它本身也和用户程序一样占用heap空间，运行时也占用CPU。当GC进程运行时，应用程序停止运行。因此，当GC运行时间较长时，用户能够感到Java程序的停顿，另外一方面，如果GC运行时间太短，则可能对象回收率太低，这意味着还有很多应该回收的对象没有被回收，仍然占用大量内存。因此，在设计GC的时候，就必须在停顿时间和回收率之间进行权衡。一个好的GC实现允许用户定义自己所需要的设置，例如有些内存有限有设备，对内存的使用量非常敏感，希望GC能够准确的回收内存，它并不在意程序速度的放慢。另外一些实时网络游戏，就不能够允许程序有长时间的中断。增量式GC就是通过一定的回收算法，把一个长时间的中断，划分为很多个小的中断，通过这种方式减少GC对用户程序的影响。虽然，增量式GC在整体性能上可能不如普通GC的效率高，但是它能够减少程序的最长停顿时间。</p>
<p>下图就表示了，增量式GC和普通GC的比较。其中灰色部分表示线程占用CPU的时间。</p>
<p>&#160;</p>
<center><img height=210 alt="" src="http://www-900.ibm.com/developerWorks/cn/java/l-JavaMemoryLeak2/1.gif" width=600 border=0></center>
<p>&#160;</p>
<p>Sun JDK提供的HotSpot JVM就能支持增量式GC。HotSpot JVM缺省GC方式为不使用增量GC，为了启动增量GC，我们必须在运行Java程序时增加-Xincgc的参数。HotSpot JVM增量式GC的实现是采用Train GC算法。它的基本想法就是，将堆中的所有对象按照创建和使用情况进行分组(分层)，将使用频繁高和具有相关性的对象放在一队中，随着程序的运行，不断对组进行调整。当GC运行时，它总是先回收最老的(最近很少访问的)的对象，如果整组都为可回收对象，GC将整组回收。这样，每次GC运行只回收一定比例的不可达对象，保证程序的顺畅运行。Train GC算法是一个非常好的算法，具体算法见<a href="http://www-900.ibm.com/developerWorks/cn/java/l-JavaMemoryLeak2/index.shtml#resources"><u><font color=#0000ff>参考资料4</font></u></a>。</p>
<p><a name=3><span class=atitle2>三 详解finalize函数</span></a></p>
<p>finalize是位于Object类的一个方法，该方法的访问修饰符为protected，由于所有类为Object的子类，因此用户类很容易访问到这个方法。由于，finalize函数没有自动实现链式调用，我们必须手动的实现，因此finalize函数的最后一个语句通常是super.finalize()。通过这种方式，我们可以实现从下到上实现finalize的调用，即先释放自己的资源，然后再释放父类的资源。</p>
<p>根据Java语言规范，JVM保证调用finalize函数之前，这个对象是不可达的，但是JVM不保证这个函数一定会被调用。另外，规范还保证finalize函数最多运行一次。</p>
<p>很多Java初学者会认为这个方法类似与C++中的析构函数，将很多对象、资源的释放都放在这一函数里面。其实，这不是一种很好的方式。原因有三，其一，GC为了能够支持finalize函数，要对覆盖这个函数的对象作很多附加的工作。其二，在finalize运行完成之后，该对象可能变成可达的，GC还要再检查一次该对象是否是可达的。因此，使用finalize会降低GC的运行性能。其三，由于GC调用finalize的时间是不确定的，因此通过这种方式释放资源也是不确定的。</p>
<p>通常，finalize用于一些不容易控制、并且非常重要资源的释放，例如一些I/O的操作，数据的连接。这些资源的释放对整个应用程序是非常关键的。在这种情况下，程序员应该以通过程序本身管理(包括释放)这些资源为主，以finalize函数释放资源方式为辅，形成一种双保险的管理机制，而不应该仅仅依靠finalize来释放资源。</p>
<p>下面给出一个例子说明，finalize函数被调用以后，仍然可能是可达的，同时也可说明一个对象的finalize只可能运行一次。</p>
<p>&#160;</p>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
    <tbody>
        <tr>
            <td>
            <pre><code><br>class MyObject{<br>    Test main; //记录Test对象，在finalize中时用于恢复可达性<br>    public MyObject(Test t)<br>    {	 <br>	main=t; //保存Test 对象<br>    }<br>    protected void finalize()<br>    {<br>	main.ref=this;// 恢复本对象，让本对象可达<br>	System.out.println("This is finalize");//用于测试finalize只运行一次<br>    }<br>}<br><br>class Test {<br>	MyObject ref;<br> 	public static void main(String[] args) {<br> 		Test test=new Test();<br> 		test.ref=new MyObject(test);<br> 		test.ref=null; //MyObject对象为不可达对象，finalize将被调用<br> 		System.gc(); <br> 		if (test.ref!=null) System.out.println("My Object还活着");	<br>	}<br>}<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p>运行结果：<br>This is finalize<br>MyObject还活着</p>
<p>此例子中，需要注意的是虽然MyObject对象在finalize中变成可达对象，但是下次回收时候，finalize却不再被调用，因为finalize函数最多只调用一次。</p>
<p><a name=4><span class=atitle2>四 程序如何与GC进行交互</span></a></p>
<p>Java2增强了内存管理功能， 增加了一个java.lang.ref包，其中定义了三种引用类。这三种引用类分别为SoftReference、WeakReference和PhantomReference。通过使用这些引用类，程序员可以在一定程度与GC进行交互，以便改善GC的工作效率。这些引用类的引用强度介于可达对象和不可达对象之间。它们的引用强度如下图所示：</p>
<p>&#160;</p>
<center><img height=270 alt="" src="http://www-900.ibm.com/developerWorks/cn/java/l-JavaMemoryLeak2/2.gif" width=492 border=0></center>
<p>&#160;</p>
<p>创建一个引用对象也非常容易，例如如果你需要创建一个Soft Reference对象，那么首先创建一个对象，并采用普通引用方式(可达对象)；然后再创建一个SoftReference引用该对象；最后将普通引用设置为null。通过这种方式，这个对象就只有一个Soft Reference引用。同时，我们称这个对象为Soft Reference 对象。</p>
<p>Soft Reference的主要特点是据有较强的引用功能。只有当内存不够的时候，才进行回收这类内存，因此在内存足够的时候，它们通常不被回收。另外，这些引用对象还能保证在Java抛出OutOfMemory 异常之前，被设置为null。它可以用于实现一些常用图片的缓存，实现Cache的功能，保证最大限度的使用内存而不引起OutOfMemory。以下给出这种引用类型的使用伪代码；</p>
<p>&#160;</p>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc border=1>
    <tbody>
        <tr>
            <td>
            <pre><code><br>//申请一个图像对象<br>Image image=new Image();//创建Image对象<br>&#8230;<br>//使用 image<br>&#8230;<br>//使用完了image，将它设置为soft 引用类型，并且释放强引用；<br>SoftReference sr=new SoftReference(image);<br>image=null;<br>	&#8230;<br>	//下次使用时<br>	if (sr!=null) image=sr.get();<br>	else{<br>	//由于GC由于低内存，已释放image，因此需要重新装载；<br>	image=new Image();<br>sr=new SoftReference(image);<br>}<br></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<p>Weak引用对象与Soft引用对象的最大不同就在于：GC在进行回收时，需要通过算法检查是否回收Soft引用对象，而对于Weak引用对象，GC总是进行回收。Weak引用对象更容易、更快被GC回收。虽然，GC在运行时一定回收Weak对象，但是复杂关系的Weak对象群常常需要好几次GC的运行才能完成。Weak引用对象常常用于Map结构中，引用数据量较大的对象，一旦该对象的强引用为null时，GC能够快速地回收该对象空间。该例子见<a href="http://www-900.ibm.com/developerWorks/cn/java/l-JavaMemoryLeak2/index.shtml#resources"><u><font color=#0000ff>参考资料4</font></u></a>;</p>
<p>Phantom引用的用途较少，主要用于辅助finalize函数的使用。Phantom对象指一些对象，它们执行完了finalize函数，并为不可达对象，但是它们还没有被GC回收。这种对象可以辅助finalize进行一些后期的回收工作，我们通过覆盖Reference的clear()方法，增强资源回收机制的灵活性。</p>
<p><a name=5><span class=atitle2>五一些Java编码的建议</span></a></p>
<p>根据GC的工作原理，我们可以通过一些技巧和方式，让GC运行更加有效率，更加符合应用程序的要求。以下就是一些程序设计的几点建议。</p>
<ol class=n01>
    <li>最基本的建议就是尽早释放无用对象的引用。大多数程序员在使用临时变量的时候，都是让引用变量在退出活动域(scope)后，自动设置为null。我们在使用这种方式时候，必须特别注意一些复杂的对象图，例如数组，队列，树，图等，这些对象之间有相互引用关系较为复杂。对于这类对象，GC回收它们一般效率较低。如果程序允许，尽早将不用的引用对象赋为null。这样可以加速GC的工作。
    <li>尽量少用finalize函数。finalize函数是Java提供给程序员一个释放对象或资源的机会。但是，它会加大GC的工作量，因此尽量少采用finalize方式回收资源。
    <li>如果需要使用经常使用的图片，可以使用soft应用类型。它可以尽可能将图片保存在内存中，供程序调用，而不引起OutOfMemory。
    <li>注意集合数据类型，包括数组，树，图，链表等数据结构，这些数据结构对GC来说，回收更为复杂。另外，注意一些全局的变量，以及一些静态变量。这些变量往往容易引起悬挂对象(dangling reference)，造成内存浪费。
    <li>当程序有一定的等待时间，程序员可以手动执行System.gc()，通知GC运行，但是Java语言规范并不保证GC一定会执行。使用增量式GC可以缩短Java程序的暂停时间。 </li>
</ol>
<p><a name=resources><span class=atitle2>参考资料</span></a></p>
<p>文章</p>
<ol class=n01>
    <li>欧阳辰，周欣 "Java与内存泄漏" <a href="http://www-900.ibm.com/developerWorks/cn/java/l-JavaMemoryLeak/index.shtml"><u><font color=#0000ff>http://www-900.ibm.com/developerWorks/cn/java/l-JavaMemoryLeak/index.shtml</font></u></a>
    <li>Y. Srinivas Ramakrishna "Atuomatic Memory Management in the Java HotSpot Virtual Machine"，此文章JavaOne2002的演讲材料, <a href="http://java.sun.com/javaone"><u><font color=#0000ff>http://java.sun.com/javaone</font></u></a>
    <li>Monica Pawlan "Reference Objects and Garbage Collector" 此文章为JDC的文章，可在<a href="http://developer.java.sun.com/"><u><font color=#0000ff>http://developer.java.sun.com/</font></u></a>上找到
    <li>Bill Venners Chapter 9 of "Inside the Java 2 Virtual Machine" <a href="http://www.artima.com/insidejvm/ed2/ch09GarbageCollectionPrint.html"><u><font color=#0000ff>http://www.artima.com/insidejvm/ed2/ch09GarbageCollectionPrint.html</font></u></a>
    <li>Sun Microsystems, "Java Language Specification, Second Version" </li>
</ol>
<br><!-- AUTHOR BIOS--><!-- Make author heading singular or plural as needed-->
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><a name=author1><span class=atitle2>关于作者</span></a> <br>欧阳辰，北京大学计算机硕士毕业，98年起开始研究基于java的软件开发、测试，参与开发、测试过多个基于Java的应用程序和Web服务项目。联系