﻿<?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/renyangok/category/17020.html</link><description /><language>zh-cn</language><lastBuildDate>Wed, 01 Aug 2007 10:38:01 GMT</lastBuildDate><pubDate>Wed, 01 Aug 2007 10:38:01 GMT</pubDate><ttl>60</ttl><item><title>英文变乱码 Windows记事本再度“闹鬼”</title><link>http://www.blogjava.net/renyangok/archive/2007/08/01/133806.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Wed, 01 Aug 2007 06:59:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/08/01/133806.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/133806.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/08/01/133806.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/133806.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/133806.html</trackback:ping><description><![CDATA[<div>还记得<a  href="http://wiki.ccw.com.cn/index.php/Windows" target="_blank"><font color="#0000cc"><strong>Windows</strong></font></a>记事本程序与&#8220;联通&#8221;之间的&#8220;闹鬼事件&#8221;么？现在又有人发现了类似的现象，这次记事本连英文也不认识了。</div>
<div>Wincustomise提供的方法如下：<!-- 正文页网画中画广告  --><br><br>1、打开Windows系统中的记事本程序(不是写字板也不是Word)。</div>
<div>2、输入&#8220;this <a  href="http://wiki.ccw.com.cn/AP" target="_blank"><font color="#0000cc"><strong>ap</strong></font></a>p <a  href="http://topic.ccw.com.cn/corpCenter/249.html" target="_blank"><font color="#0000cc"><strong>ca</strong></font></a>n break&#8221;(不含引号)。</div>
<div>3、以任意文件名将其保存到<a  href="http://wiki.ccw.com.cn/%E7%A1%AC%E7%9B%98" target="_blank"><font color="#0000cc"><strong>硬盘</strong></font></a>上。</div>
<div>4、关闭记事本。</div>
<div>5、再次使用记事本打开保存的文档。</div>
<div>怎么样？看到的还是英文么？怎么成了乱码了？</div>
<div>其实，和此前&#8220;联通&#8221;变&#8220;黑色实心方块&#8221;一样，记事本如此&#8220;闹鬼&#8221;都是出于同一bug。默认情况下，记事本是以ANSI编码保存文本文档的，正
是这种编码导致了上述怪现象。如果选择Unicode、Unicode(big
endian)、UTF-8编码保存就正常了。此外，如果以ANSI编码保存含有某些特殊符号的文本文档，再次打开后符号也会变成英文问号。</div>
<div><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img  src="http://news1.mydrivers.com/pages/images/20060615163230_80118.jpg" alt="bug,记事本" border="0"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 英文变乱码<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img  src="http://news1.mydrivers.com/pages/images/20060615163233_30084.jpg" alt="bug,记事本" border="0"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;联通变方块<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img  src="http://news1.mydrivers.com/pages/images/20060615163235_66785.jpg" alt="bug,记事本" border="0"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;都是编码惹的祸</div><img src ="http://www.blogjava.net/renyangok/aggbug/133806.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-08-01 14:59 <a href="http://www.blogjava.net/renyangok/archive/2007/08/01/133806.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>unicode,ANSI,UTF-8的故事（转）</title><link>http://www.blogjava.net/renyangok/archive/2007/07/31/133666.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 31 Jul 2007 14:35:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/07/31/133666.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/133666.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/07/31/133666.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/133666.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/133666.html</trackback:ping><description><![CDATA[<h4 class=TextColor1 id=subjcns!13d0cabaa0551ef2!196 style="MARGIN-BOTTOM: 0px">&nbsp;</h4>
<div class=bvMsg id=msgcns!13d0cabaa0551ef2!196>
<p>很久很久以前，有一群人，他们决定用8个可以开合的晶体管来组合成不同的状态，<br>以表示世界上的万物。他们看到8个开关状态是好的，于是他们把这称为"字节"。 <br>再后来，他们又做了一些可以处理这些字节的机器，机器开动了，可以用字节来组<br>合出很多状态，状态开始变来变去。他们看到这样是好的，于是它们就这机器称为"<br>计算机"。
<p>开始计算机只在美国用。八位的字节一共可以组合出256(2的8次方)种不同的状态。 <br>他们把其中的编号从0开始的32种状态分别规定了特殊的用途，一但终端、打印机遇<br>上约定好的这些字节被传过来时，就要做一些约定的动作。遇上00x10, 终端就换行，<br>遇上0x07, 终端就向人们嘟嘟叫，例好遇上0x1b, 打印机就打印反白的字，或者终端就<br>用彩色显示字母。他们看到这样很好，于是就把这些0x20以下的字节状态称为"控制码"。
<p>他们又把所有的空格、标点符号、数字、大小写字母分别用连续的字节状态表示，<br>一直编到了第127号，这样计算机就可以用不同字节来存储英语的文字了。大家看到<br>这样，都感觉很好，于是大家都把这个方案叫做 ANSI 的"Ascii"编码（American <br>Standard Code for Information Interchange，美国信息互换标准代码）。<br>当时世界上所有的计算机都用同样的ASCII方案来保存英文文字。
<p>后来，就像建造巴比伦塔一样，世界各地的都开始使用计算机，但是很多国家用的<br>不是英文，他们的字母里有许多是ASCII里没有的，为了可以在计算机保存他们的文<br>字，他们决定采用127号之后的空位来表示这些新的字母、符号，还加入了很多画表<br>格时需要用下到的横线、竖线、交叉等形状，一直把序号编到了最后一个状态255。<br>从128到255这一页的字符集被称"扩展字符集"。从此之后，贪婪的人类再没有新的状<br>态可以用了，美帝国主义可能没有想到还有第三世界国家的人们也希望可以用到计算机吧！<br>&nbsp;<br>等中国人们得到计算机时，已经没有可以利用的字节状态来表示汉字，况且有6000<br>多个常用汉字需要保存呢。但是这难不倒智慧的中国人民，我们不客气地把那些127<br>号之后的奇异符号们直接取消掉, 规定：一个小于127的字符的意义与原来相同，但<br>两个大于127的字符连在一起时，就表示一个汉字，前面的一个字节（他称之为高字<br>节）从0xA1用到0xF7，后面一个字节（低字节）从0xA1到0xFE，这样我们就可以组<br>合出大约7000多个简体汉字了。在这些编码里，我们还把数学符号、罗马希腊的字<br>母、日文的假名们都编进去了，连在 ASCII 里本来就有的数字、标点、字母都统统重<br>新编了两个字节长的编码，这就是常说的"全角"字符，而原来在127号以下的那些就<br>叫"半角"字符了。
<p>中国人民看到这样很不错，于是就把这种汉字方案叫做 "GB2312"。GB2312 是对<br>ASCII 的中文扩展。但是中国的汉字太多了，我们很快就就发现有许多人的人名没有<br>办法在这里打出来，特别是某些很会麻烦别人的国家领导人。于是我们不得不继续<br>把 GB2312 没有用到的码位找出来老实不客气地用上。
<p>后来还是不够用，于是干脆不再要求低字节一定是127号之后的内码，只要第一个字<br>节是大于127就固定表示这是一个汉字的开始，不管后面跟的是不是扩展字符集里的<br>内容。结果扩展之后的编码方案被称为 GBK 标准，GBK 包括了 GB2312 的所有内容<br>，同时又增加了近20000个新的汉字（包括繁体字）和符号。<br>&nbsp;<br>后来少数民族也要用电脑了，于是我们再扩展，又加了几千个新的少数民族的字，<br>GBK 扩成了 GB18030。从此之后，中华民族的文化就可以在计算机时代中传承了。 <br>中国的程序员们看到这一系列汉字编码的标准是好的，于是通称他们叫做 "DBCS"<br>（Double Byte Charecter Set 双字节字符集）。在DBCS系列标准里，最大的特点是两字<br>节长的汉字字符和一字节长的英文字符并存于同一套编码方案里，因此他们写的程<br>序为了支持中文处理，必须要注意字串里的每一个字节的值，如果这个值是大于127<br>的，那么就认为一个双字节字符集里的字符出现了。那时候凡是受过加持，会编程<br>的计算机僧侣们都要每天念下面这个咒语数百遍： <br>"一个汉字算两个英文字符！一个汉字算两个英文字符&#8230;&#8230;"
<p>因为当时各个国家都像中国这样搞出一套自己的编码标准，结果互相之间谁也不懂<br>谁的编码，谁也不支持别人的编码，连大陆和台湾这样只相隔了150海里，使用着同<br>一种语言的兄弟地区，也分别采用了不同的 DBCS 编码方案——当时的中国人想让电<br>脑显示汉字，就必须装上一个"汉字系统"，专门用来处理汉字的显示、输入的问题，<br>但是那个台湾的愚昧封建人士写的算命程序就必须加装另一套支持 BIG5 编码的什么"<br>倚天汉字系统"才可以用，装错了字符系统，显示就会乱了套！这怎么办？而且世界<br>民族之林中还有那些一时用不上电脑的穷苦人民，他们的文字又怎么办？ <br>真是计算机的巴比伦塔命题啊！
<p>正在这时，大天使加百列及时出现了——一个叫 ISO （国际标谁化组织）的国际组织<br>决定着手解决这个问题。他们采用的方法很简单：废了所有的地区性编码方案，重<br>新搞一个包括了地球上所有文化、所有字母和符号的编码！他们打算叫它"Universal <br>Multiple-Octet Coded Character Set"，简称 UCS, 俗称 "UNICODE"。
<p>UNICODE 开始制订时，计算机的存储器容量极大地发展了，空间再也不成为问题了。<br>于是 ISO 就直接规定必须用两个字节，也就是16位来统一表示所有的字符，对于<br>ascii里的那些&#8220;半角&#8221;字符，UNICODE 包持其原编码不变，只是将其长度由原来的8位<br>扩展为16位，而其他文化和语言的字符则全部重新统一编码。由于"半角"英文符号只<br>需要用到低8位，所以其高8位永远是0，因此这种大气的方案在保存英文文本时会多<br>浪费一倍的空间。
<p>这时候，从旧社会里走过来的程序员开始发现一个奇怪的现象：他们的strlen函数靠<br>不住了，一个汉字不再是相当于两个字符了，而是一个！是的，从 UNICODE 开始，<br>无论是半角的英文字母，还是全角的汉字，它们都是统一的"一个字符"！同时，也都<br>是统一的"两个字节"，请注意"字符"和"字节"两个术语的不同，&#8220;字节&#8221;是一个8位的物<br>理存贮单元，而&#8220;字符&#8221;则是一个文化相关的符号。在UNICODE 中，一个字符就是两<br>个字节。一个汉字算两个英文字符的时代已经快过去了。<br>&nbsp;<br>从前多种字符集存在时，那些做多语言软件的公司遇上过很大麻烦，他们为了在不<br>同的国家销售同一套软件，就不得不在区域化软件时也加持那个双字节字符集咒语<br>，不仅要处处小心不要搞错，还要把软件中的文字在不同的字符集中转来转去。<br>UNICODE 对于他们来说是一个很好的一揽子解决方案，于是从 Windows NT 开始，<br>MS 趁机把它们的操作系统改了一遍，把所有的核心代码都改成了用 UNICODE 方式<br>工作的版本，从这时开始，WINDOWS 系统终于无需要加装各种本土语言系统，就<br>可以显示全世界上所有文化的字符了。
<p>但是，UNICODE 在制订时没有考虑与任何一种现有的编码方案保持兼容，这使得<br>GBK 与UNICODE 在汉字的内码编排上完全是不一样的，没有一种简单的算术方法可<br>以把文本内容从UNICODE编码和另一种编码进行转换，这种转换必须通过查表来进行。
<p>如前所述，UNICODE 是用两个字节来表示为一个字符，他总共可以组合出65535不同<br>的字符，这大概已经可以覆盖世界上所有文化的符号。如果还不够也没有关系，ISO<br>已经准备了UCS-4方案，说简单了就是四个字节来表示一个字符，这样我们就可以组<br>合出21亿个不同的字符出来（最高位有其他用途），这大概可以用到银河联邦成立<br>那一天吧！
<p>UNICODE 来到时，一起到来的还有计算机网络的兴起，UNICODE 如何在网络上传输<br>也是一个必须考虑的问题，于是面向传输的众多 UTF（UCS Transfer Format）标准出<br>现了，顾名思义，UTF8就是每次8个位传输数据，而UTF16就是每次16个位，只不过<br>为了传输时的可靠性，从UNICODE到UTF时并不是直接的对应，而是要过一些算法<br>和规则来转换。<br>&nbsp;<br>受到过网络编程加持的计算机僧侣们都知道，在网络里传递信息时有一个很重要的<br>问题，就是对于数据高低位的解读方式，一些计算机是采用低位先发送的方法，例<br>如我们PC机采用的 INTEL 架构，而另一些是采用高位先发送的方式，在网络中交换<br>数据时，为了核对双方对于高低位的认识是否是一致的，采用了一种很简便的方法<br>，就是在文本流的开始时向对方发送一个标志符——如果之后的文本是高位在位，那<br>就发送"FEFF"，反之，则发送"FFFE"。不信你可以用二进制方式打开一个UTF-X格式<br>的文件，看看开头两个字节是不是这两个字节？
<p>讲到这里，我们再顺便说说一个很著名的奇怪现象：当你在 windows 的记事本里新<br>建一个文件，输入"联通"两个字之后，保存，关闭，然后再次打开，你会发现这两个<br>字已经消失了，代之的是几个乱码！呵呵，有人说这就是联通之所以拼不过移动的<br>原因。 <br>其实这是因为GB2312编码与UTF8编码产生了编码冲撞的原因。 <br>从网上引来一段从UNICODE到UTF8的转换规则：
<p>Unicode <br>UTF-8
<p>0000 - 007F <br>0xxxxxxx
<p>0080 - 07FF <br>110xxxxx 10xxxxxx
<p>0800 - FFFF <br>1110xxxx 10xxxxxx 10xxxxxx
<p>例如"汉"字的Unicode编码是6C49。6C49在0800-FFFF之间，所以要用3字节模板：<br>1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是：0110 1100 0100 1001，将这个比特流<br>按三字节模板的分段方法分为0110 110001 001001，依次代替模板中的x，得到：1110<br>-0110 10-110001 10-001001，即E6 B1 89，这就是其UTF8的编码。 <br>而当你新建一个文本文件时，记事本的编码默认是ANSI, 如果你在ANSI的编码输入汉<br>字，那么他实际就是GB系列的编码方式，在这种编码下，"联通"的内码是： <br>c1 1100 0001 <br>aa 1010 1010 <br>cd 1100 1101 <br>a8 1010 1000 <br>注意到了吗？第一二个字节、第三四个字节的起始部分的都是"110"和"10"，正好与<br>UTF8规则里的两字节模板是一致的，于是再次打开记事本时，记事本就误认为这是<br>一个UTF8编码的文件，让我们把第一个字节的110和第二个字节的10去掉，我们就得<br>到了"00001 101010"，再把各位对齐，补上前导的0，就得到了"0000 0000 0110 1010"，<br>不好意思，这是UNICODE的006A，也就是小写的字母"j"，而之后的两字节用UTF8解<br>码之后是0368，这个字符什么也不是。这就是只有"联通"两个字的文件没有办法在记<br>事本里正常显示的原因。 <br>而如果你在"联通"之后多输入几个字，其他的字的编码不见得又恰好是110和10开始<br>的字节，这样再次打开时，记事本就不会坚持这是一个utf8编码的文件，而会用ANSI<br>的方式解读之，这时乱码又不出现了。
<p>好了，终于可以回答NICO的问题了，在数据库里，有n前缀的字串类型就是UNICODE<br>类型，这种类型中，固定用两个字节来表示一个字符，无论这个字符是汉字还是英<br>文字母，或是别的什么。 <br>如果你要测试"abc汉字"这个串的长度，在没有n前缀的数据类型里，这个字串是7个<br>字符的长度，因为一个汉字相当于两个字符。而在有n前缀的数据类型里，同样的测<br>试串长度的函数将会告诉你是5个字符，因为一个汉字就是一个字符。 <br></p>
</div>
<img src ="http://www.blogjava.net/renyangok/aggbug/133666.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-07-31 22:35 <a href="http://www.blogjava.net/renyangok/archive/2007/07/31/133666.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jar,war,ear区别</title><link>http://www.blogjava.net/renyangok/archive/2007/02/08/98784.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Thu, 08 Feb 2007 07:22:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2007/02/08/98784.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/98784.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2007/02/08/98784.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/98784.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/98784.html</trackback:ping><description><![CDATA[
		<div class="postTitle">
				<font face="">
						<strong>        </strong>以最终客户的角度来看，.jar文件就是一种封装，他们不需要知道.jar文件中有多少个.class文件，每个文件中的功能与作用，同样可以得到他们希望的结果。除jar以外对于J2EE来说还有war和ear。区别见下表：</font>
		</div>
		<div class="postText">
				<p>
				</p>
				<table cellspacing="1" cellpadding="1" width="98%" align="center" border="1">
						<tbody>
								<tr>
										<td> </td>
										<td>
												<p align="center"> <font color="#ff0000">JAR</font></p>
										</td>
										<td>
												<p align="center"> <font color="#006400">WAR</font></p>
										</td>
										<td>
												<p align="center"> <font color="#0000ff">EAR</font></p>
										</td>
								</tr>
								<tr>
										<td> <strong>英文</strong></td>
										<td> <font color="#ff0000">Java Archive file</font></td>
										<td>  <font color="#006400">Web Archive file</font></td>
										<td> <font color="#0000ff">Enterprise Archive file</font></td>
								</tr>
								<tr>
										<td> <strong>包含内容</strong></td>
										<td> <font color="#ff0000">class、properties文件，是文件封装的最小单元；</font></td>
										<td> <font color="#006400">Servlet、JSP页面、JSP标记库、JAR库文件、HTML/XML文档和其他公用资源文件，如图片、音频文件等；</font></td>
										<td> <font color="#0000ff">除了包含JAR、WAR以外，还包括EJB组件</font></td>
								</tr>
								<tr>
										<td> <strong>部署文件</strong></td>
										<td> <font color="#ff0000">application-client.xml </font></td>
										<td> <font color="#006400">web.xml</font>   </td>
										<td> <font color="#0000ff">application.xml</font><br /></td>
								</tr>
								<tr>
										<td> <strong>级别</strong> </td>
										<td> <font color="#ff0000">小</font></td>
										<td> <font color="#006400">中</font></td>
										<td> <font color="#0000ff">大</font></td>
								</tr>
						</tbody>
				</table>
		</div>
