﻿<?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-snowolf-文章分类-JAVA类</title><link>http://www.blogjava.net/snowolf/category/7074.html</link><description>        这样的一种生活</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 12:35:36 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 12:35:36 GMT</pubDate><ttl>60</ttl><item><title>displayTag学习摘要</title><link>http://www.blogjava.net/snowolf/articles/40856.html</link><dc:creator>snowolf</dc:creator><author>snowolf</author><pubDate>Thu, 13 Apr 2006 04:28:00 GMT</pubDate><guid>http://www.blogjava.net/snowolf/articles/40856.html</guid><wfw:comment>http://www.blogjava.net/snowolf/comments/40856.html</wfw:comment><comments>http://www.blogjava.net/snowolf/articles/40856.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/snowolf/comments/commentRss/40856.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/snowolf/services/trackbacks/40856.html</trackback:ping><description><![CDATA[
		<center>
				<b>
						<span style="FONT-SIZE: 20px">displayTag学习摘要</span>
				</b>
		</center>
		<br />
		<center>qiqijava</center>
		<br />
		<br />
		<br />
		<b>1.tableTag中name属性：值默认作用域：request</b>
		<br />&lt;display:table name="accList"&gt;<br />如果作用域为session，则&lt;display:table name="sessionScope.accList"&gt;<br />tableTag中指定ID属性会把该对象加入到pageContext对象中去。如ID="test"<br />&lt;%int cate=((Role)pageContext.getAttribute("test")).getCategory();%&gt;<br />生成表格的序列号 例如：&lt;display:table id="row" name="mylist"&gt;<br />&lt;display:column title="序列号"&gt;&lt;%=pageContext.getAttribute("row_rowNum")%&gt;&lt;/display:column&gt;<br />如行号：row_rowNum &lt;c:out value="${row_rowNum}"/&gt;<br />firstName:row.firstName   &lt;c:out value="${row.firstName}"/&gt;<br />lastName: row.lastName  全部由ID来取得<br /><br /><b>2.限制页面显示的结果集数</b><br />1)全部&lt;display:table name="accList" class="its" id="test"&gt;<br />2)头5个&lt;display:table name="accList" class="its" id="test" length="5"&gt;<br />3)从第二个开始，显示下5个&lt;display:table name="accList" class="its" id="test" offset="2" length="5"&gt;<br /><br /><b>3.包装器decorators,有行包装器(必须继承TableDecorator)和列包装器(必须实现ColumnDecorator)</b><br />  在tableTag中显示list时，decorators中的方法会在list前调用，如果decorators实现类中有相关的getXXX()方法时，调用此方法，如果没有，则直接调用list<br />  在columnTag中显示value时，decorators中的方法会先调用，(应该重用)<br /><br /><b>4.传递参数，有两种方式，</b><br />  一。struts方式：有以下几个属性<br />  1)href 基本的超连接<br />  2)paramId 添加到url上的参数名<br />  &lt;display:column property="status" href="details.jsp" paramId="id" paramProperty="id" /&gt;<br />  3)paramName 传递容器内的其它bean当作参数 如：request.setAttribute("testparam", "sendamail");<br />  &lt;display:column property="email" href="details.jsp" paramId="action" paramName="testparam" paramScope="request" /&gt;<br />  4)paramScope 指定bean的作用域<br />  二。decorators方式<br />  类Wrapper方法：<br /><br /><pre class="overflow">public String getLink1()<br />        {<br />                ListObject lObject= (ListObject)getCurrentRowObject();<br />                int lIndex= getListIndex();<br />                return "&lt;a href=\"details.jsp?index=" + lIndex + "\"&gt;" + lObject.getId() + "&lt;/a&gt;";<br />        }</pre><br /><br />标签：<br /><pre class="overflow">&lt;display:table name="sessionScope.details" decorator="org.displaytag.sample.Wrapper" &gt;<br />  &lt;display:column property="link1" title="ID" /&gt;<br />  &lt;display:column property="email" /&gt;<br />&lt;/display:table&gt;</pre><br /><br /><b>5.分页</b><br />  指定属性:pagesize="10" 每页显示10条记录<br /><br /><b>6.排序</b><br />1)在list中封装的对象的属性要实现Comparable接口,(一般均实现了)<br />2) 在columnTag中指定sortable="true"<br />  可指定默认排序的列 defaultsort="1" 数值为第几列默认排序 defaultorder="descending" 指定默认为降序<br /><br /><b>7.导出 支持下列格式：'html', 'xml', 'csv', and 'excel'.</b><br />  属性：export="true",注意导出无效，当使用jsp:include or the RequestDispatcher<br />  &lt;display:column media="csv excel" title="URL" property="url"/&gt;<br />  指定该url属性值只能在csv、excel中导出<br />  需要指定export filter.<br /><br /><b>8.更改默认设置</b><br />  1)通过&lt;display:setProperty name=... value=...&gt; 标签，可以覆盖一些默认设置<br />  2)创建displaytag.properties文件，所有时区共用，建中文编码则创建displaytag_zh_cn.properties，放到类路径下,jar包内共有两个默认的属性文件TableTag.properties，message.properties<br /><br /><b>9其它</b><br />  1)当多个表在一页显示时，每个表都想要有分页、排序、导出等功能时，只需为每个table指定一个不同的ID即可。<br />  2)增加表头&lt;display:caption&gt;角色管理&lt;/display:caption&gt;<br />  3)增加表尾  &lt;display:footer&gt;&lt;tr&gt;&lt;td colspan="6" align="center" &gt;国瑞数码版权所有&lt;/td&gt;&lt;/tr&gt;&lt;/display:footer&gt;<br />  4)http和email自动链接功能，指定autolink="true"<br />  5)指定一列显示的最大长度，避免太长把表格变形 maxLength="10" style="whitespace: nowrap;"<br />  6)当列的值为null，使用nulls="false"属性把null转为空白 <img src ="http://www.blogjava.net/snowolf/aggbug/40856.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/snowolf/" target="_blank">snowolf</a> 2006-04-13 12:28 <a href="http://www.blogjava.net/snowolf/articles/40856.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DisplayTag应用指南 </title><link>http://www.blogjava.net/snowolf/articles/40855.html</link><dc:creator>snowolf</dc:creator><author>snowolf</author><pubDate>Thu, 13 Apr 2006 04:27:00 GMT</pubDate><guid>http://www.blogjava.net/snowolf/articles/40855.html</guid><wfw:comment>http://www.blogjava.net/snowolf/comments/40855.html</wfw:comment><comments>http://www.blogjava.net/snowolf/articles/40855.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/snowolf/comments/commentRss/40855.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/snowolf/services/trackbacks/40855.html</trackback:ping><description><![CDATA[　DisplayTag是一个非常好用的表格显示标签，适合MVC模式，其主页在http://displaytag.sourceforge.net<br />　　<br />　　<b>一、最简单的情况，未使用&lt;display:column/&gt;标签</b><br />　　<br />　　&lt;%request.setAttribute( "test", new ReportList(6) );%&gt;<br />　　&lt;display:table name="test" /&gt;<br />　　<br />　　标签遍历List里的每一个对象，并将对象里的所有属性显示出来。一般用于开发的时候检查对象数据的完整性。<br />　　<br />　　<b>二、使用&lt;display:column/&gt;标签的情况</b><br />　　<br />　　&lt;display:table name="test"&gt;<br />　　&lt;display:column property="id" title="ID" /&gt;<br />　　&lt;display:column property="name" /&gt;<br />　　&lt;display:column property="email" /&gt;<br />　　&lt;display:column property="status" /&gt;<br />　　&lt;display:column property="description" title="Comments"/&gt;<br />　　&lt;/display:table&gt;<br />　　<br />　　property对应List里对象的属性（用getXXX()方法取得），title则对应表格表头里的列名。定义列有两种方式：<br />　　<br />　　A、&lt;display:column property="email" /&gt;<br />　　<br />　　使用&lt;display:column/&gt;标签里的property属性来定义<br />　　<br />　　B、&lt;display:column title="email"&gt;email@it.com&lt;/display:column&gt;<br />　　<br />　　在&lt;display:column/&gt;标签体里增加内容，可以是常量，也可以用其他标签等等<br />　　<br />　　两种方式比较，用property属性来定义更加快速和利于排序。<br />　　<br />　　<b>三、表格显示样式的定义</b><br />　　<br />　　A、在&lt;display:table/&gt;和&lt;display:column/&gt;标签里指定标准的html属性，烦琐<br />　　<br />　　B、修改样式表<br />　　&lt;display:table name="test" class="mars"&gt;<br />　　&lt;display:column property="id" title="ID" class="idcol"/&gt;<br />　　&lt;display:column property="name" /&gt;<br />　　&lt;display:column property="email" /&gt;<br />　　&lt;display:column property="status" class="tableCellError" /&gt;<br />　　&lt;display:column property="description" title="Comments"/&gt;<br />　　&lt;/display:table&gt;<br />　　<br />　　通过class属性来指定所要应用的样式。可以在其默认样式表里（./css/screen.css）直接修改<br />　　<br />　　<b>四、标签取得数据的数据源</b><br />　　<br />　　有四种范围<br />　　<br />　　pageScope<br />　　requestScope (默认)　&lt;display:table name="test2" &gt;<br />　　sessionScope　&lt;display:table name="sessionScope.holder.list" &gt; 注意，这里要指定范围，非默认<br />　　applicationScope<br />　　<br />　　<b>五、通过增加id属性创建隐含的对象</b><br />　　<br />　　&lt;display:table name="test" id="testit"&gt;<br />　　&lt;display:column property="id" title="ID" /&gt;<br />　　&lt;display:column property="name" /&gt;<br />　　&lt;display:column title="static value"&gt;static&lt;/display:column&gt;<br />　　&lt;display:column title="row number (testit_rowNum)"&gt;&lt;%=pageContext.getAttribute("testit_rowNum")%&gt;&lt;/display:column&gt;<br />　　&lt;display:column title="((ListObject)testit).getMoney()"&gt;&lt;%=((ListObject)pageContext.getAttribute("testit")).getMoney()%&gt;&lt;/display:column&gt;<br />　　&lt;/display:table&gt;<br />　　<br />　　注意到在&lt;display:table/&gt;里增加了id属性，这时就在page context里创建了一个隐含对象，指向List里的当前对象，<br />　　<br />　　可以通过(ListObject)pageContext.getAttribute("id")来捕获这个对象。同时还创建了一个id_rowNum对象，同样，可<br />　　<br />　　通过pageContext.getAttribute("testit_rowNum")来捕获，它仅仅代表当前行的行数。<br />　　<br />　　有了这两个隐含对象，就可以通过其他标签来访问，例如Jstl:<br />　　<br />　　&lt;display:table id="row" name="mylist"&gt;<br />　　&lt;display:column title="row number" &gt;<br />　　&lt;c:out value="${row_rowNum}"/&gt;<br />　　&lt;/display:column&gt;<br />　　&lt;display:column title="name" &gt;<br />　　&lt;c:out value="${row.first_name}"/&gt;<br />　　&lt;c:out value="${row.last_name}"/&gt;<br />　　&lt;/display:column&gt;<br />　　&lt;/display:table&gt;<br />　　<br />　　<b>六、显示部分数据</b><br />　　<br />　　显示开始五条数据：通过设定length属性<br />　　<br />　　&lt;display:table name="test" length="5"&gt;<br />　　&lt;display:column property="id" title="ID" /&gt;<br />　　&lt;display:column property="email" /&gt;<br />　　&lt;display:column property="status" /&gt;<br />　　&lt;/display:table&gt;<br />　　<br />　　显示第三到第八条数据：通过设定offset和length属性<br />　　<br />　　&lt;display:table name="test" offset="3" length="5"&gt;<br />　　&lt;display:column property="id" title="ID" /&gt;<br />　　&lt;display:column property="email" /&gt;<br />　　&lt;display:column property="status" /&gt;<br />　　&lt;/display:table&gt;<br />　　<br />　　<b>七、对email和url地址的直接连接</b><br />　　<br />　　&lt;display:table name="test" &gt;<br />　　&lt;display:column property="id" title="ID" /&gt;<br />　　&lt;display:column property="email" autolink="true" /&gt;<br />　　&lt;display:column property="url" autolink="true" /&gt;<br />　　&lt;/display:table&gt;<br />　　<br />　　如果要显示的对象里包含email和url地址，则可以在display:column里直接设定autolink="true"来直接连接<br />　　<br />　　<b>八、使用装饰模式转换数据显示（写自己的 decorator ）</b><br />　　<br />　　A、对整个表格应用decorator<br />　　<br />　　&lt;display:table name="test" decorator="org.displaytag.sample.Wrapper" &gt;<br />　　&lt;display:column property="id" title="ID" /&gt;<br />　　&lt;display:column property="email" /&gt;<br />　　&lt;display:column property="status" /&gt;<br />　　&lt;display:column property="date" /&gt;<br />　　&lt;display:column property="money" /&gt;<br />　　&lt;/display:table&gt;<br />　　org.displaytag.sample.Wrapper即自己写的decorator，它要继承TableDecorator类，看看它的一个方法：<br />　　public String getMoney()<br />　　{<br />　　return this.moneyFormat.format(((ListObject) this.getCurrentRowObject()).getMoney());<br />　　}<br />　　<br />　　很明显，它通过父类的getCurrentRowObject()方法获得当前对象，然后对其getMoney()方法进行‘油漆’<br />　　<br />　　B、对单独的column应用decorator<br />　　<br />　　&lt;display:table name="test"&gt;<br />　　&lt;display:column property="id" title="ID" /&gt;<br />　　&lt;display:column property="email" /&gt;<br />　　&lt;display:column property="status" /&gt;<br />　　&lt;display:column property="date" decorator="org.displaytag.sample.LongDateWrapper" /&gt;<br />　　&lt;/display:table&gt;<br />　　org.displaytag.sample.LongDateWrapper要实现ColumnDecorator接口，它的方法：<br />　　public final String decorate(Object columnValue)<br />　　{<br />　　Date date = (Date) columnValue;<br />　　return this.dateFormat.format(date);<br />　　}<br />　　<br />　　显然，它获得不了当前对象（因为它实现的是接口），仅仅是获得该对象的columnValue，然后‘油漆’<br />　　<br />　　<b>九、创建动态连接</b><br />　　<br />　　有两种方法创建动态连接：<br />　　<br />　　A、在&lt;display:column/&gt;里通过增加href、paramId、paramName、paramScope、paramProperty属性<br />　　<br />　　href　　　　　　 基本的URL 地址<br />　　paramId　　　　　加在URL 地址后的参数名称<br />　　paramName　　　　数据bean的名称，一般为null（即使用当前List里的对象）<br />　　paramScope　　　 数据bean的范围，一般为null<br />　　paramProperty　　数据bean的属性名称，用来填充URL 地址后的参数值<br />　　&lt;display:table name="sessionScope.details"&gt;<br />　　&lt;display:column property="id" title="ID" href="details.jsp" paramId="id" /&gt;<br />　　&lt;display:column property="email" href="details.jsp" paramId="action" paramName="testparam" paramScope="request" /&gt;<br />　　&lt;display:column property="status" href="details.jsp" paramId="id" paramProperty="id" /&gt;<br />　　&lt;/display:table&gt;<br />　　<br />　　这种方法简便直接，但缺点是无法产生类似details.jsp?id=xx&amp;action=xx的复合URL<br />　　<br />　　B、应用decorator 创建动态连接：<br />　　<br />　　&lt;display:table name="sessionScope.details" decorator="org.displaytag.sample.Wrapper" &gt;<br />　　&lt;display:column property="link1" title="ID" /&gt;<br />　　&lt;display:column property="email" /&gt;<br />　　&lt;display:column property="link2" title="Actions" /&gt;<br />　　&lt;/display:table&gt;<br />　　org.displaytag.sample.Wrapper里的方法：<br />　　public String getLink1()<br />　　{<br />　　ListObject lObject= (ListObject)getCurrentRowObject();<br />　　int lIndex= getListIndex();<br />　　return "&lt;a href=\"details.jsp?index=" + lIndex + "\"&gt;" + lObject.getId() + "&lt;/a&gt;";<br />　　}<br />　　<br />　　public String getLink2()<br />　　{<br />　　ListObject lObject= (ListObject)getCurrentRowObject();<br />　　int lId= lObject.getId();<br />　　<br />　　return "&lt;a href=\"details.jsp?id=" + lId<br />　　+ "&amp;action=view\"&gt;View&lt;/a&gt; | "<br />　　+ "&lt;a href=\"details.jsp?id=" + lId<br />　　+ "&amp;action=edit\"&gt;Edit&lt;/a&gt; | "<br />　　+ "&lt;a href=\"details.jsp?id=" + lId<br />　　+ "&amp;action=delete\"&gt;Delete&lt;/a&gt;";<br />　　}<br />　　<br />　　<b>十、分页</b><br />　　<br />　　实现分页非常的简单，增加一个pagesize属性指定一次想显示的行数即可<br />　　<br />　　&lt;display:table name="sessionScope.test" pagesize="10"&gt;<br />　　&lt;display:column property="id" title="ID" /&gt;<br />　　&lt;display:column property="name" /&gt;<br />　　&lt;display:column property="email" /&gt;<br />　　&lt;display:column property="status" /&gt;<br />　　&lt;/display:table&gt;<br />　　<br />　　<b>十一、排序</b><br />　　<br />　　排序实现也是很简单，在需要排序的column里增加sortable="true"属性，headerClass="sortable"仅仅是<br />　　<br />　　指定显示的样式。column里的属性对象要实现Comparable接口，如果没有的话可以应用decorator<br />　　<br />　　defaultsort="1"　　　　　　　默认第一个column排序<br />　　defaultorder="descending"　　默认递减排序<br />　　&lt;display:table name="sessionScope.stest" defaultsort="1" defaultorder="descending"&gt;<br />　　&lt;display:column property="id" title="ID" sortable="true" headerClass="sortable" /&gt;<br />　　&lt;display:column property="name" sortable="true" headerClass="sortable"/&gt;<br />　　&lt;display:column property="email" /&gt;<br />　　&lt;display:column property="status" sortable="true" headerClass="sortable"/&gt;<br />　　&lt;/display:table&gt;<br />　　<br />　　注意的是，当同时存在分页时排序仅仅针对的是当前页面，而不是整个List都进行排序<br />　　<br />　　<b>十二、column 分组</b><br />　　<br />　　分组只是需要在column里增加group属性<br />　　<br />　　&lt;display:table name="test" class="simple"&gt;<br />　　&lt;display:column property="city" title="CITY" group="1"/&gt;<br />　　&lt;display:column property="project" title="PROJECT" group="2"/&gt;<br />　　&lt;display:column property="amount" title="HOURS"/&gt;<br />　　&lt;display:column property="task" title="TASK"/&gt;<br />　　&lt;/display:table&gt;<br />　　<br />　　<b>十三、导出数据到其他格式（页面溢出filter??）</b><br />　　<br />　　在&lt;display:table/&gt;里设定export="true"<br />　　<br />　　在&lt;display:column/&gt;里设定media="csv excel xml pdf" 决定该字段在导出到其他格式时被包不包含，不设定则都包含<br />　　<br />　　&lt;display:setProperty name="export.csv" value="false" /&gt;<br />　　<br />　　决定该种格式能不能在页面中导出<br />　　<br />　　&lt;display:table name="test" export="true" id="currentRowObject"&gt;<br />　　&lt;display:column property="id" title="ID"/&gt;<br />　　&lt;display:column property="email" /&gt;<br />　　&lt;display:column property="status" /&gt;<br />　　&lt;display:column property="longDescription" media="csv excel xml pdf" title="Not On HTML"/&gt;<br />　　&lt;display:column media="csv excel" title="URL" property="url"/&gt;<br />　　&lt;display:setProperty name="export.pdf" value="true" /&gt;<br />　　&lt;display:setProperty name="export.csv" value="false" /&gt;<br />　　&lt;/display:table&gt;<br />　　<br />　　<b>十四、配置属性，覆盖默认</b><br />　　<br />　　两种方法：<br />　　<br />　　A、在程序classpath下新建displaytag.properties文件<br />　　<br />　　B、对于单个表格，应用&lt;display:setProperty&gt;标签<br />　　<br />　　具体可配置的属性：http://displaytag.sourceforge.net/configuration.html<br />　　<br />　　<b>十五、一个完整的例子</b><br />　　<br />　　&lt;display:table name="test" export="true" sort="list" pagesize="8"&gt;<br />　　&lt;display:column property="city" title="CITY" group="1" sortable="true" headerClass="sortable"/&gt;<br />　　&lt;display:column property="project" title="PROJECT" group="2" sortable="true" headerClass="sortable"/&gt;<br />　　&lt;display:column property="amount" title="HOURS"/&gt;<br />　　&lt;display:column property="task" title="TASK"/&gt;<br />　　&lt;/display:table&gt;<br />　　<br />　　sort="list" 对整个list进行排序<br />　　<br />　　导出数据到其他格式时，group无效 <img src ="http://www.blogjava.net/snowolf/aggbug/40855.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/snowolf/" target="_blank">snowolf</a> 2006-04-13 12:27 <a href="http://www.blogjava.net/snowolf/articles/40855.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用HttpSessionListener实现网站在线人数统计功能</title><link>http://www.blogjava.net/snowolf/articles/32293.html</link><dc:creator>snowolf</dc:creator><author>snowolf</author><pubDate>Fri, 24 Feb 2006 06:51:00 GMT</pubDate><guid>http://www.blogjava.net/snowolf/articles/32293.html</guid><wfw:comment>http://www.blogjava.net/snowolf/comments/32293.html</wfw:comment><comments>http://www.blogjava.net/snowolf/articles/32293.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/snowolf/comments/commentRss/32293.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/snowolf/services/trackbacks/32293.html</trackback:ping><description><![CDATA[　　在网站中经常需要进行在线人数的统计。过去的一般做法是结合登录和退出功能，即当用户输入用户名密码进行登录的时候计数器加1，然后当用户点击退出按钮退出系统的时候计数器减1。这种处理方式存在一些缺点，例如：用户正常登录后，可能会忘记点击退出按钮，而直接关闭浏览器，导致计数器减1的操作没有及时执行；网站上还经常有一些内容是不需要登录就可以访问的，在这种情况下也无法使用上面的方法进行在线人数统计。<BR>　　我们可以利用Servlet规范中定义的事件监听器（Listener）来解决这个问题，实现更准确的在线人数统计功能。对每一个正在访问的用户，J2EE应用服务器会为其建立一个对应的HttpSession对象。当一个浏览器第一次访问网站的时候，J2EE应用服务器会新建一个HttpSession对象，并触发HttpSession创建事件，如果注册了HttpSessionListener事件监听器，则会调用HttpSessionListener事件监听器的sessionCreated方法。相反，当这个浏览器访问结束超时的时候，J2EE应用服务器会销毁相应的HttpSession对象，触发HttpSession销毁事件，同时调用所注册HttpSessionListener事件监听器的sessionDestroyed方法。<BR>　　可见，对应于一个用户访问的开始和结束，相应的有sessionCreated方法和sessionDestroyed方法执行。这样，我们只需要在HttpSessionListener实现类的sessionCreated方法中让计数器加1，在sessionDestroyed方法中让计数器减1，就轻松实现了网站在线人数的统计功能。<BR>　　下面就是利用HttpSessionListener实现在线人数统计的一个例子，这个例子已经在中创软件的J2EE应用服务器InforWeb中测试通过。<BR>　　首先，编写一个简单的计数器，代码如下：<BR>
