﻿<?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-我思故我强-文章分类-J2EE</title><link>http://www.blogjava.net/balajinima/category/24466.html</link><description /><language>zh-cn</language><lastBuildDate>Sun, 02 Aug 2009 00:06:12 GMT</lastBuildDate><pubDate>Sun, 02 Aug 2009 00:06:12 GMT</pubDate><ttl>60</ttl><item><title>HTTP Session （转载）</title><link>http://www.blogjava.net/balajinima/articles/289142.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Thu, 30 Jul 2009 09:07:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/289142.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/289142.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/289142.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/289142.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/289142.html</trackback:ping><description><![CDATA[HTTP协议（ &lt;<a href="http://www.w3.org/Protocols/" target="_blank">http://www.w3.org/Protocols/</a>&gt; <a href="http://www.w3.org/Protocols/" target="_blank">http://www.w3.org/Protocols/</a>）是<br />"一次性单向"协议。<br />服务端不能主动连接客户端，只能被动等待并答复客户端请求。客户端连接服务端，发<br />出一个HTTP Request，服务端处理请求，并且返回一个HTTP Response给客户端，本次<br />HTTP Request-Response Cycle结束。<br />我们看到，HTTP协议本身并不能支持服务端保存客户端的状态信息。于是，Web Server<br />中引入了session的概念，用来保存客户端的状态信息。<br />这里用一个形象的比喻来解释session的工作方式。假设Web Server是一个商场的存包<br />处，HTTP Request是一个顾客，第一次来到存包处，管理员把顾客的物品存放在某一个<br />柜子里面（这个柜子就相当于Session），然后把一个号码牌交给这个顾客，作为取包<a name="entrymore"></a><br />凭证（这个号码牌就是Session ID）。顾客（HTTP Request）下一次来的时候，就要把<br />号码牌（Session ID）交给存包处（Web Server）的管理员。管理员根据号码牌<br />（Session ID）找到相应的柜子（Session），根据顾客（HTTP Request）的请求，Web<br />Server可以取出、更换、添加柜子（Session）中的物品，Web Server也可以让顾客<br />（HTTP Request）的号码牌和号码牌对应的柜子（Session）失效。顾客（HTTP<br />Request）的忘性很大，管理员在顾客回去的时候（HTTP Response）都要重新提醒顾客<br />记住自己的号码牌（Session ID）。这样，顾客（HTTP Request）下次来的时候，就又<br />带着号码牌回来了。<br />Session ID实际上是在客户端和服务端之间通过HTTP Request和HTTP Response传来传<br />去的。号码牌（Session ID）必须包含在HTTP Request里面。关于HTTP Request的具体<br />格式，请参见HTTP协议（ &lt;<a href="http://www.w3.org/Protocols/" target="_blank">http://www.w3.org/Protocols/</a>&gt;<br /><a href="http://www.w3.org/Protocols/" target="_blank">http://www.w3.org/Protocols/</a>）。这里只做一个简单的介绍。<br />在Java Web Server（即Servlet/JSP Server）中，Session ID用jsessionid表示（请<br />参见Servlet规范）。<br />HTTP Request一般由3部分组成：<br />（1）Request Line<br />这一行由HTTP Method（如GET或POST）、URL、和HTTP版本号组成。<br />例如，GET <a href="http://www.w3.org/pub/WWW/TheProject.html" target="_blank">http://www.w3.org/pub/WWW/TheProject.html</a> HTTP/1.1<br />GET <a href="http://www.google.com/search?q=Tomcat" target="_blank">http://www.google.com/search?q=Tomcat</a> HTTP/1.1<br />POST <a href="http://www.google.com/search" target="_blank">http://www.google.com/search</a> HTTP/1.1<br />GET <a href="http://www.somsite.com/menu.do;jsessionid=1001" target="_blank">http://www.somsite.com/menu.do;jsessionid=1001</a> HTTP/1.1<br /><br />（2）Request Headers<br />这部分定义了一些重要的头部信息，如，浏览器的种类，语言，类型。Request<br />Headers中还可以包括Cookie的定义。例如：<br />User-Agent: Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0)<br />Accept-Language: en-us<br />Cookie: jsessionid=1001<br /><br />（3）Message Body<br />如果HTTP Method是GET，那么Message Body为空。<br />如果HTTP Method是POST，说明这个HTTP Request是submit一个HTML Form的结果，<br />那么Message Body为HTML Form里面定义的Input属性。例如，<br />user=guest<br />password=guest<br />jsessionid=1001<br />主意，如果把HTML Form元素的Method属性改为GET。那么，Message Body为空，所有的<br />Input属性都会加在URL的后面。你在浏览器的URL地址栏中会看到这些属性，类似于<br /><a href="http://www.somesite/login.do?user=guest&amp;password=guest&amp;jsessionid=1001" target="_blank">http://www.somesite/login.do?user=guest&amp;password=guest&amp;jsessionid=1001</a><br /><br />从理论上来说，这3个部分（Request URL，Cookie Header, Message Body）都可以用<br />来存放Session ID。由于Message Body方法必须需要一个包含Session ID的HTML<br />Form，所以这种方法不通用。<br />一般用来实现Session的方法有两种：<br />（1）URL重写。<br />Web Server在返回Response的时候，检查页面中所有的URL，包括所有的连接，和HTML<br />Form的Action属性，在这些URL后面加上";jsessionid=XXX"。<br />下一次，用户访问这个页面中的URL。jsessionid就会传回到Web Server。<br />（2）Cookie。<br />如果客户端支持Cookie，Web Server在返回Response的时候，在Response的Header部<br />分，加入一个"set-cookie: jsessionid=XXXX"header属性，把jsessionid放在<br />Cookie里传到客户端。<br />客户端会把Cookie存放在本地文件里，下一次访问Web Server的时候，再把Cookie的信<br />息放到HTTP Request的"Cookie"header属性里面，这样jsessionid就随着HTTP<br />Request返回给Web Server。<br /><br />二、相关资料<br /><br />前面是我自作聪明的一段个人浅见，下面我来找点权威资料支持。<br />&lt;<a href="http://www.w3.org/Protocols/" target="_blank">http://www.w3.org/Protocols/</a>&gt; <a href="http://www.w3.org/Protocols/" target="_blank">http://www.w3.org/Protocols/</a><br /><br />Use of HTTP State Management (<br />&lt;<a href="ftp://ftp.rfc-editor.org/in-notes/rfc2964.txt" target="_blank">ftp://ftp.rfc-editor.org/in-notes/rfc2964.txt</a>&gt; RFC 2964).<br />&lt;<a href="ftp://ftp.rfc-editor.org/in-notes/rfc2964.txt" target="_blank">ftp://ftp.rfc-editor.org/in-notes/rfc2964.txt</a>&gt;<br /><a href="ftp://ftp.rfc-editor.org/in-notes/rfc2964.txt" target="_blank">ftp://ftp.rfc-editor.org/in-notes/rfc2964.txt</a><br /><br />这个文件是定义"HTTP State Management"扩展协议部分。里面有这么一段，<br />It's important to realize that similar capabilities may also be    achieved<br />using the "bare" HTTP protocol, and/or dynamically-generated<br />HTML, without the State Management extensions.  For example, state<br />information can be transmitted from the service to the user by    embedding<br />a session identifier in one or more URLs which appear in HTTP redirects, or<br />dynamically generated HTML; and the state information may be returned from<br />the user to the service when such URLs appear in a GET or POST request.<br />HTML forms can also be used to pass state information from the service to<br />the user and back, without the user being aware of this happening.<br /><br />这段话的意思是说，不用这个 "HTTP State Management"扩展协议部分，我们也可以<br />用"纯粹"的HTTP协议实现Session -- 比如URL重写，HTML Form等。<br />这里面没有提到Cookie。因为"HTTP State Management" 扩展协议部分本身包括了关<br />于Cookie的内容。这一点可以通过<br />HTTP State Management Mechanism (RFC 2965<br />&lt;<a href="ftp://ftp.rfc-editor.org/in-notes/rfc2965.txt" target="_blank">ftp://ftp.rfc-editor.org/in-notes/rfc2965.txt</a>&gt; ),<br />&lt;<a href="ftp://ftp.rfc-editor.org/in-notes/rfc2965.txt" target="_blank">ftp://ftp.rfc-editor.org/in-notes/rfc2965.txt</a>&gt;<br /><a href="ftp://ftp.rfc-editor.org/in-notes/rfc2965.txt" target="_blank">ftp://ftp.rfc-editor.org/in-notes/rfc2965.txt</a><br />看出来。<br /><br />STATE AND SESSIONS<br />This document describes a way to create stateful sessions with HTTP<br />requests and responses.  Currently, HTTP servers respond to each    client<br />request without relating that request to previous or    subsequent requests;<br />the state management mechanism allows clients    and servers that wish to<br />exchange state information to place HTTP    requests and responses within a<br />larger context, which we term a    "session".  This context might be used to<br />create, for example, a    "shopping cart", in which user selections can be<br />aggregated before    purchase, or a magazine browsing system, in which a<br />user's previous    reading affects which offerings are presented.<br />Neither clients nor servers are required to support cookies.  A    server<br />MAY refuse to provide content to a client that does not return    the<br />cookies it sends.<br /><br />后面还给出了例子（其中的汉语部分是我加的）。<br /><br />4.1  Example 1<br />Most detail of request and response headers has been omitted.  Assume<br />the user agent has no stored cookies.<br /><br />     1. User Agent -&gt; Server<br /><br />       POST /acme/login HTTP/1.1<br />       [form data]<br /><br />       User identifies self via a form.<br /><br />     2. Server -&gt; User Agent<br /><br />       HTTP/1.1 200 OK<br />       Set-Cookie2: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"<br /><br />       Cookie reflects user's identity.<br />（这里面的 Customer="WILE_E_COYOTE" 应该就是从Form Data里面来的，这时候又传<br />回给了客户端）<br /><br />     3. User Agent -&gt; Server<br /><br />       POST /acme/pickitem HTTP/1.1<br />       Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"<br />       [form data]<br /><br />       User selects an item for "shopping basket".<br /><br />     4. Server -&gt; User Agent<br /><br />       HTTP/1.1 200 OK<br />       Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1";<br />               Path="/acme"<br /><br />       Shopping basket contains an item.<br />（这个火箭发射器显然也是从Form Data来的。但为什么<br />Part_Number="Rocket_Launcher_0001"也需要传回给客户端？<br />Customer="WILE_E_COYOTE"; 应该是在传统的"Set-Cookie"里面传回给客户端的。"<br />Set-Cookie2" 的作用应该是向Cookie里面添加东西。）<br /><br />     5. User Agent -&gt; Server<br /><br />       POST /acme/shipping HTTP/1.1<br />       Cookie: $Version="1";<br />               Customer="WILE_E_COYOTE"; $Path="/acme";<br />               Part_Number="Rocket_Launcher_0001"; $Path="/acme"<br />       [form data]<br /><br />       User selects shipping method from form.<br /><br />（客户传给服务器的Cookie里面包括了Customer和Part_Number）<br /><br />     6. Server -&gt; User Agent<br /><br />       HTTP/1.1 200 OK<br />       Set-Cookie2: Shipping="FedEx"; Version="1"; Path="/acme"<br /><br />       New cookie reflects shipping method.<br /><br />     7. User Agent -&gt; Server<br /><br />       POST /acme/process HTTP/1.1<br />       Cookie: $Version="1";<br />               Customer="WILE_E_COYOTE"; $Path="/acme";<br />               Part_Number="Rocket_Launcher_0001"; $Path="/acme";<br />               Shipping="FedEx"; $Path="/acme"<br />       [form data]<br /><br /><br />       User chooses to process order.<br /><br />     8. Server -&gt; User Agent<br /><br />       HTTP/1.1 200 OK<br /><br />       Transaction is complete.<br /><br />  The user agent makes a series of requests on the origin server, after<br />each of which it receives a new cookie.  All the cookies have the same Path<br />attribute and (default) domain.  Because the request-URIs all path-match<br />/acme, the Path attribute of each cookie, each request contains all the<br />cookies received so far.<br />（看到这里，我才大致明白，原来那个$Path="/acme" 大致起着 JSessionID的作用）<br /><br /><br />三、Tomcat5的HTTP Session实现<br /><br />下面我们来看Tomcat5的源代码如何支持HTTP 1.1 Session。<br />我们可以用jsessionid, Set-Cookie等关键字搜索Tomcat5源代码。<br /><br />首先，我们来看常量定义：<br />org.apache.catalina.Globals<br /><br />   /**<br />    * The name of the cookie used to pass the session identifier back<br />    * and forth with the client.<br />    */<br />   public static final String SESSION_COOKIE_NAME = "JSESSIONID";<br /><br /><br />   /**<br />    * The name of the path parameter used to pass the session identifier<br />    * back and forth with the client.<br />    */<br />public static final String SESSION_PARAMETER_NAME = "jsessionid";<br /><br />Cookie里面用大写的JSESSIONID，URL后缀用的是小写的jsessionid。<br />Session的具体实现类是org.apache.catalina.session.StandardSession。一个Tomcat<br />Server的所有Session都由一个Manager（拥有一个Context）统一管理。我估计有一个<br />Session Listener 专门管理Cluster之间的Session数据复制，具体的我没有追查下<br />去。<br /><br />另外几个重要的相关类是org.apache.coyote.tomcat5包下面的CoyoteRequest ,<br />CoyoteResponse, CoyoteAdapter三个类。<br /><br />org.apache.coyote.tomcat5.CoyoteResponse类的toEncoded()方法支持URL重写。<br />String toEncoded(String url, String sessionId) {<br />...<br />       StringBuffer sb = new StringBuffer(path);<br />       if( sb.length() &gt; 0 ) { // jsessionid can't be first.<br />           sb.append(";jsessionid=");<br />           sb.append(sessionId);<br />       }<br />       sb.append(anchor);<br />       sb.append(query);<br />       return (sb.toString());<br />}<br /><br />我们来看org.apache.coyote.tomcat5.CoyoteRequest的两个方法<br />configureSessionCookie()<br />doGetSession()用Cookie支持jsessionid.<br /><br />   /**<br />    * Configures the given JSESSIONID cookie.<br />    *<br />    * @param cookie The JSESSIONID cookie to be configured<br />    */<br />   protected void configureSessionCookie(Cookie cookie) {<br />      ...<br />   }<br /><br />   HttpSession doGetSession(boolean create){<br />     ...<br />       // Creating a new session cookie based on that session<br />       if ((session != null) &amp;&amp; (getContext() != null)<br />              &amp;&amp; getContext().getCookies()) {<br />           Cookie cookie = new Cookie(Globals.SESSION_COOKIE_NAME,<br />                                      session.getId());<br />           configureSessionCookie(cookie);<br />           ((HttpServletResponse) response).addCookie(cookie);<br />       }<br />     ...<br />   }<br /><br /><br />四、More<br /><br />HTTP Session的协议、规范、实现大概就是这些。<br />另外，在frameset中使用Session的情况有些复杂，不同环境表现可能不同。<br />其余相关的概念有"单点登录"(Sing Sign On - SSO), Domain, Cluster, Proxy,<br />Cache等。<!-- Added by RelatedTopic, plugin for Bo-Blog 2.0.0 --><img src ="http://www.blogjava.net/balajinima/aggbug/289142.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2009-07-30 17:07 <a href="http://www.blogjava.net/balajinima/articles/289142.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>系统权限解决方案(转载)</title><link>http://www.blogjava.net/balajinima/articles/249745.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Sun, 04 Jan 2009 07:40:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/249745.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/249745.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/249745.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/249745.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/249745.html</trackback:ping><description><![CDATA[
		<p>
				<br />每个软件中都有权限这个功能，搞了个通过tag实现的方法,复用性很强，</p>
		<p>
				<br />psy-operation.tld</p>
		<p>Xml代码 <br />&lt;?xml version="1.0" encoding="UTF-8" ?&gt;  <br />&lt;taglib xmlns="<a href="http://java.sun.com/xml/ns/j2ee">http://java.sun.com/xml/ns/j2ee</a>"  <br />    xmlns:xsi="<a href="http://www.blog.com.cn/http://www.w3.org/2001/XMLSchema-instance">http://www.blog.com.cn/http://www.w3.org/2001/XMLSchema-instance</a>"  <br />    xsi:schemaLocation="<a href="http://www.blog.com.cn/http://java.sun.com/xml/ns/j2ee">http://www.blog.com.cn/http://java.sun.com/xml/ns/j2ee</a><a href="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd</a>"  <br />    version="<a href="http://www.blog.com.cn/2.0">http://www.blog.com.cn/2.0</a>"&gt;  <br />  &lt;description&gt;  <br /> psychcn 标记库 1.0   <br />  &lt;/description&gt;  <br />  &lt;tlib-version&gt;1.0&lt;/tlib-version&gt;  <br />  &lt;short-name&gt;psydict&lt;/short-name&gt;  <br />  &lt;uri&gt;http://www.psychcn.com/taglibs&lt;/uri&gt;  <br />    &lt;tag&gt;  <br />    &lt;name&gt;op&lt;/name&gt;  <br />    &lt;description&gt;权限标签&lt;/description&gt;  <br />    &lt;tag-class&gt;com.psychcn.web.tags.OperationTag&lt;/tag-class&gt;  <br /> &lt;body-content&gt;scriptless&lt;/body-content&gt;  <br />       <br />    &lt;attribute&gt;  <br />       &lt;name&gt;code&lt;/name&gt;  <br />       &lt;required&gt;true&lt;/required&gt;  <br />       &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;  <br />    &lt;/attribute&gt;  <br />    &lt;attribute&gt;  <br />       &lt;name&gt;opset&lt;/name&gt;  <br />       &lt;required&gt;false&lt;/required&gt;  <br />       &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;  <br />    &lt;/attribute&gt;       <br />  &lt;/tag&gt;  <br />&lt;/taglib&gt;  </p>
		<p>&lt;?xml version="1.0" encoding="UTF-8" ?&gt;<br />&lt;taglib xmlns="<a href="http://java.sun.com/xml/ns/j2ee">http://java.sun.com/xml/ns/j2ee</a>"<br />    xmlns:xsi="<a href="http://www.blog.com.cn/http://www.w3.org/2001/XMLSchema-instance">http://www.blog.com.cn/http://www.w3.org/2001/XMLSchema-instance</a>"<br />    xsi:schemaLocation="<a href="http://www.blog.com.cn/http://java.sun.com/xml/ns/j2ee">http://www.blog.com.cn/http://java.sun.com/xml/ns/j2ee</a><a href="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd</a>"<br />    version="<a href="http://www.blog.com.cn/2.0">http://www.blog.com.cn/2.0</a>"&gt;<br />  &lt;description&gt;<br /> psychcn 标记库 1.0<br />  &lt;/description&gt;<br />  &lt;tlib-version&gt;1.0&lt;/tlib-version&gt;<br />  &lt;short-name&gt;psydict&lt;/short-name&gt;<br />  &lt;uri&gt;http://www.psychcn.com/taglibs&lt;/uri&gt;<br />    &lt;tag&gt;<br />    &lt;name&gt;op&lt;/name&gt;<br />    &lt;description&gt;权限标签&lt;/description&gt;<br />    &lt;tag-class&gt;com.psychcn.web.tags.OperationTag&lt;/tag-class&gt;<br /> &lt;body-content&gt;scriptless&lt;/body-content&gt;<br />    <br />    &lt;attribute&gt;<br />       &lt;name&gt;code&lt;/name&gt;<br />       &lt;required&gt;true&lt;/required&gt;<br />       &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;<br />    &lt;/attribute&gt;<br />    &lt;attribute&gt;<br />       &lt;name&gt;opset&lt;/name&gt;<br />       &lt;required&gt;false&lt;/required&gt;<br />       &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;<br />    &lt;/attribute&gt;    <br />  &lt;/tag&gt;<br />&lt;/taglib&gt; </p>
		<p>OperationTag.java</p>
		<p>Java代码 <br />import javax.servlet.jsp.JspException;   <br />import javax.servlet.jsp.tagext.SimpleTagSupport;   <br />  <br />import java.io.IOException;   <br />import java.util.*;   <br />  <br />public class OperationTag extends SimpleTagSupport {   <br /> private Set operation_set;   <br /> private String default_operation_set_name = "ops";   <br /> private String code;   <br />    <br /> public void setCode(String code) {   <br />  this.code = code;   <br /> }   <br /> public void setOpset(Set operation_set) {   <br />  this.operation_set = operation_set;   <br /> }   <br /> public void setOpsetName(String name) {   <br />  this.default_operation_set_name= name;   <br /> }   <br />    <br /> public void doTag() throws JspException, IOException{   <br />  //session中没有设置权限HashSet,给默认值   <br />  if (operation_set==null) {   <br />   Object o = this.getJspContext().findAttribute(default_operation_set_name);   <br />   if (o instanceof Set)   <br />    operation_set = (Set)o;   <br />  }   <br />     <br />  if (code == null || operation_set == null)   <br />   throw new JspException("标签属性无效,无法执行!");   <br />     <br />  //这里支持多个code,用','分割,有一个符合条件就输出,全部不满足则不输出(注意不能有空格,区分大小写)   <br />  String[] codes = code.split(",");   <br />  for (String s : codes) {   <br />   if (operation_set.contains(s)) {   <br />    this.getJspBody().invoke(this.getJspContext().getOut());   <br />    return;   <br />   }   <br />  }   <br /> }   <br />}  </p>
		<p>import javax.servlet.jsp.JspException;<br />import javax.servlet.jsp.tagext.SimpleTagSupport;</p>
		<p>import java.io.IOException;<br />import java.util.*;</p>
		<p>public class OperationTag extends SimpleTagSupport {<br /> private Set operation_set;<br /> private String default_operation_set_name = "ops";<br /> private String code;<br /> <br /> public void setCode(String code) {<br />  this.code = code;<br /> }<br /> public void setOpset(Set operation_set) {<br />  this.operation_set = operation_set;<br /> }<br /> public void setOpsetName(String name) {<br />  this.default_operation_set_name= name;<br /> }<br /> <br /> public void doTag() throws JspException, IOException{<br />  //session中没有设置权限HashSet,给默认值<br />  if (operation_set==null) {<br />   Object o = this.getJspContext().findAttribute(default_operation_set_name);<br />   if (o instanceof Set)<br />    operation_set = (Set)o;<br />  }<br />  <br />  if (code == null || operation_set == null)<br />   throw new JspException("标签属性无效,无法执行!");<br />  <br />  //这里支持多个code,用','分割,有一个符合条件就输出,全部不满足则不输出(注意不能有空格,区分大小写)<br />  String[] codes = code.split(",");<br />  for (String s : codes) {<br />   if (operation_set.contains(s)) {<br />    this.getJspBody().invoke(this.getJspContext().getOut());<br />    return;<br />   }<br />  }<br /> }<br />}<br /> </p>
		<p> </p>
		<p>底层查找权限接口：</p>
		<p>OperationService.java</p>
		<p>Java代码 <br />public java.util.HashSet&lt;String&gt; findByUserId(String userId) throws Exception;   <br />实现接口类：(//通过USERID找到对应的operation的code)   <br />  <br />OperationServiceImpImp.java   <br />  <br /> public java.util.HashSet&lt;String&gt; findByUserId(String userId) throws Exception{   <br />  Session s = getSession();   <br />  Transaction tx = s.beginTransaction();   <br />     <br />  String sql = "select DISTINCT o.code from users u " +   <br />      "inner join groupmember gm on u.userId=gm.user_Id " +    <br />      "inner join groupacl ga on gm.group_id=ga.group_id " +   <br />      "inner join operation o on ga.op_id = o.id " +   <br />      "where u.userId=?";   <br />  Query q = s.createSQLQuery(sql).setString(0,userId);   <br />  List&lt;Object&gt; ls = q.list();   <br />  HashSet ops = new HashSet();   <br />  for(Object object : ls){   <br />   ops.add(object);   <br />  }   <br />  tx.commit();   <br />  releaseSession(s);   <br />     <br />  return ops;   <br /> }  </p>
		<p>public java.util.HashSet&lt;String&gt; findByUserId(String userId) throws Exception;<br />实现接口类：(//通过USERID找到对应的operation的code)</p>
		<p>OperationServiceImpImp.java</p>
		<p> public java.util.HashSet&lt;String&gt; findByUserId(String userId) throws Exception{<br />  Session s = getSession();<br />  Transaction tx = s.beginTransaction();<br />  <br />  String sql = "select DISTINCT o.code from users u " +<br />      "inner join groupmember gm on u.userId=gm.user_Id " + <br />      "inner join groupacl ga on gm.group_id=ga.group_id " +<br />      "inner join operation o on ga.op_id = o.id " +<br />      "where u.userId=?";<br />  Query q = s.createSQLQuery(sql).setString(0,userId);<br />  List&lt;Object&gt; ls = q.list();<br />  HashSet ops = new HashSet();<br />  for(Object object : ls){<br />   ops.add(object);<br />  }<br />  tx.commit();<br />  releaseSession(s);<br />  <br />  return ops;<br /> } </p>
		<p>这样，在用户登录时，可以把该用户的权限HashSet装载到Session中</p>
		<p> </p>
		<p>//把当前用户的权限添加到HashSet<br />   </p>
		<p>Java代码 <br />HashSet ops = AppResource.operationService.findByUserId(user.getUserId());   <br />session.setAttribute("ops",ops);  </p>
		<p>HashSet ops = AppResource.operationService.findByUserId(user.getUserId());<br />session.setAttribute("ops",ops); </p>
		<p>最后，在JSP中就可以简单的使用标签来判断有没有某个权限，没有则不显示</p>
		<p>Xml代码 <br />&lt;%@ taglib prefix="psy" uri="<a href="http://www.psychcn.com/taglibs">http://www.psychcn.com/taglibs</a>" %&gt;    <br />&lt;psy:op code="Finance_Payment"&gt;看你有没有权限让我显示&lt;/psy:op&gt;  </p>
		<p>&lt;%@ taglib prefix="psy" uri="<a href="http://www.psychcn.com/taglibs">http://www.psychcn.com/taglibs</a>" %&gt; <br />&lt;psy:op code="Finance_Payment"&gt;看你有没有权限让我显示&lt;/psy:op&gt;</p>
		<p>  </p>
		<p>OK！可以根据需要修改。<br /></p>
<img src ="http://www.blogjava.net/balajinima/aggbug/249745.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2009-01-04 15:40 <a href="http://www.blogjava.net/balajinima/articles/249745.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2EE框架（struts+hibernate+spring）</title><link>http://www.blogjava.net/balajinima/articles/218466.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Tue, 29 Jul 2008 08:28:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/218466.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/218466.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/218466.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/218466.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/218466.html</trackback:ping><description><![CDATA[
		<p>框架说白了就是JAVA工作者多年以来总结出的一些开发标准。让我们可以以成功的经验模式来开发我们自已的系统，一般使用框架的好处是 <br />·在好的框架下，开发者只需要写一些必须的代码；他们不需要直接接触底层的API。 这一点很重要。 <br /><br />·经过良好设计的框架可以为程序提供清晰的结构并且提高程序的内聚性。好清晰的结构使得其他人可以更容易加入项目。 <br /><br />·一个容易使用的框架可以通过一些例子和文档为用户提供最佳实践。 <br /><br />·采用成功的框架的代码比自己的代码容易测试 <br /><br />J2EE本身提供了一些框架。比如， Enterprise Java-Beans (EJB) container或者 Servlet engine 而这些框架一般在中小工程中我们都不会使用，会让我们把大量的时间浪费在开发框架上。 <br />而现在比较流行开源框架,主要是struts,hibernate,spring等 <br />比如struts是在原有mvc基础上实现在代码分离等功能，非常好用。 <br />而hibernate可以把我们的关系型数据库转换成我们在JAVA中的面像对像来使用。从而让我们在开发时不需要直接写SQL语句，比如database.getName();就可以直接把数据库中的用户名取出来。 <br />Spring <br />J2EE框架被大规模地运用到项目中，而项目总要负责这些框架以及自己业务代码的连接，使之真正融合到一起。Spring就是专注于这个问题的，它和Hibernate融合的很好。 <br />这三种框架在一起并不冲突，所以现在最常用的框架就是 <br />struts+hibernate+spring就像我们盖房子一样，先把框架搭好，我们在在上面写代码就很规范。 <br /><br /><br />Struts框架介绍 :<br /><br />Struts只是一个MVC框架（Framework）,用于快速开发Java Web应用。Struts实现的重点在C(Controller)，包括ActionServlet/RequestProcessor和我们定制的 Action,也为V(View)提供了一系列定制标签（Custom Tag）。但Struts几乎没有涉及M(Model),所以Struts可以采用JAVA实现的任何形式的商业逻辑。 <br />Spring是一个轻型容器(light-weight container)，其核心是Bean工厂(Bean Factory)，用以构造我们所需要的M(Model)。在此基础之上，Spring提供了AOP（Aspect-Oriented Programming, 面向层面的编程）的实现，用它来提供非管理环境下申明方式的事务、安全等服务；对Bean工厂的扩展ApplicationContext更加方便我们实 现J2EE的应用；DAO/ORM的实现方便我们进行数据库的开发；Web MVC和Spring Web提供了Java Web应用的框架或与其他流行的Web框架进行集成。 <br />就是说可将两者一起使用，达到将两者自身的特点进行互补。<br /><br /><br />spring 框架介绍 :<br /><br /><br />它关注的领域是其他许多流行的Framework未曾关注的。Spring要提供的是一种管理你的业务对象的方法。 <br /><br />Spring既是全面的又是模块化的。Spring有分层的体系结构，这意味着你能选择仅仅使用它任何一个独立的部分，而它的架构又是内部一致。 因此你能从你的学习中，得到最大的价值。例如，你可能选择仅仅使用Spring来简单化JDBC的使用，或用来管理所有的业务对象。 <br /><br />它的设计从一开始就是要帮助你编写易于测试的代码。Spring是使用测试驱动开发的工程的理想框架。 <br /><br /><br />Spring不会给你的工程添加对其他的框架依赖。Spring也许称得上是个一站式解决方案，提供了一个典型应用所需要的大部分基础架构。它还涉及到了其他framework没有考虑到的内容。 <br /><br />尽管它仅仅是一个从2003年2月才开始的开源项目，但Spring有深厚的历史根基。</p>
		<p>
				<br />Spring架构上的好处 <br /><br />在我们进入细节之前，让我们来看看Spring能够给工程带来的种种好处：</p>
		<p>Spring能有效地组织你的中间层对象，不管你是否选择使用了EJB。如果你仅仅使用了Struts或其他为J2EE的 API特制的framework，Spring致力于解决剩下的问题。 <br /><br />Spring能消除在许多工程中常见的对Singleton的过多使用。根据我的经验，这是一个很大的问题，它降低了系统的可测试性和面向对象的程度。 <br /><br />通过一种在不同应用程序和项目间一致的方法来处理配置文件，Spring能消除各种各样自定义格式的属性文件的需要。曾经对某个类要寻找的是哪个 魔法般的属性项或系统属性感到不解，为此不得不去读Javadoc甚至源编码？有了Spring，你仅仅需要看看类的JavaBean属性。 Inversion of Control的使用（在下面讨论）帮助完成了这种简化。 <br /><br />通过把对接口编程而不是对类编程的代价几乎减少到没有，Spring能够促进养成好的编程习惯。 <br /><br />Spring被设计为让使用它创建的应用尽可能少的依赖于他的APIs。在Spring应用中的大多数业务对象没有依赖于Spring。 <br /><br />使用Spring构建的应用程序易于单元测试。 <br /><br />Spring能使EJB的使用成为一个实现选择,而不是应用架构的必然选择。你能选择用POJOs或local EJBs来实现业务接口，却不会影响调用代码。 <br /><br />Spring帮助你解决许多问题而无需使用EJB。Spring能提供一种EJB的替换物，它们适用于许多web应用。例如，Spring能使用AOP提供声明性事务管理而不通过EJB容器，如果你仅仅需要与单个数据库打交道，甚至不需要一个JTA实现。 <br /><br />Spring为数据存取提供了一个一致的框架,不论是使用的是JDBC还是O/R mapping产品（如Hibernate）。</p>
<img src ="http://www.blogjava.net/balajinima/aggbug/218466.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2008-07-29 16:28 <a href="http://www.blogjava.net/balajinima/articles/218466.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用filter过滤请求</title><link>http://www.blogjava.net/balajinima/articles/218464.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Tue, 29 Jul 2008 08:26:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/218464.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/218464.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/218464.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/218464.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/218464.html</trackback:ping><description><![CDATA[
		<div class="cnt" id="blog_text">
				<p>package anni;<br />public class EncodingFilter implements Filter {<br />    public void init(FilterConfig config) throws ServletException {}<br />    public void destroy() {}<br />    public void doFilter(ServletRequest request,<br />            ServletResponse response,<br />            FilterChain chain)<br />            throws IOException, ServletException {<br />        request.setCharacterEncoding("gb2312");<br />        chain.doFilter(request, response);<br />    }<br />}</p>
				<p>web.xml中<br />&lt;filter&gt;<br />    &lt;filter-name&gt;EncodingFilter&lt;/filter-name&gt;<br />    &lt;filter-class&gt;anni.EncodingFilter&lt;/filter-class&gt;<br />&lt;/filter&gt;<br />&lt;filter-mapping&gt;<br />    &lt;filter-name&gt;EncodingFilter&lt;/filter-name&gt;<br />    &lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br />&lt;/filter-mapping&gt;</p>
				<p>filter标签部分定义使用的过滤器，filter-mapping标签告诉服务器把哪些请求交给过滤器处理。这里的/*表示所有请求，/表示根路径，*（星号）代表所有请求，加在一起就变成了根路径下的所有请求。这样，所有的请求都会先被EncodingFilter拦截，并在请求里设置上指定的gb2312编码。</p>
				<p>================================<br />用filter控制用户访问权限</p>
				<p>我们要保护的页面是admin/index.jsp，为此我们在web.xml进行如下配置。<br />&lt;filter&gt;<br />    &lt;filter-name&gt;SecurityFilter&lt;/filter-name&gt;<br />    &lt;filter-class&gt;anni.SecurityFilter&lt;/filter-class&gt;<br />&lt;/filter&gt;<br />&lt;filter-mapping&gt;<br />    &lt;filter-name&gt;SecurityFilter&lt;/filter-name&gt;<br />    &lt;url-pattern&gt;/admin/*&lt;/url-pattern&gt;<br />&lt;/filter-mapping&gt;</p>
				<p>SecurityFilter过滤器:<br />public void doFilter(ServletRequest request,<br />        ServletResponse response,<br />        FilterChain chain)<br />        throws IOException, ServletException {<br />    HttpServletRequest req = (HttpServletRequest) request;<br />    HttpServletResponse res = (HttpServletResponse) response;<br />   HttpSession session = req.getSession();<br />    if (session.getAttribute("username") != null) {<br />        chain.doFilter(request, response);<br />    } else {<br />        res.sendRedirect("../failure.jsp");<br />    }<br />}</p>
				<p>首先要将ServletRequest和ServletResponse转换成HttpServletRequest和HttpServletResponse，因为Filter本来设计成为多种协议服务，http协议仅仅是其中一部分。不过我们接触到的也只有http，而且也只有转换成对应HttpServletRequest和HttpServletResponse才能进行下面的session操作和页面重定向。<br />得到了http请求之后，可以获得请求对应的session，判断session中的username变量是否为null，如果不为null，说明用户已经登录，就可以调用doFilter继续请求访问的资源。如果为null，说明用户还没有登录，禁止用户访问，并使用页面重定向跳转到failure.jsp页面显示提示信息。</p>
				<p>==================================<br />filter所谓的特性</p>
				<p>请求映射<br />filter-mapping和servlet-mapping都是将对应的filter或servlet映射到某个url-pattern上，当客户发起某一请求时，服务器先将此请求与web.xml中定义的所有url-pattern进行匹配，然后执行匹配通过的filter和servlet。</p>
				<p>你可以使用三种方式定义url-pattern。<br />1.直接映射一个请求。<br />&lt;servlet-mapping&gt;<br />    &lt;servlet-name&gt;ContactServlet&lt;/servlet-name&gt;<br />    &lt;url-pattern&gt;/contact.do&lt;/url-pattern&gt;<br />&lt;/servlet-mapping&gt;<br />                    <br />2.映射一个路径下的所有请求。<br />&lt;servlet-mapping&gt;<br />    &lt;servlet-name&gt;EncodingFilter&lt;/servlet-name&gt;<br />    &lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br />&lt;/servlet-mapping&gt;</p>
				<p>3.映射结尾相同的一类请求。<br />&lt;servlet-mapping&gt;<br />    &lt;servlet-name&gt;ControllerServlet&lt;/servlet-name&gt;<br />    &lt;url-pattern&gt;*.do&lt;/url-pattern&gt;<br />&lt;/servlet-mapping&gt;</p>
				<p>想要获得所有以user开头.do结尾的请求吗？user*.do在url-pattern是无法识别的，只能配置成*.do，再去servlet中对请求进行筛选。<br />想要让一个servlet负责多个请求吗？/user/*,/admin/*,*.do写在一起url-pattern也不认识，只能配成多个servlet-mapping。<br />&lt;servlet-mapping&gt;<br />    &lt;servlet-name&gt;ControllerServlet&lt;/servlet-name&gt;<br />    &lt;url-pattern&gt;/user/*&lt;/url-pattern&gt;<br />&lt;/servlet-mapping&gt;<br />&lt;servlet-mapping&gt;<br />    &lt;servlet-name&gt;ControllerServlet&lt;/servlet-name&gt;<br />    &lt;url-pattern&gt;/admin/*&lt;/url-pattern&gt;<br />&lt;/servlet-mapping&gt;<br />&lt;servlet-mapping&gt;<br />    &lt;servlet-name&gt;ControllerServlet&lt;/servlet-name&gt;<br />    &lt;url-pattern&gt;*.do&lt;/url-pattern&gt;<br />&lt;/servlet-mapping&gt;</p>
				<p>过滤链</p>
				<p>
				</p>
				<div forimg="1">
						<img class="blogimg" src="http://hiphotos.baidu.com/cathy%5F200182/pic/item/42cfee0f5f637dfbaa64576a.jpg" border="0" small="0" />
				</div>
				<p>服务器会按照web.xml中过滤器定义的先后循序组装成一条链，然后一次执行其中的doFilter()方法。执行的顺序就如上图所示，执行第一个过滤器的chain.doFilter()之前的代码，第二个过滤器的chain.doFilter()之前的代码，请求的资源，第二个过滤器的chain.doFilter()之后的代码，第一个过滤器的chain.doFilter()之后的代码，最后返回响应。<br />过滤链的好处是，执行过程中任何时候都可以打断，只要不执行chain.doFilter()就不会再执行后面的过滤器和请求的内容。而在实际使用时，就要特别注意过滤链的执行顺序问题，像EncodingFilter就一定要放在所有Filter之前，这样才能确保在使用请求中的数据前设置正确的编码。</p>
				<p>filter的详细配置</p>
				<p>在servlet-2.3中，Filter会过滤一切请求，包括服务器内部使用forward转发请求和&lt;%@ include file="/index.jsp"%&gt;的情况。<br />到了servlet-2.4中Filter默认下只拦截外部提交的请求，forward和include这些内部转发都不会被过滤，但是有时候我们需要forward的时候也用到Filter，这样就需要如下配置。<br />&lt;filter&gt;<br />    &lt;filter-name&gt;TestFilter&lt;/filtername&gt;<br />    &lt;filter-class&gt;anni.TestFilter&lt;/filter-class&gt;<br />&lt;/filter&gt;<br />&lt;filter-mapping&gt;<br />    &lt;filter-name&gt;TestFilter&lt;/filtername&gt;<br />    &lt;url-pattern&gt;/*&lt;/url-pattern&gt;<br />    &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;<br />    &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt;<br />    &lt;dispatcher&gt;INCLUDE&lt;/dispatcher&gt;<br />    &lt;dispatcher&gt;EXCEPTION&lt;/dispatcher&gt;<br />&lt;/filter-mapping&gt;<br />        <br />这样TestFilter就会过滤所有状态下的请求。如果我们没有进行设置，默认使用的就是REQUEST。而EXCEPTION是在isErrorPage="true"的情况下出现的，这个用处不多，看一下即可。<br />这里FORWARD是解决request.getDispatcher("index.jsp").forward(request, response);无法触发Filter的关键，配置上这个以后再进行forward的时候就可以触发过滤器了。</p>
				<p>Filter还有一个有趣的用法，在filter-mapping中我们可以直接指定servlet-mapping，让过滤器只处理一个定义在web.xml中的servlet。<br />&lt;filter-mapping&gt;<br />    &lt;filter-name&gt;TestFilter&lt;/filter-name&gt;<br />    &lt;servlet-name&gt;TestServlet&lt;/servlet-name&gt;<br />&lt;/filter-mapping&gt;<br />&lt;servlet&gt;<br />    &lt;servlet-name&gt;TestServlet&lt;/servlet-name&gt;<br />    &lt;servlet-class&gt;anni.TestServlet&lt;/servlet-class&gt;<br />&lt;/servlet&gt;<br />&lt;servlet-mapping&gt;<br />    &lt;servlet-name&gt;TestServlet&lt;/servlet-name&gt;<br />    &lt;url-pattern&gt;/TestServlet&lt;/url-pattern&gt;<br />&lt;/servlet-mapping&gt;<br />       <br />直接指定servlet-name，TestFilter便会引用TestServlet配置的url-pattern，在某些filter与servlet绑定的情况下不失为一个好办法。</p>
		</div>
<img src ="http://www.blogjava.net/balajinima/aggbug/218464.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2008-07-29 16:26 <a href="http://www.blogjava.net/balajinima/articles/218464.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>web.xml中--使用监听器Servlet</title><link>http://www.blogjava.net/balajinima/articles/218462.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Tue, 29 Jul 2008 08:20:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/218462.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/218462.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/218462.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/218462.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/218462.html</trackback:ping><description><![CDATA[
		<p>监听器概述    <br />   <br />1.Listener是Servlet的监听器     <br />2.可以监听客户端的请求、服务端的操作等。    <br />3.通过监听器，可以自动激发一些操作，如监听在线用户数量,当增加一个HttpSession时，给在线人数加1。    <br />4.编写监听器需要实现相应的接口    <br />5.编写完成后在web.xml文件中配置一下,就可以起作用了    <br />6.可以在不修改现有系统基础上,增加web应用程序生命周期事件的跟踪    <br />   <br />   <br />常用的监听接口    <br />   <br />1.ServletContextAttributeListener    <br />监听对ServletContext属性的操作，比如增加/删除/修改    <br />2.ServletContextListener    <br />监听ServletContext,当创建ServletContext时，激发contextInitialized(ServletContextEvent sce)方法；当销毁ServletContext时，激发contextDestroyed(ServletContextEvent sce)方法。    <br />3.HttpSessionListener    <br />监听HttpSession的操作。当创建一个Session时，激发session Created(SessionEvent se)方法；当销毁一个Session时，激发sessionDestroyed (HttpSessionEvent se)方法。    <br />4.HttpSessionAttributeListener    <br />监听HttpSession中的属性的操作。当在Session增加一个属性时，激发attributeAdded(HttpSessionBindingEvent se) 方法；当在Session删除一个属性时，激发attributeRemoved(HttpSessionBindingEvent se)方法；当在Session属性被重新设置时，激发attributeReplaced(HttpSessionBindingEvent se) 方法。    <br />   <br />使用范例：    <br />由监听器管理共享数据库连接    <br />   <br />生命周期事件的一个实际应用由context监听器管理共享数据库连接。在web.xml中如下定义监听器：    <br />&lt;listener&gt;    <br />    &lt;listener-class&gt;XXX.MyConnectionManager&lt;/listener-class&gt;    <br />&lt;/listener&gt; ?server创建监听器的实例,接受事件并自动判断实现监听器接口的类型。要记住的是由于监听器是配置在部署描述符web.xml中，所以不需要改变任何代码就可以添加新的监听器。    <br />   <br />public class MyConnectionManager implements ServletContextListener{      <br />public void contextInitialized(ServletContextEvent e) {     <br />        Connection con = // create connection     <br />        e.getServletContext().setAttribute("con", con);     <br />    }      <br />   public void contextDestroyed(ServletContextEvent e) {     <br />        Connection con = (Connection) e.getServletContext().getAttribute("con");     <br />        try {    <br />          con.close();     <br />        }     <br />       catch (SQLException ignored) { } // close connection     <br />    }     <br />}      <br />监听器保证每新生成一个servlet context都会有一个可用的数据库连接,并且所有的连接对会在context关闭的时候随之关闭。     <br />   <br />在web.xml中加入:    <br />&lt;listener&gt;&lt;listener-class&gt;servletlistener111111.SecondListener&lt;/listener-class&gt; &lt;/listener&gt;</p>
		<p>==================================================</p>
		<p>关于用户超时的例子：</p>
		<p>public class OnlineUserListener implements HttpSessionListener {<br />    public void sessionCreated(HttpSessionEvent event) {<br />    }<br />    public void sessionDestroyed(HttpSessionEvent event) {<br />        HttpSession session = event.getSession();<br />        ServletContext application = session.getServletContext();<br />        // 取得登录的用户名<br />        String username = (String) session.getAttribute("username");<br />        // 从在线列表中删除用户名<br />        List onlineUserList = (List) application.getAttribute("onlineUserList");<br />        onlineUserList.remove(username);<br />        System.out.println(username + "超时退出。");<br />    }<br />}</p>
		<p>以下两种情况下就会发生sessionDestoryed（会话销毁）事件：</p>
		<p>1.执行session.invalidate()方法时。例如：request.getSession().invalidate();</p>
		<p>2.如果用户长时间没有访问服务器，超过了会话最大超时时间，服务器就会自动销毁超时的session。会话超时时间可以在web.xml中进行设置。</p>
		<p>========================================</p>
		<p>使用HttpSessionBindingListener</p>
		<p>HttpSessionBindingListener虽然叫做监听器，但使用方法与HttpSessionListener完全不同。我们实际看一下它是如何使用的。</p>
		<p>我们的OnlineUserBindingListener实现了HttpSessionBindingListener接口，接口中共定义了两个方法：valueBound()和valueUnbound()，分别对应数据绑定，和取消绑定两个事件。</p>
		<p>所谓对session进行数据绑定，就是调用session.setAttribute()把HttpSessionBindingListener保存进session中。我们在LoginServlet.java中进行这一步。</p>
		<p>// 把用户名放入在线列表<br />session.setAttribute("onlineUserBindingListener", new OnlineUserBindingListener(username));<br />        <br />这就是HttpSessionBindingListener和HttpSessionListener之间的最大区别：HttpSessionListener只需要设置到web.xml中就可以监听整个应用中的所有session。HttpSessionBindingListener必须实例化后放入某一个session中，才可以进行监听。</p>
		<p>从监听范围上比较，HttpSessionListener设置一次就可以监听所有session，HttpSessionBindingListener通常都是一对一的。</p>
		<p>正是这种区别成就了HttpSessionBindingListener的优势，我们可以让每个listener对应一个username，这样就不需要每次再去session中读取username，进一步可以将所有操作在线列表的代码都移入listener，更容易维护。</p>
		<p>valueBound()方法的代码如下：</p>
		<p>public void valueBound(HttpSessionBindingEvent event) {<br />    HttpSession session = event.getSession();<br />    ServletContext application = session.getServletContext();</p>
		<p>    // 把用户名放入在线列表<br />    List onlineUserList = (List) application.getAttribute("onlineUserList");<br />    // 第一次使用前，需要初始化<br />    if (onlineUserList == null) {<br />        onlineUserList = new ArrayList();<br />        application.setAttribute("onlineUserList", onlineUserList);<br />    }<br />    onlineUserList.add(this.username);<br />}<br />        <br />username已经通过构造方法传递给listener，在数据绑定时，可以直接把它放入用户列表。</p>
		<p>与之对应的valueUnbound()方法，代码如下：</p>
		<p>public void valueUnbound(HttpSessionBindingEvent event) {<br />    HttpSession session = event.getSession();<br />    ServletContext application = session.getServletContext();</p>
		<p>    // 从在线列表中删除用户名<br />    List onlineUserList = (List) application.getAttribute("onlineUserList");<br />    onlineUserList.remove(this.username);</p>
		<p>    System.out.println(this.username + "退出。");<br />}<br />        <br />这里可以直接使用listener的username操作在线列表，不必再去担心session中是否存在username。</p>
		<p>valueUnbound的触发条件是以下三种情况：</p>
		<p>1.执行session.invalidate()时。</p>
		<p>2.session超时，自动销毁时。</p>
		<p>3.执行session.setAttribute("onlineUserListener", "其他对象");或session.removeAttribute("onlineUserListener");将listener从session中删除时。</p>
		<p>因此，只要不将listener从session中删除，就可以监听到session的销毁。</p>
<img src ="http://www.blogjava.net/balajinima/aggbug/218462.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2008-07-29 16:20 <a href="http://www.blogjava.net/balajinima/articles/218462.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jsp获得在线用户</title><link>http://www.blogjava.net/balajinima/articles/218152.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Mon, 28 Jul 2008 10:51:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/218152.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/218152.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/218152.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/218152.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/218152.html</trackback:ping><description><![CDATA[
		<div class="tit"> </div>
		<div class="date"> </div>
		<table style="TABLE-LAYOUT: fixed">
				<tbody>
						<tr>
								<td>
										<div class="cnt" id="blog_text">用HttpSessionListener 。 <br /><br />package demo.listener; <br /><br />import javax.servlet.ServletContext; <br />import javax.servlet.http.HttpSessionEvent; <br />import javax.servlet.http.HttpSessionListener; <br /><br />public class SessionCounter implements HttpSessionListener { <br />     public void sessionCreated(HttpSessionEvent event) { <br />         ServletContext ctx = event.getSession( ).getServletContext( ); <br />         Integer numSessions = (Integer) ctx.getAttribute("numSessions"); <br />         if (numSessions == null) { <br />             numSessions = new Integer(1); <br />         } <br />         else { <br />             int count = numSessions.intValue( ); <br />             numSessions = new Integer(count + 1); <br />         } <br />         ctx.setAttribute("numSessions", numSessions); <br />     } <br />     public void sessionDestroyed(HttpSessionEvent event) { <br />         ServletContext ctx = event.getSession( ).getServletContext( ); <br />         Integer numSessions = (Integer) ctx.getAttribute("numSessions"); <br />         if (numSessions == null) { <br />             numSessions = new Integer(0); <br />         } <br />         else { <br />             int count = numSessions.intValue( ); <br />             numSessions = new Integer(count - 1); <br />         } <br />         ctx.setAttribute("numSessions", numSessions); <br />     } <br />} <br /><br />在这个解决方案中，任何一个Session被创建或者销毁时，都会通知SessionCounter 这个类，当然通知的原因是必须在web.xml文件中做相关的配置工作。如下面的配置代码： <br /><br />&lt;?xml version="1.0" encoding="ISO-8859-1" ?&gt; <br />&lt;!DOCTYPE web-app PUBLIC <br />     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" <br />     "http://java.sun.com/dtd/web-app_2_3.dtd"&gt; <br />     <br />&lt;web-app&gt; <br />   &lt;display-name&gt;Struts Examples&lt;/display-name&gt; <br />   <br />   &lt;listener&gt; <br />       &lt;listener-class&gt;demo.listener.SessionCounter <br />       &lt;/listener-class&gt; <br />   &lt;/listener&gt; </div>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/balajinima/aggbug/218152.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2008-07-28 18:51 <a href="http://www.blogjava.net/balajinima/articles/218152.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>execute、executeQuery和executeUpdate之间的区别(转)</title><link>http://www.blogjava.net/balajinima/articles/215428.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Thu, 17 Jul 2008 03:31:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/215428.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/215428.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/215428.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/215428.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/215428.html</trackback:ping><description><![CDATA[在用纯JSP做一个页面报警功能的时候习惯性的用executeQuery来执行SQL语句，结果执行update时就遇到问题，语句能执行，但返回结果出现问题，另外还忽略了executeUpdate的返回值不是结果集ResultSet,而是数值！特收藏如下一篇文章（感谢网友们对各种信息的贡献）： <br />　　 <br />　　JDBCTM中Statement接口提供的execute、executeQuery和executeUpdate之间的区别 <br />　　 <br />　　 <br />　　 Statement 接口提供了三种执行 SQL 语句的方法：executeQuery、executeUpdate 和 execute。使用哪一个方法由 SQL 语句所产生的内容决定。 <br />　　 <br />　　 方法executeQuery <br />　　 用于产生单个结果集的语句，例如 SELECT 语句。 被使用最多的执行 SQL 语句的方法是 executeQuery。这个方法被用来执行 SELECT 语句，它几乎是使用最多的 SQL 语句。 <br />　　 <br />　　 方法executeUpdate <br />　　 用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQL DDL（数据定义语言）语句，例如 CREATE TABLE 和 DROP TABLE。INSERT、UPDATE 或 DELETE 语句的效果是修改表中零行或多行中的一列或多列。executeUpdate 的返回值是一个整数，指示受影响的行数（即更新计数）。对于 CREATE TABLE 或 DROP TABLE 等不操作行的语句，executeUpdate 的返回值总为零。 <br />　　 <br />　　 使用executeUpdate方法是因为在 createTableCoffees 中的 SQL 语句是 DDL （数据定义语言）语句。创建表，改变表，删除表都是 DDL 语句的例子，要用 executeUpdate 方法来执行。你也可以从它的名字里看出，方法 executeUpdate 也被用于执行更新表 SQL 语句。实际上，相对于创建表来说，executeUpdate 用于更新表的时间更多，因为表只需要创建一次，但经常被更新。 <br />　　 <br />　　 <br />　　 方法execute： <br />　　 用于执行返回多个结果集、多个更新计数或二者组合的语句。因为多数程序员不会需要该高级功能 <br />　　 <br />　　 execute方法应该仅在语句能返回多个ResultSet对象、多个更新计数或ResultSet对象与更新计数的组合时使用。当执行某个已存储过程 或动态执行未知 SQL 字符串（即应用程序程序员在编译时未知）时，有可能出现多个结果的情况，尽管这种情况很少见。 <br />　　 因为方法 execute 处理非常规情况，所以获取其结果需要一些特殊处理并不足为怪。例如，假定已知某个过程返回两个结果集，则在使用方法 execute 执行该过程后，必须调用方法 getResultSet 获得第一个结果集，然后调用适当的 getXXX 方法获取其中的值。要获得第二个结果集，需要先调用 getMoreResults 方法，然后再调用 getResultSet 方法。如果已知某个过程返回两个更新计数，则首先调用方法 getUpdateCount，然后调用 getMoreResults，并再次调用 getUpdateCount。 <br />　　 对于不知道返回内容，则情况更为复杂。如果结果是 ResultSet 对象，则方法 execute 返回 true；如果结果是 Java int，则返回 false。如果返回 int，则意味着结果是更新计数或执行的语句是 DDL 命令。在调用方法 execute 之后要做的第一件事情是调用 getResultSet 或 getUpdateCount。调用方法 getResultSet 可以获得两个或多个 ResultSet 对象中第一个对象；或调用方法 getUpdateCount 可以获得两个或多个更新计数中第一个更新计数的内容。 <br />　　 当 SQL 语句的结果不是结果集时，则方法 getResultSet 将返回 null。这可能意味着结果是一个更新计数或没有其它结果。在这种情况下，判断 null 真正含义的唯一方法是调用方法 getUpdateCount，它将返回一个整数。这个整数为调用语句所影响的行数；如果为 -1 则表示结果是结果集或没有结果。如果方法 getResultSet 已返回 null（表示结果不是 ResultSet 对象），则返回值 -1 表示没有其它结果。也就是说，当下列条件为真时表示没有结果（或没有其它结果）： <br />　　 <br />　　((stmt.getResultSet() == null) &amp;&amp; (stmt.getUpdateCount() == -1)) <br />　　 <br />　　 如果已经调用方法 getResultSet 并处理了它返回的 ResultSet 对象，则有必要调用方法 getMoreResults 以确定是否有其它结果集或更新计数。如果 getMoreResults 返回 true，则需要再次调用 getResultSet 来检索下一个结果集。如上所述，如果 getResultSet 返回 null，则需要调用 getUpdateCount 来检查 null 是表示结果为更新计数还是表示没有其它结果。 <br />　　 <br />　　 当 getMoreResults 返回 false 时，它表示该 SQL 语句返回一个更新计数或没有其它结果。因此需要调用方法 getUpdateCount 来检查它是哪一种情况。在这种情况下，当下列条件为真时表示没有其它结果： <br />　　 <br />　　((stmt.getMoreResults() == false) &amp;&amp; (stmt.getUpdateCount() == -1)) <br />　　 <br />　　 下面的代码演示了一种方法用来确认已访问调用方法 execute 所产生的全部结果集和更新计数： <br />　　 <br />　　 <br />　　stmt.execute(queryStringWithUnknownResults); <br />　　while (true) { <br />　　int rowCount = stmt.getUpdateCount(); <br />　　if (rowCount &gt; 0) { // 它是更新计数 <br />　　System.out.println("Rows changed = " + count); <br />　　stmt.getMoreResults(); <br />　　continue; <br />　　} <br />　　if (rowCount == 0) { // DDL 命令或 0 个更新 <br />　　System.out.println(" No rows changed or statement was DDL <br />　　command"); <br />　　stmt.getMoreResults(); <br />　　continue; <br />　　} <br />　　 <br />　　// 执行到这里，证明有一个结果集 <br />　　// 或没有其它结果 <br />　　 <br />　　ResultSet rs = stmt.getResultSet; <br />　　if (rs != null) { <br />　　. . . // 使用元数据获得关于结果集列的信息 <br />　　while (rs.next()) { <br />　　. . . // 处理结果 <br />　　stmt.getMoreResults(); <br />　　continue; <br />　　} <br />　　break; // 没有其它结果 <br />　　<img src ="http://www.blogjava.net/balajinima/aggbug/215428.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2008-07-17 11:31 <a href="http://www.blogjava.net/balajinima/articles/215428.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>通过Map取提交的表单域值</title><link>http://www.blogjava.net/balajinima/articles/181106.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Thu, 21 Feb 2008 09:27:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/181106.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/181106.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/181106.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/181106.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/181106.html</trackback:ping><description><![CDATA[
		<p>import java.util.Enumeration;<br />import java.util.HashMap;<br />import java.util.Map;</p>
		<p>import javax.servlet.http.HttpServletRequest;</p>
		<p>import com.mdcl.mocha.fe.developer.template.util.TemplateUtil;</p>
		<p>public class ParamUtil {</p>
		<p> /**<br />  * 把request取到的参数放到一个Map中<br />  * @param request<br />  * @return<br />  */<br /> public static Map parseRequest(HttpServletRequest request){<br />  Map map = new HashMap();<br />  String key = null;<br />  String value = null;<br />        for(Enumeration enumeration = request.getParameterNames(); enumeration.hasMoreElements(); map.put(key, value))<br />        {<br />            key = (String)enumeration.nextElement();<br />            String as[] = request.getParameterValues(key);<br />            value = as[0];<br />            for(int i = 1; i &lt; as.length; i++)<br />             value = value + "," +as[i];</p>
		<p>        }<br />  return map;<br /> }<br />}<br /></p>
<img src ="http://www.blogjava.net/balajinima/aggbug/181106.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2008-02-21 17:27 <a href="http://www.blogjava.net/balajinima/articles/181106.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jsp查看session信息</title><link>http://www.blogjava.net/balajinima/articles/165409.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Wed, 05 Dec 2007 02:29:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/165409.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/165409.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/165409.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/165409.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/165409.html</trackback:ping><description><![CDATA[
		<p>&lt;%@ page language="java" contentType="text/html; charset=GBK"<br /> import="java.util.*" pageEncoding="GBK"%&gt;<br />&lt;!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "<a href="http://www.w3.org/TR/html4/loose.dtd">http://www.w3.org/TR/html4/loose.dtd</a>"&gt;<br />&lt;html&gt;<br />&lt;head&gt;<br />&lt;meta http-equiv="Content-Type" content="text/html; charset=GBK"&gt;<br />&lt;title&gt;Insert title here&lt;/title&gt;<br />&lt;/head&gt;<br />&lt;body&gt;<br />&lt;%<br />out.print("&lt;br&gt;"+"session is new:"+session.isNew());</p>
		<p>Date created = new Date(session.getCreationTime());<br />//得到session对象创建的时间<br />Date accessed = new Date(session.getLastAccessedTime());<br />//得到最后访问该session对象的时间<br />out.println("&lt;br&gt;"+"ID " + session.getId()+" ");<br />//得到该session的id，并打印<br />out.println("&lt;br&gt;"+"Created: " + created+" ");<br />//打印session创建时间<br />out.println("&lt;br&gt;"+"Last Accessed: " + accessed+" ");<br />//打印最后访问时间</p>
		<p>session.setAttribute("Name","Tom");<br />//在session中添加变量Name=Tom<br />session.setAttribute("UID","12345678");<br />//在session中添加变量UID=12345678</p>
		<p>Enumeration e = session.getAttributeNames();<br />//得到session中变量名的枚举对象<br />while (e.hasMoreElements()) { //遍历每一个变量<br />String name = (String)e.nextElement(); //首先得到名字<br />String value = session.getAttribute(name).toString();<br />//由名字从session中得到值<br />out.println("&lt;br&gt;"+name + " = " + value+" "); //打印<br />}</p>
		<p>%&gt;<br />&lt;/body&gt;<br />&lt;/html&gt;<br /></p>
<img src ="http://www.blogjava.net/balajinima/aggbug/165409.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2007-12-05 10:29 <a href="http://www.blogjava.net/balajinima/articles/165409.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何把两个rs结果集中的内容合并到一个结果集中</title><link>http://www.blogjava.net/balajinima/articles/157690.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Fri, 02 Nov 2007 02:55:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/157690.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/157690.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/157690.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/157690.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/157690.html</trackback:ping><description><![CDATA[
		<div class="cnt">假设你的二个数据集分别是：   <br />   rs1    和    rs2   <br />    <br />                   List    list    =    new    ArrayList();   <br />                   list.add(rs1);   <br />                   list.add(rs2);   <br />                   try    {   <br />                           for    (int    i    =    0;    i    &lt;    list.size();    i++)    {   <br />                                   while    (((ResultSet)    list.get(i)).next())    {   <br />                                           //在这里取数据               <br />                                   }   <br />                           }   <br />                   }    catch    (SQLException    e)    {   <br />                           e.printStackTrace();       <br />                   }   <br />           }   <br />    <br />   不管你有多少个数据集，都放到那个list里就可以了。</div>
<img src ="http://www.blogjava.net/balajinima/aggbug/157690.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2007-11-02 10:55 <a href="http://www.blogjava.net/balajinima/articles/157690.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JSP上传图片并生成缩略图</title><link>http://www.blogjava.net/balajinima/articles/157685.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Fri, 02 Nov 2007 02:48:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/157685.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/157685.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/157685.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/157685.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/157685.html</trackback:ping><description><![CDATA[本例子使用了jspsmart组件进行上传，这里可以免费下载该组件www.jspsmart.com<br />下载解压后，将jar包复制到　\WEB-INF\lib　目录后重启服务器，jspsmart即可正常使用了<br /><br />1、uploadimage.jsp<br /><br />&lt;%@ page contentType="text/html;charset=gb2312" language="java" import="java.io.*,java.awt.Image,java.awt.image.*,com.sun.image.codec.jpeg.*,<br />java.sql.*,com.jspsmart.upload.*,java.util.*,cn.oof.database.*,cn.oof.house.*"%&gt;<br />&lt;%<br />SmartUpload mySmartUpload =new SmartUpload();<br />long file_size_max=4000000;<br />String fileName2="",ext="",testvar="";<br />String url="uploadfile/images/";       //应保证在根目录中有此目录的存在<br />//初始化<br />mySmartUpload.initialize(pageContext);<br />//只允许上载此类文件<br />try {<br />mySmartUpload.setAllowedFilesList("jpg,gif");<br />//上载文件 <br />mySmartUpload.upload();<br />} catch (Exception e){<br />%&gt;<br />   &lt;SCRIPT language=javascript&gt;<br />   alert("只允许上传.jpg和.gif类型图片文件");<br />   window.location=''upfile.jsp'';<br />   &lt;/script&gt;<br />&lt;%<br />}<br />try{<br /><br />     com.jspsmart.upload.File myFile = mySmartUpload.getFiles().getFile(0);<br />     if (myFile.isMissing()){%&gt;<br />    &lt;SCRIPT language=javascript&gt;<br />    alert("请先选择要上传的文件");<br />    window.location=''upfile.jsp'';<br />    &lt;/script&gt;<br />     &lt;%}<br />     else{<br />       //String myFileName=myFile.getFileName(); //取得上载的文件的文件名<br />    ext= myFile.getFileExt();       //取得后缀名<br />    int file_size=myFile.getSize();      //取得文件的大小  <br />    String saveurl="";<br />    if(file_size&lt;file_size_max){<br />     //更改文件名，取得当前上传时间的毫秒数值<br />     Calendar calendar = Calendar.getInstance();<br />     String filename = String.valueOf(calendar.getTimeInMillis()); <br />     saveurl=request.getRealPath("/")+url;<br />     saveurl+=filename+"."+ext;           //保存路径<br />     myFile.saveAs(saveurl,mySmartUpload.SAVE_PHYSICAL);<br />     //out.print(filename);<br />//-----------------------上传完成，开始生成缩略图-------------------------    <br />     java.io.File file = new java.io.File(saveurl);         //读入刚才上传的文件<br />     String newurl=request.getRealPath("/")+url+filename+"_min."+ext;   //新的缩略图保存地址<br />     Image src = javax.imageio.ImageIO.read(file);                      //构造Image对象<br />     float tagsize=200;<br />     int old_w=src.getWidth(null);                                      //得到源图宽<br />     int old_h=src.getHeight(null);   <br />     int new_w=0;<br />     int new_h=0;                             //得到源图长<br />     int tempsize;<br />     float tempdouble; <br />     if(old_w&gt;old_h){<br />      tempdouble=old_w/tagsize;<br />     }else{<br />      tempdouble=old_h/tagsize;<br />     }<br />     new_w=Math.round(old_w/tempdouble);<br />     new_h=Math.round(old_h/tempdouble);//计算新图长宽<br />     BufferedImage tag = new BufferedImage(new_w,new_h,BufferedImage.TYPE_INT_RGB);<br />     tag.getGraphics().drawImage(src,0,0,new_w,new_h,null);        //绘制缩小后的图<br />     FileOutputStream newimage=new FileOutputStream(newurl);           //输出到文件流<br />     JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(newimage);       <br />     encoder.encode(tag);                                                //近JPEG编码<br />      newimage.close();    <br /><br />    }<br />    else{<br />     out.print("&lt;SCRIPT language=''javascript''&gt;");<br />     out.print("alert(''上传文件大小不能超过"+(file_size_max/1000)+"K'');");<br />     out.print("window.location=''upfile.jsp;''");<br />     out.print("&lt;/SCRIPT&gt;");<br />    }<br />   }<br />}catch (Exception e){<br /><br />e.toString();<br /><br />}<br />%&gt; <br /><br />2 upload.htm<br />&lt;html&gt;<br />&lt;head&gt;<br />&lt;title&gt;请选择上传的图片&lt;/title&gt;<br />&lt;/head&gt; <br />&lt;body&gt;<br />&lt;table border="0" align="center" cellpadding="0" cellspacing="0"&gt;<br />   &lt;tr&gt;<br />     &lt;td height="45" align="center" valign="middle"&gt;&lt;form action="uploadimage.jsp" method="post" enctype="multipart/form-data" name="form1"&gt;<br />请选择上传的图片<br />     &lt;input type="file" name="file"&gt;<br />&lt;input type="submit" name="Submit" value="上传"&gt;<br />     &lt;/form&gt;&lt;/td&gt;<br />   &lt;/tr&gt;<br />&lt;/table&gt;<br />&lt;/body&gt;<br />&lt;/html&gt; <img src ="http://www.blogjava.net/balajinima/aggbug/157685.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2007-11-02 10:48 <a href="http://www.blogjava.net/balajinima/articles/157685.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于JSP实现数据库中图片的存储与显示</title><link>http://www.blogjava.net/balajinima/articles/157683.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Fri, 02 Nov 2007 02:47:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/157683.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/157683.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/157683.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/157683.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/157683.html</trackback:ping><description><![CDATA[1、引言 <br /><br />数据库应用程序，特别是基于WEB的数据库应用程序，常会涉及到图片信息的存储和显示。通常我们使用的方法是将所要显示的图片存在特定的目录下，在数据库中保存相应的图片的名称，在JSP中建立相应的数据源，利用数据库访问技术处理图片信息。但是，如果我们想动态的显示图片，上述方法就不能满足需要了。我们必须把图片存入数据库，然后通过编程动态地显示我们需要的图片。实际操作中，可以利用JSP的编程模式来实现图片的数据库存储和显示。 <br /><br />2、建立后台数据库 <br /><br />假定处理的是图片新闻，那么我们可以建立相应的数据库及数据表对象。我们要存取的数据表结构的SQL脚本如下所示： <br /><br /><br />if exists (select * from dbo.sysobjects where id = <br />object_id(N'[dbo].[picturenews]') andOBJECTPROPERTY(id, N'IsUserTable') = 1)<br />drop table [dbo].[picturenews]<br />GO<br />CREATE TABLE [dbo].[picturenews] (<br />[id] [int] IDENTITY (1, 1) NOT NULL ,<br />[image] [image] NULL ,<br />[content] [varchar] (500) COLLATE Chinese_PRC_CI_AS NULL ,<br />[detail] [varchar] (5000) COLLATE Chinese_PRC_CI_AS NULL <br />) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]<br />GO<br /><br /><br />表picturenews中，字段id作为标识，每存储一行数据，自动增加1。字段image <br /><br />用于存储图片信息，其数据类型为“image”。 <br /><br />3、向数据库存储二进制图片 <br /><br />启动Dreamweaver MX后，新建一个JSP文件。其代码如下所示。 <br /><br /><br />&lt;%@ page contentType="text/html;charset=gb2312"%&gt; <br />&lt;HTML&gt;<br />&lt;HEAD&gt;<br />&lt;TITLE&gt;存储图片&lt;/TITLE&gt;<br />&lt;/HEAD&gt;<br />&lt;body&gt;<br />&lt;!-- 下面的窗体将以Post方法，将数据传递给testimage.jsp文件 --&gt;<br />&lt;FORM METHOD=POST ACTION="testimage.jsp"&gt;<br />新 闻 标 题：&lt;INPUT TYPE="text" NAME="content"&gt;&lt;BR&gt;<br />新 闻 图 片：&lt;INPUT TYPE="file" NAME="image"&gt;&lt;BR&gt;<br />新闻内容：<br />&lt;TEXTAREA name="txtmail" rows="15" cols="90" <br />style="BORDER-BOTTOM: #000000 1px solid; BORDER-LEFT: #000000 1px solid; <br />BORDER-RIGHT: #000000 1px solid; BORDER-TOP: #000000 1px solid; FONT-SIZE: 9pt; <br />HEIGHT: 200px; WIDTH: 100%" wrap="physical" &gt;&lt;/TEXTAREA&gt;&lt;br&gt;<br />&lt;INPUT TYPE="submit"&gt;&lt;/form&gt;<br />&lt;/body&gt;<br />&lt;/HTML&gt;<br /><br /><br />将此文件保存为InputImage.jsp文件，其中testimage.jsp文件是用来将图片数据存入数据库的，具体代码如下所示： <br /><br /><br />&lt;%@ page contentType="text/html;charset=gb2312"%&gt; <br />&lt;%@ page import="java.sql.*" %&gt;<br />&lt;%@ page import="java.util.*"%&gt;<br />&lt;%@ page import="java.text.*"%&gt;<br />&lt;%@ page import="java.io.*"%&gt;<br />&lt;html&gt; <br />&lt;body&gt; <br />&lt;%<br />Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");<br />//加载驱动程序类<br />Connection con=DriverManager.getConnection("jdbc:odbc:denglu","sa","sa");<br />//建立数据库联机，其中denglu为数据库名，sa为连接数据库的帐号及密码。<br />Statement stmt=con.createStatement(); <br />//建立Statement对象<br />String content=request.getParameter("content");<br />content=new String(content.getBytes("8859_1"),"gb2312");<br />String filename=request.getParameter("image");<br />filename=new String(filename.getBytes("8859_1"),"gb2312");<br />String detail=request.getParameter("txtmail");<br />detail=new String(detail.getBytes("8859_1"),"gb2312");<br />//获得所要显示图片的标题、存储路径、内容，并进行中文编码<br />FileInputStream str=new FileInputStream(filename);<br />String sql="insert into picturenews(content,image,detail) values(?,?,?)";<br />PreparedStatement pstmt=con.prepareStatement(sql);<br />pstmt.setString(1,content);<br />pstmt.setBinaryStream(2,str,str.available());<br />pstmt.setString(3,detail);<br />pstmt.execute();<br />//将数据存入数据库<br />out.println("Success,You Have Insert an Image Successfully");<br />%&gt;<br /><br /><br /><br />4、网页中动态显示图片 <br /><br />接下来我们要编程从数据库中取出图片，其代码如下所示。 <br /><br /><br />&lt;%@ page contentType="text/html;charset=gb2312"%&gt; <br />&lt;%@ page import="java.sql.*" %&gt;<br />&lt;%@ page import="java.util.*"%&gt;<br />&lt;%@ page import="java.text.*"%&gt;<br />&lt;%@ page import="java.io.*"%&gt; <br />&lt;html&gt;<br />&lt;body&gt;<br />&lt;%<br />Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); <br />//加载驱动程序类<br />Connection con=DriverManager.getConnection("jdbc:odbc:denglu","sa","sa");<br />Statement stmt=con.createStatement();<br />ResultSet rs=null; <br />//建立ResultSet（结果集）对象<br />int id= Integer.parseInt(request.getParameter("id"));<br />//获得所要显示图片的编号id，并转换为整型<br />String sql = "select image from picturenews WHERE id="+id+""; <br />//要执行查询的SQL语句<br />rs=stmt.executeQuery(sql);<br />while(rs.next()) {<br />ServletOutputStream sout = response.getOutputStream();<br />//图片输出的输出流<br />InputStream in = rs.getBinaryStream(1);<br />byte b[] = new byte[0x7a120];<br />for(int i = in.read(b); i != -1;)<br />{<br />sout.write(b); <br />//将缓冲区的输入输出到页面<br />in.read(b);<br />}<br />sout.flush();<br />//输入完毕，清除缓冲<br />sout.close();<br />}<br />%&gt;<br />&lt;/body&gt;<br />&lt;/html&gt;<br /><br /><br />将此文件保存为testimageout.jsp文件。下一步要做的工作就是使用HTML标记： <br /><br /><br />&lt;IMG src="testimageout.jsp?id=&lt;%=rs.getInt("id")%&gt;" width=100 height=100&gt;<br /><br /><br />取出所要显示的图片，其中id是所要取出图片的编号。本例中我们输出了第一个和最后一个图片信息，详细的程序代码如下所示。 <br /><br /><br />&lt;%@ page contentType="text/html;charset=gb2312"%&gt; <br />&lt;%@ page import="java.sql.*" %&gt;<br />&lt;html&gt;<br />&lt;head&gt;<br />&lt;title&gt;动态显示数据库图片&lt;/title&gt;<br />&lt;/head&gt;<br />&lt;body&gt;<br />&lt;% <br />Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");<br />Connection con=DriverManager.getConnection("jdbc:odbc:denglu","sa","sa");<br />Statement stmt=con.createStatement();<br />String sql=new String();<br />sql= "select * from picturenews";<br />ResultSet rs=stmt.executeQuery(sql);<br />rs.last();<br />//将指针移至最后一条记录<br />%&gt; <br />&lt;table&gt;<br />&lt;tr&gt;&lt;td&gt;&lt;IMG height=99 src="testimageout.jsp?id=1" width=136&gt;&lt;/td&gt;<br />//取出第一个图片<br />&lt;td&gt;&lt;IMG height=99 src="testimageout.jsp?id=&lt;%=rs.getInt("id")%&gt;" width=136&gt;&lt;/td&gt;<br />//取出最后一个图片<br />&lt;/tr&gt;&lt;/table&gt;<br />&lt;/body&gt;<br />&lt;/html&gt;<img src ="http://www.blogjava.net/balajinima/aggbug/157683.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2007-11-02 10:47 <a href="http://www.blogjava.net/balajinima/articles/157683.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JSP中实现文件上载</title><link>http://www.blogjava.net/balajinima/articles/157682.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Fri, 02 Nov 2007 02:44:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/157682.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/157682.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/157682.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/157682.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/157682.html</trackback:ping><description><![CDATA[
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<strong>一、前言</strong>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">文件上载对于任何web应用程序都是一个很有用处的功能。比如，在基于web的email中使用文件上载在消息中加入附件；在技术支持站点可以使用文件上载接收从用户处发来的错误日志和缺陷报告文档；在web应用中使用文件上载，通过友好的web界面在用户间共享文件。本文将对如何在JSP编程中上载文件进行讨论，并给出解决方法。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<strong>二、文件上载的方式</strong>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">在TCP/IP中最早出现的文件上载机制是FTP，它是将文件由客户端发送到服务器的标准机制。它很可靠，能够考虑到跨平台的文本和二进制格式文件。但在JSP编程中不能使用FTP方法来上载文件，这是由JSP的运行机制所决定的。我们知道：JSP（JavaServer Pages）是由Sun MicroSystems公司倡导、许多公司参与，一起建立的一种动态网页技术标准。它在传统的网页HTML文件中加入JAVA程序片段和JSP标记就构成了JSP网页。web服务器在遇到访问JSP网页请求时，首先执行其中的程序片段，然后将执行结果以HTML形式返回客户。这种运行机制就要求客户与服务器的联系需要使用HTTP协议而不能是FTP协议。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">在HTTP协议中上载文件主要有两种机制：</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">1）RFC1867方式：RFC1867在作为标准发布之前首先被Netscape的Navigator 2.0中采用。随后被Microsoft的IE3.0的附件和IE3.03的一部分使用。它是一种简单实用的方法。只在表单字段中定义一个file类型的input：</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;input type="file"&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">并且在表单本身中加入了不同的编码方案，它不再使用典型的：</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;form action="test.jsp" method="post"&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">而是使用</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;form action="test.jsp" method="post" enctype="multipart/form-data"&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">这种编码方案在传送大量数据时比缺省的"application/x-url-encoded"表单编码方案要效率高得多。因为URL编码只有很有限的字符集。当使用任何超出字符集的字符时，必须用"%nn"代替（这里的nn表示相应的两个十进制数）。这样即使是普通的空格字符也要用"%20"代替。那么，通过URL编码方式上载的文件将会是原来的2-3倍大。而使用RFC1867编码方式则只是在传送数据的周围加上很简单的头部来标识文件内容。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">2）put方式：在HTTP1.1中引入了一个新的HTTP动词：put。当web服务器收到一个HTTP的put请求和对象名字（如："/myweb/images/x.gif"）时，它会验证用户，接收HTTP流的内容，并把它直接存入web服务器。由于这种方式可能会对一个web站点造成破坏，因而并不常用。而且它也失去了web服务器端的最大优势：服务器的可编程性。在使用put方式时，服务器自己处理客户的请求，没有空间让JSP介入，因此，此方式对于JSP应用开发都是毫无用处的。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<strong>三、JSP中实现文件上载的方法</strong>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">从以上分析中我们可以得出结论：RFC1867是在JSP编写的web应用程序中实现文件上载的最好方法。它是如何在实际应用的呢？</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">这里将介绍一种简单而实用的方法，只需要几行代码就可以在JSP程序中实现文件上载。这就是使用一个免费的JAVA组件JspSmartUpload（http://www.jspsmart.com）实现文件上载。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">1） JspSmartUpload的配置要求</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">安装JspSmartUpload组件需要如下的配置：</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">a. web服务器（如IIS，apache等）</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">b. JDK1.1.2以上的JAVA编译器</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">c. 支持JSP1.1及JavaServelet API 2.2的引擎。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">其中的JSP引擎有许多产品，如：</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">Tomcat: http://java.sun.com/products/jsp/tomcat</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">Allaire Jrun: http://www.allaire.com/products/jrun</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">JSWDK: http://java.sun.com/produces/jsp/</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">IBM webSphere: http://www-4.ibm.com/software/webservers/</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">Resin: http://www.caucho.com</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">注意：本文将主要讨论JspSmartUpload在Tomcat引擎中的安装及使用。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">2） JspSmartUpload的安装</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">首先， 把下载的压缩文件JspSmartUpload.zip解压缩到/tomcat/webapps/ 目录下（/tomcat目录是你的tomcat引擎安装的根目录），这样就会形成如下的目录结构/tomcat/webapps/jspsmartupload/。所有必需的文件都在这个目录下。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">其次，把如下内容粘贴到/tomcat/conf/server.xml文件中。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;Context path="/jspsmartupload" docBase="/tomcat/webapps/jspsmartupload"</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">defaultSessionTimeOut="30" isWARExpanded="true" isWARValidated="false" isInvokerEnabled="true" isWorkDirPersistent="false" /&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">最后，把如下的内容粘贴到/tomcat/conf/uniworkermap.properties文件中。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">/jspsmartupload/*.jsp=ajp12</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">/jspsmartupload/servlet/*=ajp12</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">至此，jspsmartupload在tomcat引擎中的安装就完成了。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">3） 上载文件的HTML表单</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">如前所述，在JSP中文件上载使用的是RFC1867标准。在其中也规定了上载文件所使用的HTML表单的格式。此表单的格式被Netscape 3和Microsoft IE3.02以上的浏览器所支持。上载文件的表单发布具备如下的格式：</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">a) 单所使用的方法必须是post</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">b) 在action属性中的执行程序必须能处理由客户端返回的数据</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">c) 表单必须使用MIME类型：multipart/form-data</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">d) 表单必须至少包含一个&lt;input type=”file”&gt;元素。与RFC1867兼容的浏览器将会在客户端界面上显示一个文本框和一个“游览”按钮，以方便客户端选择上载的文件。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">下面是使用此类型表单的HTML文件例子。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">upload.htm</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;HTML&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;BODY BGCOLOR="white"&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;H1&gt;jspUpload : Sample &lt;/H1&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;HR&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;form METHOD="POST" ACTION="/jspsmartupload/upload.jsp" NAME="PW" ENCTYPE="multipart/form-data"&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;table CELLSPACING="0" CELLPADDING="3" BORDER="1" WIDTH="474"&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;!-- FILE --&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;tr&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;td&gt;&lt;small&gt;&lt;font face="Verdana"&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">Select a first file : </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;/font&gt;&lt;/small&gt;&lt;/td&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;td&gt;&lt;small&gt;&lt;font face="Verdana"&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;input TYPE="file" name="FILE1"&gt;&lt;/font&gt;&lt;/small&gt;&lt;/td&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;/tr&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;tr&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;td&gt;&lt;small&gt;&lt;font face="Verdana"&gt;Select a second file :   &lt;/font&gt;&lt;/small&gt;&lt;/td&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;td&gt;&lt;small&gt;&lt;font face="Verdana"&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;input TYPE="file" name="FILE2"&gt;&lt;/font&gt;&lt;/small&gt;&lt;/td&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;/tr&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;!-- TEXT --&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;tr&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;td width="150"&gt;&lt;div align="left"&gt;&lt;p&gt;&lt;small&gt;&lt;font face="Verdana"&gt;Text :   &lt;/font&gt;&lt;/small&gt;&lt;/td&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;td width="324"&gt;&lt;small&gt;&lt;font face="Verdana"&gt;&lt;input TYPE="TEXT" name="myText" value=""&gt;&lt;br&gt;&lt;/font&gt;&lt;/small&gt;&lt;/td&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;/tr&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;!-- SUBMIT --&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;tr&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;td colspan="2" width="474"&gt;&lt;div align="center"&gt;&lt;center&gt;&lt;p&gt;&lt;small&gt;&lt;font face="Verdana"&gt;&lt;input</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">TYPE="Submit"&gt; &lt;/font&gt;&lt;/small&gt;&lt;/td&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;/tr&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;/table&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;/form&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;/BODY&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;/HTML&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">4） 使用JspSmartUpload处理表单</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">当客户端submit表单以后，服务器将会调用表单action中的JSP程序处理客户端的请求。在此JSP中就要调用JspSmartUpload来完成文件上载的请求。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">在JSP程序中，首先要在&lt;%@ page import=my.class %&gt;中声明引入JspSmartUpload类。然后使用JavaBean来实例化一个JspSmartUpload类。如：&lt;jsp:useBean id=”mybean” class=”my.class”&gt;。在实例化后，要使用JSP引擎隐含的对象pageContext对JspSmartUpload JavaBean进行初使化，这样就把客户端的请求传给了JspSmartUpload组件。然后使用组件的upload()方法对客户的请求进行分析，最后把这些数据通过save()方法存入一个文件。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">下面是处理上一个HTML表单的JSP例程。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">upload.jsp</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;%@page language="java" import="com.jspsmart.upload.*"%&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;jsp:useBean id="myUpload" scope="page" class="com.jspsmart.upload.SmartUpload" /&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;HTML&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;BODY BGCOLOR="white"&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;H1&gt;jspUpload : Sample&lt;/H1&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;HR&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;%</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">// 初使化</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">myUpload.initialize(pageContext);</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">// 分析客户数据</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">myUpload.upload(); </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">//</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">// 处理上载文件</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">//</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">out.println("&lt;BR&gt;&lt;STRONG&gt;关于文件的信息&lt;/STRONG&gt;&lt;BR&gt;");</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">out.println("上载文件的数量 = " + myUpload.getFiles().getCount() + "&lt;BR&gt;");</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">//out.println("文件大小 (bytes) = " + myUpload.getFiles().getSize() +"&lt;BR&gt;");</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">for (int i=0;i&lt;myUpload.getFiles().getCount();i++){</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">out.print(myUpload.getFiles().getFile(i).getFieldName());</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">if (!myUpload.getFiles().getFile(i).isMissing())</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">out.print(" = " + myUpload.getFiles().getFile(i).getFileName() + " (" + myUpload.getFiles().getFile(i).getSize() + ")");</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">else</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">out.print(" = 空"); </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">out.println("&lt;BR&gt;");</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">}</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">//</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">// 处理客户请求信息</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">//</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">out.println("&lt;BR&gt;&lt;BR&gt;&lt;STRONG&gt;显示客户表单数据&lt;/STRONG&gt;&lt;BR&gt;");</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">// 显示请求名</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">java.util.Enumeration e = myUpload.getRequest().getParameterNames();</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">// 得到请求的值</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">while (e.hasMoreElements()) {</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">String key = (String)e.nextElement();</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">String[] values = myUpload.getRequest().getParameterValues(key); </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">// 显示请求的值</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">for(int i = 0; i &lt; values.length; i++) {</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">out.print(key + " = ");</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">out.print(values[i] + "&lt;BR&gt;");</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">} </td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">}</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">%&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">&lt;/BODY&gt;&lt;/HTML&gt;</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">5） 运行例程</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">把上述的HTML表单例子及处理表单的JSP例程存到/tomcat/webapps/uspsmartupload/目录下，然后运行/tomcat/bin/startup.bat启动服务器。在IE或Netscape的地址栏中输入：</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">http://localhost:8080/jspsmartupload/jspupload.htm</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">就可以看到运行结果了。</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">
										<strong>四、结论</strong>
								</td>
						</tr>
				</tbody>
		</table>
		<table width="620" align="center">
				<tbody>
						<tr>
								<td class="a14">JspSmartUpload组件不仅可以把客户端的数据存入到文件中，也可以把它存入到数据库中。而且，它的安装及使用方法简单易学，是一个非常好的上传组件，可以给JSP编制的web应用程序提供更强大的功能。</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/balajinima/aggbug/157682.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2007-11-02 10:44 <a href="http://www.blogjava.net/balajinima/articles/157682.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入理解和改进JSP/Servlet会话管理机制</title><link>http://www.blogjava.net/balajinima/articles/157679.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Fri, 02 Nov 2007 02:41:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/157679.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/157679.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/157679.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/157679.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/157679.html</trackback:ping><description><![CDATA[
		<div class="tit"> </div>
		<table style="TABLE-LAYOUT: fixed">
				<tbody>
						<tr>
								<td>
										<div class="cnt">在Web服务器端编程中，会话状态管理是一个经常必须考虑的重要问题。本文分析JSP/Servlet的会话管理机制及其所面临的问题，然后提出了一种改进的会话管理方法。 <br /><br />一、Servlet的会话管理机制 <br /><br />根据设计，HTTP是一种无状态的协议。它意味着Web应用并不了解有关同一用户以前请求的信息。维持会话状态信息的方法之一是使用Servlet或者JSP容器提供的会话跟踪功能。Servlet API规范定义了一个简单的HttpSession接口，通过它我们可以方便地实现会话跟踪。 <br /><br />HttpSession接口提供了存储和返回标准会话属性的方法。标准会话属性如会话标识符、应用数据等，都以“名字-值”对的形式保存。简而言之，HttpSession接口提供了一种把对象保存到内存、在同一用户的后继请求中提取这些对象的标准办法。在会话中保存数据的方法是setAttribute(String s, Object o)，从会话提取原来所保存对象的方法是getAttribute(String s)。 <br /><br />在HTTP协议中，当用户不再活动时不存在显式的终止信号。由于这个原因，我们不知道用户是否还要再次返回，如果不采取某种方法解决这个问题，内存中会积累起大量的HttpSession对象。 <br /><br />为此，Servlet采用“超时限制”的办法来判断用户是否还在访问：如果某个用户在一定的时间之内没有发出后继请求，则该用户的会话被作废，他的HttpSession对象被释放。会话的默认超时间隔由Servlet容器定义。这个值可以通过getMaxInactiveInterval方法获得，通过setMaxInactiveInterval方法修改，这些方法中的超时时间以秒计。如果会话的超时时间值设置成-1，则会话永不超时。Servlet可以通过getLastAccessedTime方法获得当前请求之前的最后一次访问时间。 <br /><br />要获得HttpSession对象，我们可以调用HttpServletRequest对象的getSession方法。为了正确地维持会话状态，我们必须在发送任何应答内容之前调用getSession方法。 <br />用户会话既可以用手工方法作废，也可以自动作废。作废会话意味着从内存中删除HttpSession对象以及它的数据。例如，如果一定时间之内（默认30分钟）用户不再发送请求，Java Web Server自动地作废他的会话。 <br /><br />Servlet/JSP会话跟踪机制有着一定的局限，比如： <br /><br />· 会话对象保存在内存之中，占用了可观的资源。 <br /><br />· 会话跟踪依赖于Cookie。由于各种原因，特别是安全上的原因，一些用户关闭了Cookie。 <br /><br />· 会话跟踪要用到服务器创建的会话标识符。在多个Web服务器以及多个JVM的环境中，Web服务器不能识别其他服务器创建的会话标识符，会话跟踪机制无法发挥作用。 <br />要深入理解会话跟踪机制，首先我们必须理解在Servlet/JSP容器中会话如何运作。 <br /><br /><br />二、会话标识符 <br /><br />每当新用户请求一个使用了HttpSession对象的JSP页面，JSP容器除了发回应答页面之外，它还要向浏览器发送一个特殊的数字。这个特殊的数字称为“会话标识符”，它是一个唯一的用户标识符。此后，HttpSession对象就驻留在内存之中，等待同一用户返回时再次调用它的方法。 <br /><br />在客户端，浏览器保存会话标识符，并在每一个后继请求中把这个会话标识符发送给服务器。会话标识符告诉JSP容器当前请求不是用户发出的第一个请求，服务器以前已经为该用户创建了HttpSession对象。此时，JSP容器不再为用户创建新的HttpSession对象，而是寻找具有相同会话标识符的HttpSession对象，然后建立该HttpSession对象和当前请求的关联。 <br /><br />会话标识符以Cookie的形式在服务器和浏览器之间传送。如果浏览器不支持Cookie又如何呢？此时，对服务器的后继请求将不会带有会话标识符。结果，JSP容器认为该请求来自一个新用户，它会再创建一个HttpSession对象，而以前创建的HttpSession对象仍旧驻留在内存中，但该用户以前的会话信息却丢失了。 <br /><br />另外，Servlet/JSP容器只认可它自己创建的会话标识符。如果同一Web应用在“Web农场”（Web farm）的多台服务器上运行，则必须存在这样一种机制：保证来自同一用户的请求总是被定向到处理该用户第一次请求的服务器。 <br /><br />三、伪会话管理机制 <br /><br />如前所述，基于Cookie的会话管理技术面临着种种问题。下面我们要设计一种新的会话管理机制来解决这些问题。这种会话管理机制称为“伪会话”（Pseudo Session）机制，它具有如下特点： <br /><br />· 对象和数据不是保存在内存中，而是以文本文件形式保存。每一个文本文件与一个特定的用户关联，文件的名字就是会话的标识符。因此，文件名字必须是唯一的。 <br />· 文本文件保存在一个专用的目录中，所有Web服务器都可以访问这个目录。因此，伪会话可以用于Web农场。 <br />· 会话标识符不作为Cookie发送，而是直接编码到URL里面。因此，采用伪会话技术要求修改所有的超级链接，包括HTML表单的ACTION属性。 <br />此外，实现伪会话管理机制时我们还要考虑到以下几点： <br />· 它应该与应用无关，其他想要实现同样功能的开发者应该能够方便地重用它。 <br />· 考虑到安全原因，应该有一种为会话标识符生成随机数字的办法。 <br />· 为了作废过期的会话，应该设定一个超时值。同一个用户，如果他超过一定的时间之后再次返回，他将获得一个新的会话标识符。此举能够防止未经授权的用户冒用其他人的会话。 <br />· 应该有一种收集过期会话并删除相应文本文件的机制。 <br />· 如果用户使用已经过期的会话标识符再次访问服务器，即使这个会话标识符的文本文件还没有删除，系统也不应该允许用户使用原来的会话。 <br />· 同时，应该存在一种更新会话文本文件最后改动时间的机制，使得用户在会话过期时限之前返回时会话总是保持最新且合法的状态数据。 <br /><br /><br />四、实现伪会话管理机制 <br /><br />下面所介绍的工程称为PseudoSession，它是伪会话机制一个很简单的实现。考虑到移植性，我们以JavaBean的形式实现它。PseudoSessionBean的完整代码可以从本文后面下载。 <br /><br />PseudoSessionBean拥有如下域（Field）： <br />public String path;public long timeOut; <br />path是保存所有会话文本文件的目录。如果Web服务器的数量在一个以上，这个目录必须允许所有服务器访问。然而，为了防止用户直接访问这些文本文件，这个路径应该不允许用户直接访问。解决这个问题的一种方法是使用Web网站根之外的目录。 <br /><br />timeOut是用户的最后一个请求到会话过期作废之间的时间。在PseudoSessionBean的代码清单中，timeOut设置成了以毫秒表示的20分钟，这是一个比较合理的超时时间值。对于任何用户，如果他在这个超时时间之后才继续发出请求，他将得到一个新的会话标识符。 <br />PseudoSessionBean有4个方法：getSessionID，setValue，getValue，deleteAllInvalidSessions。 <br /><br />4.1 getSessionID方法 <br />getSessionID方法的声明如下： <br />public String getSessionID(HttpServletRequest request) <br />这个方法应该在每一个JSP页面的开头调用。它完成如下任务： <br />· 如果用户是第一次访问，则为该用户设定一个新的会话标识符。 <br />· 检查URL所带会话标识符的合法性。如果会话标识符已经过期，则getSessionID方法返回一个新的会话标识符。 <br />下面我们来看看getSessionID方法的工作过程。 <br />String sessionId = request.getParameter("sessionId"); <br />validSessionIdFound是一个标记，用于指示会话标识符是否合法。validSessionIdFound的初始值是false。 <br />boolean validSessionIdFound = false; <br />long类型的now变量包含请求出现时的服务器时间。该变量用于确定用户会话的合法性。 <br />long now = System.currentTimeMillis(); <br />如果找到了会话标识符，则getSessionID方法检查它的合法性。检查过程如下： <br />· 一个合法的会话标识符必须有对应的同名文本文件。 <br />· 文件的最后修改时间加上timeOut应该大于当前时间。 <br />· 如果存在与会话对应的文本文件，但文件已经过期，则原来的文件被删除。 <br />· 把合法会话标识符所对应文本文件的最后修改日期改为now。 <br />这些任务主要借助File对象完成，创建File对象的参数就是会话文本文件的路径： <br />if (sessionId!=null) {File f = new File(path + sessionId);if (f.exists()) { if (f.lastModified() + timeOut &gt; now) { // 会话合法// 使用setLastModified时，如果文件已经被其他程序锁定，// 程序不会产生任何异常，但文件数据不会改变f.setLastModified(now);validSessionIdFound = true; } else { // 会话已经过期 // 删除文件f.delete(); }} // end if (f.exists) } // end if (sessionId!=null) <br />如果不存在合法的会话标识符，则getSessionID方法生成一个会话标识符以及相应的文本文件： <br />if (!validSessionIdFound) { sessionId = Long.toString(now); // 创建文件 File f = new File(path + sessionId); try {f.createNewFile(); } catch (IOException ioe) {}} // end of if !validSessionIdFound <br />程序保证文件名字随机性的方法非常简单：把当前的系统时间直接转换成会话标识符。对于那些涉及敏感数据的应用，我们应该考虑运用更安全的随机数生成器来生成会话标识符。 <br />综上所述，getSessionID并不总是返回新的合法会话标识符：它返回的标识符可能与传递给它的标识符相同，也可能是新创建的会话标识符。 <br />为了保证JSP页面拥有合法的会话标识符以便调用setValue、getValue方法，每个JSP页面都必须在开头位置调用getSesstionID方法。 <br /><br /><br />4.2 setValue方法 <br />setValue方法保存value字符串以及与它关联的字符串名字。这种“名字-值”对很容易使人想起Dictionary对象。setValue方法要求在第一个参数中提供合法的会话标识符，它假定在自己被调用之前getSessionID方法已经执行，经过检验的合法会话标识符必然存在，因此它不再对传入的会话标识符进行合法性检验。 <br />setValue方法按如下规则保存名字-值对： <br />· 如果与value值关联的name以前还没有保存过，则新的名字-值对加入到文本文件的末尾。 <br />· 如果value字符串关联的name值以前已经保存过，则原来保存的值被新的value值替换。 <br />setValue方法按照如下格式保存名字-值对，注意“名字”是大小写敏感的： <br />name-1 value-1name-2 value-2name-3 value-3...name-n value-n <br />setValue方法的声明如下： <br />public void setValue(String sessionId, String name, String value) <br />setValue方法首先寻找与当前会话对应的文本文件。如果不能找到文本文件，则setValue方法不做任何事情直接返回。如果找到了会话文本文件，setValue方法读取文本文件的各个行，然后比较读入的行与name：如果读入的文本行开头与name一样，则说明该名字已经保存，setValue方法将替换该行后面的值；如果name不能与读入的文本行匹配，则这行文本被直接复制到一个临时文件。 <br />这部分功能的实现代码如下： <br />try { FileReader fr = new FileReader(path + sessionId); BufferedReader br = new BufferedReader(fr); FileWriter fw = new FileWriter(path + sessionId + ".tmp"); BufferedWriter bw = new BufferedWriter(fw); String s; while ((s = br.readLine()) != null)if (!s.startsWith(name + " ")) { bw.write(s); bw.newLine();} bw.write(name + " " + value); bw.newLine(); bw.close(); br.close(); fw.close(); bw.close(); . . .}catch (FileNotFoundException e) {}catch (IOException e) { System.out.println(e.toString());} <br />原来文本文件中的所有行复制到临时文件之后，setValue方法删除原来的文本文件，然后把临时文件改成会话文本文件的名字： <br />File f = new File(path + sessionId + ".tmp");File dest = new File(path + sessionId);dest.delete();f.renameTo(dest); <br /><br /><br />4.3 getValue方法 <br />getValue方法用于提取原来保存在伪会话中的数据。正如setValue方法，getValue方法也要求传入一个合法的会话标识符，而且getValue方法不再对传入的会话标识符进行合法性检查。getValue方法的第二个参数是待提取数据的name，返回值是与指定name关联的value。 <br />getValue方法的声明如下： <br />public String getValue(String sessionId, String name) <br />getValue方法的基本执行过程如下：首先找到会话文本文件，然后按行读入直至找到与name匹配的文本行；找到匹配的文本行之后，getValue方法返回该行保存的value；如果不能找到，getValue方法返回null。 <br /><br /><br />4.4 deleteAllInvalidSessions方法 <br />deleteAllInvalidSessions方法删除那些与已经过期的会话关联的文本文件。由于调用getSessionID方法时过期的会话文本文件会被删除，deleteAllInvalidSessions方法并不是关键的方法。什么时候调用这个方法由应用自己决定。例如，我们可以编写一个专用的后台程序，由这个程序每天一次清除所有过期的文本文件。最简单的办法是在JSP文件末尾调用deleteAllInvalidSessions方法，但如果网站比较繁忙，重复地调用deleteAllInvalidSessions方法将降低整个网站的响应能力。一种明智的做法是：编写一个在访问量较少的时候自动进行清理的后台程序。 <br />deleteAllInvalidSessions方法的声明如下： <br />public void deleteAllInvalidSessions() <br />它首先把所有会话文本文件的名字读入files字符串数组： <br />File dir = new File(path); String[] files = dir.list(); <br />deleteAllInvalidSessions方法比较文本文件的最后修改时间（加上超时时间）和系统当前时间，确定会话是否过期。long类型的变量now用于保存系统的当前时间。 <br />long now = System.currentTimeMillis(); <br />接下来，deleteAllInvalidSessions方法通过循环访问files数组，依次检查每个文件的lastModified属性。所有与过期会话关联的文件都将被删除： <br />for (int i=0; i&lt;files.length; i++) { File f = new File(path + files[i]); if (f.lastModified() + timeOut &gt; now) f.delete(); // 删除过期的会话文本文件} <br /><br /><br />五、应用实例 <br /><br /><br />编译好PseudoSessionBean这个JavaBean之后，我们就可以利用伪会话管理机制来管理Web应用的会话状态信息了。由于不必再使用服务器的会话管理机制，我们可以在page指令中把session属性设置为false关闭默认的JSP/Servlet会话管理功能。 <br />&lt;%@ page session="false" %&gt; <br /><br />然后，我们用JSP的&lt;jsp:useBean&gt;标记告诉JSP容器程序要使用PseudoSessionBean： <br />&lt;jsp:useBean id="PseudoSessionId" scope="application" class="pseudosession.PseudoSessionBean" /&gt; <br /><br />在上面这个&lt;jsp:useBean&gt;标记中，class属性值是“包.类名字”形式。当然，对于不同的包名字，class属性的值应该作相应的修改。注意Bean的scope属性是“application”，这是因为我们要在应用的所有页面中使用这个Bean。在这个应用中，把Bean的scope属性设置为“application”具有最好的效率，因为我们只需创建Bean对象一次就可以了。另外，正如前面所提到的，getSessionID方法必须在所有其他代码之前调用。 <br />&lt;% String sessionId = PseudoSessionId.getSessionID(request);%&gt; <br />为了说明PseudoSessionBean的应用，下面我们来看两个JSP页面，它们是index.jsp和secondPage.jsp。index.jsp页面在伪会话变量中保存用户的名字，而secondPage.jsp则提取这个用户名字。 <br /><br />index.jsp页面的代码如下： <br />&lt;%@ page session="false" contentType="text/html;charset=gb2312" %&gt;<br />&lt;jsp:useBean id="PseudoSessionId" scope="application" class="pseudosession.PseudoSessionBean" /&gt;<br />&lt;% String sessionId = PseudoSessionId.getSessionID(request);%&gt;<br />&lt;html&gt;<br />&lt;head&gt;<br />&lt;title&gt;伪会话&lt;/title&gt;<br />&lt;/head&gt;<br />&lt;body&gt;<br />&lt;h1&gt;伪会话管理机制&lt;/h1&gt;<br />&lt;% String userName = "bulbul"; PseudoSessionId.setValue(sessionId, "userName", userName);%&gt;<br />&lt;a href=secondPage.jsp?sessionId=&lt;%=sessionId%&gt;&gt;点击此处&lt;/a&gt;<br />&lt;form method="post" action=anotherPage.jsp?sessionId=&lt;%=sessionId%&gt;&gt;<br />输入数据:&lt;input type="text" name="sample"&gt;<br />&lt;input type="submit" name="Submit" value="Submit"&gt;<br />&lt;/form&gt;<br />&lt;/body&gt;<br />&lt;/html&gt;<br />&lt;% PseudoSessionId.deleteAllInvalidSessions();%&gt; <br /><br />注意，包括&lt;form&gt;标记的action属性在内，所有的超级链接都已经改写，现在都包含了会话标识符。另外也请注意页面的最后调用了deleteAllInvalidSessions方法。 <br />secondPage.jsp页面只简单地返回以前保存的用户名字。 <br />&lt;%@ contentType="text/html;charset=gb2312" page session="false" %&gt;<br />&lt;jsp:useBean id="PseudoSessionId" scope="application" class="pseudosession.PseudoSessionBean" /&gt;<br />&lt;% String sessionId = PseudoSessionId.getSessionID(request);%&gt;<br />&lt;html&gt;<br />&lt;head&gt;<br />&lt;title&gt;第2个页面&lt;/title&gt;<br />&lt;/head&gt;<br />&lt;body&gt;<br />&lt;% String userName = PseudoSessionId.getValue(sessionId, "userName"); out.println("用户名字是 " + userName);%&gt;<br />&lt;/body&gt;<br />&lt;/html&gt; </div>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/balajinima/aggbug/157679.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2007-11-02 10:41 <a href="http://www.blogjava.net/balajinima/articles/157679.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDBC基础知识 </title><link>http://www.blogjava.net/balajinima/articles/146901.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Thu, 20 Sep 2007 11:54:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/146901.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/146901.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/146901.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/146901.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/146901.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: JDBC基础知识												关键字:   JDBC基础知识     																																										java 代码																																																																															...&nbsp;&nbsp;<a href='http://www.blogjava.net/balajinima/articles/146901.html'>阅读全文</a><img src ="http://www.blogjava.net/balajinima/aggbug/146901.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2007-09-20 19:54 <a href="http://www.blogjava.net/balajinima/articles/146901.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>简单的jsp控制分页</title><link>http://www.blogjava.net/balajinima/articles/134185.html</link><dc:creator>李云泽</dc:creator><author>李云泽</author><pubDate>Fri, 03 Aug 2007 02:55:00 GMT</pubDate><guid>http://www.blogjava.net/balajinima/articles/134185.html</guid><wfw:comment>http://www.blogjava.net/balajinima/comments/134185.html</wfw:comment><comments>http://www.blogjava.net/balajinima/articles/134185.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/balajinima/comments/commentRss/134185.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/balajinima/services/trackbacks/134185.html</trackback:ping><description><![CDATA[
		<p>
				<br />&lt;%<br /> //分页显示<br /> int num = 0;<br /> int countall = infolist.size();//总数<br /> int pageSize = 28;//每页数量<br /> int pageCount;//总共页数<br /> int pageNo = 1;//当前页号<br /> int mixNum;   //当前页数据开始号<br /> int maxNum;<br /> <br /> String pNO = request.getParameter("pageNo");</p>
		<p> if((pNO != null) &amp;&amp; (!pNO.equals(""))){</p>
		<p>  pageNo=Integer.parseInt(pNO);<br /> }</p>
		<p> if (countall &gt; pageSize) {<br />  if ((countall / pageSize) * pageSize &lt; countall) {<br />   pageCount = (countall / pageSize) + 1;<br />  } <br />  else{<br />   pageCount = (countall / pageSize);<br />  }<br /> }<br /> <br /> else if (countall / pageSize == 1) {<br />  pageCount = 1;<br /> } <br /> else {<br />  pageCount = 1;<br /> }</p>
		<p> if (pageNo &gt;= pageCount) {<br />  pageNo = pageCount;<br /> }<br /> else if (pageNo &lt; 1) {<br />  pageNo = 1;<br /> }<br />    <br />    <br /> mixNum = (pageNo-1) * pageSize;<br /> maxNum = pageNo*pageSize;<br /> <br /> if((mixNum + pageSize) &gt; countall){<br />  maxNum = countall;<br /> }<br /> else{<br />  maxNum = mixNum+pageSize;<br /> }<br />%&gt;<br />---------------------------------------------------------------------------------------------<br />&lt;html&gt;<br />&lt;head&gt;<br />&lt;script language="JavaScript" type="text/JavaScript"&gt;<br />function check(){<br /> if(Jtrim(document.forms.meslistForm.textfield.value)==""){<br />  alert("请输入页数");<br /> }<br /> else if(checkNum(document.forms.meslistForm.textfield.value)){<br />  window.location.href="?pageNo="+document.forms.meslistForm.textfield.value; <br /> }else{<br />  alert("请输入数字。");<br /> }<br /> document.forms.meslistForm.textfield.value = "";<br /> document.forms.meslistForm.textfield.select();<br />}<br />function checkNum(str){<br />return str.match(/\D/)==null;<br />}</p>
		<p>function Jtrim(str)<br />{</p>
		<p>        var i = 0;<br />        var len = str.length;<br />        if ( str == "" ) return( str );<br />        j = len -1;<br />        flagbegin = true;<br />        flagend = true;<br />        while ( flagbegin == true &amp;&amp; i&lt; len)<br />        {<br />           if ( str.charAt(i) == " " )<br />                {<br />                  i=i+1;<br />                  flagbegin=true;<br />                }<br />                else<br />                {<br />                        flagbegin=false;<br />                }<br />        }</p>
		<p>        while  (flagend== true &amp;&amp; j&gt;=0)<br />        {<br />            if (str.charAt(j)==" ")<br />                {<br />                        j=j-1;<br />                        flagend=true;<br />                }<br />                else<br />                {<br />                        flagend=false;<br />                }<br />        }</p>
		<p>        if ( i &gt; j ) return ("")</p>
		<p>        trimstr = str.substring(i,j+1);<br />        return trimstr;<br />}&lt;/script&gt;<br />&lt;/head&gt;</p>
		<p>
				<br />&lt;body&gt;<br />&lt;%<br /> for(int i=mixNum;i&lt;maxNum;i++){<br /> //此处用list循环下标用i<br /> }         <br />%&gt;</p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p> </p>
		<p>
				<br /> &lt;table align="center" width="100%" border="0" cellspacing="0" cellpadding="0"&gt;<br />   &lt;tr&gt;<br />    &lt;td width="4%" height="27" align="center" valign="middle"<br />     class="word"&gt;&amp;nbsp;&lt;/td&gt;<br />    &lt;td width="56%" align="center" valign="middle" class="word"&gt;&lt;img<br />     src="&lt;%=path%&gt;/images/pagination/list_home.gif" width="13"<br />     height="13"&gt;&amp;nbsp; &lt;%<br />      if (pageNo &gt; 1) {<br />     %&gt; &lt;a href="?pageNo=1"&gt;&amp;nbsp;首页&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;<br />    &lt;%<br />                     }else{<br />                    %&gt; &amp;nbsp;首页&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;%<br />                     }<br />                    %&gt; &lt;img src="&lt;%=path%&gt;/images/pagination/list_pre.gif"<br />     width="13" height="13"&gt; &lt;%<br />      if (pageNo &gt; 1) {<br />     %&gt; &lt;a href="?pageNo=&lt;%=(pageNo-1)%&gt;"&gt;&amp;nbsp;前一页&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;<br />    &lt;%<br />                     }else{<br />                    %&gt; &amp;nbsp;前一页&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;%<br />                     }<br />                    %&gt; &lt;img<br />     src="&lt;%=path%&gt;/images/pagination/list_next.gif" width="13"<br />     height="13"&gt; &lt;%<br />      if (pageCount&gt;pageNo) {<br />     %&gt; &lt;a href="?pageNo=&lt;%=(pageNo+1)%&gt;"&gt;&amp;nbsp;&amp;nbsp;后一页&lt;/a&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;<br />    &lt;%<br />                     }else{<br />                    %&gt; &amp;nbsp;&amp;nbsp;后一页&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;%<br />                     } <br />                    %&gt; &lt;img src="&lt;%=path%&gt;/images/pagination/list_end.gif"<br />     width="13" height="13"&gt; &lt;%<br />      if (pageCount&gt;pageNo) {<br />     %&gt; &lt;a href="?pageNo=&lt;%=(pageCount)%&gt;"&gt;&amp;nbsp;&amp;nbsp;尾页&lt;/a&gt; &lt;%<br />                     }else{<br />                    %&gt; &amp;nbsp;&amp;nbsp;尾页 &lt;%<br />                     } <br />                    %&gt;<br />    &lt;td width="26%" align="center" valign="middle" class="word"&gt;共&lt;%=pageCount%&gt;页&amp;nbsp;第&lt;%=pageNo%&gt;页&amp;nbsp;<br />    跳转&lt;/td&gt;<br />    &lt;td width="4%" align="center" valign="middle" class="word"&gt;&lt;input<br />     name="textfield" type="text" class="input" size="1"&gt;&lt;/td&gt;<br />    &lt;td width="6%" align="center" valign="middle" class="word"&gt;&lt;a<br />     href="javascript:check()"&gt;&lt;img<br />     src="&lt;%=path%&gt;/images/pagination/go.gif" border="0" width="18"<br />     height="18"&gt;&lt;/a&gt;&lt;/td&gt;</p>
		<p>    &lt;td width="4%" align="left" valign="middle" class="word"&gt;&amp;nbsp;&lt;/td&gt;<br />   &lt;/tr&gt;<br />  &lt;/table&gt;&lt;/body&gt;  <br />&lt;/html&gt;</p>
<img src ="http://www.blogjava.net/balajinima/aggbug/134185.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/balajinima/" target="_blank">李云泽</a> 2007-08-03 10:55 <a href="http://www.blogjava.net/balajinima/articles/134185.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>