<img src ="http://www.blogjava.net/renyangok/aggbug/98784.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2007-02-08 15:22 <a href="http://www.blogjava.net/renyangok/archive/2007/02/08/98784.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title> Web服务器开发环境下的线程安全问题</title><link>http://www.blogjava.net/renyangok/archive/2006/12/31/91179.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Sun, 31 Dec 2006 07:31:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/31/91179.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/91179.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/31/91179.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/91179.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/91179.html</trackback:ping><description><![CDATA[
		<p>Servlet是在多线程环境下的。即可能有多个请求发给一个servelt实例，每个请求是一个线程。<br />struts下的action也类似，同样在多线程环境下。可以参考struts user guide: <a href="http://struts.apache.org/struts-action/userGuide/building_controller.html">http://struts.apache.org/struts-action/userGuide/building_controller.html</a> 中的Action Class Design Guidelines一节:  Write code for a multi-threaded environment - Our controller servlet creates only one instance of your Action class, and uses this one instance to service all requests. Thus, you need to write thread-safe Action classes. Follow the same guidelines you would use to write thread-safe Servlets.<br />译:为多线程环境编写代码。我们的controller servlet指挥创建你的Action 类的一个实例，用此实例来服务所有的请求。因此，你必须编写线程安全的Action类。遵循与写线程安全的servlet同样的方针。<br /> <br />1.什么是线程安全的代码<br />  在多线程环境下能正确执行的代码就是线程安全的。<br />  安全的意思是能正确执行，否则后果是程序执行错误，可能出现各种异常情况。</p>
		<p>2.如何编写线程安全的代码<br />  很多书籍里都详细讲解了如何这方面的问题，他们主要讲解的是如何同步线程对共享资源的使用的问题。主要是对synchronized关键字的各种用法，以及锁的概念。<br />  Java1.5中也提供了如读写锁这类的工具类。这些都需要较高的技巧，而且相对难于调试。<br /> <br />  但是，线程同步是不得以的方法,是比较复杂的,而且会带来性能的损失。等效的代码中，不需要同步 在编写容易度和性能上会更好些。<br />  我这里强调的是什么代码是始终为线程安全的、是不需要同步的。如下:<br />  1)常量始终是线程安全的，因为只存在读操作。<br />  2)对构造器的访问(new 操作)是线程安全的，因为每次都新建一个实例，不会访问共享的资源。<br />  3)最重要的是:局部变量是线程安全的。因为每执行一个方法，都会在独立的空间创建局部变量，它不是共享的资源。局部变量包括方法的参数变量。<br />    struts user guide里有：<br />    Only Use Local Variables - The most important principle that aids in thread-safe coding is to use only local variables, not instance variables , in your Action class.<br />    译:只使用用局部变量。--编写线程安全的代码最重要的原则就是，在Action类中只使用局部变量，不使用实例变量。</p>
		<p> <br />总结：<br />    在Java的Web服务器环境下开发，要注意线程安全的问题。最简单的实现方式就是在Servlet和Struts Action里不要使用类变量、实例变量，但可以使用类常量和实例常量。<br />如果有这些变量，可以将它们转换为方法的参数传入，以消除它们。<br />    注意一个容易混淆的地方：被Servlet或Action调用的类中(如值对象、领域模型类)中是否可以安全的使用实例变量？如果你在每次方法调用时<br />新建一个对象，再调用它们的方法，则不存在同步问题---因为它们不是多个线程共享的资源，只有共享的资源才需要同步---而Servlet和Action的实例对于多个线程是共享的。<br />换句话说，Servlet和Action的实例会被多个线程同时调用，而过了这一层,如果在你自己的代码中没有另外启动线程，且每次调用后续业务对象时都是先新建一个实例再调用，则都是线程安全的。</p>