<DIV class=codeStyle>
<OL>
<LI><B><FONT color=#0000ff>package</FONT></B>&nbsp;gongfei.cmc.articles.onlinecounter; 
<LI><B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>class</FONT></B>&nbsp;OnlineCounter&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>private</FONT></B>&nbsp;<B><FONT color=#0000ff>static</FONT></B>&nbsp;<B><FONT color=#0000ff>long</FONT></B>&nbsp;online&nbsp;=&nbsp;0;&nbsp;&nbsp;&nbsp;&nbsp; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>static</FONT></B>&nbsp;<B><FONT color=#0000ff>long</FONT></B>&nbsp;getOnline()&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>return</FONT></B>&nbsp;online; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>static</FONT></B>&nbsp;<B><FONT color=#0000ff>void</FONT></B>&nbsp;raise(){ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;online++; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>static</FONT></B>&nbsp;<B><FONT color=#0000ff>void</FONT></B>&nbsp;reduce(){ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;online--; 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>} </LI></OL></DIV><BR>　　然后，编写HttpSessionListener实现类，在这个实现类的sessionCreated方法中调用OnlineCounter的raise方法，在sessionDestroyed方法中调用OnlineCounter的reduce方法，代码如下：<BR>
<DIV class=codeStyle>
<OL>
<LI><B><FONT color=#0000ff>package</FONT></B>&nbsp;gongfei.cmc.articles.onlinecounter; 
<LI><B><FONT color=#0000ff>import</FONT></B>&nbsp;javax.servlet.http.HttpSessionEvent; 
<LI><B><FONT color=#0000ff>import</FONT></B>&nbsp;javax.servlet.http.<FONT class=classLink>HttpSessionListener</FONT>; 
<LI><B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>class</FONT></B>&nbsp;OnlineCounterListener&nbsp;<B><FONT color=#0000ff>implements</FONT></B>&nbsp;<FONT class=classLink>HttpSessionListener</FONT>&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>void</FONT></B>&nbsp;sessionCreated(<FONT class=classLink>HttpSessionEvent</FONT>&nbsp;hse)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OnlineCounter.raise(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;<B><FONT color=#0000ff>public</FONT></B>&nbsp;<B><FONT color=#0000ff>void</FONT></B>&nbsp;sessionDestroyed(<FONT class=classLink>HttpSessionEvent</FONT>&nbsp;hse)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OnlineCounter.reduce(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>} </LI></OL></DIV><BR>　　再然后，把这个HttpSessionListener实现类注册到网站应用中，也就是在网站应用的web.xml中加入如下内容：<BR>
<DIV class=codeStyle>
<OL>
<LI>&lt;web-app&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;…… 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;listener&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;listener-<B><FONT color=#0000ff>class</FONT></B>&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;gongfei.cmc.articles.example.OnlineCounterListener 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/listener-<B><FONT color=#0000ff>class</FONT></B>&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/listener&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;…… 
<LI>&lt;/web-app&gt; </LI></OL></DIV><BR>　　OK，在线人数统计功能已经实现，只要在JSP页面中加入下面这样的脚本就能显示但前在线人数了：<BR>
<DIV class=codeStyle>
<OL>
<LI>&lt;%@&nbsp;page&nbsp;language=<FONT color=#ff33ff>"java"</FONT>&nbsp;pageEncoding=<FONT color=#ff33ff>"GB2312"</FONT>&nbsp;%&gt; 
<LI>&lt;%@&nbsp;page&nbsp;<B><FONT color=#0000ff>import</FONT></B>=<FONT color=#ff33ff>"gongfei.cmc.articles.onlinecounter.OnlineCounter"</FONT>&nbsp;%&gt; 
<LI>&lt;html&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;head&gt;&lt;title&gt;On&nbsp;Line&nbsp;Counert&lt;/title&gt;&lt;/head&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;body&nbsp;bgcolor=<FONT color=#ff33ff>"#FFFFFF"</FONT>&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;On&nbsp;line:&lt;%=OnlineCounter.getOnline()%&gt; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/body&gt; 
<LI>&lt;/html&gt; </LI></OL></DIV><BR><BR><BR><img src ="http://www.blogjava.net/snowolf/aggbug/32293.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/snowolf/" target="_blank">snowolf</a> 2006-02-24 14:51 <a href="http://www.blogjava.net/snowolf/articles/32293.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JSP里request变量列表</title><link>http://www.blogjava.net/snowolf/articles/31953.html</link><dc:creator>snowolf</dc:creator><author>snowolf</author><pubDate>Wed, 22 Feb 2006 05:17:00 GMT</pubDate><guid>http://www.blogjava.net/snowolf/articles/31953.html</guid><wfw:comment>http://www.blogjava.net/snowolf/comments/31953.html</wfw:comment><comments>http://www.blogjava.net/snowolf/articles/31953.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/snowolf/comments/commentRss/31953.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/snowolf/services/trackbacks/31953.html</trackback:ping><description><![CDATA[&lt;%<BR>out.println("Protocol: " + request.getProtocol() + "&lt;br&gt;");<BR>out.println("Scheme: " + request.getScheme() + "&lt;br&gt;");<BR>out.println("Server Name: " + request.getServerName() + "&lt;br&gt;" );<BR>out.println("Server Port: " + request.getServerPort() + "&lt;br&gt;");<BR>out.println("Protocol: " + request.getProtocol() + "&lt;br&gt;");<BR>out.println("Server Info: " + getServletConfig().getServletContext().getServerInfo() + "&lt;br&gt;");<BR>out.println("Remote Addr: " + request.getRemoteAddr() + "&lt;br&gt;");<BR>out.println("Remote Host: " + request.getRemoteHost() + "&lt;br&gt;");<BR>out.println("Character Encoding: " + request.getCharacterEncoding() + "&lt;br&gt;");<BR>out.println("Content Length: " + request.getContentLength() + "&lt;br&gt;");<BR>out.println("Content Type: "+ request.getContentType() + "&lt;br&gt;");<BR>out.println("Auth Type: " + request.getAuthType() + "&lt;br&gt;");<BR>out.println("HTTP Method: " + request.getMethod() + "&lt;br&gt;");<BR>out.println("Path Info: " + request.getPathInfo() + "&lt;br&gt;");<BR>out.println("Path Trans: " + request.getPathTranslated() + "&lt;br&gt;");<BR>out.println("Query String: " + request.getQueryString() + "&lt;br&gt;");<BR>out.println("Remote User: " + request.getRemoteUser() + "&lt;br&gt;");<BR>out.println("Session Id: " + request.getRequestedSessionId() + "&lt;br&gt;");<BR>out.println("Request URI: " + request.getRequestURI() + "&lt;br&gt;");<BR>out.println("Servlet Path: " + request.getServletPath() + "&lt;br&gt;");<BR>out.println("Accept: " + request.getHeader("Accept") + "&lt;br&gt;");<BR>out.println("Host: " + request.getHeader("Host") + "&lt;br&gt;");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>out.println("Referer : " + request.getHeader("Referer") + "&lt;br&gt;");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>out.println("Accept-Language : " + request.getHeader("Accept-Language") + "&lt;br&gt;");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>out.println("Accept-Encoding : " + request.getHeader("Accept-Encoding") + "&lt;br&gt;");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>out.println("User-Agent : " + request.getHeader("User-Agent") + "&lt;br&gt;");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>out.println("Connection : " + request.getHeader("Connection") + "&lt;br&gt;");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>out.println("Cookie : " + request.getHeader("Cookie") + "&lt;br&gt;");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>out.println("Created : " + session.getCreationTime() + "&lt;br&gt;");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>out.println("LastAccessed : " + session.getLastAccessedTime() + "&lt;br&gt;");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>%&gt;<BR><BR>运行结果：<BR><BR>Protocol: HTTP/1.1<BR>Scheme: http<BR>Server Name: 192.168.0.1<BR>Server Port: 8080<BR>Protocol: HTTP/1.1<BR>Server Info: JavaServer Web Dev Kit/1.0 EA (JSP 1.0; Servlet 2.1; Java 1.2; Windows NT 5.0 x86; java.vendor=Sun Microsystems Inc.)<BR>Remote Addr: 192.168.0.106<BR>Remote Host: abc<BR>Character Encoding: null<BR>Content Length: -1<BR>Content Type: null<BR>Auth Type: null<BR>HTTP Method: GET<BR>Path Info: null<BR>Path Trans: null<BR>Query String: null<BR>Remote User: null<BR>Session Id: To1010mC466113890241879At<BR>Request URI: /c.jsp<BR>Servlet Path: /c.jsp<BR>Accept: */*<BR>Host: 192.168.0.1:8080<BR>Referer : null<BR>Accept-Language : zh-cn<BR>Accept-Encoding : gzip, deflate<BR>User-Agent : Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt)<BR>Connection : Keep-Alive<BR>Cookie : SESSIONID=To1010mC466113890241879At<BR>Created : 965764522168<BR>LastAccessed : 965775587088<BR><img src ="http://www.blogjava.net/snowolf/aggbug/31953.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/snowolf/" target="_blank">snowolf</a> 2006-02-22 13:17 <a href="http://www.blogjava.net/snowolf/articles/31953.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Servlet API说明文档</title><link>http://www.blogjava.net/snowolf/articles/31942.html</link><dc:creator>snowolf</dc:creator><author>snowolf</author><pubDate>Wed, 22 Feb 2006 05:02:00 GMT</pubDate><guid>http://www.blogjava.net/snowolf/articles/31942.html</guid><wfw:comment>http://www.blogjava.net/snowolf/comments/31942.html</wfw:comment><comments>http://www.blogjava.net/snowolf/articles/31942.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/snowolf/comments/commentRss/31942.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/snowolf/services/trackbacks/31942.html</trackback:ping><description><![CDATA[<FONT size=2>译者前言： <BR>近来在整理有关Servlet资料时发现，在网上竟然找不到一份中文的Java Servlet API的说明文档，而在有一本有关JSP的书后面附的Java Servlet API说明竟然不全，而这份文档的2.1a版在1998年的11月份就已定稿。所以我决定翻译一份中文的文档（其中一些与技术关系不大的部分已被略去），有兴趣的读者可以从http://java.sun.com/products/servlet/2.1/servletspec-2.1.zip下载原文阅读。 <BR><BR><BR>Java Servlet API说明文档（2.1a版） <BR>1998年11月 <BR><BR><BR>绪言 <BR>这是一份关于2.1版Java Servlet API的说明文档，作为对这本文档的补充，你可以到http://java.sun.com/products/servlet/index.html下面下载Javadoc格式的文档。 <BR><BR>谁需要读这份文档 <BR>这份文档描述了Java Servlet API的最新版本2.1版。所以，这本书对于Servlet的开发者及servlet引擎的开发者同样适用。 <BR><BR>Java Servlet API的组成 <BR>Java Servlet API由两个软件包组成：一个是对应HTTP的软件包，另一个是不对应HTTP的通用的软件包。这两个软件包的同时存在使得Java Servlet API能够适应将来的其他请求-响应的协议。 <BR>这份文档以及刚才提及的Javadoc格式的文档都描述了这两个软件包，Javadoc格式的文档还描述了你应该如何使用这两个软件包中的所有方法。 <BR><BR>有关规范 <BR>你也许对下面的这些Internet规范感兴趣，这些规范将直接影响到Servlet API的发展和执行。你可以从http://info.internet.isi.edu/7c/in-notes/rfc/.cache 找到下面提到的所有这些RFC规范。 <BR>RFC 1738 统一资源定位器(URL) <BR>RFC 1808 相关统一资源定位器 <BR>RFC 1945 超文本传输协议--HTTP/1.0 <BR>RFC 2045 多用途Internet邮件扩展(多用途网际邮件扩充协议(MIME))第一部分:Internet信息体格式 <BR>RFC 2046 多用途Internet邮件扩展(多用途网际邮件扩充协议(MIME))第二部分:媒体类型 <BR>RFC 2047 多用途网际邮件扩充协议(MIME)(多用途Internet邮件扩展)第三部分:信息标题扩展用于非ASCII文本 <BR>RFC 2048 多用途Internet邮件扩展(多用途网际邮件扩充协议(MIME))第四部分: 注册步骤 <BR>RFC 2049 多用途Internet邮件扩展(多用途网际邮件扩充协议(MIME))第五部分:一致性标准和例子 <BR>RFC 2068 超文本传输协议 -- HTTP/1.1 <BR>RFC 2069 一个扩展HTTP:摘要访问鉴定 <BR>RFC 2109 HTTP状态管理机制 <BR>RFC 2145 HTTP 版本号的使用和解释 <BR>RFC 2324 超文本Coffee Pot控制协议 (HTCPCP/1.0) <BR>万维网协会（http://www.w3.org）管理着这些协议的规范和执行。 <BR><BR><BR>有关Java Servlets <BR>JavaTM servlets是一个不受平台约束的Java小程序，它可以被用来通过多种方法扩充一个Web服务器的功能。你可以把Servlet理解成Server上的applets，它被编译成字节码，这样它就可以被动态地载入并用效地扩展主机的处理能力。 <BR>Servlet与applets不同的地方是，它不运行在Web浏览器或其他图形化的用户界面上。Servlet通过servlet引擎运行在Web服务器中，以执行请求和响应，请求、响应的典型范例是HTTP协议。 <BR>一个客户端程序，可以是一个Web浏览器，或者是非其他的可以连接上Internet的程序，它会访问Web服务器并发出请求。这个请求被运行在Web服务器上的Servlet引擎处理，并返回响应到Servlet。Servlet通过HTTP将这个响应转发到客户端。 <BR>在功能上，Servlet与CGI、NSAPI有点类似，但是，与他们不同的是：Servlet具有平台无关性。 <BR><BR>Java Servlet概论 <BR>Servlet与其他普通的server扩展机制有以下进步： <BR>因为它采用了不同的进程处理模式，所以它比CGI更快。 <BR>它使用了许多Web服务器都支持的标准的API。 <BR>它继承了Java的所有优势，包括易升级以及平台无关性。 <BR>它可以调用Java所提供的大量的API的功能模块。 <BR>这份文档说明了Java Servlet API的类和接口的方法。有关更多的信息，请参看下面的API说明。 <BR><BR>Servlet的生命周期 <BR>一个Java servlet具有一个生命周期，这个生命周期定义了一个Servlet如何被载入并被初始化，如何接收请求并作出对请求的响应，如何被从服务中清除。Servlet的生命周期被javax.servlet.Servlet这个接口所定义。 <BR>所有的Java Servlet都会直接地或间接地执行javax.servlet.Servlet接口，这样它才能在一个Servlet引擎中运行。Servlet引擎是Web 服务器按照Java Servlet API定制的扩展。Servlet引擎提供网络服务，能够理解MIME请求，并提供一个运行Servlet的容器。 <BR>javax.servlet.Servlet接口定义了在Servlet的生命周期中特定时间以及特定顺序被调用的方法。 <BR><BR>Servlet的解析和载入\r <BR>Servlet引擎解析并载入一个Servlet，这个过程可以发生在引擎启动时，需要一个Servlet去响应请求时，以及在此之间的任何时候。 <BR>Servlet引擎利用Java类载入工具载入一个Servlet，Servlet引擎可以从一个本地的文件系统、一个远程的文件系统以及网络载入Servlet。 <BR><BR>Servlet的初始化 <BR>Servlet引擎载入Servlet后，Servlet引擎必须对Servlet进行初始化，在这一过程中，你可以读取一些固定存储的数据、初始化JDBC的连接以及建立与其他资源的连接。 <BR>在初始化过程中，javax.servlet.Servlet接口的init()方法提供了Servlet的初始化信息。这样，Servlet可以对自己进行配置。 <BR>init()方法获得了一个Servlet配置对象（ServletConfig）。这个对象在Servlet引擎中执行，并允许Servlet通过它获处相关参数。这个对象使得Servlet能够访问ServletContext对象。 <BR><BR>Servlet处理请求\r <BR>Servlet被初始化之后，它已经可以处理来自客户端的请求，每一个来自客户端的请求都被描述成一个ServletRequest对象，Servlet的响应被描述成一个ServletResponse对象。 <BR>当客户端发出请求时，Servlet引擎传递给Servlet一个ServletRequest对象和一个ServletResponse对象，这两个对象作为参数传递到service()方法中。 <BR>Servlet也可以执行ServletRequest接口和ServletResponse接口。ServletRequest接口使得Servlet有权使用客户端发出的请求。Servlet可以通过ServletInputStream对象读取请求信息。 <BR>ServletResponse接口允许Servlet建立响应头和状态代码。通过执行这个接口，Servlet有权使用ServletOutputStream类来向客户端返回数据。 <BR><BR>多线程和映射\r <BR>在多线程的环境下，Servlet必须能处理许多同时发生的请求。例外的情况是这个Servlet执行了SingleThreadModel接口，如果是那样的话，Servlet只能同时处理一个请求。 <BR>Servlet依照Servlet引擎的映射来响应客户端的请求。一个映射对包括一个Servlet实例以及一个Servlet返回数据的URL，例如：HelloServlet with /hello/index.html。 <BR>然而，一个映射可能是由一个URL和许多Servlet实例组成，例如：一个分布式的Servlet引擎可能运行在不止一个的服务器中，这样的话，每一个服务器中都可能有一个Servlet实例，以平衡进程的载入。作为一个Servlet的开发者，你不能假定一个Servlet只有一个实例。 <BR><BR>Servlet的卸载 <BR>Servlet引擎并不必需保证一个Servlet在任何时候或在服务开启的任何时候都被载入。Servlet引擎可以自由的在任何时候使用或清除一个Servlet。因此，我们不能依赖一个类或实例来存储重要的信息。 <BR>当Servlet引擎决定卸载一个Servlet时（例如，如果这个引擎被关闭或者需要让资源），这个引擎必须允许Servlet释放正在使用的资源并存储有关资料。为了完成以上工作，引擎会调用Servlet的destroy()方法。 <BR>在卸载一个Servlet之前，Servlet引擎必须等待所有的service()方法完成或超时结束（Servlet引擎会对超时作出定义）。当一个Servlet被卸载时，引擎将不能给Servlet发送任何请求。引擎必须释放Servlet并完成无用存储单元的收集 <BR><BR>Servlet映射技术\r <BR>作为一个Servlet引擎的开发者，你必须对于如何映射客户端的请求到Servlet有大量的适应性。这份说明文档不规定映射如何发生。但是，你必须能够自由地运用下面的所有技术： <BR><BR>映射一个Servlet到一个URL <BR>例如，你可以指定一个特殊的Servlet它仅被来自/feedback/index.html的请求调用。 <BR><BR>映射一个Servlet到以一个指定的目录名开始的所有URL <BR>例如，你可以映射一个Servlet到/catalog，这样来自/catalog/、 /catalog/garden和/catalog/housewares/index.html的请求都会被映射到这个Servlet。但是来自/catalogtwo 或/catalog.html的请求没被映射。 <BR><BR>映射一个Servlet到所有以一个特定的字段结尾的所有URL <BR>例如，你可以映射一个来自于所有以in.thtml结尾的请求到一个特定的Servlet。 <BR><BR>映射一个Servlet到一个特殊的URL /servlet/servlet_name。 <BR>例如，如果你建立了一个名叫listattributes的Servlet，你可以通过使用/servlet/listattributes来访问这个Servlet。 <BR><BR>通过类名调用Servlet <BR>例如，如果Servlet引擎接收了来自/servlet/com.foo.servlet.MailServlet的请求，Servlet引擎会载入这个com.foo.servlet.MailServlet类，建立实例，并通过这个Servlet来处理请求。 <BR><BR>Servlet环境 <BR>ServletContext接口定义了一个Servlet环境对象，这个对象定义了一个在Servlet引擎上的Servlet的视图。通过使用这个对象，Servlet可以记录事件、得到资源并得到来自Servlet引擎的类（例如RequestDispatcher对象）。一个Servlet只能运行在一个Servlet环境中，但是不同的Servlet可以在Servlet引擎上有不同的视图。 <BR>如果Servlet引擎支持虚拟主机，每个虚拟主机有一个Servlet环境。一个Servlet环境不能在虚拟主机之间共享。 <BR>Servlet引擎能够允许一个Servlet环境有它自己的活动范围。 <BR>例如，一个Servlet环境是属于bank应用的，它将被映射到/bank目录下。在这种情况下，一个对getContext方法的调用会返回/bank的Servlet环境。 <BR><BR>HTTP会话 <BR>HTTP是一个没有状态的协议。要建立一个有效的Web服务应用，你必须能够识别一个连续的来自远端的客户机的唯一的请求。随着时间的过去，发展了许多会话跟踪的技术，但是使用起来都比较麻烦。 <BR>Java Servlet API提供了一个简单的接口，通过这个接口，Servlet引擎可以有效地跟踪用户的会话。 <BR><BR>建立Session <BR>因为HTTP是一个请求-响应协议，一个会话在客户机加入之前会被认为是一个新的会话。加入的意思是返回会话跟踪信息到服务器中，指出会话已被建立。在客户端加入之前，我们不能判断下一个客户端请求是目前会话的一部分。 <BR>在下面的情况下，Session会被认为是新的Session。 <BR>客户端的Session在此之前还不知道 <BR>客户端选择不加入Session，例如，如果客户端拒绝接收来自服务器的cookie <BR>作为一个Servlet的开发者，你必须决定你的Web应用是否处理客户机不加入或不能加入Session。服务器会在Web服务器或Servlet规定的时间内维持一个Session对象。当Session终止时，服务器会释放Session对象以及所有绑定在Session上的对象。 <BR>绑定对象到Session中 <BR>如果有助于你处理应用的数据需求，你也许需要绑定对象到Session中，你可以通过一个唯一的名字绑定任何的对象到Session中，这时，你需要使用HttpSession对象。任何绑定到Session上的对象都可以被处理同一会话的Servlet调用。 <BR>有些对象可能需要你知道什么时候会被放置到Session中或从Session中移开。你可以通过使用HttpSessionBindingListener接口获得这些信息。当你的应用存储数据到Session中，或从Session中清除数据，Servlet都会通过HttpSessionBindingListener检杳什么类被绑定或被取消绑定。这个接口的方法会通报被绑定或被取消绑定的对象。 <BR><BR><BR><BR>API对象的说明<BR>这一部分包含了对Java Servlet API的全部类和接口的详细说明。这个说明与Javadoc API差不多，但是这份文档提供了更多的信息。 <BR>API包含了两个软件包，十二个接口和九个类。 <BR>软件包：javax.servlet <BR>所包含的接口：RequestDispatcher；Servlet；ServletConfig；ServletContext；ServletRequest；ServletResponse；SingleThreadModel。 <BR>所包含的类：GenericServlet；ServletInputStream；ServletOutputStream；ServletException；UnavailableException。 <BR><BR>一、RequestDispatcher接口： <BR>定义： <BR>public interface RequestDispatcher; <BR>定义一个对象，从客户端接收请求，然后将它发给服务器的可用资源（例如Servlet、CGI、HTML文件、JSP文件）。Servlet引擎创建request dispatcher对象，用于封装由一个特定的URL定义的服务器资源。 <BR>这个接口是专用于封装Servlet的，但是一个Servlet引擎可以创建request dispatcher对象用于封装任何类型的资源。 <BR>request dispatcher对象是由Servlet引擎建立的，而不是由Servlet开发者建立的。 <BR>方法 <BR>1、forward <BR>public void forward(ServletRequest request, ServletResponse response) <BR>throws ServletException, IOException; <BR>被用来从这个Servlet向其它服务器资源传递请求。当一个Servlet对响应作了初步的处理，并要求其它的对象对此做出响应时，可以使用这个方法。 <BR>当request对象被传递到目标对象时，请求的URL路径和其他路径参数会被调整为反映目标对象的目标URL路径。 <BR>如果已经通过响应返回了一个ServletOutputStream对象或PrintWriter对象，这个方法将不能使用，否则，这个方法会抛出一个IllegalStateException。 <BR>2、include <BR>public void include(ServletRequest request, ServletResponse response) <BR>throws ServletException, IOException <BR>用来包括发送给其他服务器资源的响应的内容。本质上来说，这个方法反映了服务器端的内容。 <BR>请求对象传到目标对象后会反映调用请求的请求URL路径和路径信息。这个响应对象只能调用这个Servlet的ServletOutputStream对象和PrintWriter对象。 <BR>一个调用include的Servlet不能设置头域，如果这个Servlet调用了必须设置头域的方法（例如cookie），这个方法将不能保证正常使用。作为一个Servlet开发者，你必须妥善地解决那些可能直接存储头域的方法。例如，即使你使用会话跟踪，为了保证session的正常工作，你必须在一个调用include的Servlet之外开始你的session <BR><BR>二、Servlet接口。 <BR>定义\r <BR>public interface Servlet <BR>这个接口定义了一个Servlet：一个在Web服务器上继承了这个功能的Java类。 <BR>方法 <BR>1、init <BR>public void init(ServletConfig config) throws ServletException; <BR>Servlet引擎会在Servlet实例化之后，置入服务之前精确地调用init方法。在调用service方法之前，init方法必须成功退出。 <BR>如果init方法抛出一个ServletException，你不能将这个Servlet置入服务中，如果init方法在超时范围内没完成，我们也可以假定这个Servlet是不具备功能的，也不能置入服务中。 <BR>2、service <BR>public void service(ServletRequest request, ServletResponse response) <BR>throws ServletException, IOException; <BR>Servlet引擎调用这个方法以允许Servlet响应请求。这个方法在Servlet未成功初始化之前无法调用。在Servlet被初始化之前，Servlet引擎能够封锁未决的请求。 <BR>在一个Servlet对象被卸载后，直到一个新的Servlet被初始化，Servlet引擎不能调用这个方法 <BR>3、destroy <BR>public void destroy(); <BR>当一个Servlet被从服务中去除时，Servlet引擎调用这个方法。在这个对象的service方法所有线程未全部退出或者没被引擎认为发生超时操作时，destroy方法不能被调用。 <BR>4、getServletConfig <BR>public ServletConfig getServletConfig(); <BR>返回一个ServletConfig对象，作为一个Servlet的开发者，你应该通过init方法存储ServletConfig对象以便这个方法能返回这个对象。为了你的便利，GenericServlet在执行这个接口时，已经这样做了。 <BR>5、getServletInfo <BR>public String getServletInfo(); <BR>允许Servlet向主机的Servlet运行者提供有关它本身的信息。返回的字符串应该是纯文本格式而不应有任何标志（例如HTML，XML等）。 <BR><BR>三、ServletConfig接口 <BR>定义\r <BR>public interface ServletConfig <BR>这个接口定义了一个对象，通过这个对象，Servlet引擎配置一个Servlet并且允许Servlet获得一个有关它的ServletContext接口的说明。每一个ServletConfig对象对应着一个唯一的Servlet。 <BR>方法 <BR>1、getInitParameter <BR>public String getInitParameter(String name); <BR>这个方法返回一个包含Servlet指定的初始化参数的String。如果这个参数不存在，返加空值。 <BR>2、getInitParameterNames <BR>public Enumeration getInitParameterNames(); <BR>这个方法返回一个列表String对象，该对象包括Servlet的所有初始化参数名。如果Servlet没有初始化参数，getInitParameterNames返回一个空的列表。 <BR>3、getServletContext <BR>public ServletContext getServletContext(); <BR>返回这个Servlet的ServletContext对象。 <BR><BR>四、ServletContext接口 <BR>定义\r <BR>public interface ServletContext <BR>定义了一个Servlet的环境对象，通过这个对象，Servlet引擎向Servlet提供环境信息。 <BR>一个Servlet的环境对象必须至少与它所驻留的主机是一一对应的。在一个处理多个虚拟主机的Servlet引擎中（例如，使用了HTTP1.1的主机头域），每一个虚拟主机必须被视为一个单独的环境。此外，Servlet引擎还可以创建对应于一组Servlet的环境对象。 <BR>方法 <BR>1、getAttribute <BR>public Object getAttribute(String name); <BR>返回Servlet环境对象中指定的属性对象。如果该属性对象不存在，返回空值。这个方法允许访问有关这个Servlet引擎的在该接口的其他方法中尚未提供的附加信息。 <BR>2、getAttributeNames <BR>public Enumeration getAttributeNames(); <BR>返回一个Servlet环境对象中可用的属性名的列表。 <BR>3、getContext <BR>public ServletContext getContext(String uripath); <BR>返回一个Servlet环境对象，这个对象包括了特定URI路径的Servlets和资源，如果该路径不存在，则返回一个空值。URI路径格式是/dir/dir/filename.ext。 <BR>为了安全，如果通过这个方法访问一个受限制的Servlet的环境对象，会返回一个空值。 <BR>4、getMajorVersion <BR>public int getMajorVersion(); <BR>返回Servlet引擎支持的Servlet API的主版本号。例如对于2.1版，这个方法会返回一个整数2。 <BR>5、getMinorVersion <BR>public int getMinorVersion(); <BR>返回Servlet引擎支持的Servlet API的次版本号。例如对于2.1版，这个方法会返回一个整数2。 <BR>6、getMimeType <BR>public String getMimeType(String file); <BR>返回指定文件的MIME类型，如果这种MIME类型未知，则返回一个空值。MIME类型是由Servlet引擎的配置决定的。 <BR>7、getRealPath <BR>public String getRealPath(String path); <BR>一个符合URL路径格式的指定的虚拟路径的格式是：/dir/dir/filename.ext。用这个方法，可以返回与一个符合该格式的虚拟路径相对应的真实路径的String。这个真实路径的格式应该适合于运行这个Servlet引擎的计算机（包括其相应的路径解析器）。 <BR>不管是什么原因，如果这一从虚拟路径转换成实际路径的过程不能执行，该方法将会返回一个空值。 <BR>8、getResource <BR>public URL getResource(String uripath); <BR>返回一个URL对象，该对象反映位于给定的URL地址（格式：/dir/dir/filename.ext）的Servlet环境对象已知的资源。无论URLStreamHandlers对于访问给定的环境是不是必须的，Servlet引擎都必须执行。如果给定的路径的Servlet环境没有已知的资源，该方法会返回一个空值。 <BR>这个方法和java.lang.Class的getResource方法不完全相同。java.lang.Class的getResource方法通过装载类来寻找资源。而这个方法允许服务器产生环境变量给任何资源的任何Servlet，而不必依赖于装载类、特定区域等等。 <BR>9、getResourceAsStream <BR>public InputStream getResourceAsStream(String uripath); <BR>返回一个InputStream对象，该对象引用指定的URL的Servlet环境对象的内容。如果没找到Servlet环境变量，就会返回空值，URL路径应该具有这种格式：/dir/dir/filename.ext。 <BR>这个方法是一个通过getResource方法获得URL对象的方便的途径。请注意，当你使用这个方法时，meta-information（例如内容长度、内容类型）会丢失。 <BR>10、getRequestDispatcher <BR>public RequestDispatcher getRequestDispatcher(String uripath); <BR>如果这个指定的路径下能够找到活动的资源(例如一个Servlet，JSP页面，CGI等等)就返回一个特定URL的RequestDispatcher对象，否则，就返回一个空值，Servlet引擎负责用一个request dispatcher对象封装目标路径。这个request dispatcher对象可以用来完全请求的传送。 <BR>11、getServerInfo <BR>public String getServerInfo(); <BR>返回一个String对象，该对象至少包括Servlet引擎的名字和版本号。 <BR>12、log <BR>public void log(String msg); <BR>public void log(String msg, Throwable t); <BR>public void log(Exception exception, String msg); // 这种用法将被取消\r <BR>写指定的信息到一个Servlet环境对象的log文件中。被写入的log文件由Servlet引擎指定，但是通常这是一个事件log。当这个方法被一个异常调用时，log中将包括堆栈跟踪。 <BR>13、setAttribute <BR>public void setAttribute(String name, Object o); <BR>给予Servlet环境对象中你所指定的对象一个名称。 <BR>14、removeAttribute <BR>public void removeAttribute(String name); <BR>从指定的Servlet环境对象中删除一个属性。 <BR>注：以下几个方法将被取消\r <BR>15、getServlet <BR>public Servlet getServlet(String name) throws ServletException; <BR>最初用来返回一个指定名称的Servlet，如果没找到就返回一个空值。如果这个Servlet能够返回，这就意味着它已经被初始化，而且已经可以接受service请求。这是一个危险的方法。当调用这个方法时，可能并不知道Servlet的状态，这就可能导致有关服务器状态的问题。而允许一个Servlet访问其他Servlet的这个方法也同样的危险。 <BR>现在这个方法返回一个空值，为了保持和以前版本的兼容性，现在这个方法还没有被取消。在以后的API版本中，该方法将被取消。 <BR>16、getServletNames <BR>public Enumeration getServletNames(); <BR>最初用来返回一个String对象的列表，该列表表示了在这个Servlet环境下所有已知的Servlet对象名。这个列表总是包含这个Servlet自身。 <BR>基于与上一个方法同样的理由，这也是一个危险的方法。 <BR>现在这个方法返回一个空的列表。为了保持和以前版本的兼容性，现在这个方法还没有被取消。在以后的API版本中，该方法将被取消。 <BR>17、getServlets <BR>public Enumeration getServlets(); <BR>最初用来返回在这个Servelet环境下所有已知的Servlet对象的列表。这个列表总是包含这个Servlet自身。 <BR>基于与getServlet方法同样的理由，这也是一个危险的方法。 <BR>现在这个方法返回一个空的列表。为了保持和以前版本的兼容性，现在这个方法还没有被取消。在以后的API版本中，该方法将被取消。 <BR><BR>五、ServletRequest接口 <BR>定义\r <BR>public interface ServletRequest <BR>定义一个Servlet引擎产生的对象，通过这个对象，Servlet可以获得客户端请求的数据。这个对象通过读取请求体的数据提供包括参数的名称、值和属性以及输入流的所有数据。 <BR>方法 <BR>1、getAttribute <BR>public Object getAttribute(String name); <BR>返回请求中指定属性的值，如果这个属性不存在，就返回一个空值。这个方法允许访问一些不提供给这个接口中其他方法的请求信息以及其他Servlet放置在这个请求对象内的数据。 <BR>2、getAttributeNames <BR>public Enumeration getAttributeNames(); <BR>返回包含在这个请求中的所有属性名的列表。 <BR>3、getCharacterEncoding <BR>public String getCharacterEncoding(); <BR>返回请求中输入内容的字符编码类型，如果没有定义字符编码类型就返回空值。 <BR>4、getContentLength <BR>public int getContentLength(); <BR>请求内容的长度，如果长度未知就返回-1。 <BR>5、getContentType <BR>public String getContentType(); <BR>返回请求数据体的MIME类型，如果类型未知返回空值。 <BR>6、getInputStream <BR>public ServletInputStream getInputStream() throws IOException; <BR>返回一个输入流用来从请求体读取二进制数据。如果在此之前已经通过getReader方法获得了要读取的结果，这个方法会抛出一个IllegalStateException。 <BR>7、getParameter <BR>public String getParameter(String name); <BR>以一个String返回指定的参数的值，如果这个参数不存在返回空值。例如，在一个HTTP Servlet中，这个方法会返回一个指定的查询语句产生的参数的值或一个被提交的表单中的参数值。如果一个参数名对应着几个参数值，这个方法只能返回通过getParameterValues方法返回的数组中的第一个值。因此，如果这个参数有（或者可能有）多个值，你只能使用getParameterValues方法。 <BR>8、getParameterNames <BR>public Enumeration getParameterNames(); <BR>返回所有参数名的String对象列表，如果没有输入参数，该方法返回一个空值。 <BR>9、getParameterValues <BR>public String[] getParameterValues(String name); <BR>通过一个String对象的数组返回指定参数的值，如果这个参数不存在，该方法返回一个空值。 <BR>10、getProtocol <BR>public String getProtocol(); <BR>返回这个请求所用的协议，其形式是协议/主版本号.次版本号。例如对于一个HTTP1.0的请求，该方法返回HTTP/1.0。 <BR>11、getReader <BR>public BufferedReader getReader() throws IOException; <BR>这个方法返回一个buffered reader用来读取请求体的实体，其编码方式依照请求数据的编码方式。如果这个请求的输入流已经被getInputStream调用获得，这个方法会抛出一个IllegalStateException。 <BR>12、getRemoteAddr <BR>public String getRemoteAddr(); <BR>返回发送请求者的IP地址。 <BR>13、getRemoteHost <BR>public String getRemoteHost(); <BR>返回发送请求者的主机名称。如果引擎不能或者选择不解析主机名（为了改善性能），这个方法会直接返回IP地址。 <BR>14、getScheme <BR>public String getScheme(); <BR>返回请求所使用的URL的模式。例如，对于一个HTTP请求，这个模式就是http。 <BR>15、getServerName <BR>public String getServerName(); <BR>返回接收请求的服务器的主机名。 <BR>16、getServerPort <BR>public int getServerPort(); <BR>返回接收请求的端口号。 <BR>17、setAttribute <BR>public void setAttribute(String name, Object object); <BR>这个方法在请求中添加一个属性，这个属性可以被其他可以访问这个请求对象的对象（例如一个嵌套的Servlet）使用。 <BR>注：以下方法将被取消\r <BR>getRealPath <BR>public String getRealPath(String path); <BR>返回与虚拟路径相对应的真实路径，如果因为某种原因，这一过程不能进行，该方法将返回一个空值。 <BR>这个方法和ServletContext接口中的getRealPath方法重复。在2.1版中，ServletContext接口将阐明一个Servlet所能用的所有的路径的映射。该方法执行的结果将会与ServletContext中getRealPath方法的结果完全一样。 <BR><BR>六、ServletResponse接口 <BR>定义\r <BR>public interface ServletResponse <BR>定义一个Servlet引擎产生的对象，通过这个对象，Servlet对客户端的请求作出响应。这个响应应该是一个MIME实体，可能是一个HTML页、图象数据或其他MIME的格式。 <BR>方法 <BR>1、getCharacterEncoding <BR>public String getCharacterEncoding(); <BR>返回MIME实体的字符编码。这个字符编码可以是指定的类型，也可以是与请求头域所反映的客户端所能接受的字符编码最匹配的类型。在HTTP协议中，这个信息被通过Accept-Charset传送到Servlet引擎。 <BR>有关字符编码和MIME的更多信息请参看RFC 2047。 <BR>2、getOutputStream <BR>public ServletOutputStream getOutputStream() throws IOException; <BR>返回一个记录二进制的响应数据的输出流。 <BR>如果这个响应对象已经调用getWriter，将会抛出IllegalStateException。 <BR>3、getWriter <BR>public PrintWriter getWriter throws IOException; <BR>这个方法返回一个PrintWriter对象用来记录格式化的响应实体。如果要反映使用的字符编码，必须修改响应的MIME类型。在调用这个方法之前，必须设定响应的content类型。 <BR>如果没有提供这样的编码类型，会抛出一个UnsupportedEncodingException，如果这个响应对象已调用getOutputStream，会抛出一个getOutputStream。 <BR>4、setContentLength <BR>public void setContentLength(int length); <BR>设置响应的内容的长度，这个方法会覆盖以前对内容长度的设定。 <BR>为了保证成功地设定响应头的内容长度，在响应被提交到输出流之前必须调用这个方法。 <BR>5、setContentType <BR>public void setContentType(String type); <BR>这个方法用来设定响应的content类型。这个类型以后可能会在另外的一些情况下被隐式地修改，这里所说的另外的情况可能当服务器发现有必要的情况下对MIME的字符设置。 <BR>为了保证成功地设定响应头的content类型，在响应被提交到输出流之前必须调用这个方法。 <BR><BR>七、SingleThreadModel接口 <BR>定义\r <BR>public interface SingleThreadModel; <BR>这是一个空接口，它指定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定，那么在这个Servlet中的service方法中将不会有两个线程被同时执行。 <BR>Servlet可以通过维持一个各自独立的Servlet实例池，或者通过只让Servlet的service中只有一个线程的方法来实现这个保证。 <BR><BR>八、GenericServlet类\r <BR>public abstract class GenericServlet implements Servlet, <BR>ServletConfig, Serializable; <BR>这个类的存在使得编写Servlet更加方便。它提供了一个简单的方案，这个方案用来执行有关Servlet生命周期的方法以及在初始化时对ServletConfig对象和ServletContext对象进行说明。 <BR>方法 <BR>1、destroy <BR>public void destroy(); <BR>在这里destroy方法不做任何其他的工作。 <BR>2、getInitParameter <BR>public String getInitParameter(String name); <BR>这是一个简便的途径，它将会调用ServletConfig对象的同名的方法。 <BR>3、getInitParameterNames <BR>public Enumeration getInitParameterNames(); <BR>这是一个简便的途径，它将会调用ServletConfig对象的同名的方法。 <BR>4、getServletConfig <BR>public ServletConfig getServletConfig(); <BR>返回一个通过这个类的init方法产生的ServletConfig对象的说明。 <BR>5、getServletContext <BR>public ServletContext getServletContext(); <BR>这是一个简便的途径，它将会调用ServletConfig对象的同名的方法。 <BR>6、getServletInfo <BR>public String getServletInfo(); <BR>返回一个反映Servlet版本的String。 <BR>7、init <BR>public void init() throws ServletException; <BR>public void init(ServletConfig config) throws ServletException; <BR>init(ServletConfig config)方法是一个对这个Servlet的生命周期进行初始化的简便的途径。 <BR>init()方法是用来让你对GenericServlet类进行扩充的，使用这个方法时，你不需要存储config对象，也不需要调用super.init(config)。 <BR>init(ServletConfig config)方法会存储config对象然后调用init()。如果你重载了这个方法，你必须调用super.init(config)，这样GenericServlet类的其他方法才能正常工作。 <BR>8、log <BR>public void log(String msg); <BR>public void log(String msg, Throwable cause); <BR>通过Servlet content对象将Servlet的类名和给定的信息写入log文件中。 <BR>9、service <BR>public abstract void service(ServletRequest request, ServletResponse <BR>response) throws ServletException, IOException; <BR>这是一个抽象的方法，当你扩展这个类时，为了执行网络请求，你必须执行它。 <BR><BR>九、ServletInputStream类\r <BR>定义\r <BR>public abstract class ServletInputStream extends InputStream <BR>这个类定义了一个用来读取客户端的请求信息的输入流。这是一个Servlet引擎提供的抽象类。一个Servlet通过使用ServletRequest接口获得了对一个ServletInputStream对象的说明。 <BR>这个类的子类必须提供一个从InputStream接口读取有关信息的方法。 <BR>方法 <BR>1、readLine <BR>public int readLine(byte[] b, int off, int len) throws IOException; <BR>从输入流的指定的偏移量开始将指定长度的字节读入到指定的数组中。如果该行所有请求的内容都已被读取，这个读取的过程将结束。如果是遇到了新的一行，新的一行的首个字符也将被读入到数组中。 <BR><BR>十、ServletOutputStream类\r <BR>定义\r <BR>public abstract class ServletOutputStream extends OutputStream <BR>这是一个由Servlet引擎使用的抽象类。Servlet通过使用ServletResponse接口的使用获得了对一个这种类型的对象的说明。利用这个输出流可以将数据返回到客户端。 <BR>这个类的子类必须提供一个向OutputStream接口写入有关信息的方法。 <BR>在这个接口中，当一个刷新或关闭的方法被调用时。所有数据缓冲区的信息将会被发送到客户端，也就是说响应被提交了。请注意，关闭这种类型的对象时不一定要关闭隐含的socket流。 <BR>方法 <BR>1、print <BR>public void print(String s) throws IOException; <BR>public void print(boolean b) throws IOException; <BR>public void print(char c) throws IOException; <BR>public void print(int i) throws IOException; <BR>public void print(long l) throws IOException; <BR>public void print(float f) throws IOException; <BR>public void print(double d) throws IOException; <BR>输出变量到输出流中 <BR>2、println <BR>public void println() throws IOException; <BR>public void println(String s) throws IOException; <BR>public void println(boolean b) throws IOException; <BR>public void println(char c) throws IOException; <BR>public void println(int i) throws IOException; <BR>public void println(long l) throws IOException; <BR>public void println(float f) throws IOException; <BR>public void println(double d) throws IOException; <BR>输出变量到输出流中，并增加一个回车换行符\r <BR><BR>十一、ServletException类\r <BR>定义\r <BR>public class ServletException extends Exception <BR>当Servlet遇到问题时抛出的一个异常。 <BR>构造函数 <BR>public ServletException(); <BR>public ServletException(String message); <BR>public ServletException(String message, Throwable cause); <BR>public ServletException(Throwable cause); <BR>构造一个新的ServletException，如果这个构造函数包括一个Throwable参数，这个Throwable对象将被作为可能抛出这个异常的原因。 <BR>方法 <BR>1、getRootCause <BR>public Throwable getRootCause(); <BR>如果配置了抛出这个异常的原因，这个方法将返回这个原因，否则返回一个空值。 <BR><BR>十二、UnavailableException类\r <BR>定义\r <BR>public class UnavailableException extends ServletException <BR>不论一个Servlet是永久地还是临时地无效，都会抛出这个异常。Servlet会记录这个异常以及Servlet引擎所要采取的相应措施。 <BR>临时的无效是指Servlet在某一时间由于一个临时的问题而不能处理请求。例如，在另一个不同的应用层的服务（可能是数据库）无法使用。这个问题可能会自行纠正或者需要采取其他的纠正措施。 <BR>永久的无效是指除非管理员采取措施，这个Servlet将不能处理客户端的请求。例如，这个Servlet配置信息丢失或Servlet的状态被破坏。 <BR>Servlet引擎可以安全地处理包括永久无效在内的这两种异常，但是对临时无效的正常处理可以使得Servlet引擎更健壮。特别的，这时对Servlet的请求只是被阻止（或者是被延期）一段时间，这显然要比在service自己重新启动前完全拒绝请求更为科学。 <BR>构造函数 <BR>public UnavailableException(Servlet servlet, String message); <BR>public UnavailableException(int seconds, Servlet servlet, <BR>String message); <BR>构造一个包含指定的描述信息的新的异常。如果这个构造函数有一个关于秒数的参数，这将给出Servlet发生临时无效后，能够重新处理请求的估计时间。如果不包含这个参数，这意味着这个Servlet永久无效。 <BR>方法 <BR>1、getServlet <BR>public Servlet getServlet(); <BR>返回报告无效的Servlet。这被Servlet引擎用来识别受到影响的Servlet。 <BR>2、getUnavailableSeconds <BR>public int getUnavailableSeconds(); <BR>返回Servlet预期的无效时间，如果这个Servlet是永久无效，返回-1。 <BR>3、isPermanent <BR>public boolean isPermanent(); <BR>如果这个Servlet永久无效，返回布尔值true，指示必须采取一些管理行动以使得这个Servlet可用。 <BR><BR>Java Servlet API说明文档（2.1a版）（三） <BR>软件包：javax.servlet.http <BR>所包含的接口：HttpServletRequest；HttpServletResponse；HttpSession；HttpSessionBindingListener；HttpSessionContext。 <BR>所包含的类：Cookie；HttpServlet；HttpSessionBindingEvent；HttpUtils。 <BR><BR>一、HttpServletRequest接口 <BR>定义\ <BR>public interface HttpServletRequest extends ServletRequest; <BR>用来处理一个对Servlet的HTTP格式的请求信息。 <BR>方法 <BR>1、getAuthType <BR>public String getAuthType(); <BR>返回这个请求的身份验证模式。 <BR>2、getCookies <BR>public Cookie[] getCookies(); <BR>返回一个数组，该数组包含这个请求中当前的所有cookie。如果这个请求中没有cookie，返回一个空数组。 <BR>3、getDateHeader <BR>public long getDateHeader(String name); <BR>返回指定的请求头域的值，这个值被转换成一个反映自1970-1-1日（GMT）以来的精确到毫秒的长整数。 <BR>如果头域不能转换，抛出一个IllegalArgumentException。如果这个请求头域不存在，这个方法返回-1。 <BR>4、getHeader <BR>public String getHeader(String name); <BR>返回一个请求头域的值。（译者注：与上一个方法不同的是，该方法返回一个字符串） <BR>如果这个请求头域不存在，这个方法返回-1。 <BR>5、getHeaderNames <BR>public Enumeration getHeaderNames(); <BR>该方法返回一个String对象的列表，该列表反映请求的所有头域名。 <BR>有的引擎可能不允许通过这种方法访问头域，在这种情况下，这个方法返回一个空的列表。 <BR>6、getIntHeader <BR>public int getIntHeader(String name); <BR>返回指定的请求头域的值，这个值被转换成一个整数。 <BR>如果头域不能转换，抛出一个IllegalArgumentException。如果这个请求头域不存在，这个方法返回-1。 <BR>7、getMethod <BR>public String getMethod(); <BR>返回这个请求使用的HTTP方法（例如：GET、POST、PUT） <BR>8、getPathInfo <BR>public String getPathInfo(); <BR>这个方法返回在这个请求的URL的Servlet路径之后的请求URL的额外的路径信息。如果这个请求URL包括一个查询字符串，在返回值内将不包括这个查询字符串。这个路径在返回之前必须经过URL解码。如果在这个请求的URL的Servlet路径之后没有路径信息。这个方法返回空值。 <BR>9、getPathTranslated <BR>public String getPathTranslated(); <BR>这个方法获得这个请求的URL的Servlet路径之后的额外的路径信息，并将它转换成一个真实的路径。在进行转换前，这个请求的URL必须经过URL解码。如果在这个URL的Servlet路径之后没有附加路径信息。这个方法返回空值。 <BR>10、getQueryString <BR>public String getQueryString(); <BR>返回这个请求URL所包含的查询字符串。一个查询字串符在一个URL中由一个“？”引出。如果没有查询字符串，这个方法返回空值。 <BR>11、getRemoteUser <BR>public String getRemoteUser <BR>返回作了请求的用户名，这个信息用来作HTTP用户论证。 <BR>如果在请求中没有用户名信息，这个方法返回空值。 <BR>12、getRequestedSessionId <BR>public String getRequestedSessionId(); <BR>返回这个请求相应的session id。如果由于某种原因客户端提供的session id是无效的，这个session id将与在当前session中的session id不同，与此同时，将建立一个新的session。 <BR>如果这个请求没与一个session关联，这个方法返回空值。 <BR>13、getRequestURI <BR>public String getRequestURI(); <BR>从HTTP请求的第一行返回请求的URL中定义被请求的资源的部分。如果有一个查询字符串存在，这个查询字符串将不包括在返回值当中。例如，一个请求通过/catalog/books?id=1这样的URL路径访问，这个方法将返回/catalog/books。这个方法的返回值包括了Servlet路径和路径信息。 <BR>如果这个URL路径中的的一部分经过了URL编码，这个方法的返回值在返回之前必须经过解码。 <BR>14、getServletPath <BR>public String getServletPath(); <BR>这个方法返回请求URL反映调用Servlet的部分。例如，一个Servlet被映射到/catalog/summer这个URL路径，而一个请求使用了/catalog/summer/casual这样的路径。所谓的反映调用Servlet的部分就是指/catalog/summer。 <BR>如果这个Servlet不是通过路径匹配来调用。这个方法将返回一个空值。 <BR>15、getSession <BR>public HttpSession getSession(); <BR>public HttpSession getSession(boolean create); <BR>返回与这个请求关联的当前的有效的session。如果调用这个方法时没带参数，那么在没有session与这个请求关联的情况下，将会新建一个session。如果调用这个方法时带入了一个布尔型的参数，只有当这个参数为真时，session才会被建立。 <BR>为了确保session能够被完全维持。Servlet开发者必须在响应被提交之前调用该方法。 <BR>如果带入的参数为假，而且没有session与这个请求关联。这个方法会返回空值。 <BR>16、isRequestedSessionIdValid <BR>public boolean isRequestedSessionIdValid(); <BR>这个方法检查与此请求关联的session当前是不是有效。如果当前请求中使用的session无效，它将不能通过getSession方法返回。 <BR>17、isRequestedSessionIdFromCookie <BR>public boolean isRequestedSessionIdFromCookie(); <BR>如果这个请求的session id是通过客户端的一个cookie提供的，该方法返回真，否则返回假。 <BR>18、isRequestedSessionIdFromURL <BR>public boolean isRequestedSessionIdFromURL(); <BR>如果这个请求的session id是通过客户端的URL的一部分提供的，该方法返回真，否则返回假。请注意此方法与isRequestedSessionIdFromUrl在URL的拼写上不同。 <BR>以下方法将被取消\\r <BR><BR>19、isRequestedSessionIdFromUrl <BR>public boolean isRequestedSessionIdFromUrl(); <BR>该方法被isRequestedSessionIdFromURL代替。 <BR><BR>二、HttpServletResponse接口 <BR>定义\\r <BR><BR>public interface HttpServletResponse extends ServletResponse <BR>描述一个返回到客户端的HTTP回应。这个接口允许Servlet程序员利用HTTP协议规定的头信息。 <BR>成员变量 <BR>public static final int SC_CONTINUE = 100; <BR>public static final int SC_SWITCHING_PROTOCOLS = 101; <BR>public static final int SC_OK = 200; <BR>public static final int SC_CREATED = 201; <BR>public static final int SC_ACCEPTED = 202; <BR>public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203; <BR>public static final int SC_NO_CONTENT = 204; <BR>public static final int SC_RESET_CONTENT = 205; <BR>public static final int SC_PARTIAL_CONTENT = 206; <BR>public static final int SC_MULTIPLE_CHOICES = 300; <BR>public static final int SC_MOVED_PERMANENTLY = 301; <BR>public static final int SC_MOVED_TEMPORARILY = 302; <BR>public static final int SC_SEE_OTHER = 303; <BR>public static final int SC_NOT_MODIFIED = 304; <BR>public static final int SC_USE_PROXY = 305; <BR>public static final int SC_BAD_REQUEST = 400; <BR>public static final int SC_UNAUTHORIZED = 401; <BR>public static final int SC_PAYMENT_REQUIRED = 402; <BR>public static final int SC_FORBIDDEN = 403; <BR>public static final int SC_NOT_FOUND = 404; <BR>public static final int SC_METHOD_NOT_ALLOWED = 405; <BR>public static final int SC_NOT_ACCEPTABLE = 406; <BR>public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407; <BR>public static final int SC_REQUEST_TIMEOUT = 408; <BR>public static final int SC_CONFLICT = 409; <BR>public static final int SC_GONE = 410; <BR>public static final int SC_LENGTH_REQUIRED = 411; <BR>public static final int SC_PRECONDITION_FAILED = 412; <BR>public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413; <BR>public static final int SC_REQUEST_URI_TOO_LONG = 414; <BR>public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415; <BR>public static final int SC_INTERNAL_SERVER_ERROR = 500; <BR>public static final int SC_NOT_IMPLEMENTED = 501; <BR>public static final int SC_BAD_GATEWAY = 502; <BR>public static final int SC_SERVICE_UNAVAILABLE = 503; <BR>public static final int SC_GATEWAY_TIMEOUT = 504; <BR>public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505; <BR>以上HTTP产状态码是由HTTP/1.1定义的。 <BR>方法 <BR>1、addCookie <BR>public void addCookie(Cookie cookie); <BR>在响应中增加一个指定的cookie。可多次调用该方法以定义多个cookie。为了设置适当的头域，该方法应该在响应被提交之前调用。 <BR>2、containsHeader <BR>public boolean containsHeader(String name); <BR>检查是否设置了指定的响应头。 <BR>3、encodeRedirectURL <BR>public String encodeRedirectURL(String url); <BR>对sendRedirect方法使用的指定URL进行编码。如果不需要编码，就直接返回这个URL。之所以提供这个附加的编码方法，是因为在redirect的情况下，决定是否对URL进行编码的规则和一般情况有所不同。所给的URL必须是一个绝对URL。相对URL不能被接收，会抛出一个IllegalArgumentException。 <BR>所有提供给sendRedirect方法的URL都应通过这个方法运行，这样才能确保会话跟踪能够在所有浏览器中正常运行。 <BR>4、encodeURL <BR>public String encodeURL(String url); <BR>对包含session ID的URL进行编码。如果不需要编码，就直接返回这个URL。Servlet引擎必须提供URL编码方法，因为在有些情况下，我们将不得不重写URL，例如，在响应对应的请求中包含一个有效的session，但是这个session不能被非URL的（例如cookie）的手段来维持。 <BR>所有提供给Servlet的URL都应通过这个方法运行，这样才能确保会话跟踪能够在所有浏览器中正常运行。 <BR>5、sendError <BR>public void sendError(int statusCode) throws IOException; <BR>public void sendError(int statusCode, String message) throws <BR>IOException; <BR>用给定的状态码发给客户端一个错误响应。如果提供了一个message参数，这将作为响应体的一部分被发出，否则，服务器会返回错误代码所对应的标准信息。 <BR>调用这个方法后，响应立即被提交。在调用这个方法后，Servlet不会再有更多的输出。 <BR>6、sendRedirect <BR>public void sendRedirect(String location) throws IOException; <BR>使用给定的路径，给客户端发出一个临时转向的响应（SC_MOVED_TEMPORARILY）。给定的路径必须是绝对URL。相对URL将不能被接收，会抛出一个IllegalArgumentException。 <BR>这个方法必须在响应被提交之前调用。调用这个方法后，响应立即被提交。在调用这个方法后，Servlet不会再有更多的输出。 <BR>7、setDateHeader <BR>public void setDateHeader(String name, long date); <BR>用一个给定的名称和日期值设置响应头，这里的日期值应该是反映自1970-1-1日（GMT）以来的精确到毫秒的长整数。如果响应头已经被设置，新的值将覆盖当前的值。 <BR>8、setHeader <BR>public void setHeader(String name, String value); <BR>用一个给定的名称和域设置响应头。如果响应头已经被设置，新的值将覆盖当前的值。 <BR>9、setIntHeader <BR>public void setIntHeader(String name, int value); <BR>用一个给定的名称和整形值设置响应头。如果响应头已经被设置，新的值将覆盖当前的值。 <BR>10、setStatus <BR>public void setStatus(int statusCode); <BR>这个方法设置了响应的状态码，如果状态码已经被设置，新的值将覆盖当前的值。 <BR>以下的几个方法将被取消\ <BR>11、encodeRedirectUrl <BR>public String encodeRedirectUrl(String url); <BR>该方法被encodeRedirectURL取代。 <BR>12、encodeUrl <BR>public String encodeUrl(String url); <BR>该方法被encodeURL取代。 <BR>13、setStatus <BR>public void setStatus(int statusCode, String message); <BR>这个方法设置了响应的状态码，如果状态码已经被设置，新的值将覆盖当前的值。如果提供了一个message，它也将会被作为响应体的一部分被发送。 <BR><BR>三、HttpSession接口 <BR>定义\ <BR>public interface HttpSession <BR>这个接口被Servlet引擎用来实现在HTTP客户端和HTTP会话两者的关联。这种关联可能在多外连接和请求中持续一段给定的时间。session用来在无状态的HTTP协议下越过多个请求页面来维持状态和识别用户。 <BR>一个session可以通过cookie或重写URL来维持。 <BR>方法 <BR>1、getCreationTime <BR>public long getCreationTime(); <BR>返回建立session的时间，这个时间表示为自1970-1-1日（GMT）以来的毫秒数。 <BR>2、getId <BR>public String getId(); <BR>返回分配给这个session的标识符。一个HTTP session的标识符是一个由服务器来建立和维持的唯一的字符串。 <BR>3、getLastAccessedTime <BR>public long getLastAccessedTime(); <BR>返回客户端最后一次发出与这个session有关的请求的时间，如果这个session是新建立的，返回-1。这个时间表示为自1970-1-1日（GMT）以来的毫秒数。 <BR>4、getMaxInactiveInterval <BR>public int getMaxInactiveInterval(); <BR>返加一个秒数，这个秒数表示客户端在不发出请求时，session被Servlet引擎维持的最长时间。在这个时间之后，Servlet引擎可能被Servlet引擎终止。如果这个session不会被终止，这个方法返回-1。 <BR>当session无效后再调用这个方法会抛出一个IllegalStateException。 <BR>5、getValue <BR>public Object getValue(String name); <BR>返回一个以给定的名字绑定到session上的对象。如果不存在这样的绑定，返回空值。 <BR>当session无效后再调用这个方法会抛出一个IllegalStateException。 <BR>6、getValueNames <BR>public String[] getValueNames(); <BR>以一个数组返回绑定到session上的所有数据的名称。 <BR>当session无效后再调用这个方法会抛出一个IllegalStateException。 <BR>7、invalidate <BR>public void invalidate(); <BR>这个方法会终止这个session。所有绑定在这个session上的数据都会被清除。并通过HttpSessionBindingListener接口的valueUnbound方法发出通告。 <BR>8、isNew <BR>public boolean isNew(); <BR>返回一个布尔值以判断这个session是不是新的。如果一个session已经被服务器建立但是还没有收到相应的客户端的请求，这个session将被认为是新的。这意味着，这个客户端还没有加入会话或没有被会话公认。在他发出下一个请求时还不能返回适当的session认证信息。 <BR>当session无效后再调用这个方法会抛出一个IllegalStateException。 <BR>9、putValue <BR>public void putValue(String name, Object value); <BR>以给定的名字，绑定给定的对象到session中。已存在的同名的绑定会被重置。这时会调用HttpSessionBindingListener接口的valueBound方法。 <BR>当session无效后再调用这个方法会抛出一个IllegalStateException。 <BR>10、removeValue <BR>public void removeValue(String name); <BR>取消给定名字的对象在session上的绑定。如果未找到给定名字的绑定的对象，这个方法什么也不做。 这时会调用HttpSessionBindingListener接口的valueUnbound方法。 <BR>当session无效后再调用这个方法会抛出一个IllegalStateException。 <BR>11、setMaxInactiveInterval <BR>public int setMaxInactiveInterval(int interval); <BR>设置一个秒数，这个秒数表示客户端在不发出请求时，session被Servlet引擎维持的最长时间。 <BR>以下这个方法将被取消\ <BR>12、getSessionContext <BR>public HttpSessionContext getSessionContext(); <BR>返回session在其中得以保持的环境变量。这个方法和其他所有HttpSessionContext的方法一样被取消了。 <BR><BR>四、HttpSessionBindingListener接口 <BR>定义\ <BR>public interface HttpSessionBindingListener <BR>这个对象被加入到HTTP的session中，执行这个接口会通告有没有什么对象被绑定到这个HTTP session中或被从这个HTTP session中取消绑定。 <BR>方法 <BR>1、valueBound <BR>public void valueBound(HttpSessionBindingEvent event); <BR>当一个对象被绑定到session中，调用此方法。HttpSession.putValue方法被调用时，Servlet引擎应该调用此方法。 <BR>2、valueUnbound <BR>public void valueUnbound(HttpSessionBindingEvent event); <BR>当一个对象被从session中取消绑定，调用此方法。HttpSession.removeValue方法被调用时，Servlet引擎应该调用此方法。 <BR><BR>五、HttpSessionContext接口 <BR>定义\ <BR>此接口将被取消\ <BR>public interface HttpSessionContext <BR>这个对象是与一组HTTP session关联的单一的实体。 <BR>这个接口由于安全的原因被取消，它出现在目前的版本中仅仅是为了兼容性的原因。这个接口的方法将模拟以前的版本的定义返回相应的值。 <BR>方法 <BR>1、getSession <BR>public HttpSession getSession(String sessionId); <BR>当初用来返回与这个session id相关的session。现在返回空值。 <BR>2、getIds <BR>public Enumeration getIds(); <BR>当初用来返回这个环境下所有session id的列表。现在返回空的列表。 <BR><BR>六、Cookie类\ <BR>定义\ <BR>public class Cookie implements Cloneable <BR>这个类描述了一个cookie，有关cookie的定义你可以参照Netscape Communications Corporation的说明，也可以参照RFC 2109。 <BR>构造函数 <BR>public Cookie(String name, String value); <BR>用一个name-value对定义一个cookie。这个name必须能被HTTP/1.1所接受。 <BR>以字符$开头的name被RFC 2109保留。 <BR>给定的name如果不能被HTTP/1.1所接受，该方法抛出一个IllegalArgumentException。 <BR>方法 <BR>1、getComment <BR>public String getComment(); <BR>返回描述这个cookie目的的说明，如果未定义这个说明，返回空值。 <BR>2、getDomain <BR>public String getDomain(); <BR>返回这个cookie可以出现的区域，如果未定义区域，返回空值。 <BR>3、getMaxAge <BR>public int getMaxAge(); <BR>这个方法返回这个cookie指定的最长存活时期。如果未定义这个最长存活时期，该方法返回-1。 <BR>4、getName <BR>public String getName(); <BR>该方法返回cookie名。 <BR>5、getPath <BR>public String getPath(); <BR>返回这个cookie有效的所有URL路径的前缀，如果未定义，返回空值。 <BR>6、getSecure <BR>public boolean getSecure(); <BR>如果这个cookie只通过安全通道传输返回真，否则返回假。 <BR>7、getValue <BR>public String getValue(); <BR>该方法返回cookie的值。 <BR>8、getVersion <BR>public int getVersion(); <BR>返回cookie的版本。版本1由RFC 2109解释。版本0由Netscape Communications Corporation的说明解释。新构造的cookie默认使用版本0。 <BR>9、setComment <BR>public void setComment(String purpose); <BR>如果一个用户将这个cookie提交给另一个用户，必须通过这个说明描述这个cookie的目的。版本0不支持这个属性。 <BR>10、setDomain <BR>public void setDomain(String pattern); <BR>这个方法设置cookie的有效域的属性。这个属性指定了cookie可以出现的区域。一个有效域以一个点开头（.foo.com），这意味着在指定的域名解析系统的区域中（可能是www.foo.com但不是a.b.foo.com）的主机可以看到这个cookie。默认情况是，cookie只能返回保存它的主机。 <BR>11、setMaxAge <BR>public void setMaxAge(int expiry); <BR>这个方法设定这个cookie的最长存活时期。在该存活时期之后，cookie会被终目。负数表示这个cookie不会生效，0将从客户端删除这个cookie。 <BR>12、setPath <BR>public void setPath(String uri); <BR>这个方法设置cookie的路径属性。客户端只能向以这个给定的路径String开头的路径返回cookie。 <BR>13、setSecure <BR>public void setSecure(boolean flag); <BR>指出这个cookie只能通过安全通道（例如HTTPS）发送。只有当产生这个cookie的服务器使用安全协议发送这个cookie值时才能这样设置。 <BR>14、setValue <BR>public void setValue(String newValue); <BR>设置这个cookie的值，对于二进制数据采用BASE64编码。 <BR>版本0不能使用空格、{}、()、=、，、“”、/、?、@、：以及；。 <BR>15、setVersion <BR>public void setVersion(int v); <BR>设置cookie的版本号 <BR><BR>七、HttpServlet类\ <BR>定义\ <BR>public class HttpServlet extends GenericServlet implements <BR>Serializable <BR>这是一个抽象类，用来简化HTTP Servlet写作的过程。它是GenericServlet类的扩充，提供了一个处理HTTP协议的框架。 <BR>在这个类中的service方法支持例如GET、POST这样的标准的HTTP方法。这一支持过程是通过分配他们到适当的方法（例如doGet、doPost）来实现的。 <BR>方法 <BR>1、doDelete <BR>protected void doDelete(HttpServletRequest request, <BR>HttpServletResponse response) throws ServletException, <BR>IOException; <BR>被这个类的service方法调用，用来处理一个HTTP DELETE操作。这个操作允许客户端请求从服务器上删除URL。这一操作可能有负面影响，对此用户就负起责任。 <BR>这一方法的默认执行结果是返回一个HTTP BAD_REQUEST错误。当你要处理DELETE请求时，你必须重载这一方法。 <BR>2、doGet <BR>protected void doGet(HttpServletRequest request, <BR>HttpServletResponse response) throws ServletException, <BR>IOException; <BR>被这个类的service方法调用，用来处理一个HTTP GET操作。这个操作允许客户端简单地从一个HTTP服务器“获得”资源。对这个方法的重载将自动地支持HEAD方法。 <BR>GET操作应该是安全而且没有负面影响的。这个操作也应该可以安全地重复。 <BR>这一方法的默认执行结果是返回一个HTTP BAD_REQUEST错误。 <BR>3、doHead <BR>protected void doHead(HttpServletRequest request, <BR>HttpServletResponse response) throws ServletException, <BR>IOException; <BR>被这个类的service方法调用，用来处理一个HTTP HEAD操作。默认的情况是，这个操作会按照一个无条件的GET方法来执行，该操作不向客户端返回任何数据，而仅仅是返回包含内容长度的头信息。 <BR>与GET操作一样，这个操作应该是安全而且没有负面影响的。这个操作也应该可以安全地重复。 <BR>这个方法的默认执行结果是自动处理HTTP HEAD操作，这个方法不需要被一个子类执行。 <BR>4、doOptions <BR>protected void doOptions(HttpServletRequest request, <BR>HttpServletResponse response) throws ServletException, <BR>IOException; <BR>被这个类的service方法调用，用来处理一个HTTP OPTION操作。这个操作自动地决定支持哪一种HTTP方法。例如，一个Servlet写了一个HttpServlet的子类并重载了doGet方法，doOption会返回下面的头： <BR>Allow: GET,HEAD,TRACE,OPTIONS <BR>你一般不需要重载这个方法。 <BR>5、doPost <BR>protected void doPost(HttpServletRequest request, <BR>HttpServletResponse response) throws ServletException, <BR>IOException; <BR>被这个类的service方法调用，用来处理一个HTTP POST操作。这个操作包含请求体的数据，Servlet应该按照他行事。 <BR>这个操作可能有负面影响。例如更新存储的数据或在线购物。 <BR>这一方法的默认执行结果是返回一个HTTP BAD_REQUEST错误。当你要处理POST操作时，你必须在HttpServlet的子类中重载这一方法。 <BR>6、doPut <BR>protected void doPut(HttpServletRequest request, <BR>HttpServletResponse response) throws ServletException, <BR>IOException; <BR>被这个类的service方法调用，用来处理一个HTTP PUT操作。这个操作类似于通过FTP发送文件。 <BR>这个操作可能有负面影响。例如更新存储的数据或在线购物。 <BR>这一方法的默认执行结果是返回一个HTTP BAD_REQUEST错误。当你要处理PUT操作时，你必须在HttpServlet的子类中重载这一方法。 <BR>7、doTrace <BR>protected void doTrace(HttpServletRequest request, <BR>HttpServletResponse response) throws ServletException, <BR>IOException; <BR>被这个类的service方法调用，用来处理一个HTTP TRACE操作。这个操作的默认执行结果是产生一个响应，这个响应包含一个反映trace请求中发送的所有头域的信息。 <BR>当你开发Servlet时，在多数情况下你需要重载这个方法。 <BR>8、getLastModified <BR>protected long getLastModified(HttpServletRequest request); <BR>返回这个请求实体的最后修改时间。为了支持GET操作，你必须重载这一方法，以精确地反映最后修改的时间。这将有助于浏览器和代理服务器减少装载服务器和网络资源，从而更加有效地工作。返回的数值是自1970-1-1日（GMT）以来的毫秒数。 <BR>默认的执行结果是返回一个负数，这标志着最后修改时间未知，它也不能被一个有条件的GET操作使用。 <BR>9、service <BR>protected void service(HttpServletRequest request, <BR>HttpServletResponse response) throws ServletException, <BR>IOException; <BR>public void service(ServletRequest request, ServletResponse response) <BR>throws ServletException, IOException; <BR>这是一个Servlet的HTTP-specific方案，它分配请求到这个类的支持这个请求的其他方法。 <BR>当你开发Servlet时，在多数情况下你不必重载这个方法。 <BR><BR>八、HttpSessionBindingEvent类\ <BR>定义\ <BR>public class HttpSessionBindingEvent extends EventObject <BR>这个事件是在监听到HttpSession发生绑定和取消绑定的情况时连通HttpSessionBindingListener的。这可能是一个session被终止或被认定无效的结果。 <BR>事件源是HttpSession.putValue或HttpSession.removeValue。 <BR>构造函数 <BR>public HttpSessionBindingEvent(HttpSession session, String name); <BR>通过引起这个事件的Session和发生绑定或取消绑定的对象名构造一个新的HttpSessionBindingEvent。 <BR>方法 <BR>1、getName <BR>public String getName(); <BR>返回发生绑定和取消绑定的对象的名字。 <BR>2、getSession <BR>public HttpSession getSession(); <BR>返回发生绑定和取消绑定的session的名字。 <BR><BR>九、HttpUtils类\ <BR>定义\ <BR>public class HttpUtils <BR>收集HTTP Servlet使用的静态的有效的方法。 <BR>方法 <BR>1、getRequestURL <BR>public static StringBuffer getRequestURL(HttpServletRequest <BR>request); <BR>在服务器上重建客户端用来建立请求的URL。这个方法反映了不同的协议（例如http和https）和端口，但不包含查询字符串。 <BR>这个方法返回一个StringBuffer而不是一个String，这样URL可以被Servlet开发者有效地修改。 <BR>2、parsePostData <BR>public static Hashtable parsePostData(int len, <BR>ServletInputstream in); <BR>解析一个包含MIME类型application/x-www-form-urlencoded的数据的流，并创建一个具有关键值-数据对的hash table。这里的关键值是字符串，数据是该字符串所对应的值的列表。一个关键值可以在POST的数据中出现一次或多次。这个关键值每出现一次，它的相应的值就被加入到hash table中的字符串所对应的值的列表中。 <BR>从POST数据读出的数据将经过URL解码，+将被转换为空格以十六进制传送的数据（例如%xx）将被转换成字符。 <BR>当POST数据无效时，该方法抛出一个IllegalArgumentException。 <BR>3、parseQueryString <BR>public static Hashtable parseQueryString(String s); <BR>解析一个查询字符串，并创建一个具有关键值-数据对的hash table。这里的数据是该字符串所对应的值的列表。一个关键值可以出现一次或多次。这个关键值每出现一次，它的相应的值就被加入到hash table中的字符串所对应的值的列表中。 <BR>从查询字符串读出的数据将经过URL解码，+将被转换为空格以十六进制传送的数据（例如%xx）将被转换成字符。 <BR>当查询字符串无效时，该方法抛出一个IllegalArgumentException。 <BR><BR>Java Servlet API说明文档（2.1a版）（四） <BR>术语表\r <BR>bytecode <BR>字节码：由Java编译器和Java解释程序生成的机器代码。 <BR>cookie <BR>由Web服务器建立的数据，该数据存储在用户的计算机上，提供了一个Web站点跟踪用户的参数并存储在用户自己硬盘上的方法。 <BR>HTTP <BR>超文本传输协议。一个请求响应协议用来连接WWW服务器向客户端浏览器传输HTML页面。 <BR>输入流对象\r <BR>一个对象，由ServletInputStream类定义，被Servlet用来从客户端读取请求。 <BR>映射\r <BR>由Servlet实例和Servlet返回数据的URL组成的一对，例如，HelloServlet和/hello/index.html。 <BR>输出流对象\r <BR>一个对象，由ServletOutputStream class类定义，被Servlet用来向客户端返回数据。 <BR>request dispatcher object <BR>由RequestDispatcher接口定义的一个对象，用来从客户端接收请求，并将其发送到Web服务器上可用的其他资源（例如Servlet、CGI、HTML文件或JSP文件）。 <BR>sandboxed servlet <BR>在一个安全性约束下运行的Servlet。 <BR>servlet <BR>一个小的，具有平台无关性的，没有图形用户界面的Java程序。它可以在许多方面扩充Web服务的功能。 <BR>servlet configuration object <BR>ServletConfig接口定义的一个对象，用来配置一个Servlet。 <BR>servlet context object <BR>ServletContext接口定义的一个对象。给予Servlet有关Servlet引擎的信息。 <BR>servlet引擎\r <BR>由Web服务器提供商制作的一个环境，可以允许Servlet在具体的Web服务器上运行。 <BR>servlet请求对象 <BR>由ServletRequest接口定义的一个对象，允许Servlet获得用关客户端请求的数据。 <BR>servlet response object <BR>由ServletResponse接口定义的一个对象，允许Servlet作出响应。 <BR>servlet runner <BR>Java Servlet Developer’s Kit (JSDK)中的sun.servlet.http.HttpServer过程，它使得Servlet得以运行。 <BR>会话跟踪 <BR>在一个Web应用程序中，识别一个从同一个客户端发出的连续的唯一的请求的能力。 <BR>SSL <BR>加密套接字协议层。一个安全协议，用来在Iternet上的客户端浏览器和服务器交换密钥和加密数据。 <BR>URI <BR>统一资源标识。定义一个Internet地址，它是一个URL的超集。 <BR>URL <BR>统一资源路径。这个地址定义了到达一个WWW上的文件的路线，通常由协议前缀、域名、目录名和文件名组成。 <BR></FONT><img src ="http://www.blogjava.net/snowolf/aggbug/31942.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/snowolf/" target="_blank">snowolf</a> 2006-02-22 13:02 <a href="http://www.blogjava.net/snowolf/articles/31942.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2EE 全面简介</title><link>http://www.blogjava.net/snowolf/articles/30193.html</link><dc:creator>snowolf</dc:creator><author>snowolf</author><pubDate>Fri, 10 Feb 2006 10:08:00 GMT</pubDate><guid>http://www.blogjava.net/snowolf/articles/30193.html</guid><wfw:comment>http://www.blogjava.net/snowolf/comments/30193.html</wfw:comment><comments>http://www.blogjava.net/snowolf/articles/30193.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/snowolf/comments/commentRss/30193.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/snowolf/services/trackbacks/30193.html</trackback:ping><description><![CDATA[本文从五个方面对J2EE进行了比较全面的介绍。从J2EE的概念说起，到它的优势，到J2EE典型的四层模型，和它的框架结构，最后是J2EE十三种核心技术的一个简介。本文分门别类的对J2EE中的服务，组件，层次，容器，API都做了比较详细的介绍，相信看完此文，读者会对J2EE有一个更清晰的认识。<BR><BR>一. J2EE的概念<BR>目前，Java 2平台有3个版本，它们是适用于小型设备和智能卡的Java 2平台Micro版（Java 2 Platform Micro Edition，J2ME）、适用于桌面系统的Java 2平台标准版（Java 2 Platform Standard Edition，J2SE）、适用于创建服务器应用程序和服务的Java2平台企业版（Java 2 Platform Enterprise Edition，J2EE）。<BR>J2EE是一种利用Java 2平台来简化企业解决方案的开发、部署和管理相关的复杂问题的体系结构。J2EE技术的基础就是核心Java平台或Java 2平台的标准版，J2EE不仅巩固了标准版中的许多优点，例如"编写一次、随处运行"的特性、方便存取数据库的JDBC API、CORBA技术以及能够在Internet应用中保护数据的安全模式等等，同时还提供了对 EJB（Enterprise JavaBeans）、Java Servlets API、JSP（Java Server Pages）以及XML技术的全面支持。其最终目的就是成为一个能够使企业开发者大幅缩短投放市场时间的<BR>体系结构。<BR>J2EE体系结构提供中间层集成框架用来满足无需太多费用而又需要高可用性、高可靠性以及可扩展性的应用的需求。通过提供统一的开发平台，J2EE降低了开发多层应用的费用和复杂性，同时提供对现有应用程序集成强有力支持，完全支持Enterprise JavaBeans，有良好的向导支持打包和部署应用，添加目录支持，增强了安全机制，提高了性能。<BR><BR>二. J2EE的优势<BR>J2EE为搭建具有可伸缩性、灵活性、易维护性的商务系统提供了良好的机制:<BR>保留现存的IT资产: 由于企业必须适应新的商业需求，利用已有的企业信息系统方面的投资，而不是重新制定全盘方案就变得很重要。这样，一个以渐进的（而不是激进的，全盘否定的）方式建立在已有系统之上的服务器端平台机制是公司所需求的。J2EE架构可以充分利用用户原有的投资，如一些公司使用的BEA Tuxedo、IBM CICS, IBM Encina,、Inprise VisiBroker 以及Netscape Application Server。这之所以成为可能是因为J2EE拥有广泛的业界支持和一些重要的'企业计算'领域供应商的参与。每一个供应商都对现有的客户提供了不用废弃已有投资，进入可移植的J2EE领域的升级途径。由于基于J2EE平台的产品几乎能够在任何操作系统和硬件配置上运行，现有的操作系统和硬件也<BR>能被保留使用。<BR>高效的开发: J2EE允许公司把一些通用的、很繁琐的服务端任务交给中间件供应商去完成。这样开发人员可以集中精力在如何创建商业逻辑上，相应地缩短了开发时间。高级中间件供应商提供以下这些复杂的中间件服务:<BR>状态管理服务 -- 让开发人员写更少的代码，不用关心如何管理状态，这样能够更快地完成程序开发。<BR>持续性服务 -- 让开发人员不用对数据访问逻辑进行编码就能编写应用程序，能生成更<BR>轻巧，与数据库无关的应用程序，这种应用程序更易于开发与维护。<BR>分布式共享数据对象CACHE服务 -- 让开发人员编制高性能的系统，极大提高整体部署的<BR>伸缩性。<BR>支持异构环境: J2EE能够开发部署在异构环境中的可移植程序。基于J2EE的应用程序不<BR>依赖任何特定操作系统、中间件、硬件。因此设计合理的基于J2EE的程序只需开发一次<BR>就可部署到各种平台。这在典型的异构企业计算环境中是十分关键的。J2EE标准也允许<BR>客户订购与J2EE兼容的第三方的现成的组件，把他们部署到异构环境中，节省了由自己<BR>制订整个方案所需的费用。<BR>可伸缩性: 企业必须要选择一种服务器端平台，这种平台应能提供极佳的可伸缩性去满足那些在他们系统上进行商业运作的大批新客户。基于J2EE平台的应用程序可被部署到各种操作系统上。例如可被部署到高端UNIX与大型机系统，这种系统单机可支持64至256个处理器。（这是NT服务器所望尘莫及的）J2EE领域的供应商提供了更为广泛的负载平衡策略。能消除系统中的瓶颈，允许多台服务器集成部署。这种部署可达数千个处理器，实现可高度伸缩的系统，满足未来商业应用的需要。<BR>稳定的可用性: 一个服务器端平台必须能全天候运转以满足公司客户、合作伙伴的需要。因为INTERNET是全球化的、无处不在的，即使在夜间按计划停机也可能造成严重损失。若是意外停机，那会有灾难性后果。J2EE部署到可靠的操作环境中，他们支持长期的可用性。一些J2EE部署在WINDOWS环境中，客户也可选择健壮性能更好的操作系统如Sun Solaris、IBM OS/390。最健壮的操作系统可达到99.999%的可用性或每年只需5分钟停机时间。这是实时性很强商业系统理想的选择。<BR><BR><BR>三. J2EE 的四层模型<BR>J2EE使用多层的分布式应用模型，应用逻辑按功能划分为组件，各个应用组件根据他们<BR>所在的层分布在不同的机器上。事实上，sun设计J2EE的初衷正是为了解决两层模式(cl<BR>ient/server)的弊端，在传统模式中，客户端担当了过多的角色而显得臃肿，在这种模<BR>式中，第一次部署的时候比较容易，但难于升级或改进，可伸展性也不理想，而且经常<BR>基于某种专有的协议――通常是某种数据库协议。它使得重用业务逻辑和界面逻辑非常<BR>困难。现在J2EE 的多层企业级应用模型将两层化模型中的不同层面切分成许多层。一个<BR>多层化应用能够为不同的每种服务提供一个独立的层，以下是 J2EE 典型的四层结构:<BR>运行在客户端机器上的客户层组件<BR>运行在J2EE服务器上的Web层组件<BR>运行在J2EE服务器上的业务逻辑层组件<BR>运行在EIS服务器上的企业信息系统(Enterprise information system)层软件<BR>J2EE应用程序组件<BR>J2EE应用程序是由组件构成的.J2EE组件是具有独立功能的软件单元，它们通过相关的类<BR>和文件组装成J2EE应用程序，并与其他组件交互。J2EE说明书中定义了以下的J2EE组件<BR>:<BR>应用客户端程序和applets是客户层组件.<BR>Java Servlet和JavaServer Pages(JSP)是web层组件.<BR>Enterprise JavaBeans(EJB)是业务层组件.<BR>客户层组件<BR>J2EE应用程序可以是基于web方式的,也可以是基于传统方式的.<BR>web 层组件J2EE web层组件可以是JSP 页面或Servlets.按照J2EE规范，静态的HTML页面<BR><BR><BR>和Applets不算是web层组件。<BR>正如下图所示的客户层那样，web层可能包含某些 JavaBean 对象来处理用户输入，并把<BR>输入发送给运行在业务层上的enterprise bean 来进行处理。<BR>业务层组件<BR>业务层代码的逻辑用来满足银行，零售，金融等特殊商务领域的需要,由运行在业务层上<BR>的enterprise bean 进行处理. 下图表明了一个enterprise bean 是如何从客户端程序<BR>接收数据，进行处理(如果必要的话), 并发送到EIS 层储存的，这个过程也可以逆向进<BR>行。<BR>有三种企业级的bean: 会话(session) beans, 实体(entity) beans, 和 消息驱动(mes<BR>sage-driven) beans. 会话bean 表示与客户端程序的临时交互. 当客户端程序执行完后<BR>, 会话bean 和相关数据就会消失. 相反, 实体bean 表示数据库的表中一行永久的记录<BR>. 当客户端程序中止或服务器关闭时, 就会有潜在的服务保证实体bean 的数据得以保存<BR>.消息驱动 bean 结合了会话bean 和 JMS的消息监听器的特性, 允许一个业务层组件异<BR>步接收JMS 消息.<BR>企业信息系统层<BR>企业信息系统层处理企业信息系统软件包括企业基础建设系统例如企业资源计划 (ERP)<BR>, 大型机事务处理, 数据库系统,和其它的遗留信息系统. 例如，J2EE 应用组件可能为<BR>了数据库连接需要访问企业信息系统<BR>四. J2EE 的结构<BR>这种基于组件，具有平台无关性的J2EE 结构使得J2EE 程序的编写十分简单，因为业务<BR>逻辑被封装成可复用的组件，并且J2EE 服务器以容器的形式为所有的组件类型提供后台<BR>服务. 因为你不用自己开发这种服务, 所以你可以集中精力解决手头的业务问题.<BR><BR><BR>容器和服务<BR>容器设置定制了J2EE服务器所提供得内在支持，包括安全，事务管理，JNDI(Java Nami<BR>ng and Directory Interface)寻址,远程连接等服务，以下列出最重要的几种服务：<BR>J2EE安全(Security)模型可以让你配置 web 组件或enterprise bean ,这样只有被授权<BR>的用户才能访问系统资源. 每一客户属于一个特别的角色，而每个角色只允许激活特定<BR>的方法。你应在enterprise bean的布置描述中声明角色和可被激活的方法。由于这种声<BR>明性的方法，你不必编写加强安全性的规则。<BR>J2EE 事务管理（Transaction Management）模型让你指定组成一个事务中所有方法间的<BR>关系，这样一个事务中的所有方法被当成一个单一的单元. 当客户端激活一个enterpri<BR>se bean中的方法，容器介入一管理事务。因有容器管理事务，在enterprise bean中不<BR>必对事务的边界进行编码。要求控制分布式事务的代码会非常复杂。你只需在布置描述<BR>文件中声明enterprise bean的事务属性，而不用编写并调试复杂的代码。容器将读此文<BR>件并为你处理此enterprise bean的事务。<BR>JNDI 寻址(JNDI Lookup)服务向企业内的多重名字和目录服务提供了一个统一的接口,这<BR>样应用程序组件可以访问名字和目录服务.<BR>J2EE远程连接（Remote Client Connectivity）模型管理客户端和enterprise bean间的<BR>低层交互. 当一个enterprise bean创建后, 一个客户端可以调用它的方法就象它和客户<BR>端位于同一虚拟机上一样.<BR>生存周期管理（Life Cycle Management）模型管理enterprise bean的创建和移除,一个<BR>enterprise bean在其生存周期中将会历经几种状态。容器创建enterprise bean，并在<BR>可用实例池与活动状态中移动他，而最终将其从容器中移除。即使可以调用enterprise<BR>bean的create及remove方法，容器也将会在后台执行这些任务。<BR><BR><BR>数据库连接池（Database Connection Pooling）模型是一个有价值的资源。获取数据库<BR>连接是一项耗时的工作，而且连接数非常有限。容器通过管理连接池来缓和这些问题。<BR>enterprise bean可从池中迅速获取连接。在bean释放连接之可为其他bean使用。<BR>容器类型<BR>J2EE应用组件可以安装部署到以下几种容器中去:<BR>EJB 容器管理所有J2EE 应用程序中企业级bean 的执行. enterprise bean 和它们的容<BR>器运行在J2EE 服务器上.<BR>Web 容器管理所有J2EE 应用程序中JSP页面和Servlet组件的执行. Web 组件和它们的容<BR>器运行在J2EE 服务器上.<BR>应用程序客户端容器管理所有J2EE应用程序中应用程序客户端组件的执行. 应用程序客<BR>户端和它们的容器运行在J2EE 服务器上.<BR>Applet 容器是运行在客户端机器上的web浏览器和 Java 插件的结合.<BR>五. J2EE的核心API与组件<BR>J2EE平台由一整套服务（Services）、应用程序接口（APIs）和协议构成，它对开发基<BR>于Web的多层应用提供了功能支持，下面对J2EE中的13种技术规范进行简单的描述(限于<BR>篇幅，这里只能进行简单的描述):<BR>JDBC(Java Database Connectivity):<BR>JDBC API为访问不同的数据库提供了一种统一的途径，象ODBC一样，JDBC对开发者屏蔽<BR>了一些细节问题，另外，JDCB对数据库的访问也具有平台无关性。<BR>JNDI(Java Name and Directory Interface):<BR>JNDI API被用于执行名字和目录服务。它提供了一致的模型来存取和操作企业级的资源<BR>如DNS和LDAP，本地文件系统，或应用服务器中的对象。<BR><BR><BR>EJB(Enterprise JavaBean):<BR>J2EE技术之所以赢得某体广泛重视的原因之一就是EJB。它们提供了一个框架来开发和实<BR>施分布式商务逻辑，由此很显著地简化了具有可伸缩性和高度复杂的企业级应用的开发<BR>。EJB规范定义了EJB组件在何时如何与它们的容器进行交互作用。容器负责提供公用的<BR>服务，例如目录服务、事务管理、安全性、资源缓冲池以及容错性。但这里值得注意的<BR>是，EJB并不是实现J2EE的唯一途径。正是由于J2EE的开放性，使得有的厂商能够以一种<BR>和EJB平行的方式来达到同样的目的。<BR>RMI(Remote Method Invoke):<BR>正如其名字所表示的那样，RMI协议调用远程对象上方法。它使用了序列化方式在客户端<BR>和服务器端传递数据。RMI是一种被EJB使用的更底层的协议。<BR>Java IDL/CORBA:<BR>在Java IDL的支持下，开发人员可以将Java和CORBA集成在一起。 他们可以创建Java对<BR>象并使之可在CORBA ORB中展开, 或者他们还可以创建Java类并作为和其它ORB一起展开<BR>的CORBA对象的客户。后一种方法提供了另外一种途径，通过它Java可以被用于将你的新<BR>的应用和旧的系统相集成。<BR>JSP(Java Server Pages):<BR>JSP页面由HTML代码和嵌入其中的Java代码所组成。服务器在页面被客户端所请求以后对<BR>这些Java代码进行处理，然后将生成的HTML页面返回给客户端的浏览器。<BR>Java Servlet:<BR>Servlet是一种小型的Java程序，它扩展了Web服务器的功能。作为一种服务器端的应用<BR>，当被请求时开始执行，这和CGI Perl脚本很相似。Servlet提供的功能大多与JSP类似<BR>，不过实现的方式不同。JSP通常是大多数HTML代码中嵌入少量的Java代码，而servlet<BR><BR><BR>s全部由Java写成并且生成HTML。<BR>XML(Extensible Markup Language):<BR>XML是一种可以用来定义其它标记语言的语言。它被用来在不同的商务过程中共享数据。<BR>XML的发展和Java是相互独立的，但是，它和Java具有的相同目标正是平台独立性。通过<BR>将Java和XML的组合，您可以得到一个完美的具有平台独立性的解决方案。<BR>JMS(Java Message Service):<BR>MS是用于和面向消息的中间件相互通信的应用程序接口(API)。它既支持点对点的域，有<BR>支持发布/订阅(publish/subscribe)类型的域，并且提供对下列类型的支持：经认可的<BR>消息传递,事务型消息的传递，一致性消息和具有持久性的订阅者支持。JMS还提供了另<BR>一种方式来对您的应用与旧的后台系统相集成。<BR>JTA(Java Transaction Architecture):<BR>JTA定义了一种标准的API，应用系统由此可以访问各种事务监控。<BR>JTS(Java Transaction Service):<BR>JTS是CORBA OTS事务监控的基本的实现。JTS规定了事务管理器的实现方式。该事务管理<BR>器是在高层支持Java Transaction API (JTA)规范，并且在较底层实现OMG OTS specif<BR>ication的Java映像。JTS事务管理器为应用服务器、资源管理器、独立的应用以及通信<BR>资源管理器提供了事务服务。<BR>JavaMail:<BR>JavaMail是用于存取邮件服务器的API，它提供了一套邮件服务器的抽象类。不仅支持S<BR>MTP服务器，也支持IMAP服务器。<BR>JTA(JavaBeans Activation Framework):<BR>JavaMail利用JAF来处理MIME编码的邮件附件。MIME的字节流可以被转换成Java对象，或<BR><BR><BR>参考资料：<BR>《Develop n-tier application using J2EE》- Steven Gould<BR>《The Business Benefits of EJB and J2EE Technologies over COM+ and Windows D<BR>NA》<BR>《The J2EE Tutorial》chapter overview - Monica Pawlan<BR>本文所用图片由《The J2EE Tutorial》中的英文图片修改而成.<BR><img src ="http://www.blogjava.net/snowolf/aggbug/30193.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/snowolf/" target="_blank">snowolf</a> 2006-02-10 18:08 <a href="http://www.blogjava.net/snowolf/articles/30193.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Vector还是ArrayList这是个问题</title><link>http://www.blogjava.net/snowolf/articles/30184.html</link><dc:creator>snowolf</dc:creator><author>snowolf</author><pubDate>Fri, 10 Feb 2006 09:46:00 GMT</pubDate><guid>http://www.blogjava.net/snowolf/articles/30184.html</guid><wfw:comment>http://www.blogjava.net/snowolf/comments/30184.html</wfw:comment><comments>http://www.blogjava.net/snowolf/articles/30184.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/snowolf/comments/commentRss/30184.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/snowolf/services/trackbacks/30184.html</trackback:ping><description><![CDATA[<EM>原文：http://www.javaworld.com/javaworld/javaqa/2001-06/03-qa-0622-vector.html? <BR></EM><B>Vector 还是ArrayList――哪一个更好，为什么？</B><BR>要回答这个问题不能一概而论，有时候使用Vector比较好；有时是ArrayList，有时候这两个都不是最好的选择。你别指望能够获得一个简单肯定答案，因为这要看你用它们干什么。下面有4个要考虑的因素：<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;API<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;同步处理<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;数据增长性<BR>l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用模式<BR>下面针对这4个方面进行一一探讨<BR><B>API </B><BR>在由Ken Arnold等编著的《Java Programming Language》(Addison-Wesley, June 2000)一书中有这样的描述，Vector类似于ArrayList.。所有从API的角度来看这两个类非常相<B>似。但他们之间也还是有一些主要的区别的。<BR><BR>[b]同步性</B><BR>Vector是同步的。这个类中的一些方法保证了Vector中的对象是线程安全的。而ArrayList则是异步的，因此ArrayList中的对象并不是线程安全的。因为同步的要求会影响执行的效率，所以如果你不需要线程安全的集合那么使用ArrayList是一个很好的选择，这样可以避免由于同步带来的不必要的性能开销。<BR><B>数据增长</B><BR>从内部实现机制来讲ArrayList和Vector都是使用数组(Array)来控制集合中的对象。当你向这两种类型中增加元素的时候，如果元素的数目超出了内部数组目前的长度它们都需要扩展内部数组的长度，Vector缺省情况下自动增长原来一倍的数组长度，ArrayList是原来的50%,所以最后你获得的这个集合所占的空间总是比你实际需要的要大。所以如果你要在集合中保存大量的数据那么使用Vector有一些优势，因为你可以通过设置集合的初始化大小来避免不必要的资源开销。<BR><B>使用模式</B><BR>在ArrayList和Vector中，从一个指定的位置（通过索引）查找数据或是在集合的末尾增加、移除一个元素所花费的时间是一样的，这个时间我们用O(1)表示。但是，如果在集合的其他位置增加或移除元素那么花费的时间会呈线形增长：O(n-i)，其中n代表集合中元素的个数，i代表元素增加或移除元素的索引位置。为什么会这样呢？以为在进行上述操作的时候集合中第i和第i个元素之后的所有元素都要执行位移的操作。这一切意味着什么呢？<BR>这意味着，你只是查找特定位置的元素或只在集合的末端增加、移除元素，那么使用Vector或ArrayList都可以。如果是其他操作，你最好选择其他的集合操作类。比如，LinkList集合类在增加或移除集合中任何位置的元素所花费的时间都是一样的—O(1)，但它在索引一个元素的使用缺比较慢－O(i),其中i是索引的位置.使用ArrayList也很容易，因为你可以简单的使用索引来代替创建iterator对象的操作。LinkList也会为每个插入的元素创建对象，所有你要明白它也会带来额外的开销。<BR>最后，在《Practical Java》一书中Peter Haggar建议使用一个简单的数组（Array）来代替Vector或ArrayList。尤其是对于执行效率要求高的程序更应如此。因为使用数组(Array)避免了同步、额外的方法调用和不必要的重新分配空间的操作。<BR><BR><B>相关资源：</B><BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;《Java编程语言》 作者：Ken Arnold, James Gosling, and David Holmes http://www.amazon.com/exec/obidos/ASIN/0201704331/javaworld/ <BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;《实战 Java》作者： Peter Haggar&nbsp;&nbsp;<BR>http://www.amazon.com/exec/obidos/ASIN/0201616467/javaworld/ <BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;更多内容请看 Java Q&amp;A : <BR>http://www.javaworld.com/javaworld/javaqa/javaqa-index.html <BR><img src ="http://www.blogjava.net/snowolf/aggbug/30184.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/snowolf/" target="_blank">snowolf</a> 2006-02-10 17:46 <a href="http://www.blogjava.net/snowolf/articles/30184.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用Annotations设计一个MVC框架</title><link>http://www.blogjava.net/snowolf/articles/30169.html</link><dc:creator>snowolf</dc:creator><author>snowolf</author><pubDate>Fri, 10 Feb 2006 07:56:00 GMT</pubDate><guid>http://www.blogjava.net/snowolf/articles/30169.html</guid><wfw:comment>http://www.blogjava.net/snowolf/comments/30169.html</wfw:comment><comments>http://www.blogjava.net/snowolf/articles/30169.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/snowolf/comments/commentRss/30169.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/snowolf/services/trackbacks/30169.html</trackback:ping><description><![CDATA[<DIV class=overflow id=text><B><SPAN style="FONT-SIZE: 20px">Annotations能够帮助你去掉应用组件之间的耦合</SPAN></B><BR><BR><B><SPAN style="FONT-SIZE: 16px">摘要</SPAN></B><BR><BR>Model-View-Controller (MVC)是一种软件架构,它可以将应用程序的数据模型,用户接口,以及控制逻辑分开, 使其成为独立的组件.这样一来,对其中一个组件的修改而产生的对其他组件的影响能够被降到最低. 在这篇文章中,你将学习到如何使用annotation来设计一个几乎能够完全去掉model和view之间耦合的继承的MVC的框架. <BR><BR><SPAN style="COLOR: red">版权声明：任何获得Matrix授权的网站，转载时请务必保留以下作者信息和链接</SPAN><BR>作者:Riccardo Govoni;<A href="http://www.matrix.org.cn/user.shtml?username=wenzi_33" target=_new>wenzi_33</A><BR>原文:<A href="http://www.matrix.org.cn/resource/article/44/44204_Annotations+MVC.html<br%20/>" target=_new>http://www.matrix.org.cn/resource/article/44/44204_Annotations+MVC.html</A><BR>关键字:annotations;MVC;Framework<BR><BR>当设计一个应用程序时, 清晰的分离该程序的不同逻辑组件, 总是被证明是有益的. 同时也存在许多不同的模式来帮助开发者实现这个目标。其中最有名同时也最常用的自然是Model-View-Controller (MVC)了, 它能够将每个应用程序(或者应用程序的一部分)分成三个不同功能的组件,并且定义了把他们联结在一起的规则。Swing本身就是基于这个模式的,而且每个使用Struts,这个流行的开发Web应用框架的人也都了解隐藏在MVC后面的理论.<BR><BR>这篇文章介绍了怎么样通过使用annotation而增加一个新的组件来加强MVC,使其能够更加方便地去掉models跟views之间的耦合。这篇文章介绍了一个叫Stamps的开源库, 它是基于MVC组件之上的,但它去除了所有在开发MVC时所需的, 在models, views和controllers之间建立联系的负担。<BR><BR><B><SPAN style="FONT-SIZE: 16px">基础知识: MVC和annotations</SPAN></B><BR><BR>正如MVC这个名字所指出的, Model-View-Controller模式建议将一个应用程序分成以下三个组件: <BR>·Model: 包含了数据模型和所有用来确定应用程序状态的信息。 它一般来说是有条理的并且独立于其他组件的。<BR>·View: 从不同于model的角度出发,它定义了存储在模型中数据的展现方式。它通常被认为是你的应用程序的用户界面(或者GUI),或者以Web应用为例,场景就是你通过浏览器看到的页面。<BR>·Controller: 它代表应用程序的逻辑部分。在这里,它定义了一个用户如何和应用程序进行交互并且也定义了用户行为是如何映射到model的改变。<BR><BR>这些组件紧密的联系在一起: 用户影响view, 反过来view通知controller来更新model.最终model又更新view来反映它的新状态。图1就展现了这种典型的MVC结构。<BR><BR><IMG height=155 alt=2006_02_08_232846_XtnRHlDmNe.jpg src="http://www.blogjava.net/images/blogjava_net/snowolf/java/2006_02_08_232846_XtnRHlDmNe.jpg" width=425 border=0><BR>图1. 一个典型的MVC结构<BR><BR>作为J2SE 5.0所提供的一个新的功能，annotations允许开发者往classes，methods，fields，和其他程序元素中增加元数据。就像反射机制一样，之后很多应用程序为了某些原因能在运行时期获取并使用那些元数据。因为J2SE 5.0只是定义了怎么样编写和读取annotations，并没有说明在哪里使用他们（象@Override这样的用于提前定义的例外），开发者拥有无穷多的在许多不同场合使用他们的可能性：文档编写，与对象相关的映射，代码生成，等等.. Annotations已经变的十分流行，以至于大多数框架和库都更新自己来支持他们。至于更多的关于MVC和annotations的信息请参见资源。<BR><BR><B><SPAN style="FONT-SIZE: 16px">超越MVC: dispatcher</SPAN></B><BR><BR>就像前文提到的一样，models和views之间的一些耦合是必要的因为后者必须反映前者的状态。普通Java程序使用直接或间接的耦合将组件绑定在一起。直接耦合发生在当view和model之间有一个直接相关的时候，model包含一列需要维持的views。间接耦合通常发生在一个基于事件分派的机制中。Model会在它状态改变时激发事件，同时一些独立的views会将他们自己注册成事件侦听器。<BR><BR>通常我们比较青睐间接耦合因为它使model完全不知道view的存在，相反view必须和model保持一定的联系从而将自己注册到model上。在这篇文章里我将介绍的框架就是使用间接耦合，但是为了更好的降低组件之间的耦合，view必须不知道model的存在；也就是说，model和view没有被绑定在一起。<BR><BR>为了实现这个目标，我已经定义了一个新的组件，就是dispatcher，它能作为一个存在于views和models之间的分离层。它能处理models和views双方之间的注册并且分派由model激发的事件到注册的views上。它使用java.beans.PropertyChangeEvent对象来表现由model传送到view的事件；然而，这个框架的设计是足够开放的，它可以支持不同事件类型的实现。<BR><BR>管理注册的views列表的负担于是就从model上移开了，同时，因为view只和这个独立于应用程序的dispatcher有关，view不知道model的存在。如果你熟悉Struts内部，你也许能够看出Struts的controller就是在履行这样一个任务，它将Actions和他们关联的JSP(JavaServer Pages)表现页面联系在一起。<BR><BR>现在，我们所设计的MVC框架就像图2所描述的一样。Dispatcher在其中担当了一个于controller相称的角色。<BR><BR><IMG height=240 alt=2006_02_08_233016_NNAtmqYNNs.jpg src="http://www.blogjava.net/images/blogjava_net/snowolf/java/2006_02_08_233016_NNAtmqYNNs.jpg" width=425 border=0><BR>图2.拥有额外dispatcher组件的改进的MVC框架<BR><BR>由于dispatcher必须是独立于应用程序的，所以必须定义一些通用的联结models和views的规范。我们将使用annotations来实现这种联结，它将会被用来标注views并且确定哪个view是受哪个model的影响的，及这种影响是怎么样的。通过这种方式，annotations就像是贴在明信片上的邮票一样，驱动dispatcher来执行传递model事件的任务(这就是这一框架名字的由来)。<BR><BR><BR><B><SPAN style="FONT-SIZE: 16px">应用实例</SPAN></B><BR><BR>我们将使用一个简单的计秒器应用程序做该框架的一个应用实例：它允许用户设置时间周期来记数和启动/停止这个定时器。 一旦过去规定的时间，用户将会被询问是否取消或者重启这个定时器。这个应用程序的完全源代码可以从项目主页上找到。<BR><BR><IMG height=81 alt=2006_02_08_233211_otZImPhZpB.jpg src="http://www.blogjava.net/images/blogjava_net/snowolf/java/2006_02_08_233211_otZImPhZpB.jpg" width=219 border=0><BR>图3.一个简单的应用程序<BR><BR>这个modle是非常简单的，它只存储两个属性：周期和已经过去的秒数。注意当它其中一个属性发生变化时它是如何使用java.beans.PropertyChangeSuppor来激发事件。<BR><PRE class=overflow><FONT style="BACKGROUND-COLOR: #f5f5dc">public class TimeModel {<BR><BR>&nbsp;&nbsp; public static final int DEFAULT_PERIOD = 60;<BR><BR>&nbsp;&nbsp; private Timer timer;<BR>&nbsp;&nbsp; private boolean running;<BR><BR>&nbsp;&nbsp; private int period;<BR>&nbsp;&nbsp; private int seconds;<BR><BR>&nbsp;&nbsp; private PropertyChangeSupport propSupport;<BR><BR>&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp;&nbsp;* Getters and setters for model properties.<BR>&nbsp;&nbsp;&nbsp;&nbsp;*/<BR><BR>&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp;&nbsp;* Returns the number of counted seconds.<BR>&nbsp;&nbsp;&nbsp;&nbsp;*<BR>&nbsp;&nbsp;&nbsp;&nbsp;* @return the number of counted seconds.<BR>&nbsp;&nbsp;&nbsp;&nbsp;*/<BR>&nbsp;&nbsp; public int getSeconds() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return seconds;<BR>&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp;&nbsp;* Sets the number of counted seconds. propSupport is an instance of PropertyChangeSupport<BR>&nbsp;&nbsp;&nbsp;&nbsp;* used to dispatch model state change events.<BR>&nbsp;&nbsp;&nbsp;&nbsp;*<BR>&nbsp;&nbsp;&nbsp;&nbsp;* @param seconds the number of counted seconds.<BR>&nbsp;&nbsp;&nbsp;&nbsp;*/<BR>&nbsp;&nbsp; public void setSeconds(int seconds) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;propSupport.firePropertyChange("seconds",this.seconds,seconds);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.seconds = seconds;<BR>&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp;&nbsp;* Sets the period that the timer will count. propSupport is an instance of PropertyChangeSupport<BR>&nbsp;&nbsp;&nbsp;&nbsp;* used to dispatch model state change events.<BR>&nbsp;&nbsp;&nbsp;&nbsp;*<BR>&nbsp;&nbsp;&nbsp;&nbsp;* @param period the period that the timer will count.<BR>&nbsp;&nbsp;&nbsp;&nbsp;*/<BR>&nbsp;&nbsp; public void setPeriod(Integer period){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;propSupport.firePropertyChange("period",this.period,period);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.period = period;<BR>&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp;&nbsp;* Returns the period that the timer will count.<BR>&nbsp;&nbsp;&nbsp;&nbsp;*<BR>&nbsp;&nbsp;&nbsp;&nbsp;* @return the period that the timer will count.<BR>&nbsp;&nbsp;&nbsp;&nbsp;*/<BR>&nbsp;&nbsp; public int getPeriod() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return period;<BR>&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp;&nbsp;* Decides if the timer must restart, depending on the user answer. This method<BR>&nbsp;&nbsp;&nbsp;&nbsp;* is invoked by the controller once the view has been notified that the timer has<BR>&nbsp;&nbsp;&nbsp;&nbsp;* counted all the seconds defined in the period.<BR>&nbsp;&nbsp;&nbsp;&nbsp;*<BR>&nbsp;&nbsp;&nbsp;&nbsp;* @param answer the user answer.<BR>&nbsp;&nbsp;&nbsp;&nbsp;*/<BR>&nbsp;&nbsp; public void questionAnswer(boolean answer){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (answer) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timer = new Timer();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timer.schedule(new SecondsTask(this),1000,1000);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; running = true;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp;&nbsp;* Starts/stop the timer. This method is invoked by the controller on user input.<BR>&nbsp;&nbsp;&nbsp;&nbsp;*/<BR>&nbsp;&nbsp; public void setTimer(){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (running) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timer.cancel();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timer.purge();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setSeconds(0);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timer = new Timer();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timer.schedule(new SecondsTask(this),1000,1000);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;running = !running;<BR>&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp;&nbsp;* The task that counts the seconds.<BR>&nbsp;&nbsp;&nbsp;&nbsp;*/<BR>&nbsp;&nbsp; private class SecondsTask extends TimerTask {<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/**<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; * We're not interested in the implementation so I omit it.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<BR><BR>&nbsp;&nbsp; }<BR>}</FONT></PRE><BR><BR>Controller只定义了用户可以执行的并且能够从下列接口抽象出来的actions。<BR><PRE class=overflow><FONT style="BACKGROUND-COLOR: #f5f5dc">public interface TimeController {<BR><BR>&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp;&nbsp;* Action invoked when the user wants to start/stop the timer<BR>&nbsp;&nbsp;&nbsp;&nbsp;*/<BR>&nbsp;&nbsp; void userStartStopTimer();<BR><BR>&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp;&nbsp;* Action invoked when the user wants to restart the timer<BR>&nbsp;&nbsp;&nbsp;&nbsp;*/<BR>&nbsp;&nbsp; void userRestartTimer();<BR><BR>&nbsp;&nbsp; /**<BR>&nbsp;&nbsp;&nbsp;&nbsp;* Action invoked when the user wants to modify the timer period<BR>&nbsp;&nbsp;&nbsp;&nbsp;*<BR>&nbsp;&nbsp;&nbsp;&nbsp;* @param newPeriod the new period<BR>&nbsp;&nbsp;&nbsp;&nbsp;*/<BR>&nbsp;&nbsp; void userModifyPeriod(Integer newPeriod);<BR>}</FONT></PRE><BR><BR>你可以使用你自己喜欢的GUI编辑器来画这个view。出于我们自身的情况，我们只需要几个公共的methods就可以提供足够的功能来更新view的fields，如下面的这个例子所示：<BR><PRE class=overflow><FONT style="BACKGROUND-COLOR: #f5f5dc">/**<BR>&nbsp;&nbsp;&nbsp;&nbsp;* Updates the GUI seconds fields<BR>&nbsp;&nbsp;&nbsp;&nbsp;*/<BR>&nbsp;&nbsp; public void setScnFld(Integer sec){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// scnFld is a Swing text field<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SwingUtilities.invokeLater(new Runnable() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;scnFld.setText(sec.toString());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<BR>&nbsp;&nbsp; }</FONT></PRE><BR><BR>在这里我们注意到我们正在使用POJOs (plain-old Java objects)，同时我们不用遵守任何编码习惯或者实现特定的接口(事件激发代码除外)。剩下的就只有定义组件之间的绑定了。<BR><BR><B><SPAN style="FONT-SIZE: 16px">事件分派annotations</SPAN></B><BR><BR>绑定机制的核心就是@ModelDependent annotation的定义：<BR><PRE class=overflow><FONT style="BACKGROUND-COLOR: #f5f5dc">@Retention(RetentionPolicy.RUNTIME) <BR>@Target(ElementType.METHOD) <BR>public @interface ModelDependent {<BR><BR>&nbsp;&nbsp; String modelKey() default "";<BR><BR>&nbsp;&nbsp; String propertyKey() default "";<BR><BR>&nbsp;&nbsp; boolean runtimeModel() default false;<BR><BR>&nbsp;&nbsp; boolean runtimeProperty() default false;<BR><BR>}</FONT></PRE><BR><BR>这个annotation能被用在view的methods上，同时dispatcher也会使用这些提供的参数(即modelKey和propertyKey)来确定这个view将会响应的model事件。这个view既使用modelKey参数来指定它感兴趣的可利用的models又使用propertyKey参数来匹配分配的java.beans.PropertyChangeEvents的属性名称。<BR><BR>View method setScnFld()因此被标注以下信息(这里，timeModel提供了用来将model注册到dispatcher上的key):<BR><PRE class=overflow><FONT style="BACKGROUND-COLOR: #f5f5dc">/**<BR>&nbsp;&nbsp;&nbsp;&nbsp;* Updates the GUI seconds fields<BR>&nbsp;&nbsp;&nbsp;&nbsp;*/<BR>&nbsp;&nbsp; @ModelDependent(modelKey = "timeModel", propertyKey = "seconds")<BR>&nbsp;&nbsp; public void setScnFld(final Integer sec){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// scnFld is a Swing text field<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SwingUtilities.invokeLater(new Runnable() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;scnFld.setText(sec.toString());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<BR>&nbsp;&nbsp; }</FONT></PRE><BR><BR>由于dispatcher既知道model激发的事件又知道事件本身－例如，它知道关联的modelKey和propertyKey－这是唯一需要用来绑定views和models的信息。Model和view甚至不需要分享通信接口或者共用的数据库。<BR><BR>借助我们讨论的绑定机制，我们可以轻易的改变潜在的view而不改变其他任何东西。下面的代码是按照使用SWT(Standard Widget Toolkit)而不是Swing实现的同一个method：<BR><PRE class=overflow><FONT style="BACKGROUND-COLOR: #f5f5dc">@ModelDependent(modelKey = "timeModel", propertyKey = "seconds")<BR>&nbsp;&nbsp; public void setScnFld(final Integer sec){<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Display.getDefault().asyncExec(new Runnable() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void run() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;secondsField.setText(sec.toString());<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<BR>&nbsp;&nbsp; }</FONT></PRE><BR><BR>一个完全没有耦合的系统存在以下优点：View可以更加容易地适应model地改变，尽管model通常都是稳定地，相反view是经常被改变。加上系统可以通过使用GUI编辑器或者其他源码生成器来设计，避免了将生成地代码与model－view通信代码混合在一起。又由于model－view的绑定信息是和源码关联的元数据，于是也相对容易把它应用到IDE生成的GUIs或者将已经存在的应用程序转化成这个框架。加之拥有单独的基础代码，view和model可以被当作是独立组件来开发，这很可能简化了应用程序的开发过程。组件测试也可以被简化，因为每个组件可以被单独地测试，并且出于调试的目的，我们可以用假的model和view来代替真实的组件。<BR><BR>然而，这里也存在许多缺点。因为现在当使用接口和公共的classes来绑定model和view时，我们不能再提供编译时期的安全性了，可能出现的打字错误将导致组件之间一个绑定的遗漏，从而导致出现运行时期的错误。<BR><BR>通过使用@ModelDependent的讨论过的modelKey和propertyKey元素，你可以定义model和view之间静态的联系。然而，现实世界的应用程序证明view必须能够经常动态的适应变化的models和应用程序的状态：考虑到用户界面的不同部分能够在应用程序的生命周期内被创造和删除。因此我将介绍怎么使用这个框架与其他常用技术一起来处理此类情形。<BR><BR><B><SPAN style="FONT-SIZE: 16px">动态MVC绑定</SPAN></B><BR><BR>对于那些依赖XML绑定(或者其他一些基于配置文件的声明性绑定)的框架，存在一个问题那就是静态绑定规则。在这些框架下，动态变化是不可能的，于是通常开发者决定每次将冗余的绑定信息与一些使用正确绑定的判定算法耦合在一起。<BR><BR>为了巧妙的解决这个问题，Stamps框架提供了两种方式在运行时期改变绑定。 第一种方式是，views和models可以采用事件监听器与GUI窗口小部件联合的方式在dispatcher上注册和注销。这样允许特定的views只在需要他们的时候被通知到。例如，一个与应用程序有联系的监视控制台可以只在用户请求的时候与被它监视的对象绑定在一起。<BR><BR>第二种方式是利用@ModelDependent annotation提供的两个元素runtimeModel() 和 runtimeProperty()。他们指明了某个确定的model和它的分配事件会在运行时期被确定。如果这两个设定中有一个是正确的，那么各自的key(modelKey 或propertyKey)会在view上被method调用来得到需要使用的值。例如：一个负责显示一组新channels (每个channel就是一个model)的view，它就依赖于用户的输入来确定需要绑定的channel。<BR><BR>这种情形的实例如下：<BR><PRE class=overflow><FONT style="BACKGROUND-COLOR: #f5f5dc">// This method is invoked to display all the messages of one news channel<BR>&nbsp;&nbsp; @ModelDependent(modelKey = "dynamicChannel", propertyKey = "allmessages" , runtimeModel = true)<BR>&nbsp;&nbsp; public void setAllMessages(java.util.List messages) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Updates the user interface<BR>&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp; public String getDynamicChannel() {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Returns the channel requested by the user<BR>&nbsp;&nbsp; }</FONT></PRE><BR><BR><B><SPAN style="FONT-SIZE: 16px">附加的annotations</SPAN></B><BR><BR>由于世界并不完美，一些附加的annotations被定义来帮助解决现实世界的案例。@Namespace允许开发者为了更好的管理model domain将其再细分成不同的部分。由于单独一个dispatcher可以处理多个models，model keys中将出现的冲突。因此，它能将成群的models和相关的views分到不同的但同属一个namespace下的domains中去, 这样一来，他们就不会干扰对方。<BR><BR>@Transform annotation提供了on-the-fly对象转化, 从包含在model事件中的对象到被receiving views接受的对象的。因而，这个框架就可以适应已存的代码而不需要做任何的改动。这个annotation接受一个注册在有效转化上的单一参数(被定义成一个特殊接口的实现)。<BR><BR>@Refreshable annotation能通过标注model的属性来支持前面讨论的动态连接和分离views。使用这个annotation，该框架可以处理静态和动态的MVC布局，在不同的时间把不同的views绑定到model上。<BR><BR>要理解@Refreshable的使用，我们必须回到之前的那个监控控制台的例子。这个控制台(用MVC的术语来说就是一个view)可以动态地绑定和离开model，取决于用户的需要。当控制器连接到model的时候@Refreshable annotation可以被用来让这个控制器随时了解其model的状态。当一个view连接到这个框架时，它必须在当前model的状态下被更新。因此，dispatcher扫描model寻找@Refreshable annotations并且生成与view它本身从model普通接受到的相同的事件。这些事件接着被之前讨论过的绑定机制分派。<BR><BR><B><SPAN style="FONT-SIZE: 16px">分布式MVC网络</SPAN></B><BR><BR>Dispatcher有一个很重的负担那就是它负责处理事件的传送周期中所有重型信息的传递：<BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Model激发一个事件用来确定它已经经历过的一些改变, dispatcher处理通知model.<BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Dispatcher扫描所有注册在它那里的views, 寻找@ModelDependent annotations, 这些annotations明确了views希望通知的改变及当每个model事件发生时,需要在views上调用的method.<BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果需要,转化将会被用于事件数据上.<BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;view method在被调用时会从被激发的事件里抽取参数,接着view会更新自己. <BR><BR>从另一个方面来讲,当一个新view在dispatcher上注册时:<BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;View告诉dispatcher有关modelKey的信息,modelkey能确定它将被连接到哪一个model上(该model的事件将负责组装view)<BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果需要，dispatcher扫描model寻找@Refreshable annotations并使用他们来生产将要及时更新view假的model事件<BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这些事件将通过使用上述的顺序被分派, 接着view被更新.<BR><BR>所有这些既不涉及view也不涉及model的工作,他们站在他们各自的信息通信渠道的两端.无所谓这些信息是在一个本地JVM内部传输还是在多个远程主机上的JVM之间传输.如果想将本地应用程序转化成Client/Server应用程序所需的只是简单地改变dispatcher里面的逻辑,而model和view都不会受影响.下图就是一个示例:&nbsp;<BR><BR><IMG height=248 alt=2006_02_08_233827_rlIDvGWfPI.jpg src="http://www.blogjava.net/images/blogjava_net/snowolf/java/2006_02_08_233827_rlIDvGWfPI.jpg" width=520 border=0>&nbsp;<BR>图4. 一个基于分布式网路建立的MVC. <BR><BR>如上图所示,单一的dispatcher被一个与model处在同一个host上的transmitter(it.battlehorse.stamps.impl.BroadcastDispatcher的一个instance)和一个(或多个) 与view处在同一个host上的receiver(it.battlehorse.stamps.impl.FunnelDispatcher)所取代. Stamps 框架默认的实现使用了一个创建于JGroups上的消息传送层, JGroups是一个可靠的多点传送通信的工具包,象网络传输机制(但是不同的实现和使用)一样工作. 通过使用它可以获得一个稳定可靠的, 多协议的, 失败警觉的通信. <BR><BR>对我们应用程序(dispatcher)初步建立的一个改变, 使我们从一个单一用户界面的独立运行的应用程序转移到一个多用户分布式的应用程序.当model进入或离开这个网络(想象一个通信失败)的时候,框架可以通知无数的监听接口, 于是远程views可以采取适当的响应.例如,显示一个警告信息给用户. 这个框架也可以提供有用的methods来帮助将本地的controllers转化成远程的. <BR><BR><B><SPAN style="FONT-SIZE: 16px">总结和摘要</SPAN></B><BR>仍有许多元素需要被探索,就像设计controllers的方式一样,它在目前和dispatchers具有一致的普遍性.该框架假设普通的controller-model绑定,由于前者需要知道如何去驱动后者.未来的开发方向将是支持不同类型的views,例如使用一个Web浏览器, 网络警觉的applets,和Java与JavaScript的通信.<BR><BR>已经讨论的Stamps库说明如何在一个MVC架构中降低views和models之间的耦合以及这个框架可以有效的利用Java annotations将绑定信息从实际开发程序组件分离开.拥有隔离的绑定逻辑允许你在物理上将元件分离开并且能提供一个本地和一个client/server结构而不需要改变应用逻辑或者表示层. 这些目标提供对由一个象MVC一样坚固的设计模式与由annotations提供的功能强大的元数据结合在一起所提供的可能性的洞察.&nbsp;&nbsp;<BR><BR><B>关于作者</B><BR>Riccardo Govoni自从2003年开始就做为一名J2EE的开发者在一家位于意大利北部的金融服务公司工作.在那里,他为遗留的银行系统,数据库管理,和繁重的数据处理任务开发了许多Web前台. Govoni在J2EE多层应用程序方面拥有丰富经验并且拥有关于Java GUI 库象Swing 和SWT详尽的知识.他拥有物理学硕士学位.在闲暇时间,Govoni在网络上四处寻找最新的Java新闻,或者与朋友和(有时候)他女朋友的狗一起讨论新的项目主意. <BR><BR><B>资源</B><BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Stamps项目主页,及相关源码文件和文档:http://sourceforge.net/projects/stamps-mvc/ <BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Wikipedia上关于MVC的定义:http://en.wikipedia.org/wiki/MVC <BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最有名的MVC实现之一, Struts Web 应用框架:http://struts.apache.org/ <BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JGoodies 绑定，一个在Swing应用程序中解决model-view-controller绑定的不同方法:https://binding.dev.java.net/ <BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TikeSwing, 一个基于Swing之上的开源框架, 通过扩展Swing widgets来引入MVC “与结合Swing来使用高层次的MVC和POJOs” Tomi Tuomainen(JavaWorld, 2005年六月):http://www.javaworld.com/javaworld/jw-06-2005/jw-0620-tikeswing.html <BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sun J2SE 5.0 annotations向导:http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html <BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;J2SE 5.0 annotations 入门介绍: “Taming Tiger, 第三部分,” Tarak Modi(JavaWorld, 2004年七月):http://www.javaworld.com/javaworld/jw-07-2004/jw-0719-tiger3.html <BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;“零距离: J2SE 5.0 Annotations,” Kyle Downey (ONJava.com, 2004年十月):http://www.onjava.com/pub/a/onjava/2004/10/06/anno1.html <BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JGroups, 是一个可靠的多点传送通信的工具包, 在分布式Stamps MVC网络中象传输层一样工作:http://www.jgroups.org/javagroupsnew/docs/index.html <BR>·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;“一个Swing架构的一般了解”, 作者: Amy Fowler(Sun开发者网络)讨论Swing’s MVC的根源:http://java.sun.com/products/jfc/tsc/articles/architecture/ </DIV><img src ="http://www.blogjava.net/snowolf/aggbug/30169.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/snowolf/" target="_blank">snowolf</a> 2006-02-10 15:56 <a href="http://www.blogjava.net/snowolf/articles/30169.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>