<img src ="http://www.blogjava.net/renyangok/aggbug/91179.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-31 15:31 <a href="http://www.blogjava.net/renyangok/archive/2006/12/31/91179.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中文问题及最优解决方案（本地化）</title><link>http://www.blogjava.net/renyangok/archive/2006/12/31/91106.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Sun, 31 Dec 2006 02:13:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/31/91106.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/91106.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/31/91106.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/91106.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/91106.html</trackback:ping><description><![CDATA[ 
<div>  <!--StartFragment --><span id="BlogViewId" be:sortmode="Normal" be:sortkey="" be:firsthandle="cns!AADDE4734A6787F!135" be:lasthandle="cns!AADDE4734A6787F!107">　Abstract：本文深入分析了Java程序设计中Java编译器对java源文件和JVM对class类文件的编码/解码过程，通过此过程的解析透视出了Java编程中中文问题产生的根本原因，最后给出了建议的最优化的解决Java中文问题的方法。  
<p>　　<strong>1、中文问题的来源 </strong></p><p>    计算机最初的操作系统支持的编码是单字节的字符编码，于是，在计算机中一切处理程序最初都是以单字节编码的英文为准进行处理。随着计算机的发展，为了适应世界其它民族的语言（当然包括我们的汉字），人们提出了UNICODE编码，它采用双字节编码，兼容英文字符和其它民族的双字节字符编码，所以，目前，大多数国际***的软件内部均采用UNICODE编码，在软件运行时，它获得本地支持系统（多数时间是操作系统）默认支持的编码格式，然后再将软件内部的 UNICODE转化为本地系统默认支持的格式显示出来。Java的JDK和JVM即是如此，我这里说的JDK是指国际版的JDK，我们大多数程序员使用的是国际化的JDK版本，以下所有的JDK均指国际化的JDK版本。我们的汉字是双字节编码语言，为了能让计算机处理中文，我们自己制定的gb2312、 GBK、GBK2K等标准以适应计算机处理的需求。所以，大部分的操作系统为了适应我们处理中文的需求，均定制有中文操作系统，它们采用的是GBK, GB2312编码格式以正确显示我们的汉字。如：中文Win2K默认采用的是GBK编码显示，在中文WIN2k中保存文件时默认采用的保存文件的编码格式也是GBK的，即，所有在中文WIN2K中保存的文件它的内部编码默认均采用GBK编码，注意：GBK是在GB2312基础上扩充来的。</p><p>    由于Java语言内部采用UNICODE编码，所以在JAVA程序运行时，就存在着一个从UNICODE编码和对应的操作系统及浏览器支持的编码格式转换输入、输出的问题，这个转换过程有着一系列的步骤，如果其中任何一步出错，则显示出来的汉字就会出是乱码，这就是我们常见的JAVA中文问题。</p><p>    同时，Java是一个跨平台的编程语言，也即我们编写的程序不仅能在中文windows上运行，也能在中文Linux等系统上运行，同时也要求能在英文等系统上运行（我们经常看到有人把在中文win2k上编写的JAVA程序，移植到英文Linux上运行）。这种移植操作也会带来中文问题。</p><p>    还有，有人使用英文的操作系统和英文的IE等浏览器，来运行带中文字符的程序和浏览中文网页，它们本身就不支持中文，也会带来中文问题。</p><p>    几乎所有的浏览器默认在传递参数时都是以UTF-8编码格式来传递，而不是按中文编码传递，所以，传递中文参数时也会有问题，从而带来乱码现象。</p><p>    总之，以上几个方面是JAVA中的中文问题的主要来源，我们把以上原因造成的程序不能正确运行而产生的问题称作：JAVA中文问题。</p><p>　<strong>　2、JAVA编码转换的详细过程 </strong></p><p>    我们常见的JAVA程序包括以下类别：<br />     *直接在console上运行的类(包括可视化界面的类)<br />     *JSP代码类（注：JSP是Servlets类的变型）<br />     *Servelets类<br />     *EJB类<br />     *其它不可以直接运行的支持类</p><p>    这些类文件中，都有可能含有中文字符串，并且我们常用前三类JAVA程序和用户直接交互，用于输出和输入字符，如：我们在JSP和Servlet中得到客户端送来的字符，这些字符也包括中文字符。无论这些JAVA类的作用如何，这些JAVA程序的生命周期都是这样的：</p><p>    *编程人员在一定的操作系统上选择一个合适的编辑软件来实现源程序代码并以.java扩展名保存在操作系统中，例如我们在中文win2k中用记事本编辑一个java源程序；<br />     *编程人员用JDK中的javac.exe来编译这些源代码，形成.class类(JSP文件是由容器调用JDK来编译的)；<br />     *直接运行这些类或将这些类布署到WEB容器中去运行，并输出结果。<br />    那么，在这些过程中，JDK和JVM是如何将这些文件如何编码和解码并运行的呢？</p><p>这里，我们以中文win2k操作系统为例说明JAVA类是如何来编码和被解码的。 </p><p></p><p>    <strong>第一步，</strong>我们在中文win2k中用编辑软件如记事本编写一个Java源程序文件(包括以上五类JAVA 程序)，程序文件在保存时默认采用了操作系统默认支持GBK编码格式(操作系统默认支持的格式为file.encoding格式)形成了一个.java文件，也即，java程序在被编译前，我们的JAVA源程序文件是采用操作系统默认支持的file.encoding编码格式保存的，java源程序中含有中文信息字符和英文程序代码；要查看系统的file.encoding参数，可以用以下代码：<br />　　public class ShowSystemDefaultEncoding {<br />　　public static void main(String[] args) {<br />　　String encoding = System.getProperty("file.encoding");<br />　　System.out.println(encoding);<br />　　}}</p><p>   <strong> 第二步，</strong>我们用JDK的javac.exe文件编译我们的Java源程序，由于JDK是国际版的，在编译的时候，如果我们没有用-encoding参数指定我们的 JAVA源程序的编码格式，则javac.exe首先获得我们操作系统默认采用的编码格式，也即在编译java程序时，若我们不指定源程序文件的编码格式，JDK首先获得操作系统的file.encoding参数(它保存的就是操作系统默认的编码格式，如WIN2k，它的值为GBK)，然后JDK就把我们的java源程序从file.encoding编码格式转化为JAVA内部默认的 UNICODE格式放入内存中。然后，javac把转换后的unicode格式的文件进行编译成.class类文件，此时.class文件是 UNICODE编码的，它暂放在内存中，紧接着，JDK将此以UNICODE编码的编译后的class文件保存到我们的操作系统中形成我们见到的. class文件。对我们来说，我们最终获得的.class文件是内容以UNICODE编码格式保存的类文件，它内部包含我们源程序中的中文字符串，只不过此时它己经由file.encoding格式转化为UNICODE格式了。</p><p>    这一步中，对于JSP源程序文件是不同的，对于JSP，这个过程是这样的：即WEB容器调用JSP编译器，JSP编译器先查看JSP文件中是否设置有文件编码格式，如果JSP文件中没有设置JSP文件的编码格式，则JSP编译器调用JDK先把JSP文件用JVM默认的字符编码格式(也即WEB容器所在的操作系统的默认的file.encoding)转化为临时的Servlet类，然后再把它编译成UNICODE格式的class类，并保存在临时文件夹中。如：在中文win2k上，WEB容器就把JSP文件从GBK编码格式转化为UNICODE格式，然后编译成临时保存的Servlet类，以响应用户的请求。</p><p>    <strong>第三步，运行第二步编译出来的类，分为三种情况：</strong></p><p>    A、 直接在console上运行的类<br />    B、 EJB类和不可以直接运行的支持类(如JavaBean类)<br />    C、 JSP代码和Servlet类<br />    D、 JAVA程序和数据库之间<br />    下面我们分这四种情况来看。<br />   <strong> A、直接在console上运行的类</strong></p><p>    这种情况，运行该类首先需要JVM支持，即操作系统中必须安装有JRE。运行过程是这样的：首先java启动JVM，此时JVM读出操作系统中保存的 class文件并把内容读入内存中，此时内存中为UNICODE格式的class类，然后JVM运行它，如果此时此类需要接收用户输入，则类会默认用 file.encoding编码格式对用户输入的串进行编码并转化为unicode保存入内存（用户可以设置输入流的编码格式）。程序运行后，产生的字符串（UNICODE编码的）再回交给JVM，最后JRE把此字符串再转化为file.encoding格式(用户可以设置输出流的编码格式)传递给操作系统显示接口并输出到界面上。</p><p>    对于这种直接在console上运行的类，它的转化过程可用图1更加明确的表示出来：</p><p>图1</p><p align="center"><img style="CURSOR: pointer" src="http://www.pconline.com.cn/pcedu/empolder/gj/java/0404/pic/0430java_1.gif" /></p><p align="left">以上每一步的转化都需要正确的编码格式转化，才能最终不出现乱码现象。<br /><br />    <strong>B、EJB类和不可以直接运行的支持类(如JavaBean类)<br /><br /></strong>    由于EJB类和不可以直接运行的支持类，它们一般不与用户直接交互输入和输出，它们常常与其它的类进行交互输入和输出，所以它们在第二步被编译后，就形成了内容是UNICODE编码的类保存在操作系统中了，以后只要它与其它的类之间的交互在参数传递过程中没有丢失，则它就会正确的运行。<br />这种EJB类和不可以直接运行的支持类, 它的转化过程可用图2更加明确的表示出来： </p><p></p><p>图2</p><p align="center"><img style="CURSOR: pointer" src="http://www.pconline.com.cn/pcedu/empolder/gj/java/0404/pic/0430java_2.gif" /></p><p><br />   <strong> C、JSP代码和Servlet类</strong></p><p>    经过第二步后，JSP文件也被转化为Servlets类文件，只不过它不像标准的Servlets一校存在于classes目录中，它存在于WEB容器的临时目录中，故这一步中我们也把它做为Servlets来看。</p><p>    对于Servlets，客户端请求它时，WEB容器调用它的JVM来运行Servlet，首先，JVM把Servlet的class类从系统中读出并装入内存中，内存中是以UNICODE编码的Servlet类的代码，然后JVM在内存中运行该Servlet类，如果Servlet在运行的过程中，需要接受从客户端传来的字符如：表单输入的值和URL中传入的值，此时如果程序中没有设定接受参数时采用的编码格式，则WEB容器会默认采用ISO-8859- 1编码格式来接受传入的值并在JVM中转化为UNICODE格式的保存在WEB容器的内存中。Servlet运行后生成输出，输出的字符串是 UNICODE格式的，紧接着，容器将Servlet运行产生的UNICODE格式的串（如html语法，用户输出的串等）直接发送到客户端浏览器上并输出给用户，如果此时指定了发送时输出的编码格式，则按指定的编码格式输出到浏览器上，如果没有指定，则默认按ISO-8859-1编码发送到客户的浏览器上。这种JSP代码和Servlet类，它的转化过程可用图3更加明确地表示出来：</p><p>图3</p><p align="center"><img style="CURSOR: pointer" src="http://www.pconline.com.cn/pcedu/empolder/gj/java/0404/pic/0430java_3.gif" /></p><p align="left"><strong>D、Java程序和数据库之间</strong></p><p></p><p>    对于几乎所有数据库的JDBC驱动程序，默认的在JAVA程序和数据库之间传递数据都是以ISO-8859-1为默认编码格式的，所以，我们的程序在向数据库内存储包含中文的数据时，JDBC首先是把程序内部的UNICODE编码格式的数据转化为ISO-8859-1的格式，然后传递到数据库中，在数据库保存数据时，它默认即以ISO-8859-1保存，所以，这是为什么我们常常在数据库中读出的中文数据是乱码。<br />    对于JAVA程序和数据库之间的数据传递，我们可以用图4清晰地表示出来</p><p>图4</p><p align="center"><img style="CURSOR: pointer" src="http://www.pconline.com.cn/pcedu/empolder/gj/java/0404/pic/0430java_4.gif" /></p><p><br /><strong>    3、分析常见的JAVA中文问题几个必须清楚的原则<br /></strong><br />    首先，经过上面的详细分析，我们可以清晰地看到，任何JAVA程序的生命期中，其编码转换的关键过程是在于：最初编译成class文件的转码和最终向用户输出的转码过程。<br />    其次，我们必须了解JAVA在编译时支持的、常用的编码格式有以下几种：<br />    *ISO-8859-1，8-bit, 同8859_1,ISO-8859-1,ISO_8859_1等编码<br />    *Cp1252，美国英语编码，同ANSI标准编码<br />    *UTF-8，同unicode编码<br />    *GB2312，同gb2312-80,gb2312-1980等编码<br />    *GBK , 同MS936，它是gb2312的扩充<br />    及其它的编码，如韩文、日文、繁体中文等。同时，我们要注意这些编码间的兼容关体系如下：<br />    unicode和UTF-8编码是一一对应的关系。GB2312可以认为是GBK的子集，即GBK编码是在gb2312上扩展来的。同时，GBK编码包含了20902个汉字，编码范围为：0x8140-0xfefe，所有的字符可以一一对应到UNICODE2.0中来。</p><p>    再次，对于放在操作系统中的.java源程序文件，在编译时，我们可以指定它内容的编码格式，具体来说用-encoding来指定。注意：如果源程序中含有中文字符，而你用-encoding指定为其它的编码字符，显然是要出错的。用-encoding指定源文件的编码方式为GBK或gb2312，无论我们在什么系统上编译含有中文字符的JAVA源程序都不会有问题，它都会正确地将中文转化为UNICODE存储在class文件中。<br />    <br />    然后，我们必须清楚，几乎所有的WEB容器在其内部默认的字符编码格式都是以ISO-8859-1为默认值的，同时，几乎所有的浏览器在传递参数时都是默认以UTF-8的方式来传递参数的。所以，虽然我们的Java源文件在出入口的地方指定了正确的编码方式，但其在容器内部运行时还是以ISO-8859- 1来处理的。<br /></p></span><br /><!--StartFragment --> <span id="BlogViewId" be:sortmode="Normal" be:sortkey="" be:firsthandle="cns!AADDE4734A6787F!135" be:lasthandle="cns!AADDE4734A6787F!107"><strong>4、中文问题的分类及其建议最优解决办法</strong><p>    了解以上JAVA处理文件的原理之后，我们就可以提出了一套建议最优的解决汉字问题的办法。<br />    我们的目标是：我们在中文系统中编辑的含有中文字符串或进行中文处理的JAVA源程序经编译后可以移值到任何其它的<a href="http://www.pconline.com.cn/pcedu/soft/st/"><u><font color="#0000ff">操作系统</font></u></a>中正确运行，或拿到其它操作系统中编译后能正确运行，能正确地传递中文和英文参数，能正确地和<a href="http://www.pconline.com.cn/pcedu/empolder/db/"><u><font color="#0000ff">数据库</font></u></a>交流中英文字符串。<br />    我们的具体思路是：在JAVA程序转码的入口和出口及JAVA程序同用户有输入输出转换的地方限制编码方法使之正确即可。</p><p>    <strong>具体解决办法如下：</strong></p><p>  <strong>  1、 针对直接在console上运行的类</strong><br />    对于这种情况，我们建议在程序编写时，如果需要从用户端接收用户的可能含有中文的输入或含有中文的输出，程序中应该采用字符流来处理输入和输出，具体来说，应用以下面向字符型节点流类型：<br />    对文件：FileReader，FileWrieter <br />        其字节型节点流类型为：FileInputStream，FileOutputStream<br />    对内存（数组）：CharArrayReader，CharArrayWriter<br />        其字节型节点流类型为：ByteArrayInputStream，ByteArrayOutputStream<br />    对内存（字符串）：StringReader，StringWriter<br />    对管道：PipedReader，PipedWriter<br />        其字节型节点流类型为：PipedInputStream，PipedOutputStream<br />    同时，应该用以下面向字符型处理流来处理输入和输出：<br />    BufferedWriter，BufferedReader<br />        其字节型的处理流为：BufferedInputeStream，BufferedOutputStream<br />    InputStreamReader，OutputStreamWriter<br />    其字节型的处理流为：DataInputStream，DataOutputStream<br />    其中InputStreamReader和InputStreamWriter用于将字节流按照指定的字符编码集转换到字符流，如：<br />    InputStreamReader in = new InputStreamReader(System.in，"GB2312")；<br />    OutputStreamWriter out = new OutputStreamWriter (System.out，"GB2312")；<br />    例如：采用如下的示例JAVA编码就达到了要求：</p><p>    //Read.java<br />    import java.io.*;<br />    public class Read {<br />    public static void main(String[] args) throws IOException {<br />    String str = "\n中文测试，这是内部硬编码的串"+"\ntest english character";<br />    String strin= "";<br />    BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in,"gb2312")); //设置输入接口按中文编码<br />    BufferedWriter stdout = new BufferedWriter(new OutputStreamWriter(System.out,"gb2312")); //设置输出接口按中文编码<br />    stdout.write("请输入:");<br />    stdout.flush();<br />    strin = stdin.readLine();<br />    stdout.write("这是从用户输入的串："+strin);<br />    stdout.write(str);<br />    stdout.flush();<br />    }}<br />    同时，在编译程序时，我们用以下方式来进行：<br />    javac -encoding gb2312 Read.java<br />    其运行结果如图5所示：<br /></p><p align="center"><img style="CURSOR: pointer" src="http://www.pconline.com.cn/pcedu/empolder/gj/java/0405/pic/040508_java1.gif" /></p><p>    图5<br /><strong>2、 针对EJB类和不可以直接运行的支持类(如JavaBean类)</strong></p><p>    由于这种类它们本身被其它的类调用，不直接与用户交互，故对这种类来说，我们的建议的处理方式是内部程序中应该采用字符流来处理程序内部的中文字符串（具体如上面一节中一样），同时，在编译类时用-encoding gb2312参数指示源文件是中文格式编码的即可。</p><p><br /><strong>    3、 针对Servlet类</strong></p><p>    针对Servlet，我们建议用以下方法：（我建议将.java文件设置为UTF8编码的。当然如果是用Eclipse的话，只要设置下就是了。对于数据库，我以为编码最好设置为UTF8,便于国际化 。尽可能的使用UTF8码，  千里草）</p><p>    在编译Servlet类的源程序时，用-encoding指定编码为GBK或GB2312，且在向用户输出时的编码部分用response对象的 setContentType("text/html;charset=GBK");或gb2312来设置输出编码格式，同样在接收用户输入时，我们用 request.setCharacterEncoding("GB2312")；这样无论我们的servlet类移植到什么操作系统中，只有客户端的浏览器支持中文显示，就可以正确显示。如下是一个正确的示例：</p><p>    //HelloWorld.java<br />    package hello;<br />    import java.io.*;<br />    import javax.servlet.*;<br />    import javax.servlet.http.*;<br />    public class HelloWorld extends HttpServlet<br />    {<br />    public void init() throws ServletException { }<br />    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException<br />    {<br />    request.setCharacterEncoding("GB2312"); //设置输入编码格式<br />    response.setContentType("text/html;charset=GB2312"); //设置输出编码格式<br />    PrintWriter out = response.getWriter(); //建议使用PrintWriter输出<br />    out.println("&lt;hr&gt;");<br />    out.println("Hello World! This is created by Servlet!测试中文!");<br />    out.println("&lt;hr&gt;");<br />    }<br />    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException<br />    {<br />    request.setCharacterEncoding("GB2312"); //设置输入编码格式<br />    response.setContentType("text/html;charset=GB2312"); //设置输出编码格式<br />    String name = request.getParameter("name");<br />    String id = request.getParameter("id");<br />    if(name==null) name="";<br />    if(id==null) id="";<br />    PrintWriter out = response.getWriter(); //建议使用PrintWriter输出<br />    out.println("&lt;hr&gt;");<br />    out.println("你传入的中文字串是：" + name);<br />    out.println("&lt;hr&gt;你输入的id是：" + id);<br />    out.println("&lt;hr&gt;");<br />    }<br />    public void destroy() { }<br />    }<br />        请用javac -encoding gb2312 HelloWorld.java来编译此程序。<br />        测试此Servlet的程序如下所示：<br />    &lt;%@page contentType="text/html; charset=gb2312"%&gt;<br />    &lt;%request.setCharacterEncoding("GB2312");%&gt;<br />    &lt;html&gt;&lt;head&gt;&lt;title&gt;&lt;/title&gt;<br />    &lt;Script language="JavaScript"&gt;<br />    function Submit() {<br />    //通过URL传递中文字符串值给Servlet<br />    document.base.action = "./HelloWorld?name=中文";<br />    document.base.method = "POST";<br />    document.base.submit();<br />    }<br />    &lt;/Script&gt;<br />    &lt;/head&gt;</p><p>&lt;body bgcolor="#FFFFFF" text="#000000" topmargin="5"&gt;<br />    &lt;form name="base" method = "POST" target="_self"&gt;<br />    &lt;input name="id" type="text" value="" size="30"&gt;<br />    &lt;a href = "JavaScript:Submit()"&gt;传给Servlet&lt;/a&gt;<br />    &lt;/form&gt;&lt;/body&gt;&lt;/html&gt;<br />    其运行结果如图6所示：<br /></p><p></p><p align="center"><img style="CURSOR: pointer" src="http://www.pconline.com.cn/pcedu/empolder/gj/java/0405/pic/040508_java2.gif" /></p><p>    图6<br /> <strong>   4、 JAVA程序和数据库之间</strong></p><p>    为避免JAVA程序和数据库之间数据传递出现乱码现象，我们建议采用以下最优方法来处理：<br />    1、 对于JAVA程序的处理方法按我们指定的方法处理。<br />    2、 把数据库默认支持的编码格式改为GBK或GB2312的。</p><p>    如：在mysql中，我们可以在配置文件my.ini中加入以下语句实现：<br />    在[mysqld]区增加：<br />    default-character-set=gbk<br />    并增加：<br />    [client]<br />    default-character-set=gbk<br />    在SQL Server2K中，我们可以将数据库默认的语言设置为Simplified Chinese来达到目的。</p><p><strong>    5、 针对JSP代码</strong></p><p>    由于JSP是在运行时，由WEB容器进行动态编译的，如果我们没有指定JSP源文件的编码格式，则JSP编译器会获得服务器操作系统的 file.encoding值来对JSP文件编译的，它在移植时最容易出问题，如在中文win2k中可以很好运行的jsp文件拿到英文linux中就不行，尽管客户端都是一样的，那是因为容器在编译JSP文件时获取的操作系统的编码不同造成的（在中文wink中的file.encoding和在英文 Linux中file.encoding是不同的，且英文Linux的file.encoding对中文不支持，所以编译出来的JSP类就会有问题）。<a href="http://www.pconline.com.cn/pcedu/soft/lan/jywgl/"><u><font color="#0000ff">网络</font></u></a>上讨论的大多数是此类问题，多是因为JSP文件移植平台时不能正确显示的问题，对于这类问题，我们了解了JAVA中程序编码转换的原理，解决起来就容易多了。我们建议的解决办法如下：</p><p>    1、我们要保证JSP向客户端输出时是采用中文编码方式输出的，即无论如何我们首先在我们的JSP源代编中加入以下一行：</p><p>    &lt;<a href="mailto:%@page"><u><font color="#0000ff">%@page</font></u></a> contentType="text/html; charset=gb2312"%&gt;<br />    2、为了让JSP能正确获得传入的参数，我们在JSP源文件头加入下面一句：<br />    &lt;%request.setCharacterEncoding("GB2312");%&gt;<br />    3、为了让JSP编译器能正确地解码我们的含有中文字符的JSP文件，我们需要在JSP源文件中指定我们的JSP源文件的编码格式，具体来说，我们在JSP源文件头上加入下面的一句即可：<br />    &lt;<a href="mailto:%@page"><u><font color="#0000ff">%@page</font></u></a> pageEncoding="GB2312"%&gt;或&lt;<a href="mailto:%@page"><u><font color="#0000ff">%@page</font></u></a> pageEncoding="GBK"%&gt;<br />    这是JSP规范2.0新增加的指令。<br />    我们建议使用此方法来解JSP文件中的中文问题，下面的代码是一个正确做法的JSP文件的测试程序：</p><p>//testchinese.jsp<br />    &lt;%@page pageEncoding="GB2312"%&gt;<br />    &lt;%@page contentType="text/html; charset=gb2312"%&gt;<br />    &lt;%request.setCharacterEncoding("GB2312");%&gt;<br />    &lt;%<br />    String action = request.getParameter("ACTION");<br />    String name = "";<br />    String str = "";<br />    if(action!=null &amp;&amp; action.equals("SENT"))<br />    {<br />    name = request.getParameter("name");<br />    str = request.getParameter("str");<br />    }<br />    %&gt;<br />    &lt;html&gt;<br />    &lt;head&gt;<br />    &lt;title&gt;&lt;/title&gt;<br />    &lt;Script language="JavaScript"&gt;<br />    function Submit()<br />    {<br />    document.base.action = "?ACTION=SENT&amp;str=传入的中文";<br />    document.base.method = "POST";<br />    document.base.submit();<br />    }<br />    &lt;/Script&gt;<br />    &lt;/head&gt;<br />    &lt;body bgcolor="#FFFFFF" text="#000000" topmargin="5"&gt;<br />    &lt;form name="base" method = "POST" target="_self"&gt;<br />    &lt;input type="text" name="name" value="" size="30"&gt;<br />    &lt;a href = "JavaScript:Submit()"&gt;提交&lt;/a&gt;<br />    &lt;/form&gt;<br />    &lt;%<br />    if(action!=null &amp;&amp; action.equals("SENT"))<br />    {<br />    out.println("&lt;br&gt;你输入的字符为："+name);<br />    out.println("&lt;br&gt;你通过URL传入的字符为："+str);<br />    }<br />    %&gt;<br />    &lt;/body&gt;<br />    &lt;/html&gt;<br />    如图7是此程序运行的结果示意图：<br /></p><p></p><p align="center"><img style="WIDTH: 500px; CURSOR: pointer" height="347" alt="" hspace="0" src="http://www.pconline.com.cn/pcedu/empolder/gj/java/0405/pic/040508_java3.gif" width="500" border="0" /></p><p>    图7</p><p><strong>    5、总结</strong></p><p>    在上面的详细分析中，我们清晰地给出了JAVA在处理源程序过程中的详细转换过程，为我们正确解决JAVA编程中的中文问题提供了基础。同时，我们给出了认为是最优的解决JAVA中文问题的办法。</p></span><!--StartFragment --><span id="BlogViewId" be:sortmode="Normal" be:sortkey="" be:firsthandle="cns!AADDE4734A6787F!135" be:lasthandle="cns!AADDE4734A6787F!107"><strong><font color="#000000"><u>我的补充（需要特别注意）： 在表单（form ）提交时，如果提交的方法为get，那么request.setCharacterEncoding() 是不起作用的。此时在服务器端得到的字符编码仍然是ISO8859-1，而不是你设置的编码。所以一般我们在提交数据时，尽量使用post方法，此时 request.setCharacterEncoding()方法起效。<br /></u></font></strong></span><br /><!--StartFragment --> 如果非要使用get方法传form则要转换一下才行： <br />  -----   <br />  &lt;%@   page   contentType="text/html;charset=gb2312"%&gt;   <br />  &lt;%!   <br />          public   String   getStr(String   str){   <br />  try{   <br />  String   temp_p=str;   <br />  byte[]   temp_t=temp_p.getBytes("ISO8859-1");   <br />  String   temp=new   String(temp_t);   <br />  return   temp;   <br />  }   <br />  catch(Exception   e){   <br />  }   <br />  return   "null";   <br />    }   <br />    %&gt;   <br />  然后把String   userId=request.getParameter("userId");改成   <br />  String   userId=getStr(request.getParameter("userId"));   <br />－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br />我来说一下tomcat如何实现JSP的你就明白了。<br />预备知识：<br />　1.字节和unicode<br />　　Java内核是unicode的，就连class文件也是，但是很多媒体，包括文件/流的保存方式<br />　　是使用字节流的。 因此Java要对这些字节流经行转化。char是unicode的，而byte是字节.<br />　　Java中byte/char互转的函数在sun.io的包中间有。其中ByteToCharConverter类是中调度，<br />　　可以用来告诉你，你用的Convertor。其中两个很常用的静态函数是<br />　　 public static ByteToCharConverter getDefault() ;<br />　　 public static ByteToCharConverter getConverter(String encoding);<br />　　如果你不指定converter，则系统会自动使用当前的Encoding,GB平台上用GBK,EN平台上用<br />　　8859_1<br />　　<br />　　我们来就一个简单的例子：<br />　　　　　"你"的gb码是：0xC4E3 ,unicode是0x4F60<br />　　　　　你用:<br />　　　　　--encoding="gb2312";<br />　　　　　--byte b[]={(byte)'\u00c4',(byte)'\u00E3'};<br />　　　　　--convertor=ByteToCharConverter.getConverter(encoding);<br />　　　　　--char [] c=converter.convertAll(b);<br />　　　　　--for(int i=0;i&lt;c.length;c++)<br />　　　　　--{<br />　　　　　-- System.out.println(Integer.toHexString(c[i]));<br />　　　　　--}<br />　　　　　--打印出来是0x4F60<br />　　　　　--但是如果使用8859_1的编码，打印出来是<br />　　　　　--0x00C4,0x00E3<br />　　　　　----例１<br />　　　　 反过来：<br />　　　　  --encoding="gb2312";<br />　　　　  --char c[]={'\u4F60'};<br />　　　　　--convertor=ByteToCharConverter.getConverter(encoding);<br />　　　　　--byte [] b=converter.convertAll(c);<br />　　　　　--for(int i=0;i&lt;b.length;c++)<br />　　　　　--{<br />　　　　　-- System.out.println(Integer.toHexString(b[i]));<br />　　　　　--}<br />　　　　　　--打印出来是：0xC4,0xE3<br />　　　　　　----例２<br />　　　　　　--如果用8859_1就是0x3F，?号，表示无法转化　　　　　　--<br />　　　　　 很多中文问题就是从这两个最简单的类派生出来的。而却有很多类　　<br />　　不直接支持把Encoding输入，这给我们带来诸多不便。很多程序难得用encoding<br />　　了，直接用default的encoding，这就给我们移植带来了很多困难<br />　　--<br />　　2.UTF-8<br />　　--UTF-8是和Unicode一一对应的，其实现很简单<br />　　--<br />　　-- 7位的Unicode: 0 _ _ _ _ _ _ _<br />　　--11位的Unicode: 1 1 0 _ _ _ _ _ 1 0 _ _ _ _ _ _<br />　　--16位的Unicode: 1 1 1 0 _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _<br />　　--21位的Unicode: 1 1 1 1 0 _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _ 1 0 _ _ _ _ _ _<br />　　--大多数情况是只使用到16位以下的Unicode:<br />　　--"你"的gb码是：0xC4E3 ,unicode是0x4F60<br />　　--我们还是用上面的例子<br />　　--　　--例１：0xC4E3的二进制：<br />　　--　　--　　　 1 1 0 0 0 1 0 0 1 1 1 0 0 0 1 1<br />　　--　　--　　　 由于只有两位我们按照两位的编码来排，但是我们发现这行不通，<br />　　--　　--　　　 因为第７位不是0因此，返回"?"<br />　　--　　--　　　 <br />　　--　　--例２：0x4F60的二进制：<br />　　--　　--　　　 0 1 0 0 1 1 1 1 0 1 1 0 0 0 0 0 <br />　　--　　--　　　 我们用UTF-8补齐，变成：<br />　　--　　--　　　 11100100 10111101 10100000<br />　　--　　--　　　 E4--BD-- A0<br />　　--　　--　　　 于是返回0xE4,0xBD,0xA0<br />　　--　　--<br />　　3.String和byte[]<br />　　--String其实核心是char[],然而要把byte转化成String，必须经过编码。<br />　　--String.length()其实就是char数组的长度，如果使用不同的编码，很可<br />　　--能会错分，造成散字和乱码。<br />　　--例：<br />　　----byte [] b={(byte)'\u00c4',(byte)'\u00e3'};<br />　　----String str=new String(b,encoding);　　----<br />　　----如果encoding=8859_1，会有两个字，但是encoding=gb2312只有一个字　　----<br />　　--这个问题在处理分页是经常发生<br />　　4.Reader,Writer/InputStream,OutputStream<br />　　--Reader和Writer核心是char，InputStream和OutputStream核心是byte。<br />　　--但是Reader和Writer的主要目的是要把Char读/写InputStream/OutputStream<br />--一个reader的例子：<br />--文件test.txt只有一个"你"字，0xC4,0xE3--<br />--String encoding=;<br />--InputStreamReader reader=new InputStreamReader(<br />----new FileInputStream("text.txt"),encoding);<br />--char []c=new char[10];<br />--int length=reader.read(c);<br />--for(int i=0;i&lt;c.length;i++)<br />----System.out.println(c[i]);<br />　　--如果encoding是gb2312，则只有一个字符，如果encoding=8859_1，则有两个字符<br />　　--------<br />--<br />--<br />　　<br />　　 ----<br />　2.我们要对Java的编译器有所了解：<br />　--javac -encoding<br />　 我们常常没有用到ENCODING这个参数。其实Encoding这个参数对于跨平台的操作是很重要的。<br />　 如果没有指定Encoding，则按照系统的默认Encoding,gb平台上是gb2312，英文平台上是ISO8859_1。　 <br />　--Java的编译器实际上是调用sun.tools.javac.Main的类，对文件进行编译，这个类　--<br />　有compile函数中间有一个encoding的变量,-encoding的参数其实直接传给encoding变量。<br />　编译器就是根据这个变量来读取java文件的，然后把用UTF-8形式编译成class文件。<br />　一个例子：<br />　--public void test()<br />　--{<br />　----String str="你";<br />　----FileWriter write=new FileWriter("test.txt");<br />　----write.write(str);<br />　----write.close();<br />　--}<br />　----例３<br />--如果用gb2312编译，你会找到E4 BD A0的字段<br />--<br />--如果用8859_1编译，<br />--00C4 00E3的二进制：<br />--00000000 11000100 00000000 11100011--<br />--因为每个字符都大于7位，因此用11位编码：<br />--11000001 10000100 11000011 10100011 <br />--C1-- 84--　C3--　 A3<br />--你会找到C1 84 C3 A3 --<br />　　　　<br />　　但是我们往往忽略掉这个参数，因此这样往往会有跨平台的问题：<br />　　--　　例３在中文平台上编译，生成ZhClass<br />　　--　　例３在英文平台上编译，输出EnClass<br />　　--1.　 ZhClass在中文平台上执行OK,但是在英文平台上不行<br />　　--2.　 EnClass在英文平台上执行OK,但是在中文平台上不行<br />　　原因：<br />　--1.在中文平台上编译后，其实str在运行态的char[]是0x4F60,　----<br />　--在中文平台上运行，FileWriter的缺省编码是gb2312,因此<br />　--CharToByteConverter会自动用调用gb2312的converter,把str转化<br />　--成byte输入到FileOutputStream中，于是0xC4,0xE3放进了文件。<br />　--但是如果是在英文平台下，CharToByteConverter的缺省值是8859_1,<br />　--FileWriter会自动调用8859_1去转化str,但是他无法解释，因此他会<br />　--输出"?"　----<br />　--2.　在英文平台上编译后，其实str在运行态的char[]是0x00C4 0x00E3,　----<br />　--在中文平台上运行，中文无法识别，因此会出现??<br />　--　　在英文平台上，0x00C4--&gt;0xC4,0x00E3-&gt;0xE3，因此0xC4,0xE3被放进了<br />　--文件<br />----<br />1.对于JSP正文的解释：<br />--Tomcat首先看一下你的叶面中有没有"&lt;<a href="mailto:%@page">%@page</a> include的符号。有，则在相同<br />--地方设定response.setContentType(..);按照encoding的来读，没有他按照8859_1<br />--读取文件，然后用UTF-8写成.java文件，然后用sun.tools.Main去读取这个文件，<br />--（当然它使用UTF-8去读），然后编译成class文件<br />--setContentType改变的是out的属性，out变量缺省的encoding是8859_1<br />2.对Parameter的解释<br />--很不幸Parameter只有ISO8859_1的解释，这个质料可以在servlet的实现代码中找到。<br />3.对include的解释<br />格式的，但是很不幸，由于那个写"org.apache.jasper.compiler.Parser"的人<br />在数组JspUtil.ValidAttribute[]忘记加了一个参数：encoding,因此导致不支<br />持这种方式。你完全可以编译源代码，加上对encoding的支持<br />总结：<br />如果你在NT底下，最简单的方法就是欺骗java,不加任何Encoding变量：<br />&lt;html&gt;<br />你好&lt;%=request.getParameter("value")%&gt;<br />&lt;/html&gt;<br /><a href="http://localhost/test/test.jsp?value">http://localhost/test/test.jsp?value</a>=你<br />结果:你好你<br />但这种方法局限性较大，比如对上传的文章分段，这样的做法是死定的，最好的<br />解决方案是用这种方案：<br />&lt;%@ page contentType="text/html;charset=gb2312" %&gt;<br />&lt;html&gt;<br />你好&lt;%=new String(request.getParameter("value").getBytes("8859_1"),"gb2312")%&gt;<br />&lt;/html&gt; </div><div><div><br />&lt;select name="account.accountId" &gt;<br />    &lt;OPTION value="&lt;%=account.getAccountId()%&gt;"&gt;我的日志&lt;/OPTION&gt;<br />    &lt;OPTION value=""&gt;所有日志&lt;/OPTION&gt;<br />    &lt;OPTION &lt;%=s%&gt; value="&lt;%=a.getAccountId()%&gt;"&gt;&lt;%=a.getAccountName()%&gt;&lt;/OPTION&gt;<br />   &lt;/select&gt;</div></div><img src ="http://www.blogjava.net/renyangok/aggbug/91106.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-31 10:13 <a href="http://www.blogjava.net/renyangok/archive/2006/12/31/91106.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>初学者如何开发出一个高质量的J2EE系统</title><link>http://www.blogjava.net/renyangok/archive/2006/12/31/91105.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Sun, 31 Dec 2006 02:11:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/31/91105.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/91105.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/31/91105.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/91105.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/91105.html</trackback:ping><description><![CDATA[ 
<div>　　J2EE学习者越来越多，J2EE本身技术不断在发展，涌现出各种概念，本文章试图从一种容易理解的角度对这些概念向初学者进行解释，以便掌握学习J2EE学习方向。</div><div>　　首先我们需要知道Java和J2EE是两个不同概念，Java不只是指一种语言，已经代表与微软不同的另外一个巨大阵营，所以Java有时是指一种软件系统的流派，当然目前主要是.NET和Java两大主流体系。</div><div>　　J2EE可以说指Java在数据库信息系统上实现，数据库信息系统从早期的dBase、到Delphi/VB等C/S结构，发展到B/S（Browser浏览器/Server服务器）结构，而J2EE主要是指B/S结构的实现。</div><div>　　J2EE又是一种框架和标准，框架类似API、库的概念，但是要超出它们。如果需要详细了解框架，可先从设计模式开始学习。</div><div>　　J2EE是一个虚的大的概念，J2EE标准主要有三种子技术标准：WEB技术、EJB技术和JMS，谈到J2EE应该说最终要落实到这三个子概念上。</div><div>　　这三种技术的每个技术在应用时都涉及两个部分：容器部分和应用部分，Web容器也是指Jsp/Servlet容器，你如果要开发一个Web应用，无论是编译或运行，都必须要有Jsp/Servlet库或API支持（除了JDK/J2SE以外）。</div><div>　　Web技术中除了Jsp/Servlet技术外，还需要JavaBeans或Java Class实现一些功能或者包装携带数据，所以Web技术最初裸体简称为Jsp/Servlet+JavaBeans系统。</div><div>　　谈到JavaBeans技术，就涉及到组件构件技术（component），这是Java的核心基础部分，很多软件设计概念（设计模式）都是通过JavaBeans实现的。</div><div>　　JavaBeans不属于J2EE概念范畴中，如果一个JavaBeans对象被Web技术（也就是Jsp/Servlet）调用，那么JavaBeans就运行在J2EE的Web容器中；如果它被EJB调用，它就运行在EJB容器中。</div><div>　　EJB（企业JavaBeans）是普通JavaBeans的一种提升和规范，因为企业信息系统开发中需要一个可伸缩的性能和事务、安全机制，这样能保证企业系统平滑发展，而不是发展到一种规模重新更换一套软件系统。</div><div>　　至此，JavaBeans组件发展到EJB后，并不是说以前的那种JavaBeans形式就消失了，这就自然形成了两种JavaBeans技术：EJB 和POJO，POJO完全不同于EJB概念，指的是普通JavaBeans，而且这个JavaBeans不依附某种框架，或者干脆可以说：这个 JavaBeans是你为这个应用程序单独开发创建的。</div><div>　　J2EE应用系统开发工具有很多：如JBuilder、 Eclipse等，这些IDE首先是Java开发工具，也就是说，它们首要基本功能是可以开发出JavaBeans或Java class，但是如果要开发出J2EE系统，就要落实到要么是Web技术或EJB技术，那么就有可能要一些专门模块功能(如eclipse需要 lomboz插件)，最重要的是，因为J2EE系统区分为容器和应用两个部分，所以，在任何开发工具中开发J2EE都需要指定J2EE容器。</div><div>　　J2EE容器分为WEB容器和EJB容器，Tomcat/Resin是Web容器；JBoss是EJB容器+Web容器等，其中Web容器直接使用 Tomcat实现的。所以你开发的Web应用程序可以在上面两种容器运行，而你开发的Web+EJB应用则只可以在JBoss服务器上运行，商业产品 Websphere/Weblogic等和JBoss属于同一种性质。</div><div>　　J2EE容器也称为J2EE服务器，大部分时它们概念是一致的。</div><div>　　如果你的J2EE应用系统的数据库连接是通过JNDI获得，也就是说是从容器中获得，那么你的J2EE应用系统基本与数据库无关，如果你在你的J2EE 应用系统耦合了数据库JDBC驱动的配置，那么你的J2EE应用系统就有数据库概念色彩，作为一个成熟需要推广的J2EE应用系统，不推荐和具体数据库耦合，当然这其中如何保证J2EE应用系统运行性能又是体现你的设计水平了。</div><div>　　衡量J2EE应用系统设计开发水平高低的标准就是：解耦性；你的应用系统各个功能是否能够彻底脱离？是否不相互依赖，也只有这样，才能体现可维护性、可拓展性的软件设计目标。</div><div>　　为了达到这个目的，诞生各种框架概念，J2EE框架标准将一个系统划分为WEB和EJB主要部分，当然我们有时不是以这个具体技术区分，而是从设计上抽象为表现层、服务层和持久层，这三个层次从一个高度将J2EE分离开来，实现解耦目的。</div><div>　　因此，我们实际编程中，也要将自己的功能向这三个层次上靠，做到大方向清楚，泾渭分明，但是没有技术上约束限制要做到这点是很不容易的，因此我们还是必须借助J2EE具体技术来实现，这时，你可以使用EJB规范实现服务层和持久层，Web技术实现表现层；</div><div>　　EJB为什么能将服务层从Jsp/Servlet手中分离出来，因为它对JavaBeans编码有强制的约束，现在有一种对JavaBeans弱约束，使用Ioc模式实现的（当然EJB 3.0也采取这种方式），在Ioc模式诞生前，一般都是通过工厂模式来对JavaBeans约束，形成一个服务层，这也是是Jive这样开源论坛设计原理之一。</div><div>　　由此，将服务层从表现层中分离出来目前有两种可选架构选择：管理普通JavaBeans（POJO）框架(如 Spring、JdonFramework)以及管理EJB的EJB框架，因为EJB不只是框架，还是标准，而标准可以扩展发展，所以，这两种区别将来是可能模糊，被纳入同一个标准了。　但是，个人认为：标准制定是为某个目的服务的，总要牺牲一些换取另外一些，所以，这两种架构会长时间并存。</div><div>　　这两种架构分歧也曾经诞生一个新名词：完全POJO的系统也称为轻量级系统(lightweight)，其实这个名词本身就没有一个严格定义，更多是一个吸引人的招牌，轻量是指容易学习容易使用吗？按照这个定义，其实轻量Spring等系统并不容易学习；而且EJB 3.0（依然叫EJB）以后的系统是否可称为轻量级了呢？</div><div>　　前面谈了服务层框架，使用服务层框架可以将 JavaBeans从Jsp/Servlet中分离出来，而使用表现层框架则可以将Jsp中剩余的JavaBeans完全分离，这部分JavaBeans 主要负责显示相关，一般是通过标签库（taglib）实现，不同框架有不同自己的标签库，Struts是应用比较广泛的一种表现层框架。</div><div>　　这样，表现层和服务层的分离是通过两种框架达到目的，剩余的就是持久层框架了，通过持久层的框架将数据库存储从服务层中分离出来是其目的，持久层框架有两种方向：直接自己编写JDBC等SQL语句（如iBatis）；使用O/R Mapping技术实现的Hibernate和JDO技术；当然还有EJB中的实体Bean技术。</div><div>　　持久层框架目前呈现百花齐放，各有优缺点的现状，所以正如表现层框架一样，目前没有一个框架被指定为标准框架，当然，表现层框架现在又出来了一个JSF，它代表的页面组件概念是一个新的发展方向，但是复杂的实现让人有些忘而却步。</div><div>　　在所有这些J2EE技术中，虽然SUN公司发挥了很大的作用，不过总体来说：网络上有这样一个评价：SUN的理论天下无敌；SUN的产品用起来撞墙；对于初学者，特别是那些试图通过或已经通过SUN认证的初学者，赶快摆脱SUN的阴影，立即开溜，使用开源领域的产品来实现自己的应用系统。</div><div>　　最后，你的J2EE应用系统如果采取上面提到的表现层、服务层和持久层的框架实现，基本你也可以在无需深刻掌握设计模式的情况下开发出一个高质量的应用系统了。</div><div>　　还要注意的是: 开发出一个高质量的J2EE系统还需要正确的业务需求理解，那么域建模提供了一种比较切实可行的正确理解业务需求的方法，相关详细知识可从UML角度结合理解。</div><div>　　当然，如果你想设计自己的行业框架，那么第一步从设计模式开始吧，因为设计模式提供你一个实现JavaBeans或类之间解耦参考实现方法，当你学会了系统基本单元JavaBean或类之间解耦时，那么系统模块之间的解耦你就可能掌握，进而你就可以实现行业框架的提炼了，这又是另外一个发展方向了。</div><div>　　以上理念可以总结为一句话：Java学习开发三件宝: Domain Model（域建模）、Patterns（模式）和Framework（框架）。集三宝理念于一身，小中型J2EE项目快速开发工具：Jdon Framework <br />----------------------------------------------------------------------------------------------------</div><div>JoannaYe ask:<br />你好 Banq先生 关注你的文章很长一段时间了, 对你在Java领域的技术水平,以及在很多问题上的看法, 也非常佩服. 国内目前达到你的水平的人真是很少(当然高人也许都隐居起来了). 但是, 有几个问题想与你讨论:<br />首先,软件是一个绝对的应用技术,任何技术离开了具体的应用, 坦率地说是毫无价值的.我看,Jdon也有在这方面的尝试,如网站,网上商店生成系统等.但这与真正的企业应用还有非常大的距离. 我不了解,你在这一领域里为什么没有涉足,是因为你认为很困难,基本上是以我们国内目前的技术水平无法到达呢, 还是因为你不屑于这方面的深入, 认为你所追求的是纯粹超然的技术概念呢.<br />我的其他问题有赖于了解你关于这个问题的回答,让我们继续关注和讨论. </div><div>banq answer:<br /> <br />&gt;但这与真正的企业应用还有非常大的距离. 我不了解,你在这一领域里为什么没有涉足,是因为你认为很困难,基本上是以我们国内目前的技术</div><div>多谢探讨，这个问题很复杂，大概有下列几点：<br />1. 现在软件技术不再象以前的技术，以前的技术可以说只有做个这个行业大型软件系统的经验的人才可以说对这些软件技术有掌握，而现在的技术则不必了，J2EE 讲究架构，J2EE它是一套应用软件的规范，也就是说，J2EE是很多做过大型软件的人进行汇总后的经验精华，一个大型系统需要哪些技术部分、什么时候适合什么技术，在J2EE标准中基本都有涉及，例如EJB技术、JMS等。</div><div>这样，如果你能完全掌握和驾驭这些J2EE架构技术，你有时确实不必一定要做个大型软件经验才型，这称为站在巨人的肩膀上。</div><div>但是反过来，如果你没有丰富的软件系统实战经验，你去理解EJB/JMS等就很困难，所以这两个技术对初学者比较难的原因之一。</div><div>2. UML结合J2EE这样OO一套实施过程从方法论以及模式角度固化了软件数据库系统的分析设计开发，这也是因为有MDA（将这些过程用软件自动生成代码）诞生的原因。虽然这些简化了我们开发系统的过程，但是这只是解决了应用系统的一部分问题，工作流等尚未成熟，使用这样方式开发系统，依据我的经验，最后会将烦琐和细致的工作压在Jsp页面上，目前开发一个系统，结合标签库和用户界面需求这个工作反而花费我更多时间，希望JSF在这方面能有效率提升，等这些技术细节都能解决，基本J2EE非常成熟了。</div><div>3.目前我通过咨询角色和一些软件公司一起承接一些企业应用项目，例如去年承接一个大型外资人事系统，他们要求管理GE 等几家外资企业的人事福利（这些企业外包人事给他们），如果专为一家公司开发人事很简单，但是要求这个人事适合多家，那么重用性要求很高，设计抽象面很高，他们在新加坡有类似系统，但技术很老，我听过新加坡的系统，他们也有一些经验总结，大部分和我的J2EE设计相吻合，我和新加坡的人交流过想法，他们很惊奇，不太相信，加上费用问题，只进行了初步架构设计就搁浅了。</div><div>4.不要小看网站系统，以前网站系统都是用PHP Perl做，功能很弱，无法和企业系统相比，但是随着Inernet普及，更多人要求联网，例如如果一家公司的ERP通过互联网实现，那么老总出差就很方便，但是现在为一家公司开发一个基于internet的ERP很贵，比传统的贵，这不合理，这也是SOA提出的目的之一，以后ERP实现网上租用，就象你申请一个Blog或论坛或Email，你可以为你的企业申请一个ERP系统，这样只要企业付租费就可以了，这可理想目前已经接近，前段时间美国一家提供这种服务的企业来上海做宣传，他们的业绩增长速度极其快 500%.</div><div>通过网站提供ERP等企业服务对于软件设计的重用性要求很高，就一套邮箱系统可以服务很多用户一样，你必须设计出一套重要性、灵活性很高的ERP系统适合不同的用户，可见网站软件的水平是极其高的。前面我做的网站自动生成系统到现在我都认为完成不够好，现在很多网站都提供这种服务，这象Blog，但是Blog等只限制你网站模板，而不是自由定制页面，所以 Blog这些都是小孩玩家家，根本无发走向商业，著名的那个方兴东鼓吹Blog，其实没有技术革新，靠你媒体吹呼就是革命了。 <br /> <br />JoannaYe ask:</div><div>谢谢 Banq 先生在6月23日非常认真的回复(抱歉由于忙,没能马上回复). 总结起来, 如果我的理解不错的话, 你的结论是 1)你认为网站系统并不可小觑(同意,一个高访问量,同时能够实现网上交易的网站的确如此).2)EJB/JMS技术对于初学者来说是不容易,但是对你来说,你是可以Handdle的. 3)你也有承接企业系统的实际经验,象你说的那个HR系统. 但不知您以咨询身份参加的这个HR系统到底都解决的是实际管理中的什么样的问题?在性能方面都达到了什么样的水平? 具体来说,采用了哪些技术(诸如您帖中提到的一些技术)应对了实际中具体的什么样的问题. 此外以你在这个HR系统的经验来看, 是一个多少人的Team,采取什么样的开发方式和开发进度(人员和时间的分配比例)开发的.你认为在这样的一个项目的开发过程中最关键的是什么?最影响 Prductivity的又是什么? </div><div>对这样一些问题看上去似乎很空泛,但是实际上能够真正反映出我一开始提出的 issue,"软件是一个绝对的应用技术,任何技术离开了具体的应用, 坦率地说是毫无价值的".举个例子来说,书本上,名家们会告诉你, Value List Handler 这个设计模式是解决这样的问题:"You have a remote client that wants to iterate over a large results list." 一般来说,如果是一个大量地查找某一些"topic/dimension"下的数据,这样的问题,我们也毫无疑问地确定要用到这个模式.但是,如果对一条具体的数据,如某一个销售员,要和他的客户讨论(在线谈判)他们之间的一个具体合同,这时候会不会也需要用到这个模式.如果要用这个模式,到底是用 Stateful Session Bean 还是用 Stateless Session Bean 实现呢,他们各自在实现方法上对性能的影响是什么, 当你决定采用了某种实现方法,你到底是怎样Tradeoff的呢; 最后对这个设计模式来说,在最终的设计方案中如何把它抽象到对一个通用的,普遍的业务问题,而不是仅仅对"某一个销售员,要和他的客户讨论他们之间的一个具体合同"这样的一个特例问题,作出一个通用的解决方案,适应任意规模,任意业务的企业,真正达到软件工程的目标:高度的Reusing 和 Scalablity. 实际的企业应用系统就是充满着类似这样的问题,很有挑战.但有些技术人员就仅仅满足于自己可以用某项技术做出一些小的Demo了,就不愿意,或根本不屑于深入下去面对一个实际的应用问题.</div><div>因此, 我相信您应该能够非常理解,我为什么感兴趣了解您对我上面提出问题答案.</div><div>您的很多看法都很不错, 我非常同意, 希望我们能在今后进一步深入地探讨. 谢谢!</div><div>banq answer:</div><div>&gt;你认为在这样的一个项目的开发过程中最关键的是什么?最影响 Prductivity的又是什么? </div><div>当这样的项目使用框架组件组合后，由于系统重要重用的功能已经封装在框架软件中，所以，只要能够组装出应用系统，一般第一次测试就会立即通过，我已经不止一次体会这种快感，我现在基本告别以前那种花费大量时间在Java调试上时代，我相信很多初学者还在这个泥潭里挣扎，这就成为影响一个产品的主要原因，现在使用jdon框架开发，几乎消灭这个因素。</div><div>那么,现在最影响 Prductivity的是什么？就是技术外的因素：项目管理。</div><div>关于你提的性能方面设计达到什么水平等，这些我已经整合进入Jdon框架，使用Jdon框架开发，几乎无需考虑性能设计，一开始就具有优越的性能，这些都是有测试数据，Java产品的好处就是一切可以自己动手，不必听从第三方评价，因为那些都有失公正，服务器配置上Jprofile/Optimizeit，客户端配置Jmeter，启动几个线程一跑，Jdon框架和应用程序的性能真相就出来了，所以，在Java领域，开源和商业产品是在同一起跑线，面对不同的用户：前者是更有头脑，自己动手；后者是对自己缺乏自信的人；服务是两者的重点。</div><div>&gt;在最终的设计方案中如何把它抽象到对一个通用的,普遍的业务问题,而不&gt;是仅仅对"某一个销售员,要和他的客户讨论他们之间的一个具体合同"这&gt;样的一个特例问题</div><div>其实你说的行业框架提炼的问题，这和业务相关，Jdon框架等都是基础框架，没有这些组件框架的优雅解决方式，就没有行业框架的好的开始，我想你不希望在行业框架提炼之后，发现无法加入一些纵向功能，就象数据库设计好之后，几年以后却成为你发展的障碍。</div><div>行业框架需要资深的行业背景，这也不是一般人做的，但是工作流/Portal等都是行业框架的提炼，这些也是我们以后发展的方向。</div><div>就我个人来说，我愿意解决重要问题，然后我告诉更多人解决方向，如果他们相信，大家一起努力来解决所有问题，而不是靠我一个人解决所有问题。</div><div>JoannaYe ask:</div><div>谢谢Banq先生的回复, 你的很多观点都很好,我非常同意.象你所说最影响Prductivity的是技术外的因素：项目管理. 但我不知你能不能有一些具体的看法.因为任何行业,最终的问题, 竞争力的问题都是如何通过管理来提高Prductivity. 不知你对软件这一行业有没有特别的见解.</div><div>开源项目的确有它的优势,特别是作这些开源项目的人,往往是一些技术的精英.但是, 我还是以为应该以成熟的Commercial产品作为自己开发的基础,即所谓"巨人的肩膀". 这是因为, 成功的Commercial产品往往更注重最终用户, 这是这些产品能够给它的公司带来巨大的商业利润的源泉.纯技术的专家往往会忽视这一点.</div><div>要成就一件事(一个大型企业管理应用的项目), 是需要很多人踏踏实实,坚持不懈(这也非常重要)的努力.这和去上上课,或者在场外指导一下,有很大的区别. </div><div>我希望通过你这个论坛, 结识一些志同道合的朋友, 能够作成这样一件事.再次谢谢你的回复, 我因为很多时候很忙,有一些Deadline非常紧的事情,有时没能马上给您回复, 请你见谅. </div><div>banq answer:</div><div>非常感谢JoannaYe 讨论，从言论中感觉你是一个职业的项目经理。非常专业。</div><div>项目经理和设计师良好沟通和理解交流，是一个项目成功的关键。</div><div>关于开源和Commercial区别，我个人觉得它们之间真的没有严格的区别，只不过是两种思路的表现：开源通过免费产品卖服务；Commercial是既想卖产品又卖服务，不能因为它的产品卖钱，就是技术好，一般是市场品牌好。</div><div>就拿EJB实现来说，在所有J2EE服务器中只有开源JBoss 4.0使用AOP实现，坚持AOP的一些纯设计派认为EJB过时了，那么Weblogic /Websphere等这些以支持EJB自诩的服务器产品反而不如开源产品呢。这些人认为：EJB<br />但是正如你说：为什么客户还是购买Websphere等服务器，因为它们注重最终用户。</div><div>我认为一直试图在这两者之间寻找平衡是挑战的事情。</div><div>-------------------------------------------------------------------------------------------------------------------<br /> <br />shuiwx ask:<br /> <br />banq老师好，最近大致抽象总结出了一个比较浅显的规律，既是您平均一两个月能够发一篇比较的适合初学者的帖子，但每一篇都可以对偶的有关知识的梳理和导向能够起到很重要的作用，不敢说终生受用但也似乎会持久难忘了，在此还是要道一声感谢。</div><div>既然题目是初学者...高质量的J2EE系统，那么就题目本身这个用例来说，参与者该是“novice”了，领域模型应该是"高质量的"+"J2EE系统 ",那么能否请您再深一步的举个样例来说明何为"high quality j2ee system"呢？估计您不会选petshop,但有可能会将jive和jdon算进来，但偶真正想看到的是一个就您个人来讲曾经有过 consultant经验的项目作为例子来简要阐述下高质量+j2ee系统的概貌，或者象您前面某篇论oo和数据库的矛盾的文章一样，能否前瞻性的给出一个在您心目中最理想的高质量j2ee系统的轮廓呢？比如jsf(new version&gt;1.1)+ejb3.0+j2ee设计模式?偶觉得struts+spring+hibernate并不敢称为高质量的或是 j2ee系统，所以总觉得从现在开始既该有意识的用一下jsf+ejb3来设计了，但由于不知道有没有人在这方面开始吃螃蟹了，所以只好去随大流的关心些什么ajax,xp之类的流行名词了。但从内心来讲，无论是javascript还是组件式编程，无论是spring+hibernate还是ejb3, 无论是xp还是fdd,无非是想尽量按照客户的要求迅速提交一个界面新颖，结构稳定的一个能够跨平台的良好的系统吧。假如能预知何为一个好的系统的话，似乎事情会变的简单些，也就不必为那些喋喋不休的争论着技术名词的人们所困扰了。</div><div>但由于目前偶的能力所限和所处的时期的特殊性，似乎想马上就拿jsf+ejb3来首选做企业级开发还有点不现实，那么作为一个apprentice来说，能够做的似乎只有学习模式了，偶不知道关于模式该学到多深才合适，只相信尽量选择从建模的时候就配合着设计模式来考虑可能会有助于系统的稳定和重用，谈到这里有引申出关于题目的另外一个话题，就是 “初学者”，偶觉得如果想作为计算机编程人员的话，面对着不停的新技术名词和版本更迭，似乎偶总要做一名初学着来的说，于是最近有意识的在看一些数据结构方面的课程，希望能够从一些理论基础中来寻找那些所谓的新技术背后所蕴涵的知识，但还是那句话，由于能力有限，所得甚浅，所以希望您如果能站在一个咨询家的角度来看，能否指点一下，就您认为的如果想设计一个好的软件系统来说，或许不仅限于j2ee,该多看看哪些computer science中的理论知识呢？偶不知道这个问题提的对不对，但总觉得设计模式对于系统的意义，是类似于数据结构和算法之相对于程序的意义的，所以假如您在类似的方面能有些心得的话，希望能够得到一点指点。</div><div>（偶的废话似乎不少，希望banq老师能忍受） <br /> <br />banq answer:</div><div>很抱歉现在才回复你的问题：<br />&gt;如果想设计一个好的软件系统来说，或许不仅限于j2ee,该多看看哪些&gt;computer science中的理论知识</div><div>设计一个好的软件系统我文章里其实写了，掌握分层解耦宗旨，学习使用一些现成的框架就可以了，如果你不原意囫囵吞枣，那么研究一下这些框架设计原理和模式，这些会花费你很长探索，数据结构、编译原理这些已经成为底层机制，就象汇编是底层一样，现在的大学计算机教学完全是错误的，学习的都是正确的废话。所以没有必要在那些大学课程上浪费时间。</div><div>增强项目经验，研读源码，自己动手编写项目是提升水平的唯一道路。<br />以上只是我个人意见。</div><img src ="http://www.blogjava.net/renyangok/aggbug/91105.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-31 10:11 <a href="http://www.blogjava.net/renyangok/archive/2006/12/31/91105.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一篇关于session的好文</title><link>http://www.blogjava.net/renyangok/archive/2006/12/31/91104.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Sun, 31 Dec 2006 02:10:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/31/91104.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/91104.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/31/91104.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/91104.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/91104.html</trackback:ping><description><![CDATA[ 
<div class="ContentFont" id="NewaspContentLabel" style="PADDING-RIGHT: 10px; DISPLAY: block; PADDING-LEFT: 10px; PADDING-BOTTOM: 0px; PADDING-TOP: 0px"><font id="font_word" style="FONT-SIZE: 14px; FONT-FAMILY: 宋体,Verdana,Arial,Helvetica,sans-serif"><p><font size="2">作者：郎云鹏（dev2dev ID: hippiewolf）</font></p><p><font size="2">摘要：虽然session机制在web应用程序中被采用已经很长时间了，但是仍然有很多人不清楚session机制的本质，以至不能正确的应用这一技术。本文将详细讨论session的工作机制并且对在Java web application中应用session机制时常见的问题作出解答。</font></p><p><font size="2">目录：<br /></font><a href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=10#1"><font size="2">一、术语session</font></a><br /><a href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=10#2"><font size="2">二、HTTP协议与状态保持</font></a><br /><a href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=10#3"><font size="2">三、理解cookie机制</font></a><br /><a href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=10#4"><font size="2">四、理解session机制</font></a><br /><a href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=10#5"><font size="2">五、理解javax.servlet.http.HttpSession</font></a><br /><a href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=10#6"><font size="2">六、HttpSession常见问题</font></a><br /><a href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=10#7"><font size="2">七、跨应用程序的session共享</font></a><br /><a href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=10#8"><font size="2">八、总结</font></a><br /><a href="http://dev2dev.bea.com.cn/bbs/jishudata/ArticleShow.jsp?Id=10#9"><font size="2">参考文档</font></a></p><p id="#1"><font size="2"><b>一、术语session</b><br />在我的经验里，session这个词被滥用的程度大概仅次于transaction，更加有趣的是transaction与session在某些语境下的含义是相同的。</font></p><p><font size="2">session，中文经常翻译为会话，其本来的含义是指有始有终的一系列动作/消息，比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个 session。有时候我们可以看到这样的话“在一个浏览器会话期间，...”，这里的会话一词用的就是其本义，是指从一个浏览器窗口打开到关闭这个期间 ①。最混乱的是“用户（客户端）在一次会话期间”这样一句话，它可能指用户的一系列动作（一般情况下是同某个具体目的相关的一系列动作，比如从登录到选购商品到结账登出这样一个网上购物的过程，有时候也被称为一个transaction），然而有时候也可能仅仅是指一次连接，也有可能是指含义①，其中的差别只能靠上下文来推断②。</font></p><p><font size="2">然而当session一词与<a class="wordstyle" href="http://www.supcode.com/" target="_blank">网络</a>协议相关联时，它又往往隐含了“面向连接”和/或“保持状态”这样两个含义，“面向连接”指的是在通信双方在通信之前要先建立一个通信的渠道，比如打电话，直到对方接了电话通信才能开始，与此相对的是写信，在你把信发出去的时候你并不能确认对方的地址是否正确，通信渠道不一定能建立，但对发信人来说，通信已经开始了。“保持状态”则是指通信的一方能够把一系列的消息关联起来，使得消息之间可以互相依赖，比如一个服务员能够认出再次光临的老顾客并且记得上次这个顾客还欠店里一块钱。这一类的例子有“一个TCP session”或者“一个POP3 session”③。</font></p><p><font size="2">而到了web服务器蓬勃发展的时代，session在web开发语境下的语义又有了新的扩展，它的含义是指一类用来在客户端与服务器之间保持状态的解决方案 ④。有时候session也用来指这种解决方案的存储结构，如“把xxx保存在session里”⑤。由于各种用于web开发的语言在一定程度上都提供了对这种解决方案的支持，所以在某种特定语言的语境下，session也被用来指代该语言的解决方案，比如经常把Java里提供的 javax.servlet.http.HttpSession简称为session⑥。</font></p><p><font size="2">鉴于这种混乱已不可改变，本文中session一词的运用也会根据上下文有不同的含义，请大家注意分辨。<br />在本文中，使用中文“浏览器会话期间”来表达含义①，使用“session机制”来表达含义④，使用“session”表达含义⑤，使用具体的“HttpSession”来表达含义⑥</font></p><p id="#2"><font size="2"><b>二、HTTP协议与状态保持</b><br />HTTP协议本身是无状态的，这与HTTP协议本来的目的是相符的，客户端只需要简单的向服务器请求<a class="wordstyle" href="http://www.supdown.com/" target="_blank">下载</a>某些文件，无论是客户端还是服务器都没有必要纪录彼此过去的行为，每一次请求之间都是独立的，好比一个顾客和一个自动售货机或者一个普通的（非会员制）大卖场之间的关系一样。</font></p><p><font size="2">然而聪明（或者贪心？）的人们很快发现如果能够提供一些按需生成的动态信息会使web变得更加有用，就像给有线电视加上点播功能一样。这种需求一方面迫使 HTML逐步添加了表单、脚本、DOM等客户端行为，另一方面在服务器端则出现了CGI规范以响应客户端的动态请求，作为传输载体的HTTP协议也添加了文件上载、cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的session机制则是又一种在客户端与服务器之间保持状态的解决方案。</font></p><p><font size="2">让我们用几个例子来描述一下cookie和session机制之间的区别与联系。笔者曾经常去的一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠，然而一次性消费5杯咖啡的机会微乎其微，这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案：<br />1、该店的店员很厉害，能记住每位顾客的消费数量，只要顾客一走进咖啡店，店员就知道该怎么对待了。这种做法就是协议本身支持状态。<br />2、发给顾客一张卡片，上面记录着消费的数量，一般还有个有效期限。每次消费时，如果顾客出示这张卡片，则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。<br />3、发给顾客一张会员卡，除了卡号之外什么信息也不纪录，每次消费时，如果顾客出示该卡片，则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。</font></p><p><font size="2">由于HTTP协议是无状态的，而出于种种考虑也不希望使之成为有状态的，因此，后面两种方案就成为现实的选择。具体来说cookie机制采用的是在客户端保持状态的方案，而session机制采用的是在服务器端保持状态的方案。同时我们也看到，由于采用服务器端保持状态的方案在客户端也需要保存一个标识，所以session机制可能需要借助于cookie机制来达到保存标识的目的，但实际上它还有其他选择。</font></p><p id="#3"><font size="2"><b>三、理解cookie机制</b><br />cookie机制的基本原理就如上面的例子一样简单，但是还有几个问题需要解决：“会员卡”如何分发；“会员卡”的内容；以及客户如何使用“会员卡”。</font></p><p><font size="2">正统的cookie分发是通过扩展HTTP协议来实现的，服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如Javascript或者VBscript也可以生成cookie。</font></p><p><font size="2">而cookie 的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie，如果某个cookie所声明的作用范围大于等于将要请求的资源所在的位置，则把该cookie附在请求资源的HTTP请求头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出示，如果某家分店还发行了自己的会员卡，那么进这家店的时候除了要出示麦当劳的会员卡，还要出示这家店的会员卡。</font></p><p><font size="2">cookie的内容主要包括：名字，值，过期时间，路径和域。<br />其中域可以指定某一个域比如.google.com，相当于总店招牌，比如宝洁公司，也可以指定一个域下的具体某台机器比如www.google.com或者froogle.google.com，可以用飘柔来做比。<br />路径就是跟在域名后面的URL路径，比如/或者/foo等等，可以用某飘柔专柜做比。<br />路径与域合在一起就构成了cookie的作用范围。<br />如果不设置过期时间，则表示这个cookie的生命期为浏览器会话期间，只要关闭浏览器窗口，cookie就消失了。这种生命期为浏览器会话期的 cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里，当然这种行为并不是规范规定的。如果设置了过期时间，浏览器就会把cookie保存到硬盘上，关闭后再次打开浏览器，这些cookie仍然有效直到超过设定的过期时间。</font></p><p><font size="2">存储在硬盘上的cookie可以在不同的浏览器进程间共享，比如两个IE窗口。而对于保存在内存里的cookie，不同的浏览器有不同的处理方式。对于 IE，在一个打开的窗口上按Ctrl-N（或者从文件菜单）打开的窗口可以与原窗口共享，而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存 cookie；对于Mozilla Firefox0.8，所有的进程和标签页都可以共享同样的cookie。一般来说是用javascript的window.open打开的窗口会与原窗口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用session机制的web应用程序开发者造成很大的困扰。</font></p><p><font size="2">下面就是一个goolge设置cookie的响应头的例子<br />HTTP/1.1 302 Found<br />Location: http://www.google.com/intl/zh-CN/<br />Set-Cookie: PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com<br />Content-Type: text/html</font></p><p align="center"><font size="2"><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.supcode.com/Article/UploadPic/2005-6/200562922161847.jpg" width="408" onload="javascript:if(this.width&gt;screen.width-600)this.style.width=screen.width-600;" border="0" /></font></p><p><br /><font size="2">这是使用HTTPLook这个HTTP Sniffer<a class="wordstyle" href="http://www.supdown.com/" target="_blank">软件</a>来俘获的HTTP通讯纪录的一部分</font></p><p align="center"><font size="2"><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.supcode.com/Article/UploadPic/2005-6/200562922167755.jpg" width="432" onload="javascript:if(this.width&gt;screen.width-600)this.style.width=screen.width-600;" border="0" /></font></p><p><br /><font size="2">浏览器在再次访问goolge的资源时自动向外发送cookie</font></p><p align="center"><font size="2"><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.supcode.com/Article/UploadPic/2005-6/2005629221613750.jpg" width="421" onload="javascript:if(this.width&gt;screen.width-600)this.style.width=screen.width-600;" border="0" /></font></p><p><br /><font size="2">使用Firefox可以很容易的观察现有的cookie的值<br />使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。</font></p><p align="center"><font size="2"><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.supcode.com/Article/UploadPic/2005-6/2005629221620917.jpg" width="324" onload="javascript:if(this.width&gt;screen.width-600)this.style.width=screen.width-600;" border="0" /></font></p><p><br /><font size="2">IE也可以设置在接受cookie前询问</font></p><p align="center"><font size="2"><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.supcode.com/Article/UploadPic/2005-6/2005629221623485.jpg" width="239" onload="javascript:if(this.width&gt;screen.width-600)this.style.width=screen.width-600;" border="0" /></font></p><p><br /><font size="2">这是一个询问接受cookie的对话框。</font></p><p id="#4"><font size="2"><b>四、理解session机制</b><br />session机制是一种服务器端的机制，服务器使用一种类似于散列表的结构（也可能就是使用散列表）来保存信息。</font></p><p><font size="2">当程序需要为某个客户端的请求创建一个session的时候，服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为session id，如果已包含一个session id则说明以前已经为此客户端创建过session，服务器就按照session id把这个session检索出来使用（如果检索不到，可能会新建一个），如果客户端请求不包含session id，则为此客户端创建一个session并且生成一个与此session相关联的session id，session id的值应该是一个既不会重复，又不容易被找到规律以仿造的字符串，这个session id将被在本次响应中返回给客户端保存。</font></p><p><font size="2">保存这个session id的方式可以采用cookie，这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于 SEEESIONID，而。比如weblogic对于web应用程序生成的cookie，JSESSIONID= ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764，它的名字就是 JSESSIONID。</font></p><p><font size="2">由于 cookie可以被人为的禁止，必须有其他机制以便在cookie被禁止时仍然能够把session id传递回服务器。经常被使用的一种技术叫做URL重写，就是把session id直接附加在URL路径的后面，附加方式也有两种，一种是作为URL路径的附加信息，表现形式为http://...../xxx; jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764<br />另一种是作为查询字符串附加在URL后面，表现形式为http://...../xxx?jsessionid=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764<br />这两种方式对于用户来说是没有区别的，只是服务器在解析的时候处理的方式不同，采用第一种方式也有利于把session id的信息和正常程序参数区分开来。<br />为了在整个交互过程中始终保持状态，就必须在每个客户端可能请求的路径后面都包含这个session id。</font></p><p><font size="2">另一种技术叫做表单隐藏字段。就是服务器会自动修改表单，添加一个隐藏字段，以便在表单提交时能够把session id传递回服务器。比如下面的表单<br />&lt;form name="testform" action="/xxx"&gt;<br />&lt;input type="text"&gt;<br />&lt;/form&gt;<br />在被传递给客户端之前将被改写成<br />&lt;form name="testform" action="/xxx"&gt;<br />&lt;input type="hidden" name="jsessionid" value="ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!-145788764"&gt;<br />&lt;input type="text"&gt;<br />&lt;/form&gt;<br />这种技术现在已较少应用，笔者接触过的很古老的iPlanet6(SunONE应用服务器的前身)就使用了这种技术。<br />实际上这种技术可以简单的用对action应用URL重写来代替。</font></p><p><font size="2">在谈论session机制的时候，常常听到这样一种误解“只要关闭浏览器，session就消失了”。其实可以想象一下会员卡的例子，除非顾客主动对店家提出销卡，否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的，除非程序通知服务器删除一个session，否则服务器会一直保留，程序一般都是在用户做log off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭，因此服务器根本不会有机会知道浏览器已经关闭，之所以会有这种错觉，是大部分session机制都使用会话cookie来保存session id，而关闭浏览器后这个session id就消失了，再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上，或者使用某种手段改写浏览器发出的 HTTP请求头，把原来的session id发送给服务器，则再次打开浏览器仍然能够找到原来的session。</font></p><p><font size="2">恰恰是由于关闭浏览器不会导致session被删除，迫使服务器为seesion设置了一个失效时间，当距离客户端上一次使用session的时间超过这个失效时间时，服务器就可以认为客户端已经停止了活动，才会把session删除以节省存储空间。</font></p><p id="#5"><font size="2"><b>五、理解javax.servlet.http.HttpSession</b><br />HttpSession是Java平台对session机制的实现规范，因为它仅仅是个接口，具体到每个web应用服务器的提供商，除了对规范支持之外，仍然会有一些规范里没有规定的细微差异。这里我们以BEA的Weblogic Server8.1作为例子来演示。</font></p><p><font size="2">首先，Weblogic Server提供了一系列的参数来控制它的HttpSession的实现，包括使用cookie的开关选项，使用URL重写的开关选项，session持久化的设置，session失效时间的设置，以及针对cookie的各种设置，比如设置cookie的名字、路径、域，cookie的生存时间等。</font></p><p><font size="2">一般情况下，session都是存储在内存里，当服务器进程被停止或者重启的时候，内存里的session也会被清空，如果设置了session的持久化特性，服务器就会把session保存到硬盘上，当服务器进程重新启动或这些信息将能够被再次使用，Weblogic Server支持的持久性方式包括文件、数据库、客户端cookie保存和复制。</font></p><p><font size="2">复制严格说来不算持久化保存，因为session实际上还是保存在内存里，不过同样的信息被复制到各个cluster内的服务器进程中，这样即使某个服务器进程停止工作也仍然可以从其他进程中取得session。</font></p><p><font size="2">cookie生存时间的设置则会影响浏览器生成的cookie是否是一个会话cookie。默认是使用会话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解。</font></p><p><font size="2">cookie的路径对于web应用程序来说是一个非常重要的选项，Weblogic Server对这个选项的默认处理方式使得它与其他服务器有明显的区别。后面我们会专题讨论。</font></p><p><font size="2">关于session的设置参考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869</font></p><p id="#6"><font size="2"><b>六、HttpSession常见问题</b><br />（在本小节中session的含义为⑤和⑥的混合）</font></p><p><br /><font size="2">1、session在何时被创建<br />一个常见的误解是以为session在有客户端访问时就被创建，然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建，注意如果JSP没有显示的使用 &lt;%@page session="false"%&gt; 关闭session，则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。</font></p><p><font size="2">由于session会消耗内存资源，因此，如果不打算使用session，应该在所有的JSP中关闭它。</font></p><p><font size="2">2、session何时被删除<br />综合前面的讨论，session在下列情况下被删除a.程序调用HttpSession.invalidate();或b.距离上一次收到客户端发送的session id时间间隔超过了session的超时设置;或c.服务器进程被停止（非持久session）</font></p><p><font size="2">3、如何做到在浏览器关闭时删除session<br />严格的讲，做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作，然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。</font></p><p><font size="2">4、有个HttpSessionListener是怎么回事<br />你可以创建这样的listener去监控session的创建和销毁事件，使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和销毁动作触发listener，而不是相反。类似的与HttpSession有关的listener还有 HttpSessionBindingListener，HttpSessionActivationListener和 HttpSessionAttributeListener。</font></p><p><font size="2">5、存放在session中的对象必须是可序列化的吗<br />不是必需的。要求对象可序列化只是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。在 Weblogic Server的session中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本如果session中有不可序列化的对象，在session销毁时会有一个Exception，很奇怪。</font></p><p><font size="2">6、如何才能正确的应付客户端禁止cookie的可能性<br />对所有的URL使用URL重写，包括超链接，form的action，和重定向的URL，具体做法参见[6]<br />http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770</font></p><p><font size="2">7、开两个浏览器窗口访问应用程序会使用同一个session还是不同的session<br />参见第三小节对cookie的讨论，对session来说是只认id不认人，因此不同的浏览器，不同的窗口打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。</font></p><p><font size="2">8、如何防止用户打开两个浏览器窗口操作导致的session混乱<br />这个问题与防止表单多次提交是类似的，可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端，同时保存在session里，客户端提交表单时必须把这个id也返回服务器，程序首先比较返回的id与保存在session里的值是否一致，如果不一致则说明本次操作已经被提交过了。可以参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript window.open打开的窗口，一般不设置这个id，或者使用单独的id，以防主窗口无法操作，建议不要再window.open打开的窗口里做修改操作，这样就可以不用设置。</font></p><p><font size="2">9、为什么在Weblogic Server中改变session的值后要重新调用一次session.setValue<br />做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改变，需要向其他服务器进程复制新的session值。</font></p><p><font size="2">10、为什么session不见了<br />排除session正常失效的因素之外，服务器本身的可能性应该是微乎其微的，虽然笔者在iPlanet6SP1加若干补丁的Solaris版本上倒也遇到过；浏览器插件的可能性次之，笔者也遇到过3721插件造成的问题；理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题。<br />出现这一问题的大部分原因都是程序的错误，最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨论这个问题。</font></p><p id="#7"><font size="2">七、跨应用程序的session共享<br /><br />常常有这样的情况，一个大项目被分割成若干小项目开发，为了能够互不干扰，要求每个小项目作为一个单独的web应用程序开发，可是到了最后突然发现某几个小项目之间需要共享一些信息，或者想使用session来实现SSO(single sign on)，在session中保存login的用户信息，最自然的要求是应用程序间能够访问彼此的session。</font></p><p><font size="2">然而按照Servlet规范，session的作用范围应该仅仅限于当前应用程序下，不同的应用程序之间是不能够互相访问对方的session的。各个应用服务器从实际效果上都遵守了这一规范，但是实现的细节却可能各有不同，因此解决跨应用程序session共享的方法也各不相同。</font></p><p><font size="2">首先来看一下Tomcat是如何实现web应用程序之间session的隔离的，从Tomcat设置的cookie路径来看，它对不同的应用程序设置的 cookie路径是不同的，这样不同的应用程序所用的session id是不同的，因此即使在同一个浏览器窗口里访问不同的应用程序，发送给服务器的session id也可以是不同的。<br /></font></p><p align="center"><font size="2"><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.supcode.com/Article/UploadPic/2005-6/2005629221626822.jpg" width="288" onload="javascript:if(this.width&gt;screen.width-600)this.style.width=screen.width-600;" border="0" /><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.supcode.com/Article/UploadPic/2005-6/2005629221626261.jpg" width="257" onload="javascript:if(this.width&gt;screen.width-600)this.style.width=screen.width-600;" border="0" /></font></p><p><font size="2">根据这个特性，我们可以推测Tomcat中session的内存结构大致如下。<br /></font></p><p align="center"><font size="2"><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.supcode.com/Article/UploadPic/2005-6/2005629221626479.jpg" width="444" onload="javascript:if(this.width&gt;screen.width-600)this.style.width=screen.width-600;" border="0" /></font></p><p><font size="2">笔者以前用过的iPlanet也采用的是同样的方式，估计SunONE与iPlanet之间不会有太大的差别。对于这种方式的服务器，解决的思路很简单，实际实行起来也不难。要么让所有的应用程序共享一个session id，要么让应用程序能够获得其他应用程序的session id。</font></p><p><font size="2">iPlanet中有一种很简单的方法来实现共享一个session id，那就是把各个应用程序的cookie路径都设为/（实际上应该是/NASApp，对于应用程序来讲它的作用相当于根）。<br />&lt;session-info&gt;<br />&lt;path&gt;/NASApp&lt;/path&gt;<br />&lt;/session-info&gt;</font></p><p><font size="2">需要注意的是，操作共享的session应该遵循一些<a class="wordstyle" href="http://www.supcode.com/" target="_blank">编程</a>约定，比如在session attribute名字的前面加上应用程序的前缀，使得setAttribute("name", "neo")变成setAttribute("app1.name", "neo")，以防止命名空间冲突，导致互相覆盖。</font></p><p><br /><font size="2">在Tomcat 中则没有这么方便的选择。在Tomcat版本3上，我们还可以有一些手段来共享session。对于版本4以上的Tomcat，目前笔者尚未发现简单的办法。只能借助于第三方的力量，比如使用文件、数据库、JMS或者客户端cookie，URL参数或者隐藏字段等手段。</font></p><p><font size="2">我们再看一下Weblogic Server是如何处理session的。<br /></font></p><p align="center"><font size="2"><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.supcode.com/Article/UploadPic/2005-6/2005629221627653.jpg" width="288" onload="javascript:if(this.width&gt;screen.width-600)this.style.width=screen.width-600;" border="0" /><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.supcode.com/Article/UploadPic/2005-6/2005629221630550.jpg" width="269" onload="javascript:if(this.width&gt;screen.width-600)this.style.width=screen.width-600;" border="0" /></font></p><p><font size="2">从截屏画面上可以看到Weblogic Server对所有的应用程序设置的cookie的路径都是/，这是不是意味着在Weblogic Server中默认的就可以共享session了呢？然而一个小实验即可证明即使不同的应用程序使用的是同一个session，各个应用程序仍然只能访问自己所设置的那些属性。这说明Weblogic Server中的session的内存结构可能如下<br /></font></p><p align="center"><font size="2"><img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.supcode.com/Article/UploadPic/2005-6/2005629221630874.jpg" width="420" onload="javascript:if(this.width&gt;screen.width-600)this.style.width=screen.width-600;" border="0" /></font></p><p><font size="2">对于这样一种结构，在session机制本身上来解决session共享的问题应该是不可能的了。除了借助于第三方的力量，比如使用文件、数据库、JMS或者客户端cookie，URL参数或者隐藏字段等手段，还有一种较为方便的做法，就是把一个应用程序的session放到ServletContext 中，这样另外一个应用程序就可以从ServletContext中取得前一个应用程序的引用。示例代码如下，</font></p><p><font size="2">应用程序A<br />context.setAttribute("appA", session); </font></p><p><font size="2">应用程序B<br />contextA = context.getContext("/appA");<br />HttpSession sessionA = (HttpSession)contextA.getAttribute("appA"); </font></p><p><font size="2">值得注意的是这种用法不可移植，因为根据ServletContext的JavaDoc，应用服务器可以处于安全的原因对于context.getContext("/appA");返回空值，以上做法在Weblogic Server 8.1中通过。</font></p><p><font size="2">那么Weblogic Server为什么要把所有的应用程序的cookie路径都设为/呢？原来是为了SSO，凡是共享这个session的应用程序都可以共享认证的信息。一个简单的实验就可以证明这一点，修改首先登录的那个应用程序的描述符weblogic.xml，把cookie路径修改为/appA访问另外一个应用程序会重新要求登录，即使是反过来，先访问cookie路径为/的应用程序，再访问修改过路径的这个，虽然不再提示登录，但是登录的用户信息也会丢失。注意做这个实验时认证方式应该使用FORM，因为浏览器和web服务器对basic认证方式有其他的处理方式，第二次请求的认证不是通过session来实现的。具体请参看[7] secion 14.8 Authorization，你可以修改所附的示例程序来做这些试验。</font></p><p id="#8"><font size="2">八、总结<br />session机制本身并不复杂，然而其实现和配置上的灵活性却使得具体情况复杂多变。这也要求我们不能把仅仅某一次的经验或者某一个浏览器，服务器的经验当作普遍适用的经验，而是始终需要具体情况具体分析。</font></p><p><font size="2">关于作者：<br />郎云鹏（dev2dev ID: hippiewolf），<a class="wordstyle" href="http://www.supdown.com/" target="_blank">软件</a>工程师，从事J2EE开发<br />电子邮件：langyunpeng@yahoo.com.cn<br />地址：大连<a class="wordstyle" href="http://www.supdown.com/" target="_blank">软件</a>园路31号科技大厦A座大连博涵咨询服务有限公司</font></p><p id="#9"><font size="2">参考文档：<br />[1] Preliminary Specification http://wp.netscape.com/newsref/std/cookie_spec.html<br />[2] RFC2109 http://www.rfc-editor.org/rfc/rfc2109.txt<br />[3] RFC2965 http://www.rfc-editor.org/rfc/rfc2965.txt<br />[4] The Unofficial Cookie FAQ http://www.cookiecentral.com/faq/<br />[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869<br />[6] http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770<br />[7] RFC2616 http://www.rfc-editor.org/rfc/rfc2616.txt<br /></font></p><p><font size="2">代码<a class="wordstyle" href="http://www.supdown.com/" target="_blank">下载</a>：</font><a href="http://dev2dev.bea.com.cn/images/paihang_article/041020/sampleApp.zip"><font size="2">sampleApp.zip</font></a><br /></p></font></div><img src ="http://www.blogjava.net/renyangok/aggbug/91104.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-31 10:10 <a href="http://www.blogjava.net/renyangok/archive/2006/12/31/91104.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2ee中文问题的解决</title><link>http://www.blogjava.net/renyangok/archive/2006/12/26/90071.html</link><dc:creator>保尔任</dc:creator><author>保尔任</author><pubDate>Tue, 26 Dec 2006 02:52:00 GMT</pubDate><guid>http://www.blogjava.net/renyangok/archive/2006/12/26/90071.html</guid><wfw:comment>http://www.blogjava.net/renyangok/comments/90071.html</wfw:comment><comments>http://www.blogjava.net/renyangok/archive/2006/12/26/90071.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/renyangok/comments/commentRss/90071.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/renyangok/services/trackbacks/90071.html</trackback:ping><description><![CDATA[ 
<p><span class="postbody"><font size="2">第一，</font></span><span class="postbody"><font size="2">文件的的编码方式其实就包括两方面：存和取，</font></span><span class="postbody"></span><span class="postbody"><font size="2">存文件必须以一种编码存；读文件也必须以一种编码读。</font></span><span class="postbody"><font size="2">如果存取按照相同的编码方式，则不会有问题，关键就是很多时候存取的方式不一致，产生乱码。</font></span><span class="postbody"><font size="2">，如不特别设置取系统默认的编码，中文windows为GBK编码。<br /><br />从.java-&gt;.class过程是，先编写.java文件并按莫种编码方式保存，然后用javac方法编译此文件，注意如.java没按系统默认编码保存则要带encoding参数指明实际编码，否则出错，生成的.class文件存为系统默认编码。<br /><br />从.jsp-&gt;.java-&gt;.class，先存为某种编码的.jsp文件，然后tomcat根据pageEncoding读取并转化为servlet存为系统默认编码，然后同上面.java-&gt;.class过程。<br /><br />第二，IDE的encoding为对系统下文件打开的解码方式或保存的编码方式。特例：如果.jsp文件有&lt;%@ page language="java" pageEncoding="UTF-8"%&gt;，则eclipse会自动存为UTF-8方式，不管eclipse的encoding是什么，这也是 eclipse的聪明之处。<br /><br />第三，<br />pageEncoding="UTF-8"表示此文件的编码方式，必须与此文件存储方式一致（所以eclipse会首选根据它来存文件），tomcat根据这个来读此.jsp文件并编译为servlet（至于编译成的.java和.class文件应该为tomcat服务器默认编码）。<br />contentType="text/html;charset=UTF-8"表示当服务器给浏览器传页面文件时编码方式为UTF-8，形式为HTML。例如：<br />&lt;%@ page language="java" pageEncoding="UTF-8"%&gt;<br />&lt;%@ page contentType="text/html;charset=GBK"%&gt;<br />&lt;html&gt;<br /> &lt;head&gt;<br />  &lt;title&gt;test&lt;/title&gt;<br /> &lt;/head&gt;<br /> &lt;body&gt;<br />  我是个好人<br /> &lt;/body&gt;<br />&lt;/html&gt;<br /></font></span></p><p><span class="postbody"></span>表示本jsp文件存为UTF-8字符集，当浏览器打开此页面后，查看原码就会发现源码为GBK字符集。<br /><br />第四，<br />request.setCharacterEncoding("UTF-8")是把提交内容的字符集设为UTF－8<br />response.setCharacterEncoding("UTF-8")可以把页面中的<font size="2">&lt;%@ page contentType="text/html;charset=iso8859-1"%&gt;换为charset=</font><font size="3">UTF-8，是给告诉浏览器我这个文件的编码方式。</font><br /><br />第五，表单提交：无论何种表单提交都可以在后台的java文件中通过String des = new String(s.getBytes("iso8859-1"),"UTF-8");来转换成你想要的UTF－8编码方式。但如果每处都加词句太麻烦，故分post和get两种方式区分提交（tomcat5以后分开处理，之前处理方式一样，即都可以用 request.setCharacterEncoding("UTF-8")方法处理，不过tomcat5以后get提交方法用此语句无效）。<span class="postbody"><font size="2"><br />1,post提交的数据: <br />程序加上org.springframework.web.filter.CharacterEncodingFilter过滤器. <br />&lt;filter&gt; <br />&lt;filter-name&gt;encodingFilter&lt;/filter-name&gt; <br />&lt;filter-class&gt;org.springframework.web.filter.CharacterEncodingFilter&lt;/filter-class&gt; <br />&lt;init-param&gt; <br />&lt;param-name&gt;encoding&lt;/param-name&gt; <br />&lt;param-value&gt;UTF8&lt;/param-value&gt; <br />&lt;/init-param&gt; <br />&lt;init-param&gt; <br />&lt;param-name&gt;forceEncoding&lt;/param-name&gt; <br />&lt;param-value&gt;true&lt;/param-value&gt; <br />&lt;/init-param&gt; <br />&lt;/filter&gt; <br /><br />&lt;filter-mapping&gt; <br />&lt;filter-name&gt;encodingFilter&lt;/filter-name&gt; <br />&lt;url-pattern&gt;*.html&lt;/url-pattern&gt; <br />&lt;/filter-mapping&gt; <br />&lt;filter-mapping&gt; <br />&lt;filter-name&gt;encodingFilter&lt;/filter-name&gt; <br />&lt;url-pattern&gt;*.jsp&lt;/url-pattern&gt; <br />&lt;/filter-mapping&gt; <br /><br />因为规范要求浏览器提交数据都要用utf8编码,所以这里设置编码方式为UTF8. <br /><br />特别注意: <br />a,这个过滤器只是简单的调用:request.setCharacterEncoding(this.encoding); <br />在这个语句之前不能调用任何的request.getParameter()方法,否则会设置tomcat的缺省字符集为"ISO-8859-1",并且使 setCharacterEncoding的调用失效.所以在这个过滤器之前的过滤器中不能有对getParameter这类方法的调用,比较安全的做法就是把这个过滤器尽量靠前放. <br />b,在server.xml中不能加上&lt;Valve className="org.apache.catalina.valves.RequestDumperValve"/&gt; <br />这个value也设置tomcat的缺省字符集为"ISO-8859-1",使setCharacterEncoding的调用失效.可能其他的value也有这个问题,我没有测试过. <br />如果要观察http请求参数,可以考虑用过滤器或者其他工具,例如ethereal(http://www.ethereal.com/) <br /><br />2,get提交的数据: <br />两种情况: <br />a,如果从地址栏直接输入汉字,则一般编码为"GBK",需要用 <br />new String(request.getParameter("something").getBytes("ISO-8859-1"),"GBK") <br />取出 <br />b,如果是页面超连接连接中带的汉字,则编码根据页面编码的不同而不同,如果页面的 <br />content="text/html; charset=utf-8",则在tomcat/conf/server.xml中的配置文件中: <br />&lt;!-- Define a non-SSL Coyote HTTP/1.1 Connector on port 8080 --&gt; <br />&lt;Connector port="8080" <br />maxThreads="150" minSpareThreads="25" maxSpareThreads="75" <br />enableLookups="false" redirectPort="8443" acceptCount="100" <br />debug="0" connectionTimeout="20000" useBodyEncodingForURI="true" <br />disableUploadTimeout="true" /&gt; <br /><br />加上:useBodyEncodingForURI="true"即可正常使用getParameter取出正确内容. <br />如果content="text/html; charset=GBK",需用 <br />new String(request.getParameter("something").getBytes("ISO-8859-1"),"GBK") <br />取出,其他情况类似. <br /><br />总结: <br />1,所有页面使用utf8编码, <br />2,服务器加上过滤器, <br />3,server.xml中不要使用 <br />&lt;Valve className="org.apache.catalina.valves.RequestDumperValve"/&gt; <br />4,server.xml文件加上useBodyEncodingForURI="true" <br />这样应该可以搞定大多数前台的中文问题.至于地址栏输入中文,不支持也罢,一般的程序很少要求 <br />从这里输入.<br /><br />第六，连接数据库</font></span> </p><p>1、mysql配置文件：<br />修改mysql在windows\my.ini里default-character-set=utf-8</p><p>2、mysql里数据库和表也都设为utf8_unicode_ci</p><p>3、数据库连结：jdbc:mysql://localhost/mydb?useUnicode=true&amp;characterEncoding=utf-8<br />注意，关键就在于此：此句中间是'&amp;'不是'&amp;amp;'这是因为数据库连结时，在.jsp和.java文件中应该用&amp;号，而XML文件中需要用&amp;amp</p><img src ="http://www.blogjava.net/renyangok/aggbug/90071.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/renyangok/" target="_blank">保尔任</a> 2006-12-26 10:52 <a href="http://www.blogjava.net/renyangok/archive/2006/12/26/90071.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>