﻿<?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-weidagang2046的专栏-文章分类-Java</title><link>http://www.blogjava.net/weidagang2046/category/1064.html</link><description>物格而后知致</description><language>zh-cn</language><lastBuildDate>Thu, 01 Mar 2007 18:27:06 GMT</lastBuildDate><pubDate>Thu, 01 Mar 2007 18:27:06 GMT</pubDate><ttl>60</ttl><item><title>Ajax中文乱码问题解决方案（servlet）</title><link>http://www.blogjava.net/weidagang2046/articles/86610.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sat, 09 Dec 2006 15:06:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/86610.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/86610.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/86610.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/86610.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/86610.html</trackback:ping><description><![CDATA[
		<div class="postbody">最近研究ajax，在界面上text中输入中文，发送出去后返回乱码。经过一个晚上的测试，以及得到满意答案，代码如下：<br /><br />HTML：<br />    ……<br />    //实际上这里的charset=utf-8 也是可以的，因为在中文平台下<br />    //用了GB2312<br />    &lt;meta http-equiv="Content-Type" content="text/html; charset=GB2312"&gt;<br /><br />JS：<br />    <font color="#ff0000">我用了两个方法提交：GET 和 POST。<br />    在服务器端要对应不同的提交方式转换不同的编码。</font><br />   ……<br />    //要传递的参数<br />   var queryString = "firstName=" + firstName + "&amp;lastName=" + lastName<br />                          + "&amp;birthday=" + birthday;    function  <br /> <br />    //GET方式提交<br />    doRequestUsingGET() {<br />        createXMLHttpRequest();<br />        var url = "GetAndPostExample?" + queryString + "&amp;timeStamp="<br />                          + new Date().getTime();<br />        xmlHttp.onreadystatechange = handleStateChange;<br />        xmlHttp.open("GET", url, true);<br />        xmlHttp.send(null);<br />    }<br /><br />    //POST方式提交<br />    function doRequestUsingPOST() {<br />        createXMLHttpRequest();<br />        var url = "GetAndPostExample?timeStamp=" + new Date().getTime();<br />        xmlHttp.open("POST", url, true);<br />        xmlHttp.onreadystatechange = handleStateChange;<br />        xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");<br />        xmlHttp.send(queryString);<br />    }<br /><br />servlet：<br />        response.setContentType("text/xml");<br /><br />        //<font color="#ff0000">这个一定要设置，这里的设置应该跟HTML中的一样，但是我在这里</font><br />        //用了 uft-8， 结果也是一样。<br />        response.setCharacterEncoding("GB2312");<br />      <br />        //<font color="#ff0000">当用POST方法时，一定要设置成utf-8，否则乱码</font><br />        String firstName = new String(request.getParameter("firstName").getBytes("ISO-8859-1"), "<font color="#ff0000">utf-8</font>");<br /><br />        //<font color="#ff0000">当用GET方法时，要设置成GB2312，否则乱码。</font><br />        String lastName = new String(request.getParameter("lastName").getBytes("ISO-8859-1"), "<font color="#ff0000">GB2312</font>");<br /><br />测试结果：<br />因为界面上两个控件firstName 和  lastName都输入中文。<br />接收xmlHttp.responseText后，会发现其中一个为乱码，一个可以正常显示中文。<br /><br />在网上还发现有人说用：<br />“老问题了，最简单的方法是，全部escape后发送。取回后unescape，绝对没有编码问题。”<br />我测试后发现escape后的东西全部变成null了。不知道有谁成功用过这个方法的，或者还有什么更好的解决乱码的方法，请拿出来共享一下吧 ：）<br /><br />from: <a href="http://www.themore.net/page/7/37/2006_08/detail_407_1.html">http://www.themore.net/page/7/37/2006_08/detail_407_1.html</a></div>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/86610.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-12-09 23:06 <a href="http://www.blogjava.net/weidagang2046/articles/86610.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>编写高性能Web应用程序的10个入门技巧</title><link>http://www.blogjava.net/weidagang2046/articles/85980.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Wed, 06 Dec 2006 15:57:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/85980.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/85980.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/85980.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/85980.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/85980.html</trackback:ping><description><![CDATA[
		<p>编写高性能Web应用程序的10个入门技巧<br /><br />  数据层性能 <br />  技巧 1 — 返回多个结果集 <br />  技巧 2 — 分页的数据访问 <br />  技巧 3 — 连接池 <br />  技巧 4 — ASP.NET 缓存 API <br />  技巧 5 — 每请求缓存 <br />  技巧 6 — 后台处理 <br />  技巧 7 — 页输出缓存和代理<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a><br />  技巧 8 — 运行 IIS 6.0（只要用于内核缓存） <br />  技巧 9 — 使用 Gzip 压缩 <br />  技巧 10 — <a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>控件视图状态 <br />  <br /><br />使用 ASP.NET 编写 Web 应用程序的简单程度令人不敢相信。正因为如此简单，所以很多<br />开发人员就不会花时间来设计其应用程序的结构，以获得更好的性能了。在本文中，我将<br />讲述 10 个用于编写高性能 Web 应用程序的技巧。但是我并不会将这些建议仅局限于 <br />ASP.NET 应用程序，因为这些应用程序只是 Web 应用程序的一部分。本文不作为对 Web <br />应用程序进行性能调整的权威性指南 — 一整本书恐怕都无法轻松讲清楚这个问题。请将<br />本文视作一个很好的起点。 <br /><br />成为工作狂之前，我原来喜欢攀岩。在进行任何大型攀岩活动之前，我都会首先仔细查看<br />指南中的路线，阅读以前游客提出的建议。但是，无论指南怎么好，您都需要真正的攀岩<br />体验，然后才能尝试一个特别具有挑战性的攀登。与之相似，当您面临修复性能问题或者<br />运行一个高吞吐量站点的问题时，您只能学习如何编写高性能 Web 应用程序。<br /><br />我的个人体验来自在 Microsoft 的 ASP.NET 部门作为基础架构程序经理的经验，在此期<br />间我运行和管理 www.ASP.NET，帮助设计社区<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>的结构，社区<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>是几个著名 <br />ASP.NET 应用程序（组合到一个平台的 ASP.NET Forums、.Text 和 nGallery）。我确信<br />有些曾经帮助过我的技巧对您肯定也会有所帮助。<br /><br />您应该考虑将应用程序分为几个逻辑层。您可能听说过 3 层（或者 n 层）物理体系结构<br />一词。这些通常都是规定好的体系结构方式，将功能在进程和/或硬件之间进行了物理分离<br />。当系统需要扩大时，可以很轻松地添加更多的硬件。但是会出现一个与进程和机器跳跃<br />相关的性能下降，因此应该避免。所以，如果可能的话，请尽量在同一个应用程序中一起<br />运行 ASP.NET 页及其相关组件。<br /><br />因为代码分离以及层之间的边界，所以使用 Web 服务或远程处理将会使得性能下降 20% <br />甚至更多。<br /><br />数据层有点与众不同，因为通常情况下，最好具有专用于<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>的硬件。然而进程跳跃到<br /><a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>的成本依然很高，因此数据层的性能是您在优化代码时首先要考虑的问题。<br /><br />在深入应用程序的性能修复问题之前，请首先确保对应用程序进行剖析，以便找出具体的<br />问题所在。主要性能计数器（如表示执行垃圾回收所需时间百分比的计数器）对于找出应<br />用程序在哪些位置花费了其主要时间也非常有用。然而花费时间的位置通常非常不直观。<br /><br />本文讲述了两种类型的性能改善：大型优化（如使用 ASP.NET 缓存），和进行自身重复的<br />小型优化。这些小型优化有时特别有意思。您对代码进行一点小小的更改，就会获得很多<br />很多时间。使用大型优化，您可能会看到整体性能的较大飞跃。而使用小型优化时，对于<br />某个特定请求可能只会节省几毫秒的时间，但是每天所有请求加起来，则可能会产生巨大<br />的改善。<br /><br />数据层性能<br /><br /><br />谈到应用程序的性能调整，有一个试纸性的测试可用来对工作进行优先级划分：代码是否<br />访问<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>？如果是，频率是怎样的？请注意，这一相同测试也可应用于使用 Web 服务或<br />远程处理的代码，但是本文对这些内容未做讲述。<br /><br />如果某个特定的代码路径中必需进行<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>请求，并且您认为要首先优化其他领域（如字<br />符串操作），则请停止，然后执行这个试纸性测试。如果您的性能问题不是非常严重的话<br />，最好花一些时间来优化一下与<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>、返回的数据量、进出<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>的往返频率相关的花<br />费时间。<br /><br />了解这些常规信息之后，我们来看一下可能会有助于提高应用程序性能的十个技巧。首先<br />，我要讲述可能会引起最大改观的更改。<br /><br /><br />技巧 1 — 返回多个结果集<br /><br /><br />仔细查看您的<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>代码，看是否存在多次进入<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>的请求路径。每个这样的往返都会<br />降低应用程序可以提供的每秒请求数量。通过在一个<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>请求中返回多个结果集，可以<br />节省与<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>进行通信所需的总时间长度。同时因为减少了<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a><a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>管理请求的工作<br />，还会使得系统伸缩性更强。<br /><br />虽然可以使用动态 SQL 返回多个结果集，但是我首选使用存储过程。关于业务逻辑是否应<br />该驻留于存储过程的问题还存在一些争议，但是我认为，如果存储过程中的逻辑可以约束<br />返回数据的话（缩小数据集的大小、缩短网络上所花费时间，不必筛选逻辑层的数据），<br />则应赞成这样做。<br /><br />使用 SqlCommand 实例及其 ExecuteReader 方法填充强类型的业务类时，可以通过调用 <br />NextResult 将结果集指针向前移动。图 1 显示了使用类型类填充几个 ArrayList 的示例<br />会话。只从<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>返回您需要的数据将进一步减少<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>上的内存分配。 <br /><br />Figure 1 Extracting Multiple Resultsets from a DataReader<br />// read the first resultset<br />reader = command.ExecuteReader();<br /><br />// read the data from that resultset<br />while (reader.Read()) {<br />    suppliers.Add(PopulateSupplierFromIDataReader( reader ));<br />}<br /><br />// read the next resultset<br />reader.NextResult();<br /><br />// read the data from that second resultset<br />while (reader.Read()) {<br />    products.Add(PopulateProductFromIDataReader( reader ));<br />}<br /><br /><br />技巧 2 — 分页的数据访问<br /><br /><br />ASP.NET DataGrid 具有一个很好的功能：数据分页支持。在 DataGrid 中启用分页时，一<br />次会显示固定数量的记录。另外，在 DataGrid 的底部还会显示分页 UI，以便在记录之间<br />进行导航。该分页 UI 使您能够在所显示的数据之间向前和向后导航，并且一次显示固定<br />数量的记录。<br /><br />还有一个小小的波折。使用 DataGrid 的分页需要所有数据均与网格进行绑定。例如，您<br />的数据层需要返回所有数据，那么 DataGrid 就会基于当前页筛选显示的所有记录。如果<br />通过 DataGrid 进行分页时返回了 100,000 个记录，那么针对每个请求会放弃 99,975 个<br />记录（假设每页大小为 25 个记录）。当记录的数量不断增加时，应用程序的性能就会受<br />到影响，因为针对每个请求必须发送越来越多的数据。<br /><br />要编写性能更好的分页代码，一个极佳的方式是使用存储过程。图 2 显示了针对 <br />Northwind <a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>中的 Orders 表进行分页的一个示例存储过程。简而言之，您此时要做<br />的只是传递页索引和页大小。然后就会计算合适的结果集，并将其返回。 <br /><br />Figure 2 Paging Through the Orders Table<br />CREATE PROCEDURE northwind_OrdersPaged<br />(<br />    @PageIndex int, <br />    @PageSize int<br />)<br />AS<br />BEGIN<br />DECLARE @PageLowerBound int<br />DECLARE @PageUpperBound int<br />DECLARE @RowsToReturn int<br /><br />-- First set the rowcount<br />SET @RowsToReturn = @PageSize * (@PageIndex + 1)<br />SET ROWCOUNT @RowsToReturn<br /><br />-- Set the page bounds<br />SET @PageLowerBound = @PageSize * @PageIndex<br />SET @PageUpperBound = @PageLowerBound + @PageSize + 1<br /><br />-- Create a temp table to store the select results<br />CREATE TABLE #PageIndex <br />(<br />    IndexId int IDENTITY (1, 1) NOT NULL,<br />    OrderID int<br />)<br /><br />-- Insert into the temp table<br />INSERT INTO #PageIndex (OrderID)<br />SELECT <br />    OrderID<br />FROM <br />    Orders<br />ORDER BY <br />    OrderID DESC<br /><br />-- Return total count<br />SELECT COUNT(OrderID) FROM Orders<br /><br />-- Return paged results<br />SELECT <br />    O.*<br />FROM <br />    Orders O,<br />    #PageIndex PageIndex<br />WHERE <br />    O.OrderID = PageIndex.OrderID AND<br />    PageIndex.IndexID &gt; @PageLowerBound AND<br />    PageIndex.IndexID &lt; @PageUpperBound<br />ORDER BY <br />    PageIndex.IndexID<br /><br />END<br /><br /><br /><br />在社区<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>中，我们编写了一个分页<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>控件，以完成所有的数据分页。您将会看到<br />，我使用的就是技巧 1 中讨论的理念，从一个存储过程返回两个结果集：记录的总数和请<br />求的数据。<br /><br />返回记录的总数可能会根据所执行查询的不同而有所变化。例如，WHERE 子句可用来约束<br />返回的数据。为了计算在分页 UI 中显示的总页数，必须了解要返回记录的总数。例如，<br />如果总共有 1,000,000 条记录，并且要使用一个 WHERE 子句将其筛选为 1000 条记录，<br />那么分页逻辑就需要了解记录的总数才能正确呈现分页 UI。<br /><br /><br />技巧 3 — 连接池<br /><br /><br />在 Web 应用程序和 <a href="http://www.chinahtml.com/databases/2/" target="_blank">SQL Server</a>? 之间设置 TCP 连接可能是一个非常消耗资源的操作。Mi<br />crosoft 的开发人员到目前为止能够使用连接池已经有一段时间了，这使得他们能够重用<br /><a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>连接。他们不是针对每个请求都设置一个新的 TCP 连接，而是只在连接池中没有任<br />何连接时才设置新连接。当连接关闭时，它会返回连接池，在其中它会保持与<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>的连<br />接，而不是完全破坏该 TCP 连接。<br /><br />当然，您需要小心是否会出现泄漏连接。当您完成使用连接时，请一定要关闭这些连接。<br />再重复一遍：无论任何人对 Microsoft?.NET Framework 中的垃圾回收有什么评论，请一<br />定要在完成使用连接时针对该连接显式调用 Close 或 Dispose。不要相信公共语言运行库<br />(CLR) 会在预先确定的时间为您清除和关闭连接。尽管 CLR 最终会破坏该类，并强制连<br />接关闭，但是当针对对象的垃圾回收真正发生时，并不能保证。 <br /><br />要以最优化的方式使用连接池，需要遵守一些规则。首先打开连接，执行操作，然后关闭<br />该连接。如果您必须如此的话，可以针对每个请求多次打开和关闭连接（最好应用技巧 1<br />），但是不要一直将连接保持打开状态并使用各种不同的方法对其进行进出传递。第二，<br />使用相同的连接字符串（如果使用集成身份验证的话，还要使用相同的线程标识）。如果<br />不使用相同的连接字符串，例如根据登录的用户自定义连接字符串，那么您将无法得到连<br />接池提供的同一个优化值。如果您使用集成身份验证，同时还要模拟大量用户，连接池的<br />效率也会大大下降。尝试跟踪与连接池相关的任何性能问题时，.NET CLR 数据性能计数器<br />可能非常有用。 <br /><br />每当应用程序连接资源时，如在另一个进程中运行的<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>，您都应该重点考虑连接该资<br />源所花时间、发送或检索数据所花时间，以及往返的数量，从而进行优化。优化应用程序<br />中任何种类的进程跳跃都是获得更佳性能的首要一点。<br /><br />应用层包含了连接数据层、将数据转换为有意义类实例和业务流程的逻辑。例如社区服务<br />器，您要在其中填充Forums 或 Threads集合，应用业务规则（如权限）；最重要的是要在<br />其中执行缓存逻辑。<br /><br /><br />技巧 4 — ASP.NET 缓存 API<br /><br /><br />编写应用程序代码行之前，一个首要完成的操作是设计应用层的结构，以便最大化利用 <br />ASP.NET 缓存功能。<br /><br />如果您的组件要在 ASP.NET 应用程序中运行，则只需在该应用程序项目中包括一个 <br />System.Web.dll 引用。当您需要访问该缓存时，请使用 HttpRuntime.Cache 属性（通过 <br />Page.Cache 和 HttpContext.Cache 也可访问这个对象）。<br /><br />对于缓存数据，有几个规则。首先，如果数据可能会多次使用时，则这是使用缓存的一个<br />很好的备选情况。第二，如果数据是通用的，而不特定于某个具体的请求或用户时，则也<br />是使用缓存的一个很好的备选情况。如果数据是特定于用户或请求的，但是寿命较长的话<br />，仍然可以对其进行缓存，但是这种情况可能并不经常使用。第三，一个经常被忽略的规<br />则是，有时可能您缓存得太多。通常在一个 x86 计算机上，为了减少内存不足错误出现的<br />机会，您会想使用不高于 800MB 的专用字节运行进程。因此缓存应该有个限度。换句话说<br />，您可能能够重用某个计算结果，但是如果该计算采用 10 个参数的话，您可能要尝试缓<br />存 10 个排列，这样有可能给您带来麻烦。一个要求 ASP.NET 的最常见支持是由于过度缓<br />存引起的内存不足错误，尤其是对于大型数据集。<br /><br /><br />缓存有几个极佳的功能，您需要对它们有所了解。首先，缓存会实现最近最少使用的算法<br />，使得 ASP.NET 能够在内存运行效率较低的情况下强制缓存清除 － 从缓存自动删除未使<br />用过的项目。第二，缓存支持可以强制失效的过期依赖项。这些依赖项包括时间、密钥和<br />文件。时间经常会用到，但是对于 ASP.NET 2.0，引入了一个功能更强的新失效类型：数<br />据库缓存失效。它指的是当<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a>中的数据发生变化时自动删除缓存中的项。有关<a href="http://www.chinahtml.com/databases/" target="_blank">数据库</a><br />缓存失效的详细信息，请参阅 MSDN?Magazine 2004 年 7 月的 Dino Esposito Cutting <br />Edge 专栏。要了解缓存的体系结构，请参阅图 3。<br /><br />技巧 5 — 每请求缓存<br /><br />在本文前面部分，我提到了经常遍历代码路径的一些小改善可能会导致较大的整体性能收<br />益。对于这些小改善，其中有一个绝对是我的最爱，我将其称之为"每请求缓存"。<br /><br />缓存 API 的设计目的是为了将数据缓存较长的一段时间，或者缓存至满足某些条件时，但<br />每请求缓存则意味着只将数据缓存为该请求的持续时间。对于每个请求，要经常访问某个<br />特定的代码路径，但是数据却只需提取、应用、修改或更新一次。这听起来有些理论化，<br />那么我们来举一个具体的示例。<br /><br />在社区<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>的论坛应用程序中，页面上使用的每个<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>控件都需要个性化的数据来确<br />定使用什么外观、使用什么样式表，以及其他个性化数据。这些数据中有些可以长期缓存<br />，但是有些数据却只针对每个请求提取一次，然后在执行该请求期间对其重用多次，如要<br />用于控件的外观。<br /><br />为了达到每请求缓存，请使用 ASP.NET HttpContext。对于每个请求，都会创建一个 <br />HttpContext 实例，在该请求期间从 HttpContext.Current 属性的任何位置都可访问该实<br />例。该 HttpContext 类具有一个特殊的 Items 集合属性；添加到此 Items 集合的对象和<br />数据只在该请求持续期间内进行缓存。正如您可以使用缓存来存储经常访问的数据一样，<br />您也可以使用 HttpContext.Items 来存储只基于每个请求使用的数据。它背后的逻辑非常<br />简单：数据在它不存在的时候添加到 HttpContext.Items 集合，在后来的查找中，只是返<br />回 HttpContext.Items 中的数据。<br /><br /><br />技巧 6 — 后台处理<br /><br /><br />通往代码的路径应该尽可能快速，是吗？可能有时您会觉得针对每个请求执行的或者每 <br />n 个请求执行一次的任务所需资源非常多。发送电子邮件或者分析和验证传入数据就是这<br />样的一些例子。<br /><br />剖析 ASP.NET Forums 1.0 并重新构建组成社区<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>的内容时，我们发现添加新张贴的<br />代码路径非常慢。每次添加新张贴时，应用程序首先需要确保没有重复的张贴，然后必须<br />使用"坏词"筛选器分析该张贴，分析张贴的字符图释，对张贴添加标记并进行索引，请求<br />时将张贴添加到合适的队列，验证附件，最终张贴之后，立即向所有订阅者发出电子邮件<br />通知。很清楚，这涉及很多操作。<br /><br />经研究发现，大多数时间都花在了索引逻辑和发送电子邮件上。对张贴进行索引是一个非<br />常耗时的操作，人们发现内置的 System.Web.Mail 功能要连接 SMYP <a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>，然后连续发<br />送电子邮件。当某个特定张贴或主题领域的订阅者数量增加时，执行 AddPost 功能所需的<br />时间也越来越长。<br /><br />并不需要针对每个请求都进行电子邮件索引。理想情况下，我们想要将此操作进行批处理<br />，一次索引 25 个张贴或者每五分钟发送一次所有电子邮件。我们决定使用以前用于对数<br />据缓存失效进行原型设计的代码，这个失效是用于最终进入 Visual Studio? 2005 的内容<br />的。<br /><br />System.Threading 命名空间中的 Timer 类非常有用，但是在 .NET Framework 中不是很<br />有名，至少对于 Web 开发人员来说是这样。创建之后，这个 Timer 类将以一个可配置的<br />间隔针对 ThreadPool 中的某个线程调用指定的回调。这就表示，您可以对代码进行设置<br />，使其能够在没有对 ASP.NET 应用程序进行传入请求的情况下得以执行，这是后台处理的<br />理想情况。您还可以在此后台进程中执行如索引或发送电子邮件之类的操作。 <br /><br />但是，这一技术有几个问题。如果应用程序域卸载，该计时器实例将停止触发其事件。另<br />外，因为 CLR 对于每个进程的线程数量具有一个硬性标准，所以可能会出现这样的情形：<br /><a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>负载很重，其中计时器可能没有可在其基础上得以完成的线程，在某种程度上可能<br />会造成延迟。ASP.NET 通过在进程中保留一定数量的可用线程，并且仅使用总线程的一部<br />分用于请求处理，试图将上述情况发生的机会降到最低。但是，如果您具有很多异步操作<br />时，这可能就是一个问题了。 <br /><br />这里没有足够的空间来放置该代码，但是您可以下载一个可以看懂的示例，网址是 <br />www.rob-howard.net。请了解一下 Blackbelt TechEd 2004 演示中的幻灯片和演示。<br /><br /><br />技巧 7 — 页输出缓存和代理<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a><br /><br /><br />ASP.NET 是您的表示层（或者说应该是您的表示层）；它由页、用户控件、<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>控件（H<br />ttpHandlers 和 HttpModules）以及它们生成的内容组成。如果您具有一个 ASP.NET 页，<br />它会生成输出（HTML、XML、图像或任何其他数据），并且您针对每个请求运行此代码时，<br />它都会生成相同的输出，那么您就拥有一个可用于页输出缓存的绝佳备选内容。 <br /><br />将此行内容添加页的最上端 <br /><br />&lt;%@ Page OutputCache VaryByParams="none" Duration="60" %&gt; <br /><br />就可以高效地为此页生成一次输出，然后对它进行多次重用，时间最长为 60 秒，此时该<br />页将重新执行，输出也将再一次添加到 ASP.NET 缓存。通过使用一些低级程序化 API 也<br />可以完成此行为。对于输出缓存有几个可配置的设置，如刚刚讲到的 VaryByParams 属性<br />。VaryByParams 刚好被请求到，但还允许您指定 HTTP GET 或 HTTP POST 参数来更改缓<br />存项。例如，只需设置 VaryByParam="Report" 即可对 default.aspx?Report=1 或 <br />default.aspx?Report=2 进行输出缓存。通过指定一个以分号分隔的列表，还可以指定其<br />他参数。 <br /><br />很多人都不知道何时使用输出缓存，ASP.NET 页还会生成一些位于缓存<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>下游的 <br />HTTP 标头，如 Microsoft Internet Security and Acceleration Server 或 Akamai 使<br />用的标头。设置了 HTTP 缓存标头之后，可以在这些网络资源上对文档进行缓存，客户端<br />请求也可在不必返回原始<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>的情况下得以满足。<br /><br />因此，使用页输出缓存不会使得您的应用程序效率更高，但是它可能会减少<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>上的负<br />载，因为下游缓存技术会缓存文档。当然，这可能只是匿名内容；一旦它成为下游之后，<br />您就再也不会看到这些请求，并且再也无法执行身份验证以阻止对它的访问了。<br /><br /><br />技巧 8 — 运行 IIS 6.0（只要用于内核缓存）<br /><br /><br />如果您未运行 IIS 6.0 (<a href="http://www.chinahtml.com/systems/2/" target="_blank">Windows</a> Server? 2003)，那么您就错过了 Microsoft Web 服务<br />器中的一些很好的性能增强。在技巧 7 中，我讨论了输出缓存。在 IIS 5.0 中，请求是<br />通过 IIS 然后进入 ASP.NET 的。涉及到缓存时，ASP.NET 中的 HttpModule 会接收该请<br />求，并返回缓存中的内容。<br /><br />如果您正在使用 IIS 6.0，就会发现一个很好的小功能，称为内核缓存，它不需要对 <br />ASP.NET 进行任何代码更改。当请求由 ASP.NET 进行输出缓存时，IIS 内核缓存会接收缓<br />存数据的一个副本。当请求来自网络驱动程序时，内核级别的驱动程序（无上下文切换到<br />用户模式）就会接收该请求，如果经过了缓存，则会将缓存的数据刷新到响应，然后完成<br />执行。这就表示，当您将内核模式缓存与 IIS 和 ASP.NET 输出缓存一起使用时，就会看<br />到令人不敢相信的性能结果。在 ASP.NET 的 Visual Studio 2005 开发过程中，我一度是<br />负责 ASP.NET 性能的程序经理。开发人员完成具体工作，但是我要看到每天进行的所有报<br />告。内核模式缓存结果总是最有意思的。最常见的特征是网络充满了请求/响应，而 IIS <br />运行时的 CPU 使用率只有大约 5%。这太令人震惊了！当然使用 IIS 6.0 还有一些其他原<br />因，但是内核模式缓存是其中最明显的一个。<br /><br /><br />技巧 9 — 使用 Gzip 压缩<br /><br /><br />虽然使用 gzip 并不一定是<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>性能技巧（因为您可能会看到 CPU 使用率的提高），但<br />是使用 gzip 压缩可以减少<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>发送的字节数量。这就使人们觉得页速度加快了，并且<br />还减少了带宽的用量。根据所发送数据、可以压缩的程度以及客户端浏览器是否支持（IIS<br />只会向支持 gzip 压缩的客户端发送经过 gzip 压缩的内容，如 Internet Explorer <br />6.0 和 Firefox），您的<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>每秒可以服务于更多的请求。实际上，几乎每当您减少所<br />返回数据的数量时，都会增加每秒请求数。 <br /><br />Gzip 压缩已经内置到 IIS 6.0 中，并且其性能比 IIS 5.0 中使用的 gzip 压缩要好的多<br />，这是好消息。但不幸的是，当尝试在 IIS 6.0 中打开 gzip 压缩时，您可能无法在 <br />IIS 的属性对话中找到该设置。IIS 小组在该<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>中置入了卓越的 gzip 功能，但是忘<br />了包括一个用于启用该功能的管理 UI。要启用 gzip 压缩，您必须深入到 IIS 6.0 的 <br />XML 配置设置内部（这样不会引起心脏虚弱）。顺便提一句，这归功于 OrcsWeb 的 <br />Scott Forsyth，他帮助我提出了在 OrcsWeb 上宿主的 www.asp.net <a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>的这个问题。<br /><br /><br />本文就不讲述步骤了，请阅读 Brad Wilson 的文章，网址是 IIS6 Compression。还有一<br />篇有关为 ASPX 启用压缩的知识库文章，网址是 Enable ASPX Compression in IIS。但是<br />您应该注意，由于一些实施细节，IIS 6.0 中不能同时存在动态压缩和内核缓存。<br /><br /><br />技巧 10 — <a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>控件视图状态<br /><br /><br />视图状态是一个有趣的名称，用于表示在所生成页的隐藏输出字段中存储一些状态数据的 <br />ASP.NET。当该页张贴回<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>时，<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>可以分析、验证、并将此视图状态数据应用回该<br />页的控件树。视图状态是一个非常强大的功能，因为它允许状态与客户端一起保持，并且<br />它不需要 cookie 或<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>内存即可保存此状态。很多 ASP.NET <a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>控件都使用视图状<br />态来保持在与页元素进行交互期间创建的设置，例如保存对数据进行分页时显示的当前页<br />。 <br /><br />然而使用视图状态也有一些缺点。首先，服务或请求页时，它都会增加页的总负载。对张<br />贴回<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>的视图状态数据进行序列化或取消序列化时，也会发生额外的开销。最后，视<br />图状态会增加<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>上的内存分配。<br /><br />几个<a href="http://www.chinahtml.com/systems/" target="_blank">服务器</a>控件有着过度使用视图状态的趋势，即使在并不需要的情况下也要使用它，其<br />中最著名的是 DataGrid。ViewState 属性的默认行为是启用，但是如果您不需要，则可以<br />在控件或页级别关闭。在控件内，只需将 EnableViewState 属性设置为 false，或者在页<br />中使用下列设置即可对其进行全局设置： <br /><br />&lt;%@ Page EnableViewState="false" %&gt;<br /><br />如果您不回发页，或者总是针对每个请求重新生成页上的控件，则应该在页级别禁用视图<br />状态。 <br /><br /><br />我为您讲述了一些我认为在编写高性能 ASP.NET 应用程序时有所帮助的技巧。正如我在本<br />文前面部分提到的那样，这是一个初步指南，并不是 ASP.NET 性能的最后结果。（有关改<br />善 ASP.NET 应用程序性能的信息，请参阅 Improving ASP.NET Performance。）只有通过<br />自己的亲身体验才能找出解决具体性能问题的最好方法。但是，在您的旅程中，这些技巧<br />应该会为您提供一些好的指南。在软件开发中，几乎没有绝对的东西；每个应用程序都是<br />唯一的。<br /><br />from: <a href="http://www.chinahtml.com/programming/8/2006/11622676777784_2.shtml">http://www.chinahtml.com/programming/8/2006/11622676777784_2.shtml</a><br /></p>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/85980.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-12-06 23:57 <a href="http://www.blogjava.net/weidagang2046/articles/85980.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于cookie的小总结</title><link>http://www.blogjava.net/weidagang2046/articles/85658.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Tue, 05 Dec 2006 11:38:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/85658.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/85658.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/85658.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/85658.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/85658.html</trackback:ping><description><![CDATA[前两天看见有兄弟问cookie为什么删除不了,所以写了给小总结,希望对用cookie的各位兄弟有帮助<br />对于cookie,最主要的当然是读取和设置了,下面分两方面说明.<br />一、设置<br />Cookie是通过HttpServletResponse的addCookie方法加入到Set-Cookie应答头中的<br />例如：<br />Cookie userCookie = new Cookie("user", "admin"); <br />response.addCookie(userCookie);<br />和设置有关系的还有以下两个重要方法<br />1.setMaxAge<br />设置Cookie过期之前的时间，以秒计。如果不设置该值，则Cookie只在当前会话内有效，而且这些Cookie不会保存到磁盘上。<br />注意：删除cookie就是通过该方法实现的。将要删除的cookie的过期之前的时间指定为0就可以达到删除该cookie的目的。<br />2.setPath<br />设置Cookie适用的路径。如果不指定路径，Cookie将返回给当前页面(JSP页面或者Servlet的映射)所在目录及其子目录下的所有页面。<br />注意：<br />A：所有的cookie都是有路径的<br />B：该方法设置的路径为客户端路径，即“/”代表服务器根目录，而不是WEB应用根目录<br />C：该方法设置路径时，“/myWeb/”与“/myWeb”是不同的，要特别注意；前者可以关联到服务器的myWeb目录下，而或者则不可以。<br />D：该方法设置路径时，没有相对目录可言，即不论在哪个目录下设置setPath(“/myWeb/”)，该cookie都将关联到服务器的myWeb目录下(setPath(“/myWeb”)则不可以)，而不是当前目录的myWeb的子目录下；同样，设置setPath(“myWeb/”)和setPath(“myWeb”)也不能关联到当前目录的myWeb的子目录下<br />这里有个奇怪的例子，就是在一个web应用下设置的cookie可以在另一个web应用下获得（两个web应用在同一个服务器下）<br />目录结构：在服务器根目录上有web1和web2两个目录，在web1下有setcookie.jsp和getcookie.jsp、在web2下有getcookie.jsp<br />web1下的setcookie.jsp<br />&lt;%<br />Cookie userCookie = new Cookie("user", "admin"); <br />userCookie.setMaxAge(24*60*60);<br />userCookie.setPath("/web2/");<br />response.addCookie(userCookie);<br />%&gt;<br />web1下的getcookie.jsp<br />&lt;%<br />Cookie[] cookie = request.getCookies();<br />String user = new String();<br />if ( cookie != null ) {<br />for (int i = 0; i &lt; cookie.length; i++) {<br />Cookie myCookie = cookie[i];<br />if (myCookie.getName().equals("user")) {<br />user = myCookie.getValue();<br />}<br />}<br />}<br />out.println("user = " + user);<br />%&gt;<br />web2下的getcookie.jsp<br />&lt;%<br />Cookie[] cookie = request.getCookies();<br />String user = new String();<br />if ( cookie != null ) {<br />for (int i = 0; i &lt; cookie.length; i++) {<br />Cookie myCookie = cookie[i];<br />if (myCookie.getName().equals("user")) {<br />user = myCookie.getValue();<br />}<br />}<br />}<br />out.println("user = " + user);<br />%&gt;<br />先访问web1下的setcookie.jsp，然后分别访问web1和web2下面的getcookie.jsp文件，你会发现奇怪的现象，web1下的getcookie.jsp中user为空而web2下的getcookie.jsp中user却有值，这就实现了从一个web应用下设置的cookie在另一个web应用下获得。<br />大多数人删除cookie不成功都是因为目录原因。一个典型的原因是在某一个目录中设置了cookie（没有调用setPath方法）却在另一个目录中删除该cookie（其实是调用setMaxAge方法）<br /><br />二、读取<br />从客户端读取Cookie时调用的是HttpServletRequest的getCookies方法。该方法返回一个与HTTP请求头中的内容对应的Cookie对象数组。得到这个数组之后，一般是用循环访问其中的各个元素，调用getName检查各个Cookie的名字，直至找到目标Cookie。然后对这个目标Cookie调用getValue，根据获得的结果进行其他处理。<br />注意：若JSP和Servlet所在目录（Servlet为其映射目录）的父目录中有同名cookie，则request.getCookie()方法得到的Cookie数组中保存的是其父目录中的cookie的信息；<br /><br />from: <a href="http://www.cndiy8.com/data/web5409/20050311/20050311__3812885.html">http://www.cndiy8.com/data/web5409/20050311/20050311__3812885.html</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/85658.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-12-05 19:38 <a href="http://www.blogjava.net/weidagang2046/articles/85658.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一劳永逸的数据库编码解决方案</title><link>http://www.blogjava.net/weidagang2046/articles/85178.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 03 Dec 2006 05:22:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/85178.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/85178.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/85178.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/85178.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/85178.html</trackback:ping><description><![CDATA[
		<p>　　<strong>问题提出</strong></p>
		<p>　　现在几乎所有的应用系统都无法避免使用数据库系统。在JAVA世界里访问数据库是一件非常轻松的事情，JDBC为JAVA应用程序访问数据库提供了一个统一的接口，通过使用JDBC接口开发者无需关心系统最终采用哪种数据库，因为JDBC仅仅是定义了访问几个JAVA的接口类，具体的实现是由数据库厂商提供的，这种做法其实与其他数据库连接方式例如ODBC是类似的。但是在实际的应用过程中，开发者发现离JDBC设计的初衷还是有一定距离，就比如说在存储字符串时的编码问题，我想很多开发者都会遇见这个问题，倒不是因为说解决它有什么技术方面的难度，而是它的的确确非常繁琐。我们必须在每次写入或者读出字符串的时候进行编码和反编码处理；或者说我们可以写一个方法可以进行编码处理的，但又必须在每次数据库操作的时候调用，虽然调用很简单，可是我非得这样吗？要是忘了编码那又要DEBUG了。当然你可能觉得这并没有什么，或者你可能很勤快，喜欢写大量重复的代码，可是你难道没有觉得这种繁琐的工作正在浪费你过于宝贵的青春吗？停止你的键盘输入，让我们来解决这个问题吧！</p>
		<p>　　<strong>解决思路</strong></p>
		<p>　　在传统的应用程序中数据库操作部分我们可以想象成两层，如图所示：一个是数据库的"连接池"，另外一个业务数据操作层。在这里数据库的连接池是广义的，你可以把JDBC中的DriverManager也当成是连接池，具体的意思就是我们可以通过这层来获取到指定数据库的连接而不去关心它是怎么获取的。如果这个时候数据库系统（有如Informix，SQL Server）要求对字符串进行转码才能存储（例如最常见的GBK-&gt;ISO8859_1转码），那我们就必须在业务数据操作层来进行，这样有多少业务数据操作我们就要做多少编码转码的工作，太麻烦了，代码中充斥中大量重复的内容。本文提出的解决方案就是利用对获取到的数据库连接实例进行二次封装，也就是在数据库连接池与业务数据操作层之间加入了连接封装层，当然了，我们也完全可以直接将连接封装集成到数据库连接池内部。关于连接池的实现请参照我的另外一篇文章《使用JAVA动态代理实现数据库连接池》</p>
		<p align="center">
				<img height="173" src="http://www.javafan.net/uploadfiles/20041212111952100.gif" width="247" border="0" />
				<br />图一</p>
		<p>　　我们知道进行编码和转码工作都是集中在JDBC的两个接口PreparedStatement和ResultSet上进行的，主要涉及PreparedStatement的setString方法以及ResultSet的getString方法。前面我们讲过需要加入一个连接封装层来对数据库连接实例进行二次封装，但是怎么通过这个封装来改变PreparedStatement和ResultSet这两个接口的行为呢？这个问题其实也很简单，因为PreparedStatement接口必须通过Connection接口来获取实例，而ResultSet接口又必须从Statement或者PreparedStatement接口来获取实例，有了这样的级联关系，问题也就迎刃而解了。还是利用我在文章《使用JAVA动态代理实现数据库连接池》中使用的动态接口代理技术。首先我们设计Connection接口的代理类_Connection，这个代理类接管了Connection接口中所有可能获取到Statement或者PreparedStatement接口实例的方法，例如：prepareStatement和createStatement。改变这两个方法使之返回的是经过接管后的Statement或者PreparedStatement实例。通过对于Statement接口也有相应的代理类_Statement，这个代理类接管用于获取ResultSet接口实例的所有方法，包括对setString方法的接管以决定是否对字符串进行编码处理。对于接口ResultSet的接管类_ResultSet就相应的比较简单，它只需要处理getString方法即可。</p>
		<p>　　<strong>关键代码</strong></p>
		<p>　　前面我们大概介绍了这个解决方案的思路，下面我们给出关键的实现代码包括Connection的代理类，Statement的代理类，ResultSet的代理类。这些代码是在原来关于数据库连接池实现的基础上进行扩充使之增加对自动编码处理的功能。有需要源码打包的可以通过电子邮件跟我联系。</p>
		<p>_Connection.java</p>
		<p style="BACKGROUND: #eeeeee">/* <br /> * Created on 2003-10-23 by Liudong <br /> */<br />package lius.pool;<br />import java.sql.*;<br />import java.lang.reflect.*;<br /><br />/*<br /> * <br /> * 数据库连接的代理类 <br /> * @author Liudong <br /> */<br /> class _Connection implements InvocationHandler{<br /> private Connection conn = null;<br /> private boolean coding = false;<br /> //指定是否进行字符串转码操作<br /> _Connection(Connection conn, boolean coding){<br />  this.conn = conn;<br />  this.coding = coding;<br />  initConnectionParam(this.conn);<br /> <br /> }<br /> <br /> /**  <br />  * Returns the conn.  <br />  * @return Connection  <br />  */<br />  <br /> public Connection getConnection() {<br />  Class[] interfaces = conn.getClass().getInterfaces();<br />  if(interfaces==null||interfaces.length==0){<br />   interfaces = new Class[1];<br />   interfaces[0] = Connection.class;<br />  <br />  }<br /> <br />  Connection conn2 = (Connection)Proxy.newProxyInstance( conn.getClass().getClassLoader(), interfaces,this);<br />  return conn2;<br /> <br /> }<br /> <br /> /**  <br />  * @see java.lang.reflect.InvocationHandler#invoke  <br />  */<br /> public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { <br />  String method = m.getName();<br />  //调用相应的操作<br />  Object obj = null;<br />  try{<br />   obj = m.invoke(conn, args);<br />   //接管用于获取语句句柄实例的方法<br />   if((CS.equals(method)||PS.equals(method))&amp;&amp;coding) <br />    return new _Statement((Statement)obj,true).getStatement();<br />  <br />  } catch(InvocationTargetException e) {<br />   throw e.getTargetException();<br />  }<br />  return obj;<br /> }<br /> <br /> private final static String PS = "prepareStatement";<br /> private final static String CS = "createStatement";<br />}</p>
		<p>
				<br />_Statement.java</p>
		<p style="BACKGROUND: #eeeeee">/* <br /> * Created on 2003-10-23 by Liudong <br /> */<br /> <br />package lius.pool;<br />import java.sql.*;<br />import java.lang.reflect.*;<br /><br />/** <br /> * 数据库语句对象实例的代理类 <br /> * @author Liudong <br /> */<br />class _Statement implements InvocationHandler{ <br /> private Statement statement ; //保存所接管对象的实例 <br /> private boolean decode = false; //指定是否进行字符串转码 <br /><br /> public _Statement(Statement stmt,boolean decode) { <br />  this.statement = stmt;<br />  this.decode = decode;<br /> }<br /> <br /> /**  <br />  * 获取一个接管后的对象实例  <br />  * @return  <br />  */<br /> public Statement getStatement() {<br />  Class[] interfaces = statement.getClass().getInterfaces();<br />  if(interfaces==null||interfaces.length==0){ <br />   interfaces = new Class[1];<br />   interfaces[0] = Statement.class;<br />  } <br />  Statement stmt = (Statement)Proxy.newProxyInstance(   <br />   statement.getClass().getClassLoader(), <br />   interfaces,this);<br />  return stmt;<br /> <br /> }<br /> <br /> /**  <br />  * 方法接管  <br />  */<br /> public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {<br />  String method = m.getName(); //接管setString方法 <br />  if(decode &amp;&amp; SETSTRING.equals(method)) {<br />   try{<br />    String param = (String)args[1];<br />    if(param!=null)<br />     param = new String(param.getBytes(),"8859_1");<br />    return m.invoke(statement,new Object[]{args[0],param});<br />   } catch(InvocationTargetException e){<br />    throw e.getTargetException();<br />        <br />   }  <br />  }<br />  <br />  //接管executeQuery方法<br />  <br />  if(decode &amp;&amp; EXECUTEQUERY.equals(method)){<br />   try{<br />    ResultSet rs = (ResultSet)m.invoke(statement,args);<br />    return new _ResultSet(rs,decode).getResultSet();<br />   <br />   }catch(InvocationTargetException e){<br />    throw e.getTargetException();<br />    }  <br />  }<br />  <br />  try{<br />   return m.invoke(statement, args);<br />  } catch(InvocationTargetException e) {<br />   throw e.getTargetException();<br />   } <br /> }<br /> //两个要接管的方法名<br /> <br /> private final static String SETSTRING = "setString";<br /> private final static String EXECUTEQUERY = "executeQuery";<br />}</p>
		<p>
				<br />_ResultSet.java</p>
		<p style="BACKGROUND: #eeeeee">/* <br /> * Created on 2003-10-23 by Liudong <br /> */<br /> <br />package lius.pool;<br />import java.sql.ResultSet;<br />import java.lang.reflect.InvocationHandler;<br />import java.lang.reflect.InvocationTargetException;<br />import java.lang.reflect.Method;<br />import java.lang.reflect.Proxy;<br /><br />/** <br /> * 数据库结果集的代理类 <br /> * @author Liudong <br /> */<br /> class _ResultSet implements InvocationHandler{ <br /> private ResultSet rs = null;<br /> private boolean decode = false;<br /> <br /> public _ResultSet(ResultSet rs,boolean decode) {<br />  this.rs = rs;<br />  this.decode = decode;<br /> }<br /> <br /> public ResultSet getResultSet(){ <br />  Class[] interfaces = rs.getClass().getInterfaces();<br />  if(interfaces==null||interfaces.length==0){<br />   interfaces = new Class[1];<br />   interfaces[0] = ResultSet.class;  <br />  }<br /> <br />  ResultSet rs2 = (ResultSet)Proxy.newProxyInstance(rs.getClass().getClassLoader(),interfaces,this);<br />  return rs2;<br /> <br /> }<br /><br /> /**  <br />  * 结果getString方法  <br />  */<br /> public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { <br />  String method = m.getName();<br />  if(decode &amp;&amp; GETSTRING.equals(method)){<br />   try{<br />    String result = (String)m.invoke(rs,args);<br />    if(result!=null)     <br />     return new String(result.getBytes("8859_1"));<br />    return null;<br />   <br />   }catch(InvocationTargetException e){<br />    throw e.getTargetException();<br />    }<br />   <br />  } <br />  <br />  try{<br />   return m.invoke(rs, args);<br />  }catch(InvocationTargetException e){<br />   throw e.getTargetException();<br />  }<br /> }<br /> <br /> private final static String GETSTRING = "getString";<br /><br />}</p>
		<p>　　现在我们已经把三个接口的代理类做好了，下一步就是怎么来使用这三个类。其实对于使用者来讲并不需要关心三个类，只需要了解_Connection就可以了，因为另外两个是_Connection直接调用的。为了使用_Connection我们必须传入两个参数，第一个是数据库实际的数据库连接实例，另外一个是布尔值代表是否进行转码处理。我们必须先通过实际的情况获取到数据库连接后再传入_Connection的构造函数作为参数，下面例子告诉你如何来使用_Connection这个类：</p>
		<p style="BACKGROUND: #eeeeee">　　Connection conn = getConnection(); //获取数据库连接<br />　　boolean coding = false; //从配置或者其他地方读取是否进行转码的配置 <br />　　//接管数据库连接实例 <br />　　_Connection _conn = new _Connection(conn,coding);<br />　　//获得接管后的数据库连接实例，以后直接使用conn2而不是conn <br />　　Connection conn2 = _conn.getConnection();</p>
		<p>　　因为对一个应用系统来讲，数据库连接的获取必然有统一的方法，在这个方法中加入对连接的接管就可以一劳永逸的解决数据库的编码问题。</p>
		<p>　　<strong>性能比较</strong></p>
		<p>　　功能没有问题了，开发者接下来就会关心性能的问题，因为在进行一些对响应速度要求很高或者大数据量的处理情况下性能就成为一个非常突出的问题。由于JAVA中的动态接口代理采用的是反射（Reflection）机制，同时又加入我们自己的一些代码例如方法名判断，字符串转码等操作因此在性能上肯定比不上直接使用没有经过接管的数据库连接。但是这点性能上的差别是不是我们可以忍受的呢，为此我做了一个试验对二者进行了比较：</p>
		<p>　　测试环境简单描述：</p>
		<p>　　使用ACCESS数据库，建两张结构一样的表，计算从获取连接后到插入数据完毕后的时间差，两个程序（直连数据库和使用连接接管）都进行的字符串的转码操作。</p>
		<p>　　测试结果：</p>
		<table cellspacing="1" cellpadding="1" width="500" align="center" bgcolor="#999999" border="0">
				<tbody>
						<tr bgcolor="#ffffff">
								<td align="middle" height="25">插入记录数</td>
								<td align="middle">直连数据库程序耗时 单位：ms</td>
								<td align="middle">使用连接接管程序耗时</td>
								<td align="middle">性能比较</td>
						</tr>
						<tr bgcolor="#ffffff">
								<td height="25">1000</td>
								<td>2063</td>
								<td>2250</td>
								<td>9.0%</td>
						</tr>
						<tr bgcolor="#ffffff">
								<td height="25">5000</td>
								<td>8594</td>
								<td>8359</td>
								<td>-2.7%</td>
						</tr>
						<tr bgcolor="#ffffff">
								<td height="25">10000</td>
								<td>16750</td>
								<td>17219</td>
								<td>2.8%</td>
						</tr>
						<tr bgcolor="#ffffff">
								<td height="25">15000</td>
								<td>22187</td>
								<td>23000</td>
								<td>3.6%</td>
						</tr>
						<tr bgcolor="#ffffff">
								<td height="25">20000</td>
								<td>27031</td>
								<td>27813</td>
								<td>2.9%</td>
						</tr>
				</tbody>
		</table>
		<p>　　从上面这张测试结果表中来看，二者的性能的差别非常小，尽管在两万条数据的批量插入的时候时间差别也不会多于一秒钟，这样的结果应该说还是令人满意的，毕竟为了程序良好的结构有时候牺牲一点点性能还是值得的。</p>
		<p>　　本文算是我之前文章《使用JAVA动态代理实现数据库连接池》中提出的数据库连接池实现的进一步完善，同样使用动态接口代理的技术来解决数据库编码的问题。JAVA的这个高级技术可以用来解决许多实际中非常棘手的问题，就像本文提到的编码问题的处理以及数据库连接池的实现，同时在WEB开发框架的实现上也有非常大的作为。欢迎对这方面感兴趣的朋友来信共同来研究。<br /><br />from: <a href="http://www.javafan.net/article/20041212111952983.html">http://www.javafan.net/article/20041212111952983.html</a></p>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/85178.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-12-03 13:22 <a href="http://www.blogjava.net/weidagang2046/articles/85178.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中关于汉字问题原理深入谈 </title><link>http://www.blogjava.net/weidagang2046/articles/85023.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sat, 02 Dec 2006 07:29:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/85023.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/85023.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/85023.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/85023.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/85023.html</trackback:ping><description><![CDATA[
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">一、主题：关于JAVA的中文问题 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">JAVA的中文问题比较突出，主要表现在控制面板输出，JSP页面输出和数据库访问上。本文尽量避开字体问题，而只谈编码。通过本文，你可以了解JAVA中文问题的由来，问题的解决方法，其中提了一下用JDBC访问数据库的方法。 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">二、问题描述： </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">1）在中文W2000中文窗口编译和运行，用的是国际版的JDK，连接的是中文W2000下的Cp936编码的SQL SERVER数据库： </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">J:\exercise\demo\encode\HelloWorld&gt;make </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">Created by XCompiler. PhiloSoft All Rights Reserved. </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">Wed May 30 02:54:45 CST 2001 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">J:\exercise\demo\encode\HelloWorld&gt;run </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">Created by XRunner. PhiloSoft All Rights Reserved. </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">Wed May 30 02:51:33 CST 2001 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">中文 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">[B@7bc8b569 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">[B@7b08b569 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">[B@7860b569 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">中文 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">中文 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">???? </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">中文 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">中文 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">???? </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">?? </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">?? </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">?? </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">2）如果在中文W2000的西文窗口（编码为437）下编译，用JAVA运行则由于无字体而无法正常显示，如果象上面一样在中文W2000的中文窗口运行，输出为： </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">J:\exercise\demo\encode\HelloWorld&gt;run </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">Created by XRunner. PhiloSoft All Rights Reserved. </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">Wed May 30 02:51:33 CST 2001 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">???? </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">[B@7bc0b66a </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">[B@7b04b66a </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">[B@7818b66a </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">???? </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">???? </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">???? </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">???? </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">???? </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">???? </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">中文 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">中文 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">???? </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">三）分析 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">1）出现有乱码（也就是？）。由于只出现？而没出现小方框，说明只是编码有问题，而不是字体问题。 在编码中，如果从一种字符集转换到别一种字符集，比较典型的是从GB2312转换到ISO8859_1（即ASCII），那么很多汉字（半个汉字）是无法映射到西文字符中去的，在这种情形下，系统就把这些字符用？代替。同样，也存在小字符集无法到大字符集的情况，具体原因这里就不详谈了。 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">2）出现了中文环境编译，中文环境运行时汉字显示有正确也有不正确的地方，同样，在西文环境下编译，在中文环境下运行时也出现类似情况。这是由于自动（默认）或手工（也就new String(bytes[,encode])和bytes getBytes([encode])）转码的结果。 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">2.1）在JAVA源文件--&gt;JAVAC--&gt;Class--&gt;Java--&gt;getBytes()--&gt;new String()--&gt;显示的过程中，每一步都有编码的转换过程，这个过程总是存在的，只是有的时候用默认的参数进行。下面我们一步一步分析为什么出现上面的情形。 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">2.2）这里是源代码： </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">HelloWorld.java: </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">------------------------ </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">public class HelloWorld </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">{ </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">public static void main(String[] argv){ </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">try{ </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">System.out.println("中文");//1 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">System.out.println("中文".getBytes());//2 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">System.out.println("中文".getBytes("GB2312"));//3 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">System.out.println("中文".getBytes("ISO8859_1"));//4 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">System.out.println(new String("中文".getBytes()));//5 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">System.out.println(new String("中文".getBytes(),"GB2312"));//6 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">System.out.println(new String("中文".getBytes(),"ISO8859_1"));//7 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">System.out.println(new String("中文".getBytes("GB2312")));//8 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">System.out.println(new String("中文".getBytes("GB2312"),"GB2312"));//9 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">System.out.println(new </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">String("中文".getBytes("GB2312"),"ISO8859_1"));//10 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">System.out.println(new String("中文".getBytes("ISO8859_1")));//11 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">System.out.println(new </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">String("中文".getBytes("ISO8859_1"),"GB2312"));//12 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">System.out.println(new </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">String("中文".getBytes("ISO8859_1"),"ISO8859_1"));//13 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">} </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">catch(Exception e){ </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">e.printStackTrace(); </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">} </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">} </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">} </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">为了方便起见，在每个转换的后面加了操作序号，分别为1,2,...,13。 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">2.3）需要说明的是，JAVAC是以系统默认编码读入源文件，然后按UNICODE进行编码的。在JAVA运行的时候，JAVA也是采用UNICODE编码的，并且默认输入和输出的都是操作系统的默认编码，也就是说在new String(bytes[,encode])中，系统认为输入的是编码为encode的字节流，换句话说，如果按encode来翻译bytes才能得到正确的结果，这个结果最后要在JAVA中保存，它还是要从这个encode转换成Unicode，也就是说有bytes--&gt;encode字符--&gt;Unicode字符的转换；而在String.getBytes([encode])中，系统要做一个Unicode字符--&gt;encode字符--&gt;bytes的转换。 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">在这个例子中，除那个英文窗口编码的时候除外，其实情形下默认编码都是GBK（在本例中，我们暂且把GBK和GB2312等同看待）。 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">2.4）由于在未指明在上面的两个用代码实现的转换中，如果未指定encode，系统将采用默认的编码（这里为GBK），我们认为上面的5,6,7和8,9,10是一样的，8和9、11和12也是一样的，所以我们在讨论中将只讨论1,9,10,12,13。其中的2,3,4只是用于测试，不在我们的讨论范围之内。 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">2.5）下面我们来跟踪程序中的“中”字的转换历程，我们先说在中文窗口下作的编译和运行过程，注意在下面的字母下标中，我有意识地使用了一些数字，以表示相同，相异还是相关2.5.1)我们先以上面的13个代码段中的的代码9为例： </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">步骤 内容 地点 说明 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">01： C1 HelloWorld.java C1泛指一个GBK字符 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">02： U1 JAVAC读取 U1泛指一个Unicode字符 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">03： C1 getBytes()第一步 JAVA先和操作系统交流 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">04： B1,B2 getBytes()第二步 然后返回字节数组 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">05： C1 new String()第一步 JAVA先和操作系统交流 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">06： U1 new String()第二步 然后返回字符 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">07： C1 println(String) 能显示“中”字，内容和原来的相同 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">2.5.2）然后再以代码段10为例，我们注意到只是： </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">步骤 内容 地点 说明 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">01： C1 HelloWorld.java C1泛指一个GBK字符 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">02： U1 JAVAC读取 U1泛指一个Unicode字符 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">03： C1 getBytes()第一步 JAVA先和操作系统交流 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">04： B1,B2 getBytes()第二步 然后返回字节数组 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">05： C3,C4 new String()第一步 JAVA先和操作系统交流，这时解析错误 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">06： U5,U6 new String()第二步 然后返回字符 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">07： C3,C4 println(String) 由于中字给分成了两半，在ISO8859_1中刚好也没有字符 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">能映射上，所以显示为“??”。在上面的示例中， </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">“中文”两个字就显示为“？？？？” </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">2.5.3）在完全中文模式下的其它情形类似，我就不多说了 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">2.6）我们接着看为什么在西文DOS窗口下编译出来的类在中文窗口下也出现类似情形，特别是为什么居然有的情形下还能正确显示汉字。 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">2.6.1）我们还是先以代码段9为例： </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">步骤 内容 地点 说明 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">01： C1C2 HelloWorld.java C1C2分别泛指一个ISO8859_1字符，“中”字被拆开 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">02： U3U4 JAVAC读取 U1U2泛指一个Unicode字符 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">03： C5C6 getBytes()第一步 JAVA先和操作系统交流，这时解析错误 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">04： B5B6B7B8 getBytes()第二步 然后返回字节数组 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">05： C5C6 new String()第一步 JAVA先和操作系统交流 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">06： U3U4 new String()第二步 然后返回字符 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">07： C5C6 println(String) 虽然同是两个字符，但已不是最初的“两个ISO8859_1字 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">符”，而是“两个BGK字符”，“中”显示成了“？？” </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">而“中文”就显示成了“？？？？” </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">2.6.2）下面我们以代码段12为例，因为它能正确显示汉字 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">步骤 内容 地点 说明 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">01： C1C2 HelloWorld.java C1C2分别泛指一个ISO8859_1字符，“中”字被拆开 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">02： U3U4 JAVAC读取 U1U2泛指一个Unicode字符 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">03： C1C2 getBytes()第一步 JAVA先和操作系统交流（注意还是正确的哦！） </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">04： B5B6 getBytes()第二步 然后返回字节数组（这是很关键的一步！） </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">05： C12 new String()第一步 JAVA先和操作系统交流（这是更关键的一步，JAVA已经知道B5B6要解析成一个汉字！） </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">06： U7 new String()第二步 然后返回字符（真是一个项两！U7包含了U3U4的信息） </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">07： C12 println(String) 这就原来的“中”字，很委屈被JAVAC冤枉了一回，不过被程序员拨乱反正了一下！当然，“中文”两个字都能正确显示了！ </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">3）那为什么有的时候用JDBC的 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">new String(Recordset.getBytes(int)[,encode]) </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">Recordset.getSting(int) </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">Recordset.setBytes(String.getBytes([encode])) </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">和 </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">Recordset.setString(String) </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">的时候会出现乱码了呢？ </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">
				</font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font face="Courier New" size="2">其实问题就出现在编写JDBC的的也考虑了编码问题，它从数据库读取数据后，可能自作主张做了一个从GB2312（默认编码）到Unicode的转换，我的这个WebLogic For SQL Server的JDBC Driver就是这样的，当我读字串的时候，发出读到的不是正确的汉字，可恨的是我却可以直接写汉字字串，这让人多少有点难以接受！ </font>
		</p>
		<p style="TEXT-INDENT: 2em">
				<font size="2">
						<font face="Courier New">也就是说，我们不得不在读或写的时候进行转码，尽管这个转码有的时候不是那么明显，这是因为我们使用了默认的编码进行转码。JDBC Driver所做的操作，我们只有进入到源代码内部才能清楚，不是吗？<br /><br />from: </font>
						<a href="http://java.ccidnet.com/art/3737/20060605/571079_1.html">
								<font face="Courier New">http://java.ccidnet.com/art/3737/20060605/571079_1.html</font>
						</a>
				</font>
		</p>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/85023.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-12-02 15:29 <a href="http://www.blogjava.net/weidagang2046/articles/85023.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>URL中文参数传递问题</title><link>http://www.blogjava.net/weidagang2046/articles/84943.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Fri, 01 Dec 2006 12:43:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/84943.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/84943.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/84943.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/84943.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/84943.html</trackback:ping><description><![CDATA[
		<p>(1)确定JSP页面头部是否有:&lt;%@ page contentType="text/html; charset=GBK" %&gt;<br />(2)用这个转码:<br />     String param= new String(request.getParameter("param").getBytes("ISO-8859-1"), "GBK");<br />(3)添加filter字符过滤器</p>
		<p>(4)如果是通过"a.jsp?param=中文"传递参数,则需要:<br />     a.在传参数之前先把参数进行转码:java.net.URLEncoder.encode(param);<br />       取值用java.net.URLDncoder.decode(param);再转回中文<br />     b.在你的Tomcat目录--&gt;conf目录--&gt;server.xml里找出这段:<br />       &lt;Connector <br />         port="8080"               maxThreads="150" minSpareThreads="25" maxSpareThreads="75"<br />               enableLookups="false" redirectPort="8443" acceptCount="100"<br />               debug="0" connectionTimeout="20000" <br />               disableUploadTimeout="true" &lt;!--在里边加上这个参数--&gt;URIEncoding="gb2312"/&gt; <br /><br />from: <a href="http://www.yuanma.org/data/2006/0911/article_1503.htm">http://www.yuanma.org/data/2006/0911/article_1503.htm</a></p>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/84943.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-12-01 20:43 <a href="http://www.blogjava.net/weidagang2046/articles/84943.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>url传递中文的解决方案总结 </title><link>http://www.blogjava.net/weidagang2046/articles/84941.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Fri, 01 Dec 2006 12:40:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/84941.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/84941.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/84941.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/84941.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/84941.html</trackback:ping><description><![CDATA[1.设置web.config文件。<br />&lt;system.web&gt; <br />...... <br />&lt;globalization requestEncoding="gb2312" responseEncoding="gb2312" culture="zh-CN" fileEncoding="gb2312" /&gt; <br />...... <br />&lt;/system.web&gt; <br /><br />或者：<br />aspx文件中:<br />&lt;meta http-equiv="Content-Type" content="text/html; charset=gb2312"&gt;<br /><br />2.传递中文之前，将要传递的中文参数进行编码，在接收时再进行解码。 <br />&gt;&gt; 进行传递 <br />string Name = "中文参数"; <br />Response.Redirect("B.aspx?Name="+Server.UrlEncode(Name)); <br /><br />&gt;&gt; 进行接收 <br />string Name = Request.QueryString["Name"]; <br />Response.Write(Server.UrlDecode(Name)); <br />或者：<br /><br />NavigateURL='&lt;%# "WebForm2.aspx?Singer=" + HttpUtility.UrlEncode("中国人", System.Text.Encoding.GetEncoding("GB2312")) %&gt;'<br /><br /><br />3.如果是从 .HTML 文件向 .Aspx 文件进行传递中文参数的话（即不从后台用 Redirect()方法进行 Url 转换）。一样要将传递的中文参数进行编码，在接收时再进行解码。 <br />&gt;&gt; 进行传递 <br />&lt;script language="JavaScript"&gt; <br />function GoUrl() <br />{ <br />var Name = "中文参数"; <br />location.href = "B.aspx?Name="+escape(Name); <br />} <br />&lt;/script&gt; <br />&lt;body onclick="GoUrl()"&gt; <br />&gt;&gt; 进行接收 <br />string Name = Request.QueryString["Name"]; <br />Response.Write(Server.UrlDecode(Name)); <br /><br />一般来说。设置web.config文件就可以了。但是如果你用 JavaScript 调用 webservice 方法的话（往webservice里面传递中文参数）。设置 web.config 文件好象无效。 <br /><br />————————————————————<br />在html中实现编解码：<br /><br />&lt;script language="javascript"&gt;<br />function openUrl(src)<br />{<br />var strUrl=escape(src);<br />window.open(strUrl);<br />}<br /><br /><br />function change_url(src)<br />{<br />document.location.href=escape(src);<br />}<br /><br />&lt;/script&gt; <br /><br />在新窗口保存<br />&lt;a href='javascript:openUrl("css/20040603123628交易中心烟叶网上集中交易系统合同.doc");' &gt;20040603123628交易中心网上集中交易系统合同&lt;/a&gt;<br /><br /><br />当前位置保存，无闪烁。<br />&lt;a href="#" onclick=javascript:change_url("css/20040603123628交易中心烟叶网上集中交易系统合同.doc")&gt;20040603123628交易中心网上集中交易系统合同&lt;/a&gt;<br /><br />注意：路径中的斜线是：“/”，而不是“\”，否则也不行啊。<br /><br /><br /><br /><br />－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－<br />前一阵遇到在做.net Web开发时，碰到一个很奇怪的问题，就是Url中，如果将中文字符作为参数值传递时，QueryString得到的值可能会出错。简单的说，比如下面这个Url：<br />UrlParmTest.aspx?parm1=中国&amp;parm2=中国人<br />在Request.QueryString时，parm1和parm2得到都是"中国"，显然出现了问题，可是在某些情况下却是正常的。<br /><br />如果请求不是直接通过URL，而使用Response.Redirect在服务器端操作，没有遇到过类似的问题。<br /><br />当时我想中文是双字节编码，可能传递的时候就是有不确定性，还是用英文好。<br /><br />可是为什么在Server端Redirect就是正常的，问题在哪里呢？<br /><br />：<br />如果在.cs文件中设置中文参数，请在中文参数外使用Server.UrlEncode("中文")对中文进行Encode<br />如果在.aspx文件中设置，请使用&lt;%=Server.UrlEncode("中文")%&gt;进行Encode<br />在QueryString时，不用再进行Decode，可以获得正常的中文字符串<br /><br />下面是给出的一些解释：<br />UrlEncode把一些多字节字符转换成url里允许的单字节字符，本来浏览器就会自动做的，但是目前确实存在一些问题，所以自己再Encode一下，在接受端会自动对Url进行Decode。<br /><br />我想Response.Redirect可能可以确保作Encode的工作，所以没有问题。<br /><br />from: <a href="http://blog.iecn.net/article/html/tid-870.html">http://blog.iecn.net/article/html/tid-870.html</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/84941.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-12-01 20:40 <a href="http://www.blogjava.net/weidagang2046/articles/84941.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>解决Tomcat 5.0.19中文参数传递问题</title><link>http://www.blogjava.net/weidagang2046/articles/84939.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Fri, 01 Dec 2006 12:38:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/84939.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/84939.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/84939.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/84939.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/84939.html</trackback:ping><description><![CDATA[
		<span class="javascript" id="text44042">實際運用 Tomcat 5.0.19，我們了解在不修改 Tomcat 原始碼的狀況下，使用者透過 Form submit 的資料將一律以 ISO8859-1 處理，程式設計師必須自行將字串將轉換為 Big5(繁體中文) or GB2312/GBK(簡體中文)，我們在應用程式中，對所有的 request.getParameter("xx"); 作了 toBig5String() 的處理，理論上，所有的中文問題應該不會出現才對，結果，還是發現某些狀況下，中文還是變成亂碼！ <br /><br />經過分析整理，我們發現問題出在 QueryString 的解析，以前在 Tomcat 4.x 時代，無論 SUBMIT 時採用 GET or POST，Tomcat server 對 parameters 的處理都採用相同的編碼，但在 Tomcat 5.x 版，不知何故，卻將 QueryString 的解析獨立出來，目前確認，Form 的 Method 採用 GET 及直接將參數寫在 URL 上的中文，上傳到 Tomcat 時，無論如何轉碼，都會變成亂碼，那怕你事先作過 URLEncode 也一樣。 <br /><br />網站上，有人針對這個問題，建議將所有中文改採用 base64 編碼，到了 server 上，程式將自行土 base64 decode 回來，確保中文不會發生問題。這樣作法當然可以解決這個問題，但是所有網頁變成限定要採用 POST，且程式設計師要隨時分清楚，那個參數是採用 GET 上傳，那個參數是採用 POST 上傳，然後再針對不同的方式採用不同的解析，這樣的程式一點兒移植性都沒有，更別提跨平台、跨國際語言了。 <br /><br />研究 Tomcat 的文件及原始碼，我們找到了問題所在及解決的方法，只有按著以下的作法，才能使 Form submit 的資料完全按著 ISO8859-1 的編碼，當然，若是全照著 Tomcat 的文件說明去作，肯定還是不行，你還是得加上這個參數到 server.xml 中才行。 <br /><br />解決方案 <br /><br />請先研究 $TOMCAT_HOME/webapps/tomcat-docs/config/http.html 這個說明檔，擷錄重點如下： <br /><b>URIEncoding</b>：This specifies the character encoding used to decode the URI bytes, after %xx decoding the URL. If not specified, ISO-8859-1 will be used. <br /><br /><b>useBodyEncodingForURI</b>：This specifies if the encoding specified in contentType should be used for URI query parameters, instead of using the URIEncoding. This setting is present for compatibility with Tomcat 4.1.x, where the encoding specified in the contentType, or explicitely set using Request.setCharacterEncoding method was also used for the parameters from the URL. The default value is false. <br /><br />上述二個 Tomcat 參數，是設定在 server.xml 中的 http &lt;Connector /&gt; 區塊，要解決 QueryString 中文變成亂碼的問題，你必須至少設定這二個參數其中之一。 <br />URIEncoding 請設定為 URIEncoding="ISO-8859-1" 指定為 "ISO-8859-1" 編碼，讓 QueryString 的字元編碼與 post body 相同。 <br />useBodyEncodingForURI 這是用來相容 Tomcat 4.x 版的，設定的值是 "true" or "false"，意思是指 "要不要讓 QueryString 與 POST BODY 採用相同的字元編碼 ?"，若是設成 true，那也可達到 "ISO-8859-1" 編碼的需求。 <br />建議，採用 URIEncoding 的設定，畢竟 useBodyEncodingForURI 的作法是為了相容 Tomcat 4.X。不過若照原文的說明，理論上這二個參數都不設，Tomcat 也該採用 "ISO-8859-1" 的編碼，那為什麼還是會有問題呢 ? 我們由 Tomcat Source Code 來看就清楚了。 <br /><table class="java" cellspacing="1" cellpadding="3" bgcolor="#999999" border="0"><tbody><tr><td valign="top" align="left" width="1" bgcolor="#dddddd"><pre><font color="#555555">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br />16<br />17<br />18<br />19<br />20<br />21<br />22<br />23<br />24<br />25<br />26<br />27<br />28<br />29<br /></font></pre></td><td valign="top" align="left" bgcolor="#ffffff"><pre><font class="java-comment">// 這一段碼是 Tomcat 用來解 QueryString 的程式，</font><font class="java-comment">// 在 org.apache.tomcat.util.http.Parameters 這個 class 裡。</font><font class="java-reserved_word"><b>private</b></font> String urlDecode(ByteChunk bc, String enc)
  <font class="java-reserved_word"><b>throws</b></font> IOException <font class="java-bracket">{</font><font class="java-reserved_word"><b>if</b></font>( urlDec==<font class="java-reserved_word"><b>null</b></font> ) <font class="java-bracket">{</font>
     urlDec=<font class="java-reserved_word"><b>new</b></font> UDecoder(); 
  <font class="java-bracket">}</font>
  urlDec.convert(bc);
  String result = <font class="java-reserved_word"><b>null</b></font>;
  <font class="java-reserved_word"><b>if</b></font> (enc != <font class="java-reserved_word"><b>null</b></font>) <font class="java-bracket">{</font>
    bc.setEncoding(enc);
    result = bc.toString();
  <font class="java-bracket">}</font><font class="java-reserved_word"><b>else</b></font><font class="java-bracket">{</font>
    CharChunk cc = tmpNameC;
    cc.allocate(bc.getLength(), -1);
    <font class="java-comment">// Default encoding: fast conversion</font><font class="java-reserved_word"><b>byte</b></font>[] bbuf = bc.getBuffer();
    <font class="java-reserved_word"><b>char</b></font>[] cbuf = cc.getBuffer();
    <font class="java-reserved_word"><b>int</b></font> start = bc.getStart();
    <font class="java-reserved_word"><b>for</b></font> (<font class="java-reserved_word"><b>int</b></font> i = 0; i &lt; bc.getLength(); i++) <font class="java-bracket">{</font>
      cbuf[i] = (<font class="java-reserved_word"><b>char</b></font>) (bbuf[i + start] &amp; 0xff);
    <font class="java-bracket">}</font>
    cc.setChars(cbuf, 0, bc.getLength());
    result = cc.toString();
    cc.recycle();
  <font class="java-bracket">}</font><font class="java-reserved_word"><b>return</b></font> result;
<font class="java-bracket">}</font></pre></td></tr></tbody></table><br />請特別注意紅色區塊，當 Tomcat 發現 QueryString 並沒有設定 encode 時，並非像文件中所說預設採用 ISO-8859-1 的編碼，而是用一段 fast conversion 來處理，才會造成中文問題，所以，還是必須在 Server.xml 中，加上 URLEncoding 的參數設定才行哦。 <br /><br />Connector 的設定範例： <br /><table class="java" cellspacing="1" cellpadding="3" bgcolor="#999999" border="0"><tbody><tr><td valign="top" align="left" width="1" bgcolor="#dddddd"><pre><font color="#555555">1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br />12<br />13<br />14<br />15<br /></font></pre></td><td valign="top" align="left" bgcolor="#ffffff"><pre>&lt;Connector
debug=<font class="java-string">"0"</font>
acceptCount=<font class="java-string">"100"</font>
connectionTimeout=<font class="java-string">"20000"</font>
disableUploadTimeout=<font class="java-string">"true"</font>
port=<font class="java-string">"80"</font>
redirectPort=<font class="java-string">"8443"</font>
enableLookups=<font class="java-string">"false"</font>
minSpareThreads=<font class="java-string">"25"</font>
maxSpareThreads=<font class="java-string">"75"</font>
maxThreads=<font class="java-string">"150"</font>
maxPostSize=<font class="java-string">"0"</font>
URIEncoding=<font class="java-string">"ISO-8859-1"</font>
&gt;
&lt;/Connector&gt;
</pre></td></tr></tbody></table></span>
		<br />from: <a href="http://www.javaworld.com.tw/jute/post/view?bid=9&amp;id=44042&amp;sty=1&amp;tpg=1&amp;age=0">http://www.javaworld.com.tw/jute/post/view?bid=9&amp;id=44042&amp;sty=1&amp;tpg=1&amp;age=0</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/84939.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-12-01 20:38 <a href="http://www.blogjava.net/weidagang2046/articles/84939.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JDBC性能优化技巧</title><link>http://www.blogjava.net/weidagang2046/articles/84051.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Tue, 28 Nov 2006 06:59:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/84051.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/84051.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/84051.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/84051.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/84051.html</trackback:ping><description><![CDATA[ 
<p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">如果可能，避免访问数据库</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings; mso-bidi-font-weight: bold">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">为应用选择最好最快的 JDBC </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">驱动 ,参考<a href="http://www.flyjava.com/performance/index.htm"><font color="#0000ff">本站文章</font></a> 。 JDBC3.0</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">提供了新的特性来提高性能，诸如连接池， statemente池的改进</span><b><span lang="EN-US"><o:p>   </o:p></span></b></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings; mso-bidi-font-weight: bold">l<span style="FONT: 7pt 'Times New Roman'">        </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对数据库使用连接池并且重用连接，而不要重复打开和关闭连接。最佳的连接池大小是当连接池大到足够使服务请求不等待</span><b><span lang="EN-US"><o:p></o:p></span></b></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">尽量使用支持 JDBC3.0 的驱动，因为 JDBC3.0 支持包括 DataSource 对象，连接池，分布式事务支持， RowSets 和 prepared statement 池等性能增强特性</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">Prepared statement 池（自从 JDBC3.0 开始有）高速缓存已经预先优化并运行了的 SQL 查询，这样，他们被再次请求的时候，不必经历再次的优化预处理（避免最优化步骤，诸如检查语法，验证地址，优化访问路径和执行计划）。 Statement 池是一个很好的，重要的性能优化方法</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-list: l172 level1 lfo14; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">JDBC3.0 中的 Statement 池和连接池能合作共享 statement 池，这样，能使用一个已高速缓存的 statement （该 statement 来自另外一个连接）的连接，在由任一连接执行的 一些SQL 首次被执行时，产生的 statement 准备开销仅一次</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">RowSet对象与 ResultSet 对象相似，但是能提供当断开连接的时候对数据库数据的访问。这允许数据以它最简单的形式被高效的高速缓存</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-list: l172 level1 lfo14; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">用同一个连接执行多个 statements</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">关闭 autocommit ，但不要让事务打开太久</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">避免将事务分布开（事务跨越多个连接）</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">最小化数据库的行和列数据获取。使用 setMaxRows, setMaxFieldSize,</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">和 SetFetchSize</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings; mso-bidi-font-weight: bold">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用最高效的数据类型：字符串比整数型快，整数型比浮点类型和时间戳类型都要高效（是否不太理解^&amp;^，这是针对DB2数据库处理来说的，处理character类型最快，而处理integer类型通常需要一些转换或者字节排序）</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用 updateXXX()</span>方法<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">更新： updateXXX() 在可更新的结果集上调用。结果集已经定位到了一行 , 因此当使用一个 UPDATE statement 时，可以消除通常的查找要更新的数据行的开销</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">Cache任何请求的元数据（ metadata ）并尽可能少的使用元数据 方法，其慢的程度一用便知</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">避免在元数据 查询中使用 null 参数</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用虚拟查询获得一行的元数据，不要使用getcolumns()（假如应用允许用户使用列数据，应用是使用getColumns来返回列的信息给用户还是准备一个虚拟查询而后调用getMetadata呢？</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用存储过程，避免多余的网络传输</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在存储过程中使用参量，不要将数据挨个地放在statement中，最小化解析开销。此条针对DB2来说，其它数据库未必适用。SQL总是以字符串形式发送给DB2数据库，例如：</span><br /><font color="#ff0000">CallableStatement cstmt = conn.prepareCall ("call getCustName (12345)");<br />ResultSet rs = cstmt.executeQuery ();</font><br />DB2服务器必须解析该SQL，验证参量类型，并将参量转化为正确的数据类型。</p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对需要重复执行的statement使用预处理statement（PreparedStatement）</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l159 level1 lfo12; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">选择使用最佳游标：对连续读取使用游标；对双向滚动使用游标。对仅返回一行的查询避免使用游标。</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在JVM中Cache频繁请求的数据，避免不必要的数据库请求</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">采用预读取机制， 批量取行，而不要一次一行 。调整批大小和预取行的数量。避免使用预取 BLOB 数据。</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">除非绝对需要，否则避免移动数据</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在数据穿过网络之前要使流化数据（ Streamline data ）</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">避免每次处理一行，尽可能一起处理多行。</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在表中统计个数（例如：使用 select count(*) from myTable,yourTable where …</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）属于资源密集型的。试试首先选入临时表，仅返回该计数（count），然后发送精确的二次查询获得临时表中的行的子集。</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">恰</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">当的使用 SQL 能减少资源请求。使用返回所需数据的最小值的查询：避免 select * 查询。一个返回小的数据子集的复杂查询，比一个简单的，返回超过所需的大量数据的简单查询更高效。</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使你的查询尽可能精巧，例如：尽可能精确地最小化要传输的数据，使其是所需的子集</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">努力批量更新：将 statement 收集到一起，然后在一个事务里面一起执行。如果可能，使用有条件的逻辑和临时变量来达到 statement 批处理</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">永远不要让 DBMS 事务跨越用户输入</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">考虑使用乐观锁。乐观锁使用时间戳验证数据是否还没有被其他用户改变，否则事务失败</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用 恰当的更新，例如：更新行</span>/<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表中已经存在的数据，而不要添加或者删除行</span>/<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">表。在适当的位置更新数据要比移动数据快得多，如果更新需要的空间比表设计能提供的更多，这可能是需要的。如果你设计的行需要空间初始化，更新将会更快。交易是你的表可能需要更多的磁盘空间，但可能速度更快。由于磁盘空间是便宜的，使用一点点能提高性能</span>，这应该说是非常有价值的投资</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">分开存储正在操作的数据和历史数据（更一般的情况是将频繁使用的数据和不常使用的数据分开存储）</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">尽可能小的保留你的操作数据集，避免必须读那些不相关的数据</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">DBMS可以很好的并行运转，尽量将应用设计成当和 DBMS交互时应用能做其他事情。</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使用流水线操作和并行操作。 将应用设计成支持大量并行进程， 使应用运行更快。如果要处理多步，努力设计好应用，以使后来的步骤能够在任何优先的进程已经完成的数据部分上开始工作，而不是必须等到优先进程完成</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">      </span></span> <span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">事物的保护级别越高，性能损失就越大。事物级别按增长的顺序为： TRANSACTION_NONE, TRANSACTION_READ_UNCOMMITTED, TRANSACTION_READ_COMMITTED, TRANSACTION_REPEATABLE_READ, TRANSACTION_SERIALIZABLE。使用Connection.setTransactionIsolation() 设置你想要的事物级别</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">      </span></span><span lang="EN-US" style="FONT-WEIGHT: normal; FONT-SIZE: 7pt; FONT-STYLE: normal; FONT-FAMILY: Wingdings; FONT-VARIANT: normal"></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">默认的自动提交模式由于使每一个数据库命令都成为一个单独的事务，这会严重影响性能，关闭自动提交（Connection.setAutoCommit(false) ），明确声明事务</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">通过整合多个事务为一个的批量操作，并在一个statement中使用Statement.addBatch() 和Statement.executeBatch()</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">     </span></span><span style="FONT: 7pt 'Times New Roman'"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman"> Savepoints (from JDBC3.0)需要昂贵的资源。一旦不再需要，就立刻使用Connection.releaseSavepoint()释放掉Savepoints</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">ConnectionPoolDataSource (from JDBC3.0)和PooledConnection接口为连接池提供了built-in支持</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT: 7pt 'Times New Roman'">  </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">使用setLogWriter() (from Driver, DataSource, or ConnectionPooledDataSource; from JDBC3.0) 帮助跟踪JDBC流</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT: 7pt 'Times New Roman'">  </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">使用Connection.setReadOnly(true)优化只读数据库（操作）交互</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span lang="EN-US"></span><span style="FONT: 7pt 'Times New Roman'">  <span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"></span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">使用Connection.nativeSQL()察看SQL查询如何在数据库种执行，帮助确保SQL已被优化</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT: 7pt 'Times New Roman'"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">切记：一旦可能，立刻关闭Statement和ResultSet</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT: 7pt 'Times New Roman'"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">使用DatabaseMetaData获得数据库功能性信息</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT: 7pt 'Times New Roman'"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">一直捕捉和处理数据库警告和异常</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT: 7pt 'Times New Roman'"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">使用最恰当的数据类型明确数据的类型，例如：以date类型存储日期，儿不要用varchar</span></p><p class="MsoNormal" style="MARGIN-LEFT: 21pt; TEXT-INDENT: -21pt; mso-margin-top-alt: auto; mso-margin-bottom-alt: auto; mso-list: l77 level1 lfo11; tab-stops: list 21.0pt"><span lang="EN-US" style="FONT-FAMILY: Wingdings">l<span style="FONT: 7pt 'Times New Roman'">         </span></span><span style="FONT: 7pt 'Times New Roman'"> </span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: Times New Roman; mso-hansi-font-family: Times New Roman">使用可滚动ResultSet (JDBC 2.0)<br /><br />from: <a href="http://www.ijsp.net/2/2003-9/20/0000431.shtml">http://www.ijsp.net/2/2003-9/20/0000431.shtml</a></span></p><img src ="http://www.blogjava.net/weidagang2046/aggbug/84051.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-11-28 14:59 <a href="http://www.blogjava.net/weidagang2046/articles/84051.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>放弃 ORM 改用 SqlMap 的 N 个理由</title><link>http://www.blogjava.net/weidagang2046/articles/83620.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Sun, 26 Nov 2006 08:02:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/83620.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/83620.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/83620.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/83620.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/83620.html</trackback:ping><description><![CDATA[
		<p>1. 在项目中经常碰到的数据库分页查询, ORM 一般都支持的不好, 如果用 ORM, 这部分程序往往要自己扩展, 而 SqlMap 对各种查询语句不分彼此, 一概在 Map 文件里定义。</p>
		<p>2. 对一些稍微复杂些的语句, 例如在对金额等敏感数据操作时, 一个常用的操作序列是:<br />a. 先取出当前金额<br />b. 运算后得到更新的金额<br />c. 执行 Update 语句: Update &lt; tableName &gt; set amount= &lt; New amount &gt; where amount= &lt; Old amount &gt; <br />这种操作是 ORM 不能支持的, SqlMap 能很好的支持。</p>
		<p>3. SqlMap 的 Domain 对象可以直接放在业务层, 一般 ORM 的对数据访问的基类要放在数据访问层(因为带有对数据访问的接口, 放在业务层不合适), 增加了代码的冗余度。</p>
		<p>4. 用 ORM 的目的是什么, 最主要的目的是减少重复的底层编程工作量, SqlMap 完全可以做到。</p>
		<p>再说说 SqlMap 的不足:</p>
		<p>1. 因为不象 ORM 那样生成稳定可靠的对数据访问的基类, 所以要对 Map 操作做好充足的单元测试, 增加了测试的工作量。</p>
		<p>2. 每次改动数据库, Map 和 Domain 文件往往要手工修改, 因为 SqlMap 的灵活性, 往往我们会手工调整 Map 而不会直接使用 Generator 生成的代码。<br /><br />from: <a href="http://matrix.foresee.cn/blogs/simon/archives/001638.html">http://matrix.foresee.cn/blogs/simon/archives/001638.html</a></p>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/83620.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-11-26 16:02 <a href="http://www.blogjava.net/weidagang2046/articles/83620.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用JSP操作Cookie</title><link>http://www.blogjava.net/weidagang2046/articles/82349.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Mon, 20 Nov 2006 10:05:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/82349.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/82349.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/82349.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/82349.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/82349.html</trackback:ping><description><![CDATA[Cookie应该是一种应用较久的技术了。早在HTML刚刚出现的时候，在每个独立的页面之间没有办法记录和标识不同的用户。后来人们就发明了Cookie技术，当用户访问网页时，它能够在访问者的机器上创立一个文件，我们把它叫作Cookie，写一段内容进去，来标识不同的用户。如果下次用户再访问这个网页的时候，它又能够读出这个文件里面的内容，这样网页就知道上次这个用户已经访问过该网页了。 <br /><br />虽然现在网页的制作技术比起几年以前已经发展了许多。不过有些时候，Cookie还是能够帮我们很多忙的。接下来，我们就来看看，如何在写JSP文件的时候，用JSP操作Cookie。 <br /><br />二、 写入Cookie <br /><br />其实用JSP操作Cookie是非常简单的，我们来看下面一段JSP程序： <br /><br />........(中间略) <br /><br /><br />＜% <br />String cookieName="Sender"; <br />Cookie cookie=new Cookie(cookieName, "Test_Content"); <br />cookie.setMaxAge(10); <br />response.addCookie(cookie); <br />%＞ <br />........(其他内容) <br /><br /><br />这样我们就设置了一个Cookie，很简单吧？ <br /><br />我们来仔细研究一下这段代码： <br /><br />　　Cookie cookie=new Cookie(cookieName, "Test_Content"); <br /><br />这一行建立了一个Cookie对象，初始化有两个参数，第一个参数cookieName定义了Cookie的名字，后一个参数，也是一个字符串，定义了Cookie的内容。也就是我们希望网页在用户的机器上标识的文件内容。 <br /><br />接下来一行：cookie.setMaxAge(10)，调用了Cookie中的setMaxAge方法，设定Cookie在用户机器硬盘上的存活期为10秒。一个Cookie在用户的硬盘里面存在的时间并不是无限期的，在建立Cookie对象的时候，我们必须制定Cookie的存活期，超过了这个存活期后，Cookie文件就不再起作用，会被用户的浏览器自行删除。如果我们希望用户在下次访问这个页面的时候，Cookie文件仍然有效而且可以被网页读出来的话，我们可以将Cookie的存活期设得稍微长一些。比如cookie.setMaxAge(365*24*60*60)可以让Cookie文件在一年内有效。 <br /><br />三、 读出Cookie <br /><br />Cookie文件创建好后，自然还需要我们把它读出来，否则我们不是白费力气吗？接下来我们看看如何读出在用户硬盘上的Cookie。 <br /><br />........(中间略) <br /><br />Name value <br /><br />＜% <br />Cookie cookies[]=request.getCookies(); <br />Cookie sCookie=null; <br />String svalue=null; <br />String sname=null; <br />for(int i=0;i{ <br />sCookie=cookies[i]; <br />svalue=sCookie.getValue(); <br />sname=sCookie.getName(); <br />%＞ <br /><br />＜% <br />} <br />%＞ <br /><br />name value<br />＜%=name%＞ ＜%=svalue%＞<br /><br />........(其他内容) <br /><br />这一小段JSP文件可以读出用户硬盘上的所有有效的Cookie，也就是仍然在存活期内的Cookie文件。并用表格的形式列出每个Cookie的名字和内容。 <br /><br />我们来逐行分析一下这段代码： <br /><br />Cookie cookies[]=request.getCookies() 我们用request.getCookies()读出用户硬盘上的Cookie，并将所有的Cookie放到一个cookie对象数组里面。 <br /><br />接下来我们用一个循环语句遍历刚才建立的Cookie对象数组，我们用sCookie=cookies[i]取出数组中的一个Cookie对象，然后我们用sCookie.getValue()和sCookie.getName()两个方法来取得这个Cookie的名字和内容。 <br /><br />通过将取出来的Cookie的名字和内容放在字符串变量中，我们就能对其进行各种操作了。在上面的例子里，可通过循环语句的遍历，将所有Cookie放在一张表格中进行显示。<br /><br />四、 需要注意的一些问题 <br /><br />通过上面两个简单的例子，可以看到，用JSP进行Cookie的操作，是非常简单的。不过我们在实际操作中还要注意一些问题：<br /><br />1. Cookie的兼容性问题 <br /><br />Cookie的格式有2个不同的版本，第一个版本，我们称为Cookie Version 0，是最初由Netscape公司制定的，也被几乎所有的浏览器支持。而较新的版本，Cookie Version 1，则是根据RFC 2109文档制定的。为了确保兼容性，JAVA规定，前面所提到的涉及Cookie的操作都是针对旧版本的Cookie进行的。而新版本的Cookie目前还不被Javax.servlet.http.Cookie包所支持。 <br /><br />2. Cookie的内容 <br /><br />同样的Cookie的内容的字符限制针对不同的Cookie版本也有不同。在Cookie Version 0中，某些特殊的字符，例如：空格，方括号，圆括号，等于号（=），逗号，双引号，斜杠，问号，@符号，冒号，分号都不能作为Cookie的内容。这也就是为什么我们在例子中设定Cookie的内容为"Test_Content"的原因。 <br /><br />虽然在Cookie Version 1规定中放宽了限制，可以使用这些字符，但是考虑到新版本的Cookie规范目前仍然没有为所有的浏览器所支持，因而为保险起见，我们应该在Cookie的内容中尽量避免使用这些字符。<br /><br />from: <a href="http://www.3lian.com/zl/2004/10-5/22162.html">http://www.3lian.com/zl/2004/10-5/22162.html</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/82349.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-11-20 18:05 <a href="http://www.blogjava.net/weidagang2046/articles/82349.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用Cookie的Servlet程序应用和实例</title><link>http://www.blogjava.net/weidagang2046/articles/82348.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Mon, 20 Nov 2006 10:02:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/82348.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/82348.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/82348.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/82348.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/82348.html</trackback:ping><description><![CDATA[
		<p align="left">
				<font face="Verdana" size="2">Cookie是一小块可以嵌入HTTP请求和响应的数据。它在服务器上产生，并作为响应头域的一部分返回给客户。浏览器在收到有Cookie的响应后，会把Cookie的内容以"关键字/值"的形式写入到一个专为存放Cookie的文本文件中。浏览器会把Cookie的信息与请求发送给服务器，这样服务器就可以再次读取Cookie中存放的数据了。Cookie可以对有效期进行设置，浏览器不会把过期的Cookie发送给服务器。 </font>
		</p>
		<p>
				<font face="Verdana" size="2">    Servlet API提供了一个Cookie类，封装了对Cookie的基本操作。如下所示。 </font>
		</p>
		<p>
				<font face="Verdana" size="2">java.lang.Object<br />                            |-javax.servlet.http.Cookie<br />public class Cookie extends Object implements Cloneable<br />     Cookie类的方法如表14-4所示。 </font>
		</p>
		<p>
				<font face="Verdana" size="2">表14-4  Cookie类的方法<br />方    法                   意    义<br />Cookie(String, String) 生成一个有名和值的Cookie<br />clone()                返回当前对象的一个拷贝<br />getComment()        返回描述该Cookie的注释，没有就为空<br />getDomain()            返回该Cookie的域名<br />getMaxAge()            返回该Cookie的最大寿命<br />getName()            返回该Cookie的名字<br />getPath()            返回使用该Cookie的所有URL前缀<br />getSecure()            返回该Cookie的安全标志<br />getValue()            返回该Cookie的值<br />getVersion()        返回该Cookie的版本<br />setComment(String)    设置描述该Cookie的注释<br />setDomain(String)    设置该Cookie的域名<br />setMaxAge(int)        设置该Cookie的最大寿命<br />setPath(String) 设置该Cookie只能被从使用该URL前缀的请求提出<br />setSecure(boolean)   设置该Cookie的安全标志<br />setValue(String)   设置该Cookie的值<br />setVersion(int)       设置该Cookie所使用的协议的版本号<br />     实例：使用Cookie的Servlet程序 </font>
		</p>
		<p>
				<font face="Verdana" size="2">    下面的程序示例14-6就是对Cookie的基本运用。<br />    【程序源代码】 </font>
		</p>
		<p>
				<font face="Verdana" size="2">1 // ==================== Program Description ======================<br />2 // 程序名称：示例14-6: CookieCounter.java<br />3 // 程序目的：编写Cookie的Servlet程序<br />4 // ==========================================================<br />5 package test.cookie;<br />6 <br />7 import java.io.*;<br />8 import java.util.*;<br />9 import javax.servlet.*;<br />10 import javax.servlet.http.*;<br />11 <br />12 public class CookieCounter extends HttpServlet {<br />   public void init(ServletConfig config) throws ServletException {<br />14       super.init(config);<br />15    }<br />16 <br />   public void service(HttpServletRequest request, <br />   HttpServletResponse response) throws<br />18        IOException {<br />      boolean cookieFound = false;<br />20       Cookie thisCookie = null;<br />21       response.setContentType("text/html;charset=gb2312;");<br />22       PrintWriter out = response.getWriter();<br />23 <br />24       Cookie[] cookies = request.getCookies();<br />25       if (cookies != null){<br />26          for (int i = 0; i &lt; cookies.length; i++) {<br />27             thisCookie = cookies[0];<br />            if (thisCookie.getName().equals("CookieCount")) {<br />29                cookieFound = true;<br />30                break;<br />31             }<br />32          }<br />33       }<br />34 <br />35       if (cookieFound == false) {<br />36          thisCookie = new Cookie("CookieCount", "1");<br />37          thisCookie.setMaxAge(10);<br />38          response.addCookie(thisCookie);<br />39       }<br />40 <br />41       out.println(" " +<br />42                   " " +<br />43                   " " +<br />44                   " </font>
		</p>
		<p>
				<font color="red">
						<font face="Verdana" size="2">" +<br />45                   " </font>
						<center>
								<h3>
										<font face="Verdana" size="2">阅微草堂</font>
								</h3>
						</center>
				</font>
				<font face="Verdana" size="2">");<br />46       out.println(" </font>
		</p>
		<p>
				<font face="Verdana" size="2">欢迎来到阅微草堂！</font>
		</p>
		<font face="Verdana" size="2">");<br />47 <br />48       if (cookieFound) {<br />49          int cookieCount = Integer.parseInt(thisCookie.getValue());<br />50          cookieCount++;<br />51          thisCookie.setValue(String.valueOf(cookieCount));<br />52          thisCookie.setMaxAge(10);<br />53          response.addCookie(thisCookie);<br />54 <br />55          out.println(" </font>
		<p>
				<font face="Verdana" size="2">这是您在近" + 10 *<br />(Integer.parseInt((thisCookie.getValue()))-1)<br />56                    + "秒钟内第 " + thisCookie.getValue() + " 次光临寒舍！</font>
		</p>
		<font face="Verdana" size="2">");<br />57       }<br />58       else {<br />59          out.println( " </font>
		<p>
				<font face="Verdana" size="2">您至少已经10秒钟没有光临寒舍了！</font>
		</p>
		<font face="Verdana" size="2">");<br />60       }<br />61       out.println("");<br />62    }<br />63 }<br />    相应的配置文件web.xml的内容如下： </font>
		<p>
		</p>
		<p>
				<p>
						<br />
						<br />
						<web-app>
								<br />
								<font face="Verdana">
										<font size="2">  <servlet><br />    <servlet-name>CookieCounter</servlet-name><br />    <servlet-class>test.cookie.CookieCounter</servlet-class><br />  </servlet></font>
								</font>
						</web-app>
				</p>
				<p>
						<font face="Verdana" size="2">  <servlet-mapping><br />    <servlet-name>CookieCounter</servlet-name><br />    <url-pattern>/CookieCounter</url-pattern><br />  </servlet-mapping><br /><br />    【程序输出结果】如图14-8所示。 </font>
				</p>
				<p>
						<font face="Verdana">
								<br />
								<font size="2">
								</font>
						</font>
				</p>
				<p align="center">
						<font face="Verdana" size="2">
								<img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.jspcn.net/upload/2005/8/16/23/13/11242052102500240.gif" onload="javascript:if(this.width onclick=" border="0" javascript:window.open(this.src);?="" />（a）<br /></font>
				</p>
				<p align="center">
						<font face="Verdana" size="2">
								<img onmousewheel="return bbimg(this)" style="CURSOR: pointer" onclick="javascript:window.open(this.src);" src="http://www.jspcn.net/upload/2005/8/16/23/13/11242052108281310.gif" onload="javascript:if(this.width onclick=" border="0" javascript:window.open(this.src);?="" />（b）<br />图14-8 客户端返回有关Cookie的信息</font>
				</p>
				<p>
						<br />
						<font face="Verdana" size="2">    【程序注解】<br />    本例是在Resin 2.1.8服务器下调试运行的。首先在webapps目录下建一个名为cookie的Web应用。Servlet编译生成的class文件放在cookie/WEB-INF/classes/test/cookie目录下，web.xml文件直接放在cookie/WEB-INF目录下。由于web.xml中url-pattern的配置为"/CookieCounter"，所以在浏览器中输入"http://localhost:8080/cookie/CookieCounter"即可访问到该Servlet。 </font>
				</p>
				<p>
						<font face="Verdana" size="2">    下面对源程序做一简单分析。在service()方法中，用getCookies()函数获得客户端的Cookies，查找是否有名为"CookieCount"的Cookie。如果不存在就生成一个Cookie，名称为"CookieCount"，值为"1"（thisCookie = new Cookie("CookieCount", "1")），并指定了该Cookie的最大寿命为10秒钟（setMaxAge(10)），然后将该Cookie发送给客户端（addCookie()）。如果Cookie已经存在，就将Cookie的值加1之后再发送给客户端。 </font>
				</p>
				<p>
						<font face="Verdana" size="2">    这样，如果客户端第一次访问CookieCounter Servlet，或者两次访问之间的时间间隔超过了10秒，就将看到如图14-8（a）所示的输出结果。如果客户端在Cookie的生命周期结束之前连续访问该Servlet，则Cookie的值将不断增加。如图14-8（b）所示的就是在10秒钟内连续访问两次Servlet时的输出结果。<br /><br />from: </font>
						<a href="http://www.jspcn.net/htmlnews/11242052102341354.html">
								<font face="Verdana" size="2">http://www.jspcn.net/htmlnews/11242052102341354.html</font>
						</a>
				</p>
		</p>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/82348.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-11-20 18:02 <a href="http://www.blogjava.net/weidagang2046/articles/82348.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java NIO原理和使用</title><link>http://www.blogjava.net/weidagang2046/articles/81855.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Fri, 17 Nov 2006 13:52:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/81855.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/81855.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/81855.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/81855.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/81855.html</trackback:ping><description><![CDATA[
		<p align="left">Java　NIO非堵塞应用通常适用用在I/O读写等方面，我们知道，系统运行的性能瓶颈通常在I/O读写，包括对端口和文件的操作上，过去，在打开一个I/O通道后，read()将一直等待在端口一边读取字节内容，如果没有内容进来，read()也是傻傻的等，这会影响我们程序继续做其他事情，那么改进做法就是开设线程，让线程去等待，但是这样做也是相当耗费资源的。</p>
		<p align="left">Java NIO非堵塞技术实际是采取Reactor模式，或者说是Observer模式为我们监察I/O端口，如果有内容进来，会自动通知我们，这样，我们就不必开启多个线程死等，从外界看，实现了流畅的I/O读写，不堵塞了。</p>
		<p align="left">Java NIO出现不只是一个技术性能的提高，你会发现网络上到处在介绍它，因为它具有里程碑意义，从JDK1.4开始，Java开始提高性能相关的功能，从而使得Java在底层或者并行分布式计算等操作上已经可以和C或Perl等语言并驾齐驱。</p>
		<p align="left">如果你至今还是在怀疑Java的性能，说明你的思想和观念已经完全落伍了，Java一两年就应该用新的名词来定义。从JDK1.5开始又要提供关于线程、并发等新性能的支持，Java应用在游戏等适时领域方面的机会已经成熟，Java在稳定自己中间件地位后，开始蚕食传统C的领域。</p>
		<p align="left">本文主要简单介绍NIO的基本原理，在下一篇文章中，将结合Reactor模式和著名线程大师<a href="http://gee.cs.oswego.edu/dl/">Doug Lea</a>的一篇文章深入讨论。</p>
		<p align="left">NIO主要原理和适用。</p>
		<p align="left">NIO 有一个主要的类Selector,这个类似一个观察者，只要我们把需要探知的socketchannel告诉Selector,我们接着做别的事情，当有事件发生时，他会通知我们，传回一组SelectionKey,我们读取这些Key,就会获得我们刚刚注册过的socketchannel,然后，我们从这个Channel中读取数据，放心，包准能够读到，接着我们可以处理这些数据。</p>
		<p align="left">Selector内部原理实际是在做一个对所注册的channel的轮询访问，不断的轮询(目前就这一个算法)，一旦轮询到一个channel有所注册的事情发生，比如数据来了，他就会站起来报告，交出一把钥匙，让我们通过这把钥匙来读取这个channel的内容。</p>
		<p align="left">了解了这个基本原理，我们结合代码看看使用，在使用上，也在分两个方向，一个是线程处理，一个是用非线程，后者比较简单，看下面代码：</p>
		<table cellspacing="0" cellpadding="0" width="100%" bgcolor="#cccccc" border="0">
				<tbody>
						<tr>
								<td>
										<br />import java.io.*;<br />import java.nio.*;<br />import java.nio.channels.*;<br />import java.nio.channels.spi.*;<br />import java.net.*;<br />import java.util.*; 
<p>/**<br />*<br />* @author Administrator<br />* @version<br />*/<br />public class NBTest {</p><p><br />　　/** Creates new NBTest */<br />　　public NBTest()<br />　　{<br />　　}</p><p>　　public void startServer() throws Exception<br />　　{<br />　　int channels = 0;<br />　　int nKeys = 0;<br />　　int currentSelector = 0;<br /><br />　　//使用Selector<br />　　Selector selector = Selector.open();<br /><br />　　//建立Channel 并绑定到9000端口<br />　　ServerSocketChannel ssc = ServerSocketChannel.open();<br />　　InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(),9000); <br />　　ssc.socket().bind(address);<br /><br />　　//使设定non-blocking的方式。<br />　　ssc.configureBlocking(false);</p><p>　　//向Selector注册Channel及我们有兴趣的事件<br />　　SelectionKey s = ssc.register(selector, SelectionKey.OP_ACCEPT);<br />　　printKeyInfo(s);</p><p>　　while(true) //不断的轮询<br />　　{<br />　　　　debug("NBTest: Starting select");<br /><br />　　　　//Selector通过select方法通知我们我们感兴趣的事件发生了。<br />　　　　nKeys = selector.select();<br />　　　　//如果有我们注册的事情发生了，它的传回值就会大于0<br />　　　　if(nKeys &gt; 0)<br />　　　　{<br />　　　　　　debug("NBTest: Number of keys after select operation: " +nKeys);<br /><br />　　　　　　//Selector传回一组SelectionKeys<br />　　　　　　//我们从这些key中的channel()方法中取得我们刚刚注册的channel。<br />　　　　　　Set selectedKeys = selector.selectedKeys();<br />　　　　　　Iterator i = selectedKeys.iterator();<br />　　　　　　while(i.hasNext())<br />　　　　　　{<br />　　　　　　 　 s = (SelectionKey) i.next();<br />　　　　　　 　 printKeyInfo(s);<br />　　　　　　 　 debug("NBTest: Nr Keys in selector: " +selector.keys().size());<br /><br />　　　　　　 　 //一个key被处理完成后，就都被从就绪关键字（ready keys）列表中除去<br />　　　　　　 　 i.remove();<br />　　　　　　 　 if(s.isAcceptable())<br />　　　　　　 　 {<br />　　　　　　 　 　 // 从channel()中取得我们刚刚注册的channel。<br />　　　　　　 　 　 Socket socket = ((ServerSocketChannel)s.channel()).accept().socket();<br />　　　　　　 　 　 SocketChannel sc = socket.getChannel();<br /><br />　　　　　　 　 　 sc.configureBlocking(false);<br />　　　　　　 　 　 sc.register(selector, SelectionKey.OP_READ |SelectionKey.OP_WRITE);<br />　　　　　　 　 　 　　　　　　 　 　 System.out.println(++channels);<br />　　　　　　 　 }<br />　　　　　　 　 else<br />　　　　　　 　 {<br />　　　　　　 　 　 debug("NBTest: Channel not acceptable");<br />　　　　　　 　 }<br />　　　　　 }<br />　　　}<br />　　　else<br />　　　{<br />　　　　　　debug("NBTest: Select finished without any keys.");<br />　　　}</p><p>　 }<br /><br />}</p><p><br />private static void debug(String s)<br />{<br />　 System.out.println(s);<br />}</p><p><br />private static void printKeyInfo(SelectionKey sk)<br />{<br />　 String s = new String();</p><p>　 s = "Att: " + (sk.attachment() == null ? "no" : "yes");<br />　 s += ", Read: " + sk.isReadable();<br />　 s += ", Acpt: " + sk.isAcceptable();<br />　 s += ", Cnct: " + sk.isConnectable();<br />　 s += ", Wrt: " + sk.isWritable();<br />　 s += ", Valid: " + sk.isValid();<br />　 s += ", Ops: " + sk.interestOps();<br />　 debug(s);<br />}</p><p><br />/**<br />* @param args the command line arguments<br />*/<br />public static void main (String args[])<br />{<br />　 NBTest nbTest = new NBTest();<br />　 try<br />　 {<br />　 　 nbTest.startServer();<br />　 }<br />　 　 catch(Exception e)<br />　 {<br />　 　 e.printStackTrace();<br />　 }<br />}</p><p>}</p><p><br /></p></td>
						</tr>
				</tbody>
		</table>
		<p align="left">这是一个守候在端口9000的noblock server例子，如果我们编制一个客户端程序，就可以对它进行互动操作，或者使用telnet 主机名　90000 可以链接上。</p>
		<p align="left">通过仔细阅读这个例程，相信你已经大致了解NIO的原理和使用方法，下一篇，我们将使用多线程来处理这些数据，再搭建一个自己的Reactor模式。<br /><br />from: <a href="http://www.jdon.com/concurrent/nio%D4%AD%C0%ED%D3%A6%D3%C3.htm">http://www.jdon.com/concurrent/nio%D4%AD%C0%ED%D3%A6%D3%C3.htm</a></p>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/81855.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-11-17 21:52 <a href="http://www.blogjava.net/weidagang2046/articles/81855.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用Java实现HTTP文件队列下载</title><link>http://www.blogjava.net/weidagang2046/articles/81696.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Fri, 17 Nov 2006 02:26:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/81696.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/81696.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/81696.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/81696.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/81696.html</trackback:ping><description><![CDATA[
		<font face="Verdana">
				<strong>序言<br /><br /></strong>　　许多用户可能会遇到这样的情况：在网站上发现一个很好的资源，但是这个资源是分成了很多个文件存放的，如果想把它保存到本地，只有靠用户点击另存来完成保存，如果资源分了几百甚至上千上万，那简直是个灾难。 <br /><br />　　在Internet上很多的资源分成多个文件存放时，它的文件命名是有一定的规则的；正因如此，我们就可以用程序来完成这个资源的完全下载。<br /><br />　　<strong>1. 基础知识</strong><br /><br />　　在Internet上，我们要下载网站上的某个资源，我们会获得一个URL（Uniform Resource Locator），它是一个服务器资源定位的描述，下载的过程总是如下步骤:<br /><br />　　步骤1:客户端发起连接请求一个URL <br /><br />　　步骤2:服务器解析URL，并将指定的资源返回一个输入流给客户 <br /><br />　　步骤3:客户端接收输入流，将流中的内容存到文件 <br /><br />　　<strong>2. 网络连接的建立</strong><br /><br />　　Java提供了对URL访问和大量的流操作的的API，我们可以很容易的完成对网络上资源的存取,下面的代码段就完成了对一个网站的资源进行访问:<br /><br /></font>
		<font face="Verdana">
				<code>......<br />destUrl="http://www.ebook.com/java/网络编程001.zip";<br />url = new URL(destUrl);<br />httpUrl = (HttpURLConnection) url.openConnection();<br />//连接指定的网络资源<br />httpUrl.connect();<br />//获取网络输入流<br />bis = new BufferedInputStream(httpUrl.getInputStream());<br />......</code>
				<br />　　<strong>3. 代理的访问</strong><br /><br />　　Java 中通过代理服务器访问外网的方法已经是世人皆知的秘密了。这里就不再多描述了，访问的JAVA代码如下:<br /><br /></font>
		<font face="Verdana">
				<code>//设置代理服务器<br />System.getProperties().put("proxySet", "true");<br />System.getProperties().put("proxyHost", "10.154.134.110");<br />System.getProperties().put("proxyPort", "8080");</code>
				<br />　　<strong>4. 网络资源的保存</strong><br /><br />　　在上节中，我们已经获取了指定网络资源的输入流，接下来我们要完成的就是读取输入流中的所以内容，并将其保存在文件中。示例代码:<br /><br /></font>
		<font face="Verdana">
				<code>......<br />fos = new FileOutputStream(fileName);<br />if (this.DEBUG) <br />System.out.println("正在获取链接[" + destUrl + "]的内容...\n将其保存为文件[" + fileName +"]");<br /><br />//保存文件<br />while ( (size = bis.read(buf)) != -1)<br />fos.write(buf, 0, size);<br />......</code>
				<br />　　上面的示例代码就将网络资源的内容保存到了本地指定的文件中。<br /><br />　　<strong>5. 代码清单</strong><br /><br /></font>
		<code>
				<font face="Verdana">import java.io.*;<br />import java.net.*;<br />import java.util.*;<br /><br />/**<br />* ＜p＞Title: 个人开发的API＜/p＞<br />* ＜p＞Description: 将指定的HTTP网络资源在本地以文件形式存放＜/p＞<br />* ＜p＞Copyright: Copyright (c) 2004＜/p＞<br />* ＜p＞Company: NewSky＜/p＞<br />* @author MagicLiao<br />* @version 1.0<br />*/<br />public class HttpGet {<br /><br />　 public final static boolean DEBUG = true;//调试用<br />　 private static int BUFFER_SIZE = 8096;//缓冲区大小<br />　 private Vector vDownLoad = new Vector();//URL列表<br />　 private Vector vFileList = new Vector();//下载后的保存文件名列表<br /><br />　 /**<br />　 * 构造方法<br />　 */<br />　 public HttpGet() {}<br /><br />　 /**<br />　 * 清除下载列表<br />　 */<br />　 public void resetList() {<br />　　 vDownLoad.clear();<br />　　 vFileList.clear();<br />　 }<br /><br />　 /**<br />　 * 增加下载列表项<br />　 *<br />　 * @param url String<br />　 * @param filename String<br />　 */<br /><br />public void addItem(String url, String filename) {<br />　 vDownLoad.add(url);<br />　 vFileList.add(filename);<br />}<br /><br />　 /**<br />　 * 根据列表下载资源<br />　 */<br />public void downLoadByList() {<br />　 String url = null;<br />　 String filename = null;<br /><br />　 //按列表顺序保存资源<br />　 for (int i = 0; i ＜ vDownLoad.size(); i++) {<br />　　 url = (String) vDownLoad.get(i);<br />　　 filename = (String) vFileList.get(i);<br /><br />　　 try {<br />　　　 saveToFile(url, filename);<br />　　 }<br />　　 catch (IOException err) {<br />　　　 if (DEBUG) {<br />　　　　 System.out.println("资源[" + url + "]下载失败!!!");<br />　　　 }<br />　　 }<br />　 }<br /><br />　 if (DEBUG) {<br />　　 System.out.println("下载完成!!!");<br />　 }<br />}<br /><br />/**<br />* 将HTTP资源另存为文件<br />*<br />* @param destUrl String<br />* @param fileName String<br />* @throws Exception<br />*/<br />public void saveToFile(String destUrl, String fileName) throws IOException {<br />　 FileOutputStream fos = null;<br />　 BufferedInputStream bis = null;<br />　 HttpURLConnection httpUrl = null;<br />　 URL url = null;<br />　 byte[] buf = new byte[BUFFER_SIZE];<br />　 int size = 0;<br /><br />　 //建立链接<br />　 url = new URL(destUrl);<br />　 httpUrl = (HttpURLConnection) url.openConnection();<br />　 //连接指定的资源<br />　 httpUrl.connect();<br />　 //获取网络输入流<br />　 bis = new BufferedInputStream(httpUrl.getInputStream());<br />　 //建立文件<br />　 fos = new FileOutputStream(fileName);<br /><br />　 if (this.DEBUG) <br />　　 System.out.println("正在获取链接[" + destUrl + "]的内容...\n将其保存为文件[" + fileName + "]");<br /><br />　 //保存文件<br />　 while ( (size = bis.read(buf)) != -1) <br />　　 fos.write(buf, 0, size);<br /><br />　 fos.close();<br />　 bis.close();<br />　 httpUrl.disconnect();<br />}<br /><br />/**<br />* 设置代理服务器<br />*<br />* @param proxy String<br />* @param proxyPort String<br />*/<br />public void setProxyServer(String proxy, String proxyPort) {<br />　 //设置代理服务器 <br />　 System.getProperties().put("proxySet", "true");<br />　 System.getProperties().put("proxyHost", proxy);<br />　 System.getProperties().put("proxyPort", proxyPort);<br />}<br /><br />/**<br />* 设置认证用户名与密码<br />*<br />* @param uid String<br />* @param pwd String<br />*/<br />public void setAuthenticator(String uid, String pwd) {<br />Authenticator.setDefault(new MyAuthenticator(uid, pwd));<br />}<br /><br />/**<br />* 主方法(用于测试)<br />*<br />* @param argv String[]<br />*/<br />public static void main(String argv[]) {<br />　 HttpGet oInstance = new HttpGet();<br />　 try {<br />　　 //增加下载列表（此处用户可以写入自己代码来增加下载列表）<br />　　 oInstance.addItem("http://www.ebook.com/java/网络编程001.zip","./网络编程1.zip");<br />　　 oInstance.addItem("http://www.ebook.com/java/网络编程002.zip","./网络编程2.zip");<br />　　 oInstance.addItem("http://www.ebook.com/java/网络编程003.zip","./网络编程3.zip");<br />　　 oInstance.addItem("http://www.ebook.com/java/网络编程004.zip","./网络编程4.zip");<br />　　 oInstance.addItem("http://www.ebook.com/java/网络编程005.zip","./网络编程5.zip");<br />　　 oInstance.addItem("http://www.ebook.com/java/网络编程006.zip","./网络编程6.zip");<br />　　 oInstance.addItem("http://www.ebook.com/java/网络编程007.zip","./网络编程7.zip");<br />　　 //开始下载<br />　　 oInstance.downLoadByList();<br />　 }<br />　 catch (Exception err) {<br />　　 System.out.println(err.getMessage());<br />　 }<br />}<br />}<br /><br />from: <a href="http://www.1-100.org/other/11548.htm">http://www.1-100.org/other/11548.htm</a></font>
		</code>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/81696.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-11-17 10:26 <a href="http://www.blogjava.net/weidagang2046/articles/81696.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入理解和改进JSP/Servlet会话管理机制 </title><link>http://www.blogjava.net/weidagang2046/articles/81121.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Tue, 14 Nov 2006 14:50:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/81121.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/81121.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/81121.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/81121.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/81121.html</trackback:ping><description><![CDATA[在Web服务器端编程中，会话状态管理是一个经常必须考虑的重要问题。本文分析JSP/Servlet的会话管理机制及其所面临的问题，然后提出了一种改进的会话管理方法。 <br /><br />一、Servlet的会话管理机制 <br /><br />根据设计，HTTP是一种无状态的协议。它意味着Web应用并不了解有关同一用户以前请求的信息。维持会话状态信息的方法之一是使用Servlet或者JSP容器提供的会话跟踪功能。Servlet API规范定义了一个简单的HttpSession接口，通过它我们可以方便地实现会话跟踪。 <br /><br />HttpSession接口提供了存储和返回标准会话属性的方法。标准会话属性如会话标识符、应用数据等，都以“名字-值”对的形式保存。简而言之，HttpSession接口提供了一种把对象保存到内存、在同一用户的后继请求中提取这些对象的标准办法。在会话中保存数据的方法是setAttribute(String s, Object o)，从会话提取原来所保存对象的方法是getAttribute(String s)。 <br /><br />在HTTP协议中，当用户不再活动时不存在显式的终止信号。由于这个原因，我们不知道用户是否还要再次返回，如果不采取某种方法解决这个问题，内存中会积累起大量的HttpSession对象。 <br /><br />为此，Servlet采用“超时限制”的办法来判断用户是否还在访问：如果某个用户在一定的时间之内没有发出后继请求，则该用户的会话被作废，他的HttpSession对象被释放。会话的默认超时间隔由Servlet容器定义。这个值可以通过getMaxInactiveInterval方法获得，通过setMaxInactiveInterval方法修改，这些方法中的超时时间以秒计。如果会话的超时时间值设置成-1，则会话永不超时。Servlet可以通过getLastAccessedTime方法获得当前请求之前的最后一次访问时间。 <br /><br />要获得HttpSession对象，我们可以调用HttpServletRequest对象的getSession方法。为了正确地维持会话状态，我们必须在发送任何应答内容之前调用getSession方法。 <br />用户会话既可以用手工方法作废，也可以自动作废。作废会话意味着从内存中删除HttpSession对象以及它的数据。例如，如果一定时间之内（默认30分钟）用户不再发送请求，Java Web Server自动地作废他的会话。 <br /><br />Servlet/JSP会话跟踪机制有着一定的局限，比如： <br /><br />· 会话对象保存在内存之中，占用了可观的资源。 <br /><br />· 会话跟踪依赖于Cookie。由于各种原因，特别是安全上的原因，一些用户关闭了Cookie。 <br /><br />· 会话跟踪要用到服务器创建的会话标识符。在多个Web服务器以及多个JVM的环境中，Web服务器不能识别其他服务器创建的会话标识符，会话跟踪机制无法发挥作用。 <br />要深入理解会话跟踪机制，首先我们必须理解在Servlet/JSP容器中会话如何运作。 <br /><br /><br />二、会话标识符 <br /><br />每当新用户请求一个使用了HttpSession对象的JSP页面，JSP容器除了发回应答页面之外，它还要向浏览器发送一个特殊的数字。这个特殊的数字称为“会话标识符”，它是一个唯一的用户标识符。此后，HttpSession对象就驻留在内存之中，等待同一用户返回时再次调用它的方法。 <br /><br />在客户端，浏览器保存会话标识符，并在每一个后继请求中把这个会话标识符发送给服务器。会话标识符告诉JSP容器当前请求不是用户发出的第一个请求，服务器以前已经为该用户创建了HttpSession对象。此时，JSP容器不再为用户创建新的HttpSession对象，而是寻找具有相同会话标识符的HttpSession对象，然后建立该HttpSession对象和当前请求的关联。 <br /><br />会话标识符以Cookie的形式在服务器和浏览器之间传送。如果浏览器不支持Cookie又如何呢？此时，对服务器的后继请求将不会带有会话标识符。结果，JSP容器认为该请求来自一个新用户，它会再创建一个HttpSession对象，而以前创建的HttpSession对象仍旧驻留在内存中，但该用户以前的会话信息却丢失了。 <br /><br />另外，Servlet/JSP容器只认可它自己创建的会话标识符。如果同一Web应用在“Web农场”（Web farm）的多台服务器上运行，则必须存在这样一种机制：保证来自同一用户的请求总是被定向到处理该用户第一次请求的服务器。 <br /><br />三、伪会话管理机制 <br /><br />如前所述，基于Cookie的会话管理技术面临着种种问题。下面我们要设计一种新的会话管理机制来解决这些问题。这种会话管理机制称为“伪会话”（Pseudo Session）机制，它具有如下特点： <br /><br />· 对象和数据不是保存在内存中，而是以文本文件形式保存。每一个文本文件与一个特定的用户关联，文件的名字就是会话的标识符。因此，文件名字必须是唯一的。 <br />· 文本文件保存在一个专用的目录中，所有Web服务器都可以访问这个目录。因此，伪会话可以用于Web农场。 <br />· 会话标识符不作为Cookie发送，而是直接编码到URL里面。因此，采用伪会话技术要求修改所有的超级链接，包括HTML表单的ACTION属性。 <br />此外，实现伪会话管理机制时我们还要考虑到以下几点： <br />· 它应该与应用无关，其他想要实现同样功能的开发者应该能够方便地重用它。 <br />· 考虑到安全原因，应该有一种为会话标识符生成随机数字的办法。 <br />· 为了作废过期的会话，应该设定一个超时值。同一个用户，如果他超过一定的时间之后再次返回，他将获得一个新的会话标识符。此举能够防止未经授权的用户冒用其他人的会话。 <br />· 应该有一种收集过期会话并删除相应文本文件的机制。 <br />· 如果用户使用已经过期的会话标识符再次访问服务器，即使这个会话标识符的文本文件还没有删除，系统也不应该允许用户使用原来的会话。 <br />· 同时，应该存在一种更新会话文本文件最后改动时间的机制，使得用户在会话过期时限之前返回时会话总是保持最新且合法的状态数据。 <br /><br /><br />四、实现伪会话管理机制 <br /><br />下面所介绍的工程称为PseudoSession，它是伪会话机制一个很简单的实现。考虑到移植性，我们以JavaBean的形式实现它。PseudoSessionBean的完整代码可以从本文后面下载。 <br /><br />PseudoSessionBean拥有如下域（Field）： <br />public String path;public long timeOut; <br />path是保存所有会话文本文件的目录。如果Web服务器的数量在一个以上，这个目录必须允许所有服务器访问。然而，为了防止用户直接访问这些文本文件，这个路径应该不允许用户直接访问。解决这个问题的一种方法是使用Web网站根之外的目录。 <br /><br />timeOut是用户的最后一个请求到会话过期作废之间的时间。在PseudoSessionBean的代码清单中，timeOut设置成了以毫秒表示的20分钟，这是一个比较合理的超时时间值。对于任何用户，如果他在这个超时时间之后才继续发出请求，他将得到一个新的会话标识符。 <br />PseudoSessionBean有4个方法：getSessionID，setValue，getValue，deleteAllInvalidSessions。 <br /><br />4.1 getSessionID方法 <br />getSessionID方法的声明如下： <br />public String getSessionID(HttpServletRequest request) <br />这个方法应该在每一个JSP页面的开头调用。它完成如下任务： <br />· 如果用户是第一次访问，则为该用户设定一个新的会话标识符。 <br />· 检查URL所带会话标识符的合法性。如果会话标识符已经过期，则getSessionID方法返回一个新的会话标识符。 <br />下面我们来看看getSessionID方法的工作过程。 <br />String sessionId = request.getParameter("sessionId"); <br />validSessionIdFound是一个标记，用于指示会话标识符是否合法。validSessionIdFound的初始值是false。 <br />boolean validSessionIdFound = false; <br />long类型的now变量包含请求出现时的服务器时间。该变量用于确定用户会话的合法性。 <br />long now = System.currentTimeMillis(); <br />如果找到了会话标识符，则getSessionID方法检查它的合法性。检查过程如下： <br />· 一个合法的会话标识符必须有对应的同名文本文件。 <br />· 文件的最后修改时间加上timeOut应该大于当前时间。 <br />· 如果存在与会话对应的文本文件，但文件已经过期，则原来的文件被删除。 <br />· 把合法会话标识符所对应文本文件的最后修改日期改为now。 <br />这些任务主要借助File对象完成，创建File对象的参数就是会话文本文件的路径： <br />if (sessionId!=null) {File f = new File(path + sessionId);if (f.exists()) { if (f.lastModified() + timeOut &gt; now) { // 会话合法// 使用setLastModified时，如果文件已经被其他程序锁定，// 程序不会产生任何异常，但文件数据不会改变f.setLastModified(now);validSessionIdFound = true; } else { // 会话已经过期 // 删除文件f.delete(); }} // end if (f.exists) } // end if (sessionId!=null) <br />如果不存在合法的会话标识符，则getSessionID方法生成一个会话标识符以及相应的文本文件： <br />if (!validSessionIdFound) { sessionId = Long.toString(now); // 创建文件 File f = new File(path + sessionId); try {f.createNewFile(); } catch (IOException ioe) {}} // end of if !validSessionIdFound <br />程序保证文件名字随机性的方法非常简单：把当前的系统时间直接转换成会话标识符。对于那些涉及敏感数据的应用，我们应该考虑运用更安全的随机数生成器来生成会话标识符。 <br />综上所述，getSessionID并不总是返回新的合法会话标识符：它返回的标识符可能与传递给它的标识符相同，也可能是新创建的会话标识符。 <br />为了保证JSP页面拥有合法的会话标识符以便调用setValue、getValue方法，每个JSP页面都必须在开头位置调用getSesstionID方法。 <br /><br /><br />4.2 setValue方法 <br />setValue方法保存value字符串以及与它关联的字符串名字。这种“名字-值”对很容易使人想起Dictionary对象。setValue方法要求在第一个参数中提供合法的会话标识符，它假定在自己被调用之前getSessionID方法已经执行，经过检验的合法会话标识符必然存在，因此它不再对传入的会话标识符进行合法性检验。 <br />setValue方法按如下规则保存名字-值对： <br />· 如果与value值关联的name以前还没有保存过，则新的名字-值对加入到文本文件的末尾。 <br />· 如果value字符串关联的name值以前已经保存过，则原来保存的值被新的value值替换。 <br />setValue方法按照如下格式保存名字-值对，注意“名字”是大小写敏感的： <br />name-1 value-1name-2 value-2name-3 value-3...name-n value-n <br />setValue方法的声明如下： <br />public void setValue(String sessionId, String name, String value) <br />setValue方法首先寻找与当前会话对应的文本文件。如果不能找到文本文件，则setValue方法不做任何事情直接返回。如果找到了会话文本文件，setValue方法读取文本文件的各个行，然后比较读入的行与name：如果读入的文本行开头与name一样，则说明该名字已经保存，setValue方法将替换该行后面的值；如果name不能与读入的文本行匹配，则这行文本被直接复制到一个临时文件。 <br />这部分功能的实现代码如下： <br />try { FileReader fr = new FileReader(path + sessionId); BufferedReader br = new BufferedReader(fr); FileWriter fw = new FileWriter(path + sessionId + ".tmp"); BufferedWriter bw = new BufferedWriter(fw); String s; while ((s = br.readLine()) != null)if (!s.startsWith(name + " ")) { bw.write(s); bw.newLine();} bw.write(name + " " + value); bw.newLine(); bw.close(); br.close(); fw.close(); bw.close(); . . .}catch (FileNotFoundException e) {}catch (IOException e) { System.out.println(e.toString());} <br />原来文本文件中的所有行复制到临时文件之后，setValue方法删除原来的文本文件，然后把临时文件改成会话文本文件的名字： <br />File f = new File(path + sessionId + ".tmp");File dest = new File(path + sessionId);dest.delete();f.renameTo(dest); <br /><br /><br />4.3 getValue方法 <br />getValue方法用于提取原来保存在伪会话中的数据。正如setValue方法，getValue方法也要求传入一个合法的会话标识符，而且getValue方法不再对传入的会话标识符进行合法性检查。getValue方法的第二个参数是待提取数据的name，返回值是与指定name关联的value。 <br />getValue方法的声明如下： <br />public String getValue(String sessionId, String name) <br />getValue方法的基本执行过程如下：首先找到会话文本文件，然后按行读入直至找到与name匹配的文本行；找到匹配的文本行之后，getValue方法返回该行保存的value；如果不能找到，getValue方法返回null。 <br /><br /><br />4.4 deleteAllInvalidSessions方法 <br />deleteAllInvalidSessions方法删除那些与已经过期的会话关联的文本文件。由于调用getSessionID方法时过期的会话文本文件会被删除，deleteAllInvalidSessions方法并不是关键的方法。什么时候调用这个方法由应用自己决定。例如，我们可以编写一个专用的后台程序，由这个程序每天一次清除所有过期的文本文件。最简单的办法是在JSP文件末尾调用deleteAllInvalidSessions方法，但如果网站比较繁忙，重复地调用deleteAllInvalidSessions方法将降低整个网站的响应能力。一种明智的做法是：编写一个在访问量较少的时候自动进行清理的后台程序。 <br />deleteAllInvalidSessions方法的声明如下： <br />public void deleteAllInvalidSessions() <br />它首先把所有会话文本文件的名字读入files字符串数组： <br />File dir = new File(path); String[] files = dir.list(); <br />deleteAllInvalidSessions方法比较文本文件的最后修改时间（加上超时时间）和系统当前时间，确定会话是否过期。long类型的变量now用于保存系统的当前时间。 <br />long now = System.currentTimeMillis(); <br />接下来，deleteAllInvalidSessions方法通过循环访问files数组，依次检查每个文件的lastModified属性。所有与过期会话关联的文件都将被删除： <br />for (int i=0; i&lt;files.length; i++) { File f = new File(path + files[i]); if (f.lastModified() + timeOut &gt; now) f.delete(); // 删除过期的会话文本文件} <br /><br /><br />五、应用实例 <br /><br /><br />编译好PseudoSessionBean这个JavaBean之后，我们就可以利用伪会话管理机制来管理Web应用的会话状态信息了。由于不必再使用服务器的会话管理机制，我们可以在page指令中把session属性设置为false关闭默认的JSP/Servlet会话管理功能。 <br />&lt;%@ page session="false" %&gt; <br /><br />然后，我们用JSP的&lt;jsp:useBean&gt;标记告诉JSP容器程序要使用PseudoSessionBean： <br />&lt;jsp:useBean id="PseudoSessionId" scope="application" class="pseudosession.PseudoSessionBean" /&gt; <br /><br />在上面这个&lt;jsp:useBean&gt;标记中，class属性值是“包.类名字”形式。当然，对于不同的包名字，class属性的值应该作相应的修改。注意Bean的scope属性是“application”，这是因为我们要在应用的所有页面中使用这个Bean。在这个应用中，把Bean的scope属性设置为“application”具有最好的效率，因为我们只需创建Bean对象一次就可以了。另外，正如前面所提到的，getSessionID方法必须在所有其他代码之前调用。 <br />&lt;% String sessionId = PseudoSessionId.getSessionID(request);%&gt; <br />为了说明PseudoSessionBean的应用，下面我们来看两个JSP页面，它们是index.jsp和secondPage.jsp。index.jsp页面在伪会话变量中保存用户的名字，而secondPage.jsp则提取这个用户名字。 <br /><br />index.jsp页面的代码如下： <br />&lt;%@ page session="false" contentType="text/html;charset=gb2312" %&gt;<br />&lt;jsp:useBean id="PseudoSessionId" scope="application" class="pseudosession.PseudoSessionBean" /&gt;<br />&lt;% String sessionId = PseudoSessionId.getSessionID(request);%&gt;<br />&lt;html&gt;<br />&lt;head&gt;<br />&lt;title&gt;伪会话&lt;/title&gt;<br />&lt;/head&gt;<br />&lt;body&gt;<br />&lt;h1&gt;伪会话管理机制&lt;/h1&gt;<br />&lt;% String userName = "bulbul"; PseudoSessionId.setValue(sessionId, "userName", userName);%&gt;<br />&lt;a href=secondPage.jsp?sessionId=&lt;%=sessionId%&gt;&gt;点击此处&lt;/a&gt;<br />&lt;form method="post" action=anotherPage.jsp?sessionId=&lt;%=sessionId%&gt;&gt;<br />输入数据:&lt;input type="text" name="sample"&gt;<br />&lt;input type="submit" name="Submit" value="Submit"&gt;<br />&lt;/form&gt;<br />&lt;/body&gt;<br />&lt;/html&gt;<br />&lt;% PseudoSessionId.deleteAllInvalidSessions();%&gt; <br /><br />注意，包括&lt;form&gt;标记的action属性在内，所有的超级链接都已经改写，现在都包含了会话标识符。另外也请注意页面的最后调用了deleteAllInvalidSessions方法。 <br />secondPage.jsp页面只简单地返回以前保存的用户名字。 <br />&lt;%@ contentType="text/html;charset=gb2312" page session="false" %&gt;<br />&lt;jsp:useBean id="PseudoSessionId" scope="application" class="pseudosession.PseudoSessionBean" /&gt;<br />&lt;% String sessionId = PseudoSessionId.getSessionID(request);%&gt;<br />&lt;html&gt;<br />&lt;head&gt;<br />&lt;title&gt;第2个页面&lt;/title&gt;<br />&lt;/head&gt;<br />&lt;body&gt;<br />&lt;% String userName = PseudoSessionId.getValue(sessionId, "userName"); out.println("用户名字是 " + userName);%&gt;<br />&lt;/body&gt;<br />&lt;/html&gt; <br /><br />from: <a href="http://www.cnsdn.com.cn/inc/show.asp?id=3688">http://www.cnsdn.com.cn/inc/show.asp?id=3688</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/81121.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-11-14 22:50 <a href="http://www.blogjava.net/weidagang2046/articles/81121.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>What's the difference between "redirect" &amp; "forward" in servelt?</title><link>http://www.blogjava.net/weidagang2046/articles/78822.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Thu, 02 Nov 2006 18:19:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/78822.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/78822.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/78822.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/78822.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/78822.html</trackback:ping><description><![CDATA[Suppose you have a servlet that just does another redirect to another URL:<br /><br /><pre><font color="navy"><b>protected</b></font><font color="navy"><b>void</b></font> doGet(HttpServletRequest request, HttpServletResponse response)
<font color="navy">{</font>
  response.sendRedirect(<font color="red">"/anotherURL"</font>);
<font color="navy">}</font></pre><br /><br />This servlet is accessible through the URL /redirectServlet.<br /><br />When the client browser makes a request for /redirectServlet, it receives a response (an HTTP 302 response) that tells it that the document it requested can actually be found at /anotherURL. The client uses the URL it gets and makes a new request to /anotherURL.<br /><br />The redirect URL doesn't have to be on the same server as the original URL. If the redirect URL (/anotherURL) points to another servlet, new request and response objects will be created to handle the new request.<br /><br />Suppose our redirect servlet is now changed to do a forward instead of a redirect:<br /><br /><pre><font color="navy"><b>protected</b></font><font color="navy"><b>void</b></font> doGet(HttpServletRequest request, HttpServletResponse response)
<font color="navy">{</font>
  ServletContext context = getServletContext();
  RequestDispatcher dispatcher = context.getRequestDispatcher(<font color="red">"/anotherURL"</font>);
  dispatcher.forward(request, response);
<font color="navy">}</font></pre><br /><br />This modified servlet is accessible through the URL /forwardServlet.<br /><br />When the client makes a request to our forwarding servlet this time, it no longer receives the redirect response. The dispatcher.forward line causes the servlet container to run the servlet associated with the url /anotherURL directly. The client will receive the result of /anotherURL, even though they requested /forwardServlet.<br /><br />A few more notes:<br /><br />* Redirect is a two step process. The web server tells the browser to request a second URL.<br />* If the user reloads, the second URL will be reloaded (/anotherURL instead of /redirectServlet).<br />* Redirect is always slower than a forward because it requires a second client request.<br />* Attributes placed in the request scope of the redirecting servlet are not available in the request scope of the second rrequest.<br /><br />* Forwards are performed internally to the web server.<br />* The browser is completely unaware that the forward took place - the original URL (/forwardServlet) remains in the address bar.<br />* If the user does a reload, the browser will repeat the original request with the original URL (/forwardServlet).<br /><br />from: <a href="http://forum.java.sun.com/thread.jspa?threadID=627801&amp;messageID=3597659">http://forum.java.sun.com/thread.jspa?threadID=627801&amp;messageID=3597659</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/78822.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-11-03 02:19 <a href="http://www.blogjava.net/weidagang2046/articles/78822.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>表单提交中Get和Post方式的区别有5点 </title><link>http://www.blogjava.net/weidagang2046/articles/78815.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Thu, 02 Nov 2006 16:55:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/78815.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/78815.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/78815.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/78815.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/78815.html</trackback:ping><description><![CDATA[
		<span style="FONT-SIZE: 12px">
				<font face="Courier New" size="2">表单提交中get和post方式的区别归纳如下几点：<br /><br />1. get是从服务器上获取数据，post是向服务器传送数据。<br />2. get是把参数数据队列加到提交表单的ACTION属性所指的URL中，值和表单内各个字段一一对应，在URL中可以看到。post是通过HTTP post机制，将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。<br />3. 对于get方式，服务器端用Request.QueryString获取变量的值，对于post方式，服务器端用Request.Form获取提交的数据。<br />4. get传送的数据量较小，不能大于2KB。post传送的数据量较大，一般被默认为不受限制。但理论上，IIS4中最大量为80KB，IIS5中为100KB。<br />5. get安全性非常低，post安全性较高。<br /><br />from: <a href="http://club.yesky.com/bbs.php?url=viewthread2.php?tid=1508192">http://club.yesky.com/bbs.php?url=viewthread2.php?tid=1508192</a></font>
		</span>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/78815.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-11-03 00:55 <a href="http://www.blogjava.net/weidagang2046/articles/78815.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入理解和改进JSP/Servlet会话管理机制</title><link>http://www.blogjava.net/weidagang2046/articles/78352.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Tue, 31 Oct 2006 12:11:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/78352.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/78352.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/78352.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/78352.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/78352.html</trackback:ping><description><![CDATA[ 在Web服务器端编程中，会话状态管理是一个经常必须考虑的重要问题。本文分析JSP/Servlet的会话管理机制及其所面临的问题，然后提出了一种改进的会话管理方法。  <br /><br />一、Servlet的会话管理机制  <br /><br />根据设计，HTTP是一种无状态的协议。它意味着Web应用并不了解有关同一用户以前请求的信息。维持会话状态信息的方法之一是使用Servlet或者 JSP容器提供的会话跟踪功能。Servlet API规范定义了一个简单的HttpSession接口，通过它我们可以方便地实现会话跟踪。  <br /><br />HttpSession 接口提供了存储和返回标准会话属性的方法。标准会话属性如会话标识符、应用数据等，都以“名字-值”对的形式保存。简而言之，HttpSession接口提供了一种把对象保存到内存、在同一用户的后继请求中提取这些对象的标准办法。在会话中保存数据的方法是setAttribute(String s,  Object o)，从会话提取原来所保存对象的方法是getAttribute(String s)。  <br /><br />在HTTP协议中，当用户不再活动时不存在显式的终止信号。由于这个原因，我们不知道用户是否还要再次返回，如果不采取某种方法解决这个问题，内存中会积累起大量的HttpSession对象。  <br /><br />为此，Servlet采用“超时限制”的办法来判断用户是否还在访问：如果某个用户在一定的时间之内没有发出后继请求，则该用户的会话被作废，他的 HttpSession对象被释放。会话的默认超时间隔由Servlet容器定义。这个值可以通过getMaxInactiveInterval方法获得，通过setMaxInactiveInterval方法修改，这些方法中的超时时间以秒计。如果会话的超时时间值设置成-1，则会话永不超时。 Servlet可以通过getLastAccessedTime方法获得当前请求之前的最后一次访问时间。  <br /><br />要获得HttpSession对象，我们可以调用HttpServletRequest对象的getSession方法。为了正确地维持会话状态，我们必须在发送任何应答内容之前调用getSession方法。  <br />用户会话既可以用手工方法作废，也可以自动作废。作废会话意味着从内存中删除HttpSession对象以及它的数据。例如，如果一定时间之内（默认30分钟）用户不再发送请求，Java Web Server自动地作废他的会话。  <br /><br />Servlet/JSP会话跟踪机制有着一定的局限，比如：  <br /><br />· 会话对象保存在内存之中，占用了可观的资源。  <br /><br />· 会话跟踪依赖于Cookie。由于各种原因，特别是安全上的原因，一些用户关闭了Cookie。  <br /><br />· 会话跟踪要用到服务器创建的会话标识符。在多个Web服务器以及多个JVM的环境中，Web服务器不能识别其他服务器创建的会话标识符，会话跟踪机制无法发挥作用。  <br />要深入理解会话跟踪机制，首先我们必须理解在Servlet/JSP容器中会话如何运作。  <br /><br /><br />二、会话标识符  <br /><br />每当新用户请求一个使用了HttpSession对象的JSP页面，JSP容器除了发回应答页面之外，它还要向浏览器发送一个特殊的数字。这个特殊的数字称为“会话标识符”，它是一个唯一的用户标识符。此后，HttpSession对象就驻留在内存之中，等待同一用户返回时再次调用它的方法。  <br /><br />在客户端，浏览器保存会话标识符，并在每一个后继请求中把这个会话标识符发送给服务器。会话标识符告诉JSP容器当前请求不是用户发出的第一个请求，服务器以前已经为该用户创建了HttpSession对象。此时，JSP容器不再为用户创建新的HttpSession对象，而是寻找具有相同会话标识符的 HttpSession对象，然后建立该HttpSession对象和当前请求的关联。  <br /><br />会话标识符以Cookie的形式在服务器和浏览器之间传送。如果浏览器不支持Cookie又如何呢？此时，对服务器的后继请求将不会带有会话标识符。结果，JSP容器认为该请求来自一个新用户，它会再创建一个HttpSession对象，而以前创建的HttpSession对象仍旧驻留在内存中，但该用户以前的会话信息却丢失了。  <br /><br />另外，Servlet/JSP容器只认可它自己创建的会话标识符。如果同一Web应用在“Web农场”（Web farm）的多台服务器上运行，则必须存在这样一种机制：保证来自同一用户的请求总是被定向到处理该用户第一次请求的服务器。  <br /><br />三、伪会话管理机制  <br /><br />如前所述，基于Cookie的会话管理技术面临着种种问题。下面我们要设计一种新的会话管理机制来解决这些问题。这种会话管理机制称为“伪会话”（Pseudo Session）机制，它具有如下特点：  <br /><br />· 对象和数据不是保存在内存中，而是以文本文件形式保存。每一个文本文件与一个特定的用户关联，文件的名字就是会话的标识符。因此，文件名字必须是唯一的。  <br />· 文本文件保存在一个专用的目录中，所有Web服务器都可以访问这个目录。因此，伪会话可以用于Web农场。  <br />· 会话标识符不作为Cookie发送，而是直接编码到URL里面。因此，采用伪会话技术要求修改所有的超级链接，包括HTML表单的ACTION属性。  <br />此外，实现伪会话管理机制时我们还要考虑到以下几点：  <br />· 它应该与应用无关，其他想要实现同样功能的开发者应该能够方便地重用它。  <br />· 考虑到安全原因，应该有一种为会话标识符生成随机数字的办法。  <br />· 为了作废过期的会话，应该设定一个超时值。同一个用户，如果他超过一定的时间之后再次返回，他将获得一个新的会话标识符。此举能够防止未经授权的用户冒用其他人的会话。  <br />· 应该有一种收集过期会话并删除相应文本文件的机制。  <br />· 如果用户使用已经过期的会话标识符再次访问服务器，即使这个会话标识符的文本文件还没有删除，系统也不应该允许用户使用原来的会话。  <br />· 同时，应该存在一种更新会话文本文件最后改动时间的机制，使得用户在会话过期时限之前返回时会话总是保持最新且合法的状态数据。  <br /><br /><br />四、实现伪会话管理机制  <br /><br />下面所介绍的工程称为PseudoSession，它是伪会话机制一个很简单的实现。考虑到移植性，我们以JavaBean的形式实现它。PseudoSessionBean的完整代码可以从本文后面下载。  <br /><br />PseudoSessionBean拥有如下域（Field）：  <br />public String path;public long timeOut;  <br />path是保存所有会话文本文件的目录。如果Web服务器的数量在一个以上，这个目录必须允许所有服务器访问。然而，为了防止用户直接访问这些文本文件，这个路径应该不允许用户直接访问。解决这个问题的一种方法是使用Web网站根之外的目录。  <br /><br />timeOut 是用户的最后一个请求到会话过期作废之间的时间。在PseudoSessionBean的代码清单中，timeOut设置成了以毫秒表示的20分钟，这是一个比较合理的超时时间值。对于任何用户，如果他在这个超时时间之后才继续发出请求，他将得到一个新的会话标识符。  <br />PseudoSessionBean有4个方法：getSessionID，setValue，getValue，deleteAllInvalidSessions。  <br /><br />4.1 getSessionID方法  <br />getSessionID方法的声明如下：  <br />public String getSessionID(HttpServletRequest request)  <br />这个方法应该在每一个JSP页面的开头调用。它完成如下任务：  <br />· 如果用户是第一次访问，则为该用户设定一个新的会话标识符。  <br />· 检查URL所带会话标识符的合法性。如果会话标识符已经过期，则getSessionID方法返回一个新的会话标识符。  <br />下面我们来看看getSessionID方法的工作过程。  <br />String sessionId = request.getParameter("sessionId");  <br />validSessionIdFound是一个标记，用于指示会话标识符是否合法。validSessionIdFound的初始值是false。  <br />boolean validSessionIdFound = false;  <br />long类型的now变量包含请求出现时的服务器时间。该变量用于确定用户会话的合法性。  <br />long now = System.currentTimeMillis();  <br />如果找到了会话标识符，则getSessionID方法检查它的合法性。检查过程如下：  <br />· 一个合法的会话标识符必须有对应的同名文本文件。  <br />· 文件的最后修改时间加上timeOut应该大于当前时间。  <br />· 如果存在与会话对应的文本文件，但文件已经过期，则原来的文件被删除。  <br />· 把合法会话标识符所对应文本文件的最后修改日期改为now。  <br />这些任务主要借助File对象完成，创建File对象的参数就是会话文本文件的路径：  <br />if  (sessionId!=null) {File f = new File(path + sessionId);if (f.exists()) { if  (f.lastModified() + timeOut &gt; now) { // 会话合法// 使用setLastModified时，如果文件已经被其他程序锁定，// 程序不会产生任何异常，但文件数据不会改变f.setLastModified(now); validSessionIdFound = true; } else { // 会话已经过期 // 删除文件f.delete(); }}  // end if (f.exists) } // end if (sessionId!=null)  <br />如果不存在合法的会话标识符，则getSessionID方法生成一个会话标识符以及相应的文本文件：  <br />if  (!validSessionIdFound) { sessionId = Long.toString(now); // 创建文件 File f  = new File(path + sessionId); try {f.createNewFile(); } catch (IOException ioe) {}} // end of if !validSessionIdFound  <br />程序保证文件名字随机性的方法非常简单：把当前的系统时间直接转换成会话标识符。对于那些涉及敏感数据的应用，我们应该考虑运用更安全的随机数生成器来生成会话标识符。  <br />综上所述，getSessionID并不总是返回新的合法会话标识符：它返回的标识符可能与传递给它的标识符相同，也可能是新创建的会话标识符。  <br />为了保证JSP页面拥有合法的会话标识符以便调用setValue、getValue方法，每个JSP页面都必须在开头位置调用getSesstionID方法。  <br /><br /><br />4.2 setValue方法  <br />setValue 方法保存value字符串以及与它关联的字符串名字。这种“名字-值”对很容易使人想起Dictionary对象。setValue方法要求在第一个参数中提供合法的会话标识符，它假定在自己被调用之前getSessionID方法已经执行，经过检验的合法会话标识符必然存在，因此它不再对传入的会话标识符进行合法性检验。  <br />setValue方法按如下规则保存名字-值对：  <br />· 如果与value值关联的name以前还没有保存过，则新的名字-值对加入到文本文件的末尾。  <br />· 如果value字符串关联的name值以前已经保存过，则原来保存的值被新的value值替换。  <br />setValue方法按照如下格式保存名字-值对，注意“名字”是大小写敏感的：  <br />name-1 value-1name-2 value-2name-3 value-3...name-n value-n  <br />setValue方法的声明如下：  <br />public void setValue(String sessionId, String name, String value)  <br />setValue 方法首先寻找与当前会话对应的文本文件。如果不能找到文本文件，则setValue方法不做任何事情直接返回。如果找到了会话文本文件，setValue 方法读取文本文件的各个行，然后比较读入的行与name：如果读入的文本行开头与name一样，则说明该名字已经保存，setValue方法将替换该行后面的值；如果name不能与读入的文本行匹配，则这行文本被直接复制到一个临时文件。  <br />这部分功能的实现代码如下：  <br />try { FileReader fr = new FileReader(path + sessionId); BufferedReader br = new BufferedReader(fr); FileWriter fw = new FileWriter(path + sessionId + ".tmp"); BufferedWriter bw = new BufferedWriter(fw); String s; while ((s = br.readLine()) != null)if (!s.startsWith(name + " ")) { bw.write(s); bw.newLine();} bw.write(name + " " + value); bw.newLine(); bw.close(); br.close(); fw.close(); bw.close(); . . .}catch (FileNotFoundException e) {}catch (IOException e) { System.out.println(e.toString());}  <br />原来文本文件中的所有行复制到临时文件之后，setValue方法删除原来的文本文件，然后把临时文件改成会话文本文件的名字：  <br />File f = new File(path + sessionId + ".tmp");File dest = new File(path + sessionId);dest.delete();f.renameTo(dest);  <br /><br /><br />4.3 getValue方法  <br />getValue 方法用于提取原来保存在伪会话中的数据。正如setValue方法，getValue方法也要求传入一个合法的会话标识符，而且getValue方法不再对传入的会话标识符进行合法性检查。getValue方法的第二个参数是待提取数据的name，返回值是与指定name关联的value。  <br />getValue方法的声明如下：  <br />public String getValue(String sessionId, String name)  <br />getValue方法的基本执行过程如下：首先找到会话文本文件，然后按行读入直至找到与name匹配的文本行；找到匹配的文本行之后，getValue方法返回该行保存的value；如果不能找到，getValue方法返回null。  <br /><br /><br />4.4 deleteAllInvalidSessions方法  <br />deleteAllInvalidSessions 方法删除那些与已经过期的会话关联的文本文件。由于调用getSessionID方法时过期的会话文本文件会被删除， deleteAllInvalidSessions方法并不是关键的方法。什么时候调用这个方法由应用自己决定。例如，我们可以编写一个专用的后台程序，由这个程序每天一次清除所有过期的文本文件。最简单的办法是在JSP文件末尾调用deleteAllInvalidSessions方法，但如果网站比较繁忙，重复地调用deleteAllInvalidSessions方法将降低整个网站的响应能力。一种明智的做法是：编写一个在访问量较少的时候自动进行清理的后台程序。  <br />deleteAllInvalidSessions方法的声明如下：  <br />public void deleteAllInvalidSessions()  <br />它首先把所有会话文本文件的名字读入files字符串数组：  <br />File dir = new File(path); String[] files = dir.list();  <br />deleteAllInvalidSessions方法比较文本文件的最后修改时间（加上超时时间）和系统当前时间，确定会话是否过期。long类型的变量now用于保存系统的当前时间。  <br />long now = System.currentTimeMillis();  <br />接下来，deleteAllInvalidSessions方法通过循环访问files数组，依次检查每个文件的lastModified属性。所有与过期会话关联的文件都将被删除：  <br />for  (int i=0; i&lt;files.length; i++) { File f = new File(path + files[i]); if  (f.lastModified() + timeOut &gt; now) f.delete(); // 删除过期的会话文本文件}  <br /><br /><br />五、应用实例  <br /><br /><br />编译好PseudoSessionBean这个JavaBean之后，我们就可以利用伪会话管理机制来管理Web应用的会话状态信息了。由于不必再使用服务器的会话管理机制，我们可以在page指令中把session属性设置为false关闭默认的JSP/Servlet会话管理功能。  <br />&lt;%@ page session="false" %&gt;  <br /><br />然后，我们用JSP的&lt;jsp:useBean&gt;标记告诉JSP容器程序要使用PseudoSessionBean：  <br />&lt;jsp:useBean id="PseudoSessionId" scope="application" class="pseudosession.PseudoSessionBean" /&gt;  <br /><br />在上面这个&lt;jsp:useBean&gt;标记中，class属性值是“包.类名字”形式。当然，对于不同的包名字，class属性的值应该作相应的修改。注意Bean的scope属性是“application”，这是因为我们要在应用的所有页面中使用这个Bean。在这个应用中，把Bean的 scope属性设置为“application”具有最好的效率，因为我们只需创建Bean对象一次就可以了。另外，正如前面所提到的， getSessionID方法必须在所有其他代码之前调用。  <br />&lt;% String sessionId = PseudoSessionId.getSessionID(request);%&gt;  <br />为了说明PseudoSessionBean的应用，下面我们来看两个JSP页面，它们是index.jsp和secondPage.jsp。index.jsp页面在伪会话变量中保存用户的名字，而secondPage.jsp则提取这个用户名字。  <br /><br />index.jsp页面的代码如下：  <br />&lt;%@ page session="false" contentType="text/html;charset=gb2312" %&gt; <br />&lt;jsp:useBean id="PseudoSessionId" scope="application" class="pseudosession.PseudoSessionBean" /&gt; <br />&lt;% String sessionId = PseudoSessionId.getSessionID(request);%&gt; <br />&lt;html&gt; <br />&lt;head&gt; <br />&lt;title&gt;伪会话&lt;/title&gt; <br />&lt;/head&gt; <br />&lt;body&gt; <br />&lt;h1&gt;伪会话管理机制&lt;/h1&gt; <br />&lt;% String userName = "bulbul"; PseudoSessionId.setValue(sessionId, "userName", userName);%&gt; <br />&lt;a href=secondPage.jsp?sessionId=&lt;%=sessionId%&gt;&gt;点击此处&lt;/a&gt; <br />&lt;form method="post" action=anotherPage.jsp?sessionId=&lt;%=sessionId%&gt;&gt; <br />输入数据:&lt;input type="text" name="sample"&gt; <br />&lt;input type="submit" name="Submit" value="Submit"&gt; <br />&lt;/form&gt; <br />&lt;/body&gt; <br />&lt;/html&gt; <br />&lt;% PseudoSessionId.deleteAllInvalidSessions();%&gt;  <br /><br />注意，包括&lt;form&gt;标记的action属性在内，所有的超级链接都已经改写，现在都包含了会话标识符。另外也请注意页面的最后调用了deleteAllInvalidSessions方法。  <br />secondPage.jsp页面只简单地返回以前保存的用户名字。  <br />&lt;%@ contentType="text/html;charset=gb2312" page session="false" %&gt; <br />&lt;jsp:useBean id="PseudoSessionId" scope="application" class="pseudosession.PseudoSessionBean" /&gt; <br />&lt;% String sessionId = PseudoSessionId.getSessionID(request);%&gt; <br />&lt;html&gt; <br />&lt;head&gt; <br />&lt;title&gt;第2个页面&lt;/title&gt; <br />&lt;/head&gt; <br />&lt;body&gt; <br />&lt;% String userName = PseudoSessionId.getValue(sessionId, "userName"); out.println("用户名字是 " + userName);%&gt; <br />&lt;/body&gt; <br />&lt;/html&gt; <br /><br />from: <a href="http://www.tech521.com/autoGenFile/techData/17/1708.htm">http://www.tech521.com/autoGenFile/techData/17/1708.htm</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/78352.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-10-31 20:11 <a href="http://www.blogjava.net/weidagang2046/articles/78352.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Servlet和JSP的线程安全问题</title><link>http://www.blogjava.net/weidagang2046/articles/78114.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Mon, 30 Oct 2006 09:50:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/78114.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/78114.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/78114.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/78114.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/78114.html</trackback:ping><description><![CDATA[
		<p style="TEXT-INDENT: 2em">编写Servlet和JSP的时候,线程安全问题很容易被忽略,如果忽视了这个问题,你的程序就存在潜在的隐患. </p>
		<p style="TEXT-INDENT: 2em">
		</p>
		<p style="TEXT-INDENT: 2em">1.Servlet的生命周期 </p>
		<p style="TEXT-INDENT: 2em">Servlet的生命周期是由Web容器负责的,当客户端第一次请求Servlet 时,容器负责初始化Servlet,也就是实例化这个Servlet类.以后这个实例就负责客户端的请求,一般不会再实例化其他Servlet类,也就是有多个线程在使用这个实例.Servlet之所以比CGI效率高就是因为Servlet是多线程的.如果该Servlet被声明为单线程模型的话,容器就会维护一个实例池,那么将存在多个实例. </p>
		<p style="TEXT-INDENT: 2em">
		</p>
		<p style="TEXT-INDENT: 2em">2.Servlet的线程安全 </p>
		<p style="TEXT-INDENT: 2em">Servlet规范已经声明Servlet不是线程安全的,所以在开发Servlet的时候要注要这个问题.这里以一个现实的模型来说明问题,先定义一个Servlet类,再定义一个SmulateMultiThread类和WebContainer类. </p>
		<p style="TEXT-INDENT: 2em">import javax.servlet.http.HttpServlet; </p>
		<p style="TEXT-INDENT: 2em">import javax.servlet.ServletException; </p>
		<p style="TEXT-INDENT: 2em">import javax.servlet.http.HttpServletRequest; </p>
		<p style="TEXT-INDENT: 2em">import javax.servlet.http.HttpServletResponse; </p>
		<p style="TEXT-INDENT: 2em">import java.io.IOException; </p>
		<p style="TEXT-INDENT: 2em">
		</p>
		<p style="TEXT-INDENT: 2em">//该类模拟多线程Servlet的情况 </p>
		<p style="TEXT-INDENT: 2em">public class SmulateMultiThread implements Runnable{ </p>
		<p style="TEXT-INDENT: 2em">public SmulateMultiThread() { </p>
		<p style="TEXT-INDENT: 2em">} </p>
		<p style="TEXT-INDENT: 2em">public static void main(String[] args) { </p>
		<p style="TEXT-INDENT: 2em">//处理100个请求 </p>
		<p style="TEXT-INDENT: 2em">for(int i=0;i&lt;100;i++) </p>
		<p style="TEXT-INDENT: 2em">{ </p>
		<p style="TEXT-INDENT: 2em">new Thread(new SmulateMultiThread()).start(); </p>
		<p style="TEXT-INDENT: 2em">} </p>
		<p style="TEXT-INDENT: 2em">} </p>
		<p style="TEXT-INDENT: 2em">public void run() { </p>
		<p style="TEXT-INDENT: 2em">HttpServletRequest request=null; </p>
		<p style="TEXT-INDENT: 2em">HttpServletResponse response=null; </p>
		<p style="TEXT-INDENT: 2em">try { </p>
		<p style="TEXT-INDENT: 2em">WebContainer.getServlet().doGet(request, response); </p>
		<p style="TEXT-INDENT: 2em">} </p>
		<p style="TEXT-INDENT: 2em">catch (IOException ex) { </p>
		<p style="TEXT-INDENT: 2em">} </p>
		<p style="TEXT-INDENT: 2em">catch (ServletException ex) { </p>
		<p style="TEXT-INDENT: 2em">} </p>
		<p style="TEXT-INDENT: 2em">} </p>
		<p style="TEXT-INDENT: 2em">} </p>
		<p style="TEXT-INDENT: 2em">//这是一个Servlet类 </p>
		<p style="TEXT-INDENT: 2em">class UnsafeServlet extends HttpServlet{ </p>
		<p style="TEXT-INDENT: 2em">private String unsafe; </p>
		<p style="TEXT-INDENT: 2em">public void init() throws ServletException { </p>
		<p style="TEXT-INDENT: 2em">} </p>
		<p style="TEXT-INDENT: 2em">//Process the HTTP Get request </p>
		<p style="TEXT-INDENT: 2em">public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { </p>
		<p style="TEXT-INDENT: 2em">unsafe=Thread.currentThread().getName(); </p>
		<p style="TEXT-INDENT: 2em">System.out.println(unsafe); </p>
		<p style="TEXT-INDENT: 2em">} </p>
		<p style="TEXT-INDENT: 2em">} </p>
		<p style="TEXT-INDENT: 2em">//这个是容器类 </p>
		<p style="TEXT-INDENT: 2em">class WebContainer{ </p>
		<p style="TEXT-INDENT: 2em">private static UnsafeServlet us=new UnsafeServlet(); </p>
		<p style="TEXT-INDENT: 2em">public static UnsafeServlet getServlet(){ </p>
		<p style="TEXT-INDENT: 2em">return us; </p>
		<p style="TEXT-INDENT: 2em">} </p>
		<p style="TEXT-INDENT: 2em">} </p>
		<p style="TEXT-INDENT: 2em">输出了100不同的线程名称,如果有100个请求同时被这个Servlet处理的话,那么unsafe就可能有100种去值,最后客户端将得到错误的值.比如客户1请求的线程名为thread-1,但是返回给他的可能是thread- 20.表现在现实中就是,我登陆的用户名是user1,登陆后变成了user2. </p>
		<p style="TEXT-INDENT: 2em">那么怎样才能是Servlet安全呢,凡是多个线程可以共享的就不要使用(实例变量+类变量),就这么简单.也可以使用synchronized同步方法,但是这样效率不高,还可以使用单线程模型,这样的话效率就更低了,100个请求同时来的时候就要实例化100个实例. </p>
		<p style="TEXT-INDENT: 2em">方法中的临时变量是不会影响线程安全的,因为他们是在栈上分配空间,而且每个线程都有自己私有的栈空间. </p>
		<p style="TEXT-INDENT: 2em">
		</p>
		<p style="TEXT-INDENT: 2em">3.JSP中线程安全 </p>
		<p style="TEXT-INDENT: 2em">JSP的本质是Servlet,所有只要明白了Servlet的安全问题,JSP的安全问题应该很容易理解.使用&lt;%! %&gt;声明的变量是Servlet的实例变量,不是线程安全的,其他都是线程安全的. </p>
		<p style="TEXT-INDENT: 2em">&lt;%! String unsafeVar; %&gt;//不是线程安全的 </p>
		<p style="TEXT-INDENT: 2em">&lt;% String safeVar; %&gt;// 线程安全的 </p>
		<p style="TEXT-INDENT: 2em">
		</p>
		<p style="TEXT-INDENT: 2em">总结:线程安全问题主要是由实例变量造成的,不管在Servlet还是JSP,或者在Struts的Action里面,不要使用实例变量,任何方法里面都不要出现实例变量,你的程序就是线程安全的.<br /><br />from: <a href="http://www.dwww.cn/new/200610111726432966.html">http://www.dwww.cn/new/200610111726432966.html</a></p>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/78114.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-10-30 17:50 <a href="http://www.blogjava.net/weidagang2046/articles/78114.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>编写线程安全的JSP程序</title><link>http://www.blogjava.net/weidagang2046/articles/78113.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Mon, 30 Oct 2006 09:48:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/78113.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/78113.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/78113.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/78113.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/78113.html</trackback:ping><description><![CDATA[
		<span id="zoom">
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">JSP默认是以多线程方式执行的，这是JSP与</font>
						<a href="http://dev.21tx.com/web/asp/" target="_blank">
								<font face="Courier New">ASP</font>
						</a>
						<font face="Courier New">，</font>
						<a href="http://dev.21tx.com/web/php/" target="_blank">
								<font face="Courier New">PHP</font>
						</a>
						<font face="Courier New">，</font>
						<a href="http://dev.21tx.com/web/perl/" target="_blank">
								<font face="Courier New">Perl</font>
						</a>
						<font face="Courier New">等脚本语言不一样的地方，也是它的优势之一，但如果不注意多线程中的同步问题，会使所写的JSP程序有难以发现的错误。下面以一个例子说明JSP中的多线程问题及解决方法。 <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">一、JSP的中存在的多线程问题： <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">当客户端第一次请求某一个JSP文件时，服务端把该JSP编译成一个CLASS文件，并创建一个该类的实例，然后创建一个线程处理CLIENT端的请求。如果有多个客户端同时请求该JSP文件，则服务端会创建多个线程。每个客户端请求对应一个线程。以多线程方式执行可大大降低对系统的资源需求,提高系统的并发量及响应时间.对JSP中可能用的的变量说明如下: <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">实例变量 <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">实例变量是在堆中分配的,并被属于该实例的所有线程共享，所以不是线程安全的. <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">JSP系统提供的8个类变量 <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">JSP中用到的OUT,REQUEST,RESPONSE,SESSION,CONFIG,PAGE,PAGECONXT是线程安全的,APPLICATION在整个系统内被使用,所以不是线程安全的. <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">局部变量 <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">局部变量在堆栈中分配,因为每个线程都有它自己的堆栈空间,所以是线程安全的. <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">静态类 <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">静态类不用被实例化,就可直接使用,也不是线程安全的. <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">外部资源: <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">在程序中可能会有多个线程或进程同时操作同一个资源(如:多个线程或进程同时对一个文件进行写操作).此时也要注意同步问题. <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">二、下面的例子存在的多线程问题： <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">&lt;%@ page import=" <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<a href="http://dev.21tx.com/java/" target="_blank">
								<font face="Courier New">Java</font>
						</a>
						<font face="Courier New">x.naming.*, <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">java.util.*, <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">java.sql.*, <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<a href="http://dev.21tx.com/web/" target="_blank">
								<font face="Courier New">Web</font>
						</a>
						<font face="Courier New">logic.common.* <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">" %&gt; <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">&lt;% <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">String name <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">String product; <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">long quantity; <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">name=request.getParameter("name"); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">product=request.getParameter("product"); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">quantity=request.getParameter("quantity"); /*(1)*/ <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">savebuy(); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">%&gt; <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">&lt;%! <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">public void savebuy() <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">{ <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">/*进行</font>
						<a href="http://dev.21tx.com/database/" target="_blank">
								<font face="Courier New">数据库</font>
						</a>
						<font face="Courier New">操作，把数据保存到表中*/ <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">try { <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">Properties props = new Properties(); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">props.put("user","scott"); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">props.put("password","tiger"); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">props.put("server","DEMO"); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">Driver myDriver = (Driver) iver").newInstance(); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">conn = myDriver.connect("</font>
						<a href="http://dev.21tx.com/java/adv/jdbc/" target="_blank">
								<font face="Courier New">JDBC</font>
						</a>
						<font face="Courier New">:weblogic:</font>
						<a href="http://dev.21tx.com/database/oracle/" target="_blank">
								<font face="Courier New">Oracle</font>
						</a>
						<font face="Courier New">", props); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">stmt = conn.createStatement(); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">String inssql = "insert into buy(empid, name, dept) values (?, ?, ?,?)"; <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">stmt = conn.prepareStatement(inssql); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">stmt.setString(1, name); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">stmt.setString(2, procuct); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">stmt.setInt(3, quantity); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">stmt.execute(); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">} <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">catch (Exception e) <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">{ <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">System.out.println("SQLException was thrown: " + e.getMessage()); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">} <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">finally //close connections and { <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">try { <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">if(stmt != null) <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">stmt.close(); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">if(conn != null) <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">conn.close(); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">} catch (SQLException sqle) { <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">System.out.println("SQLException was thrown: " + sqle.getMessage()); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">} <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">} <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">} <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">%&gt; <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">上面的程序模拟网上购物中的一部分,把用户在浏览器中输入的用户名,购买的物品名称,数量保存到表BUY中。在savebuy()函数中用到了实例变量,所以它不是线程安全的.因为:程序中的每一条语句都不是原子操作,如name= request.getParameter("name");在执行是会对应多个机器指令,在任何时候都可能因系统调度而转入睡眠状态,让其他的线程继续执行.如果线程A在执行到(1)的时候转入睡眠状态,线程B开始执行并改变QUANTITY的值,那么当又到A执行时,它会从调用savebuy()函数开始执行，这样它保存到表中的QUANTITY是被线程B改过的值,那么线程A对应的用户所实际购买的数量与保持到表中的数据不一致.这是个很严重的问题. <br /><br /></font>
						<span id="zoom">
						</span>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">三、解决方法 <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">采用单线程方式 <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">在该JSP文件中加上: ,使它以单线程方式执行,这时，仍然只有一个实例，所有客户端的请求以串行方 式执行。这样会降低系统的性能. <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">对函数savebuy()加synchronized进行线程同步,该JSP仍然以多线程方式执行，但也会降低系统的性能 <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">public synchronized void savebuy() <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">{ <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">...... <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">} <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">采用局部变量代替实例变量,函数savebuy()声明如下: <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">因为在savebuy()中使用的是传给他的形参,是在堆栈中分配的,所以是线程安全的. <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">public void savebuy(String name,String product, int quantity) <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">{ <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">...... <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">} <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">调用方式改为: <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">&lt;% <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">String name <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">String product; <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">long quantity; <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">name=request.getParameter("name"); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">product=request.getParameter("product"); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">quantity=request.getParameter("quantity"); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">savebuy(name,product,quantity) <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">%&gt; <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">如果savebuy的参数很多,或这些数据要在很多地方用到,也可声明一个类,并用他做参数,如: <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">public class buyinfo <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">{ <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">String name; <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">String product; <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">long quantity; <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">} <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">public void savebuy(buyinfo info) <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">{ <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">...... <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">} <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">调用方式改为: <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">&lt;% <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">buyinfo userbuy = new buyinfo(); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">userbuy.name=request.getParameter("name"); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">userbuy.product=request.getParameter("product"); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">userbuy.quantity=request.getParameter("quantity"); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">savebuy(userbuy); <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">%&gt; <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">
								<br />
						</font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">所以最好是用3,因为1,2会降低系统的性能. <br /></font>
				</p>
				<p style="TEXT-INDENT: 2em">
						<font face="Courier New">多线程问题一般只有在在大并发量访问时，才有可能出现，并且很难重复出现，所以应在编程时就时刻注意。 <br /><br />from: <a href="http://java.ccidnet.com/art/3737/20030321/530503_1.html">http://java.ccidnet.com/art/3737/20030321/530503_1.html</a></font>
				</p>
		</span>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/78113.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-10-30 17:48 <a href="http://www.blogjava.net/weidagang2046/articles/78113.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入研究Servlet线程安全性问题</title><link>http://www.blogjava.net/weidagang2046/articles/78107.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Mon, 30 Oct 2006 09:33:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/78107.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/78107.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/78107.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/78107.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/78107.html</trackback:ping><description><![CDATA[Servlet/JSP技术和ASP、PHP等相比，由于其多线程运行而具有很高的执行效率。由于Servlet/JSP默认是以多线程模式执行的，所以，在编写代码时需要非常细致地考虑多线程的安全性问题。然而，很多人编写Servlet/JSP程序时并没有注意到多线程安全性的问题，这往往造成编写的程序在少量用户访问时没有任何问题，而在并发用户上升到一定值时，就会经常出现一些莫明其妙的问题。<br />　　<br />　　<b>Servlet的多线程机制</b><br />　　<br />　　Servlet体系结构是建立在Java多线程机制之上的，它的生命周期是由Web容器负责的。当客户端第一次请求某个Servlet时， Servlet容器将会根据web.xml配置文件实例化这个Servlet类。当有新的客户端请求该Servlet时，一般不会再实例化该 Servlet类，也就是有多个线程在使用这个实例。Servlet容器会自动使用线程池等技术来支持系统的运行，如图1所示。<br />　　
<center>　<img src="http://java.chinaitlab.com/imgfiles/2005.5.23.9.53.47.1.jpg" /></center><br />　　
<center>图1 Servlet线程池</center><br />　　<br />　　这样，当两个或多个线程同时访问同一个Servlet时，可能会发生多个线程同时访问同一资源的情况，数据可能会变得不一致。所以在用Servlet构建的Web应用时如果不注意线程安全的问题，会使所写的Servlet程序有难以发现的错误。<br />　　<br />　　<b>Servlet的线程安全问题</b><br />　　<br />　　Servlet的线程安全问题主要是由于实例变量使用不当而引起的，这里以一个现实的例子来说明。<br />　　<br />　　Import javax.servlet. *;<br />　　Import javax.servlet.http. *;<br />　　Import java.io. *;<br />　　Public class Concurrent Test extends HttpServlet {PrintWriter output;<br />　　Public void service (HttpServletRequest request,<br />　　HttpServletResponse response) throws ServletException, IOException {String username;<br />　　Response.setContentType ("text/html; charset=gb2312");<br />　　Username = request.getParameter ("username");<br />　　Output = response.getWriter ();<br />　　Try {Thread. sleep (5000); //为了突出并发问题，在这设置一个延时<br />　　} Catch (Interrupted Exception e){}<br />　　output.println("用户名:"+Username+"&lt;BR&gt;");<br />　　}<br />　　}<br />　　<br />　　该Servlet中定义了一个实例变量output，在service方法将其赋值为用户的输出。当一个用户访问该Servlet时，程序会正常的运行，但当多个用户并发访问时，就可能会出现其它用户的信息显示在另外一些用户的浏览器上的问题。这是一个严重的问题。为了突出并发问题，便于测试、观察，我们在回显用户信息时执行了一个延时的操作。假设已在web.xml配置文件中注册了该Servlet，现有两个用户a和b同时访问该Servlet（可以启动两个IE浏览器，或者在两台机器上同时访问）,即同时在浏览器中输入：<br />　　<br />　　a： http://localhost: 8080/servlet/ConcurrentTest? Username=a<br />　　<br />　　b： http://localhost: 8080/servlet/ConcurrentTest? Username=b<br />　　<br />　　如果用户b比用户a回车的时间稍慢一点，将得到如图2所示的输出：<br />　　
<center>　<img src="http://java.chinaitlab.com/imgfiles/2005.5.23.9.53.56.2.jpg" /></center><br />　　
<center>图2 a用户和b用户的浏览器输出</center><br />　　<br />　　从图2中可以看到，Web服务器启动了两个线程分别处理来自用户a和用户b的请求，但是在用户a的浏览器上却得到一个空白的屏幕，用户a的信息显示在用户b的浏览器上。该Servlet存在线程不安全问题。下面我们就从分析该实例的内存模型入手,观察不同时刻实例变量output的值来分析使该 Servlet线程不安全的原因。<br />　　<br />　　Java的内存模型JMM（Java Memory Model）JMM主要是为了规定了线程和内存之间的一些关系。根据JMM的设计，系统存在一个主内存(Main Memory)，Java中所有实例变量都储存在主存中，对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory)，工作内存由缓存和堆栈两部分组成，缓存中保存的是主存中变量的拷贝，缓存可能并不总和主存同步，也就是缓存中变量的修改可能没有立刻写到主存中；堆栈中保存的是线程的局部变量，线程之间无法相互直接访问堆栈中的变量。根据JMM，我们可以将论文中所讨论的Servlet实例的内存模型抽象为图3所示的模型。<br />　　
<center>　<img src="http://java.chinaitlab.com/imgfiles/2005.5.23.9.54.4.3.jpg" /></center><br />　　
<center>图3 Servlet实例的JMM模型</center><br />　　<br />　　下面根据图3所示的内存模型，来分析当用户a和b的线程（简称为a线程、b线程）并发执行时，Servlet实例中所涉及变量的变化情况及线程的执行情况，如图4所示。<br />　　
<center><img src="http://java.chinaitlab.com/imgfiles/2005.5.23.9.54.11.4.GIF" /></center><br />　　
<center>图4 Servlet实例的线程调度情况</center><br />　　<br />　　从图4中可以清楚的看到，由于b线程对实例变量output的修改覆盖了a线程对实例变量output的修改，从而导致了用户a的信息显示在了用户b的浏览器上。如果在a线程执行输出语句时，b线程对output的修改还没有刷新到主存，那么将不会出现图2所示的输出结果，因此这只是一种偶然现象，但这更增加了程序潜在的危险性。<br /><br />from: <a href="http://java.chinaitlab.com/JavaSecurity/31737.html">http://java.chinaitlab.com/JavaSecurity/31737.html</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/78107.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-10-30 17:33 <a href="http://www.blogjava.net/weidagang2046/articles/78107.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Servlet及JSP中的多线程同步问题</title><link>http://www.blogjava.net/weidagang2046/articles/78106.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Mon, 30 Oct 2006 09:31:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/78106.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/78106.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/78106.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/78106.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/78106.html</trackback:ping><description><![CDATA[　　Servlet/JSP技术和ASP、PHP等相比，由于其多线程运行而具有很高的执行效率。由于Servlet/JSP默认是以多线程模式执行的，所以，在编写代码时需要非常细致地考虑多线程的同步问题。然而，很多人编写Servlet/JSP程序时并没有注意到多线程同步的问题，这往往造成编写的程序在少量用户访问时没有任何问题，而在并发用户上升到一定值时，就会经常出现一些莫明其妙的问题，对于这类随机性的问题调试难度也很大。<br />　　<br />　　<b>一、在Servlet/JSP中的几种变量类型</b><br />　　<br />　　在编写Servlet/JSP程序时，对实例变量一定要小心使用。因为实例变量是非线程安全的。在Servlet/JSP中，变量可以归为下面的几类：<br />　　<br />　　<b>1. 类变量</b><br />　　<br />　　request，response，session，config，application，以及JSP页面内置的page, pageContext。其中除了application外，其它都是线程安全的。<br />　　<br />　　<b>2. 实例变量</b><br />　　<br />　　实例变量是实例所有的，在堆中分配。在Servlet/JSP容器中，一般仅实例化一个Servlet/JSP实例，启动多个该实例的线程来处理请求。而实例变量是该实例所有的线程所共享，所以，实例变量不是线程安全的。<br />　　<br />　　<b>3. 局部变量</b><br />　　<br />　　局部变量在堆栈中分配，因为每一个线程有自己的执行堆栈，所以，局部变量是线程安全的。<br />　　<br />　　<b>二、在Servlet/JSP中的多线程同步问题</b><br />　　<br />　　在JSP中，使用实例变量要特别谨慎。首先请看下面的代码：<br />　　<br />　　// instanceconcurrenttest.jsp<br />　　&lt;%@ page contentType="text/html;charset=GBK" %&gt;<br />　　&lt;%!<br />　　//定义实例变量<br />　　String username;<br />　　String password;<br />　　java.io.PrintWriter output;<br />　　%&gt;<br />　　&lt;%<br />　　//从request中获取参数<br />　　username = request.getParameter("username");<br />　　password = request.getParameter("password");<br />　　output = response.getWriter();<br />　　showUserInfo();<br />　　%&gt;<br />　　&lt;%!<br />　　public void showUserInfo() {<br />　　//为了突出并发问题，在这儿首先执行一个费时操作<br />　　int i =0;<br />　　double sum = 0.0;<br />　　while (i++ &lt; 200000000) {<br />　　sum += i;<br />　　}<br />　　<br />　　output.println(Thread.currentThread().getName() + "&lt;br&gt;");<br />　　output.println("username:" + username + "&lt;br&gt;");<br />　　output.println("password:" + password + "&lt;br&gt;");<br />　　}<br />　　%&gt;<br />　　<br />　　在这个页面中，首先定义了两个实例变量，username和password。然后在从request中获取这两个参数，并调用 showUserInfo()方法将请求用户的信息回显在该客户的浏览器上。在一个用户访问是，不存在问题。但在多个用户并发访问时，就会出现其它用户的信息显示在另外一些用户的浏览器上的问题。这是一个严重的问题。为了突出并发问题，便于测试、观察，我们在回显用户信息时执行了一个模拟的费时操作，比如，下面的两个用户同时访问（可以启动两个IE浏览器，或者在两台机器上同时访问）：<br />　　<br />　　a： http://localhost:8080/instanceconcurrenttest.jsp?username=a&amp;password=123<br />　　<br />　　b： http://localhost:8080/instanceconcurrenttest.jsp?username=b&amp;password=456<br />　　<br />　　如果a点击链接后，b再点击链接，那么，a将返回一个空白屏幕，b则得到a以及b两个线程的输出。请看下面的屏幕截图：<br />　　
<center>　<img src="http://java.chinaitlab.com/imgfiles/2005.7.5.16.28.16.11.gif" /></center><br />　　
<center>图1：a的屏幕</center><br />　　
<center>　<img src="http://java.chinaitlab.com/imgfiles/2005.7.5.16.28.50.22.gif" /></center><br />　　
<center>图2：b的屏幕</center><br />　　从运行结果的截图上可以看到，Web服务器启动了两个线程分别来处理来自a和b的请求，但是在a却得到一个空白的屏幕。这是因为上面程序中的 output, username和password都是实例变量，是所有线程共享的。在a访问该页面后，将output设置为a的输出，username, password分别置为a的信息，而在a执行printUserInfo()输出username和password信息前，b又访问了该页面，把 username和password置为了b的信息，并把输出output指向到了b。随后a的线程打印时，就打印到了b的屏幕了，并且，a的用户名和密码也被b的取代。请参加下图所示：<br />　　<br />　　
<center><img src="http://java.chinaitlab.com/imgfiles/2005.7.5.16.29.7.33.gif" /></center><br />　　
<center>图3：a、b两个线程的时间线</center><br />　　<br />　　而实际程序中，由于设置实例变量，使用实例变量这两个时间点非常接近，所以，像本例的同步问题并没有这么突出，可能会偶尔出现，但这却更加具有危险性，也更加难于调试。<br />　　<br />　　同样，对于Servlet也存在实例变量的多线程问题，请看上面页面的Servlet版：<br />　　<br />　　// InstanceConcurrentTest.java<br />　　import javax.servlet.*;<br />　　import javax.servlet.http.*;<br />　　import java.io.PrintWriter;<br />　　public class InstanceConcurrentTest extends HttpServlet<br />　　{<br />　　String username;<br />　　String password;<br />　　PrintWriter out;<br />　　public void doGet(HttpServletRequest request,<br />　　HttpServletResponse response)<br />　　throws ServletException,java.io.IOException<br />　　{<br />　　//从request中获取参数<br />　　username = request.getParameter("username");<br />　　password = request.getParameter("password");<br />　　System.out.println(Thread.currentThread().getName() +<br />　　" | set username:" + username);<br />　　out = response.getWriter();<br />　　showUserInfo();<br />　　}<br />　　public void showUserInfo() {<br />　　//为了突出并发问题，在这儿首先执行一个费时操作<br />　　int i =0;<br />　　double sum = 0.0;<br />　　while (i++ &lt; 200000000) {<br />　　sum += i;<br />　　}<br />　　out.println("thread:" + Thread.currentThread().getName());<br />　　out.println("username:"+ username);<br />　　out.println("password:" + password);<br />　　}<br />　　}<br />　　<br />　　<b>三、解决方案</b><br />　　<br />　　<b>1. 以单线程运行Servlet/JSP</b><br />　　<br />　　在JSP中，通过设置：，在Servlet中，通过实现javax.servlet.SingleThreadModel，此时Web容器将保证JSP或Servlet实例以单线程方式运行。<br />　　<br />　　重要提示：在测试中发现，Tomcat 4.1.17不能正确支持isThreadSafe属性，所以，指定isTheadSafe为false后，在Tomcat 4.1.17中仍然出现多线程问题，这是Tomcat 4.1.17的Bug。在Tomcat 3.3.1和Resin 2.1.5中测试通过。<br />　　<br />　　<b>2. 去除实例变量，通过参数传递</b><br />　　<br />　　从上面的分析可见，应该在Servlet/JSP中尽量避免使用实例变量。比如，下面的修正代码，去除了实例变量，通过定义局部变量，并参数进行传递。这样，由于局部变量是在线程的堆栈中进行分配的，所以是线程安全的。不会出现多线程同步的问题。代码如下：<br />　　<br />　　&lt;%@ page contentType="text/html;charset=GBK" %&gt;<br />　　&lt;%<br />　　//使用局部变量<br />　　String username;<br />　　String password;<br />　　java.io.PrintWriter output;<br />　　//从request中获取参数<br />　　username = request.getParameter("username");<br />　　password = request.getParameter("password");<br />　　output = response.getWriter();<br />　　showUserInfo(output, username, password);<br />　　%&gt;<br />　　&lt;%!<br />　　public void showUserInfo(java.io.PrintWriter _output,<br />　　String _username, String _password) {<br />　　//为了突出并发问题，在这儿首先执行一个费时操作<br />　　int i =0;<br />　　double sum = 0.0;<br />　　while (i++ &lt; 200000000) {<br />　　sum += i;<br />　　}<br />　　_output.println(Thread.currentThread().getName() + "&lt;br&gt;");<br />　　_output.println("username:" + _username + "&lt;br&gt;");<br />　　_output.println("password:" + _password + "&lt;br&gt;");<br />　　}<br />　　%&gt;<br />　　<br />　　<b>注：</b>有的资料上指出在printUserInfo()方法或者实例变量的相关操作语句上使用synchronized关键字进行同步，但这样并不能解决多线程的问题。因为，这样虽然可以使对实例变量的操作代码进行同步，但并不能阻止一个线程使用另外一个线程修改后的“脏的”实例变量。所以，除了降低运行效率外，不会起到预期效果。<br /><br />from: <a href="http://java.chinaitlab.com/ServletJsp/33087.html">http://java.chinaitlab.com/ServletJsp/33087.html</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/78106.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-10-30 17:31 <a href="http://www.blogjava.net/weidagang2046/articles/78106.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2EE</title><link>http://www.blogjava.net/weidagang2046/articles/74586.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Wed, 11 Oct 2006 08:06:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/74586.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/74586.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/74586.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/74586.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/74586.html</trackback:ping><description><![CDATA[
		<font face="Verdana">    </font>
		<a href="http://www.itisedu.com/phrase/200603091447335.html" target="_new">
				<font face="Verdana">J2EE</font>
		</a>
		<font face="Verdana">（Java 2 Enterprise Edition）是建立在Java 2平台上的企业级应用的解决方案。J2EE技术的基础便是Java 2平台，不但有J2SE平台的所有功能，同时还提供了对</font>
		<a href="http://www.itisedu.com/phrase/200604241156485.html" target="_new">
				<font face="Verdana">EJB</font>
		</a>
		<font face="Verdana">，</font>
		<a href="http://www.itisedu.com/phrase/200603091005185.html" target="_new">
				<font face="Verdana">Servlet</font>
		</a>
		<font face="Verdana">，JSP，</font>
		<a href="http://www.itisedu.com/phrase/200604231236585.html" target="_new">
				<font face="Verdana">XML</font>
		</a>
		<font face="Verdana">等技术的全面支持，其最终目标是成为一个支持企业级应用开发的</font>
		<a href="http://www.itisedu.com/phrase/200603122156385.html" target="_new">
				<font face="Verdana">体系结构</font>
		</a>
		<font face="Verdana">，简化企业解决方案的开发，部署和管理等复杂问题。事实上，J2EE已经成为企业级开发的工业标准和首选平台。</font>
		<p>
				<font face="Verdana">　　J2EE并非一个产品，而是一系列的标准。市场上可以看到很多实现了J2EE的产品，如BEA WebLogic，IBM WebSphere以及</font>
				<a href="http://www.itisedu.com/phrase/200603091812115.html" target="_new">
						<font face="Verdana">开源</font>
				</a>
				<font face="Verdana">的</font>
				<a href="http://www.itisedu.com/phrase/200605111343075.html" target="_new">
						<font face="Verdana">JBoss</font>
				</a>
				<font face="Verdana">等等。</font>
		</p>
		<p>
				<font face="Verdana">      J2EE，是sun公司提出的一个标准，符合这个标准的产品叫"实现"；其中你下载的sun公司的j2ee开发包中就有一个这样的"实现"，而jboss，weblogic，websphere都是j2ee标准的一个"实现"。由于jboss，weblogic，websphere自身带有j2ee的<a href="http://www.itisedu.com/phrase/200604241228185.html" target="_new">api</a>，所以可以不使用sun的j2ee实现。</font>
		</p>
		<p>
				<font face="Verdana">
						<strong>一. J2EE的概念</strong>
				</font>
		</p>
		<p>
				<font face="Verdana">      目前，Java 2平台有3个版本，它们是适用于小型设备和智能卡的Java 2平台Micro版（Java 2 Platform Micro Edition，<a href="http://www.itisedu.com/phrase/200603091545555.html" target="_new">J2ME</a>）、适用于桌面系统的Java 2平台标准版（Java 2 Platform Standard Edition，J2SE）、适用于创建服务器应用<a href="http://www.itisedu.com/phrase/200604232224305.html" target="_new">程序</a>和服务的Java2平台企业版（Java 2 Platform Enterprise Edition，J2EE）。</font>
		</p>
		<p>
				<font face="Verdana">      J2EE是一种利用Java 2平台来简化企业解决方案的开发、部署和管理相关的复杂问题的体系结构。J2EE技术的基础就是核心Java平台或Java 2平台的标准版，J2EE不仅巩固了标准版中的许多优点，例如"编写一次、随处运行"的特性、方便存取<a href="http://www.itisedu.com/phrase/200602271218062.html" target="_new">数据库</a>的<a href="http://www.itisedu.com/phrase/200604151904545.html" target="_new">JDBC</a> API、<a href="http://www.itisedu.com/phrase/200604031336425.html" target="_new">CORBA</a>技术以及能够在Internet应用中保护数据的安全<a href="http://www.itisedu.com/phrase/200603061709535.html" target="_new">模式</a>等等，同时还提供了对 EJB（<a href="http://www.itisedu.com/phrase/200603091138035.html" target="_new">Enterprise JavaBean</a>s）、Java Servlets API、JSP（Java Server Pages）以及XML技术的全面支持。其最终目的就是成为一个能够使企业开发者大幅缩短投放市场时间的体系结构。 </font>
		</p>
		<p>
				<font face="Verdana">      J2EE体系结构提供<a href="http://www.itisedu.com/phrase/200603101709095.html" target="_new">中间层</a>集成<a href="http://www.itisedu.com/phrase/200603061723295.html" target="_new">框架</a>用来满足无需太多费用而又需要高可用性、高可靠性以及可扩展性的应用的<a href="http://www.itisedu.com/phrase/200603101518295.html" target="_new">需求</a>。通过提供统一的开发平台，J2EE降低了开发多层应用的费用和复杂性，同时提供对现有应用程序集成强有力支持，完全支持Enterprise JavaBeans，有良好的向导支持打包和部署应用，添加目录支持，增强了安全机制，提高了性能。</font>
		</p>
		<p>
				<font face="Verdana">
						<strong>二. J2EE的优势</strong>
				</font>
		</p>
		<p>
				<font face="Verdana">     J2EE为搭建具有可伸缩性、灵活性、易维护性的商务系统提供了良好的机制:<br />      保留现存的IT资产: 由于企业必须适应新的商业需求，利用已有的企业<a href="http://www.itisedu.com/phrase/200603011147495.html" target="_new">信息系统</a>方面的投资，而不是重新制定全盘方案就变得很重要。这样，一个以渐进的（而不是激进的，全盘否定的）方式建立在已有系统之上的服务器端平台机制是公司所需求的。J2EE<a href="http://www.itisedu.com/phrase/200604241328115.html" target="_new">架构</a>可以充分利用用户原有的投资，如一些公司使用的BEA Tuxedo、IBM CICS, IBM Encina,、Inprise VisiBroker 以及Netscape Application Server。这之所以成为可能是因为J2EE拥有广泛的业界支持和一些重要的'企业计算'领域供应商的参与。每一个供应商都对现有的客户提供了不用废弃已有投资，进入可移植的J2EE领域的升级途径。由于基于J2EE平台的产品几乎能够在任何<a href="http://www.itisedu.com/phrase/200602281634075.html" target="_new">操作系统</a>和硬件配置上运行，现有的操作系统和硬件也能被保留使用。</font>
		</p>
		<p>
				<font face="Verdana">      高效的开发: J2EE允许公司把一些通用的、很繁琐的服务端任务交给<a href="http://www.itisedu.com/phrase/200604241155005.html" target="_new">中间件</a>供应商去完成。这样开发人员可以集中精力在如何创建商业逻辑上，相应地缩短了开发时间。高级中间件供应商提供以下这些复杂的中间件服务:</font>
		</p>
		<p>
				<font face="Verdana">      状态管理服务 -- 让开发人员写更少的代码，不用关心如何管理状态，这样能够更快地完成程序开发。<br />      持续性服务 -- 让开发人员不用对数据访问逻辑进行编码就能编写应用程序，能生成更轻巧，与数据库无关的应用程序，这种应用程序更易于开发与维护。<br />      分布式共享数据<a href="http://www.itisedu.com/phrase/200603090845215.html" target="_new">对象</a>CACHE服务 -- 让开发人员编制高性能的系统，极大提高整体部署的伸缩性。<br />      支持异构环境: J2EE能够开发部署在异构环境中的可移植程序。基于J2EE的应用程序不依赖任何特定操作系统、中间件、硬件。因此设计合理的基于J2EE的程序只需开发一次就可部署到各种平台。这在典型的异构企业计算环境中是十分关键的。J2EE标准也允许客户订购与J2EE兼容的第三方的现成的<a href="http://www.itisedu.com/phrase/200603302222545.html" target="_new">组件</a>，把他们部署到异构环境中，节省了由自己制订整个方案所需的费用。<br />      可伸缩性: 企业必须要选择一种服务器端平台，这种平台应能提供极佳的可伸缩性去满足那些在他们系统上进行商业运作的大批新客户。基于J2EE平台的应用程序可被部署到各种操作系统上。例如可被部署到高端UNIX与大型机系统，这种系统单机可支持64至256个处理器。（这是NT服务器所望尘莫及的）J2EE领域的供应商提供了更为广泛的负载平衡策略。能消除系统中的瓶颈，允许多台服务器集成部署。这种部署可达数千个处理器，实现可高度伸缩的系统，满足未来商业应用的需要。<br />      稳定的可用性: 一个服务器端平台必须能全天候运转以满足公司客户、合作伙伴的需要。因为INTERNET是全球化的、无处不在的，即使在夜间按计划停机也可能造成严重损失。若是意外停机，那会有灾难性后果。J2EE部署到可靠的操作环境中，他们支持长期的可用性。一些J2EE部署在WINDOWS环境中，客户也可选择健壮性能更好的操作系统如Sun Solaris、IBM <a href="http://www.itisedu.com/phrase/200604232131175.html" target="_new">OS</a>/390。最健壮的操作系统可达到99.999%的可用性或每年只需5分钟停机时间。这是实时性很强商业系统理想的选择。</font>
		</p>
		<p>
				<font face="Verdana">
						<strong>三. J2EE 的四层模型</strong>
				</font>
		</p>
		<p>
				<font face="Verdana">      J2EE使用多层的分布式应用模型，应用逻辑按功能划分为组件，各个应用组件根据他们所在的层分布在不同的机器上。事实上，sun设计J2EE的初衷正是为了解决两层模式(<a href="http://www.itisedu.com/phrase/200604231337375.html" target="_new">client</a>/server)的弊端，在传统模式中，<a href="http://www.itisedu.com/phrase/200603082208195.html" target="_new">客户端</a>担当了过多的角色而显得臃肿，在这种模式中，第一次部署的时候比较容易，但难于升级或改进，可伸展性也不理想，而且经常基于某种专有的协议――通常是某种数据库协议。它使得重用业务逻辑和界面逻辑非常困难。现在J2EE 的多层企业级应用模型将两层化模型中的不同层面切分成许多层。一个多层化应用能够为不同的每种服务提供一个独立的层，以下是 J2EE 典型的四层结构:</font>
		</p>
		<p>
				<font face="Verdana">      运行在客户端机器上的客户层组件<br />      运行在J2EE服务器上的Web层组件<br />      运行在J2EE服务器上的业务逻辑层组件<br />      运行在<a href="http://www.itisedu.com/phrase/200604232115455.html" target="_new">EIS</a>服务器上的企业信息系统(Enterprise <a href="http://www.itisedu.com/phrase/200604222047055.html" target="_new">information system</a>)层<a href="http://www.itisedu.com/phrase/200604232134205.html" target="_new">软件</a></font>
		</p>
		<p>
				<font face="Verdana">      J2EE应用程序组件<br />      J2EE应用程序是由组件构成的.J2EE组件是具有独立功能的软件单元，它们通过相关的<a href="http://www.itisedu.com/phrase/200603090857555.html" target="_new">类</a>和<a href="http://www.itisedu.com/phrase/200602282323195.html" target="_new">文件</a>组装成J2EE应用程序，并与其他组件交互。J2EE说明书中定义了以下的J2EE组件:<br />      应用客户端程序和applets是客户层组件.<br />      Java Servlet和JavaServer Pages(JSP)是web层组件.<br />      Enterprise JavaBeans(EJB)是业务层组件.</font>
		</p>
		<p>
				<font face="Verdana">      客户层组件<br />      J2EE应用程序可以是基于web方式的,也可以是基于传统方式的.<br />      web 层组件J2EE web层组件可以是JSP 页面或Servlets.按照J2EE规范，静态的HTML页面和Applets不算是web层组件。</font>
		</p>
		<p>
				<font face="Verdana">      正如下图所示的客户层那样，web层可能包含某些 <a href="http://www.itisedu.com/phrase/200604251741535.html" target="_new">JavaBean</a> 对象来处理用户输入，并把<br />输入发送给运行在业务层上的enterprise bean 来进行处理。</font>
		</p>
		<p>
				<font face="Verdana">      业务层组件<br />      业务层代码的逻辑用来满足银行，零售，金融等特殊商务领域的需要,由运行在业务层上的enterprise bean 进行处理. 下图表明了一个enterprise bean 是如何从客户端程序接收数据，进行处理(如果必要的话), 并发送到EIS 层储存的，这个过程也可以逆向进行。</font>
		</p>
		<p>
				<font face="Verdana">      有三种企业级的bean: 会话(session) beans, 实体(entity) beans, 和 <a href="http://www.itisedu.com/phrase/200603090938465.html" target="_new">消息</a>驱动(message-driven) beans. 会话bean 表示与客户端程序的临时交互. 当客户端程序执行完后, 会话bean 和相关数据就会消失. 相反, 实体bean 表示数据库的表中一行永久的记录. 当客户端程序中止或服务器关闭时, 就会有潜在的服务保证实体bean 的数据得以保存.消息驱动 bean 结合了会话bean 和 <a href="http://www.itisedu.com/phrase/200604261605045.html" target="_new">JMS</a>的消息监听器的特性, 允许一个业务层组件异步接收JMS 消息.</font>
		</p>
		<p>
				<font face="Verdana">      企业信息系统层<br />      企业信息系统层处理企业信息系统<a href="http://www.itisedu.com/phrase/200602281756135.html" target="_new">软件包</a>括企业基础建设系统例如企业资源计划 (ERP), 大型机事务处理, <a href="http://www.itisedu.com/phrase/200603011056245.html" target="_new">数据库系统</a>,和其它的遗留信息系统. 例如，J2EE 应用组件可能为了数据库连接需要访问企业信息系统</font>
		</p>
		<p>
				<font face="Verdana">      我们就J2EE的各种组件、服务和API，进行更加详细的阐述，看看在开发不同<a href="http://www.itisedu.com/phrase/200603051002565.html" target="_new">类型</a>的企业级应用时，根据各自需求和目标的不同，应当如何灵活使用并组合不同的组件和服务。</font>
		</p>
		<p>
				<font face="Verdana">· Servlet</font>
		</p>
		<p>
				<font face="Verdana">      Servlet是Java平台上的CGI技术。Servlet在服务器端运行，动态地生成Web页面。与传统的CGI和许多其它类似CGI的技术相比，Java Servlet具有更高的效率并更容易使用。对于Servlet，重复的请求不会导致同一程序的多次转载，它是依靠<a href="http://www.itisedu.com/phrase/200603091754305.html" target="_new">线程</a>的方式来支持并发访问的。</font>
		</p>
		<p>
				<font face="Verdana">· JSP</font>
		</p>
		<p>
				<font face="Verdana">      JSP(Java Server Page)是一种实现普通静态HTML和动态页面输出混合编码的技术。从这一点来看，非常类似Microsoft ASP、<a href="http://www.itisedu.com/phrase/200604241503425.html" target="_new">PHP</a>等技术。借助形式上的内容和外观表现的分离，Web页面制作的任务可以比较方便地划分给页面设计人员和程序员，并方便地通过JSP来合成。在运行时态，JSP将会被首先转换成Servlet，并以Servlet的形态编译运行，因此它的效率和功能与Servlet相比没有差别，一样具有很高的效率。</font>
		</p>
		<p>
				<font face="Verdana">· EJB</font>
		</p>
		<p>
				<font face="Verdana">      EJB定义了一组可重用的组件：Enterprise Beans。开发人员可以利用这些组件，像搭积木一样建立分布式应用。在装配组件时，所有的Enterprise Beans都需要配置到EJB服务器(一般的Weblogic、WebSphere等<a href="http://www.itisedu.com/phrase/200606221217315.html" target="_new">J2EE应用服务器</a>都是EJB服务器)中。EJB服务器作为容器和低层平台的桥梁管理着EJB容器，并向该容器提供访问系统服务的能力。所有的EJB实例都运行在EJB容器中。EJB容器提供了系统级的服务，控制了EJB的生命周期。EJB容器为它的开发人员代管了诸如安全性、远程连接、生命周期管理及事务管理等技术环节，简化了商业逻辑的开发。EJB中定义了三种Enterprise Beans：</font>
		</p>
		<p>
				<font face="Verdana">◆ Session Beans</font>
		</p>
		<p>
				<font face="Verdana">◆ Entity Beans</font>
		</p>
		<p>
				<font face="Verdana">◆ Message-driven Beans</font>
		</p>
		<p>
				<font face="Verdana">· JDBC</font>
		</p>
		<p>
				<font face="Verdana">      JDBC(Java <a href="http://www.itisedu.com/phrase/200604231244235.html" target="_new">Database</a> Connectivity，<a href="http://www.itisedu.com/phrase/200604241247155.html" target="_new">Java数据库连接</a>)API是一个标准<a href="http://www.itisedu.com/phrase/200604022014515.html" target="_new">SQL</a>(<a href="http://www.itisedu.com/phrase/200604241049435.html" target="_new">Structured Query Language</a>，结构化查询语言)数据库访问接口，它使数据库开发人员能够用标准Java API编写数据库应用程序。JDBC API主要用来连接数据库和直接调用SQL命令执行各种SQL语句。利用JDBC API可以执行一般的SQL语句、动态SQL语句及带IN和OUT参数的存储过程。Java中的JDBC相当与Microsoft平台中的<a href="http://www.itisedu.com/phrase/200604151956245.html" target="_new">ODBC</a>(<a href="http://www.itisedu.com/phrase/200604241248475.html" target="_new">Open Database Connectivity</a>)。</font>
		</p>
		<p>
				<font face="Verdana">· JMS</font>
		</p>
		<p>
				<font face="Verdana">      JMS(<a href="http://www.itisedu.com/phrase/200604261607125.html" target="_new">Java Message Service</a>，<a href="http://www.itisedu.com/phrase/200604261600355.html" target="_new">Java消息服务</a>)是一组Java应用接口，它提供创建、发送、接收、读取消息的服务。JMS API定义了一组公共的应用程序接口和相应语法，使得Java应用能够和各种消息中间件进行通信，这些消息中间件包括IBM MQ-Series、Microsoft MSMQ及纯Java的SonicMQ。通过使用JMS API，开发人员无需掌握不同消息产品的使用方法，也可以使用统一的JMS API来操纵各种消息中间件。通过使用JMS，能够最大限度地提升消息应用的可移植性。 JMS既支持点对点的消息通信，也支持发布/订阅式的消息通信。</font>
		</p>
		<p>
				<font face="Verdana">· JNDI</font>
		</p>
		<p>
				<font face="Verdana">      由于J2EE应用程序组件一般分布在不同的机器上，所以需要一种机制以便于组件客户使用者查找和引用组件及资源。在J2EE体系中，使用JNDI(Java Naming and Directory Interface)定位各种对象，这些对象包括EJB、数据库驱动、JDBC数据源及消息连接等。JNDI API为应用程序提供了一个统一的接口来完成标准的目录操作，如通过对象属性来查找和定位该对象。由于JNDI是独立于目录协议的，应用还可以使用JNDI访问各种特定的目录服务，如LDAP、NDS和DNS等。</font>
		</p>
		<p>
				<font face="Verdana">· JTA</font>
		</p>
		<p>
				<font face="Verdana">      JTA(Java Transaction API)提供了J2EE中处理事务的标准接口，它支持事务的开始、回滚和提交。同时在一般的J2EE平台上，总提供一个JTS(Java Transaction Service)作为标准的事务处理服务，开发人员可以使用JTA来使用JTS。</font>
		</p>
		<p>
				<font face="Verdana">· JCA</font>
		</p>
		<p>
				<font face="Verdana">      JCA(J2EE Connector <a href="http://www.itisedu.com/phrase/200604241327575.html" target="_new">Architecture</a>)是J2EE体系架构的一部分，为开发人员提供了一套连接各种企业信息系统(EIS，包括ERP、<a href="http://www.itisedu.com/phrase/200604231327195.html" target="_new">SCM</a>、CRM等)的体系架构，对于EIS开发商而言，它们只需要开发一套基于JCA的EIS连接适配器，开发人员就能够在任何的J2EE应用服务器中连接并使用它。基于JCA的连接适配器的实现，需要涉及J2EE中的事务管理、安全管理及连接管理等服务组件。</font>
		</p>
		<p>
				<font face="Verdana">· <a href="http://www.itisedu.com/phrase/200604261751455.html" target="_new">JMX</a></font>
		</p>
		<p>
				<font face="Verdana">      JMX(<a href="http://www.itisedu.com/phrase/200604262159065.html" target="_new">Java Management Extensions</a>)的前身是JMAPI。JMX致力于解决分布式<a href="http://www.itisedu.com/phrase/200604022111095.html" target="_new">系统管理</a>的问题。JMX是一种<a href="http://www.itisedu.com/phrase/200604111116455.html" target="_new">应用编程接口</a>、可扩展对象和方法的集合体，可以跨越各种异构操作系统平台、系统体系结构和网络传输协议，开发无缝集成的面向系统、网络和服务的管理应用。JMX是一个完整的网络管理应用程序开发环境，它同时提供了厂商需要收集的完整的特性清单、可生成资源清单表格、图形化的用户接口；访问SNMP的网络API；主机间<a href="http://www.itisedu.com/phrase/200604241405415.html" target="_new">远程过程调用</a>；数据库访问方法等。</font>
		</p>
		<p>
				<font face="Verdana">· JAAS</font>
		</p>
		<p>
				<font face="Verdana">      JAAS(Java Authentication and Authorization Service)实现了一个Java版本的标准Pluggable Authentication Module(PAM)的框架。JAAS可用来进行用户身份的鉴定，从而能够可靠并安全地确定谁在执行Java代码。同时JAAS还能通过对用户进行授权，实现基于用户的访问控制。</font>
		</p>
		<p>
				<font face="Verdana">· <a href="http://www.itisedu.com/phrase/200605111438435.html" target="_new">JAC</a>C</font>
		</p>
		<p>
				<font face="Verdana">      JACC(Java Authorization Service Provider Contract for Containers)在J2EE应用服务器和特定的授权认证服务器之间定义了一个连接的协约，以便将各种授权认证服务器插入到J2EE产品中去。</font>
		</p>
		<p>
				<font face="Verdana">· JAX-RPC</font>
		</p>
		<p>
				<font face="Verdana">      通过使用JAX-RPC(Java API for XML-based RPC)，已有的Java类或Java应用都能够被重新包装，并以Web Services的形式发布。JAX-RPC提供了将RPC参数(in/out)编码和解码的API，使开发人员可以方便地使用SOAP消息来完成RPC调用。同样，对于那些使用EJB(Enterprise JavaBeans)的商业应用而言，同样可以使用JAX-RPC来包装成Web服务，而这个Web Servoce的WSDL界面是与原先的EJB的方法是对应一致的。JAX-RPC为用户包装了Web服务的部署和实现，对Web服务的开发人员而言，SOAP/WSDL变得透明，这有利于加速Web服务的开发周期。</font>
		</p>
		<p>
				<font face="Verdana">· JAXR</font>
		</p>
		<p>
				<font face="Verdana">      JAXR（Java API for XML Registries）提供了与多种类型注册服务进行交互的API。JAXR运行客户端访问与JAXR规范相兼容的Web Servcices，这里的Web Services即为注册服务。一般来说，注册服务总是以Web Services的形式运行的。JAXR支持三种注册服务类型：JAXR Pluggable Provider、Registry-specific JAXR Provider、JAXR Bridge Provider(支持UDDI Registry和ebXML Registry/Repository等)。</font>
		</p>
		<p>
				<font face="Verdana">· SAAJ</font>
		</p>
		<p>
				<font face="Verdana">      SAAJ(SOAP with Attachemnts API for Java)是JAX-RPC的一个增强，为进行低层次的SOAP消息操纵提供了支持。</font>
		</p>
		<p>
				<font face="Verdana">
						<strong>四. J2EE 的结构</strong>
				</font>
		</p>
		<p>
				<font face="Verdana">      这种基于组件，具有平台无关性的J2EE 结构使得J2EE 程序的编写十分简单，因为业务逻辑被封装成可复用的组件，并且J2EE 服务器以容器的形式为所有的组件类型提供后台服务. 因为你不用自己开发这种服务, 所以你可以集中精力解决手头的业务问题.</font>
		</p>
		<p>
				<font face="Verdana">      容器和服务</font>
		</p>
		<p>
				<font face="Verdana">      容器设置定制了J2EE服务器所提供得内在支持，包括安全，事务管理，JNDI(Java Naming and Directory Interface)寻址,远程连接等服务，以下列出最重要的几种服务：</font>
		</p>
		<p>
				<font face="Verdana">      J2EE安全(Security)模型可以让你配置 web 组件或enterprise bean ,这样只有被授权的用户才能访问系统资源. 每一客户属于一个特别的角色，而每个角色只允许激活特定的方法。你应在enterprise bean的布置描述中声明角色和可被激活的方法。由于这种声明性的方法，你不必编写加强安全性的规则。</font>
		</p>
		<p>
				<font face="Verdana">      J2EE 事务管理（Transaction Management）模型让你指定组成一个事务中所有方法间的关系，这样一个事务中的所有方法被当成一个单一的单元. 当客户端激活一个enterprise bean中的方法，容器介入一管理事务。因有容器管理事务，在enterprise bean中不必对事务的边界进行编码。要求控制分布式事务的代码会非常复杂。你只需在布置描述文件中声明enterprise bean的事务属性，而不用编写并调试复杂的代码。容器将读此文件并为你处理此enterprise bean的事务。</font>
		</p>
		<p>
				<font face="Verdana">      JNDI 寻址(JNDI Lookup)服务向企业内的多重名字和目录服务提供了一个统一的接口,这样应用程序组件可以访问名字和目录服务.</font>
		</p>
		<p>
				<font face="Verdana">      J2EE远程连接（Remote Client Connectivity）模型管理客户端和enterprise bean间的低层交互. 当一个enterprise bean创建后, 一个客户端可以调用它的方法就象它和客户端位于同一虚拟机上一样.</font>
		</p>
		<p>
				<font face="Verdana">      生存周期管理（Life Cycle Management）模型管理enterprise bean的创建和移除,一个enterprise bean在其生存周期中将会历经几种状态。容器创建enterprise bean，并在可用实例池与活动状态中移动他，而最终将其从容器中移除。即使可以调用enterprisebean的create及remove方法，容器也将会在后台执行这些任务。</font>
		</p>
		<p>
				<font face="Verdana">
						<strong>五、企业级应用示例</strong>
				</font>
		</p>
		<p>
				<font face="Verdana">      下面我们通过假设一个企业应用的J2EE实现，来了解各种组件和服务的应用。假设应用对象是<a href="http://www.itisedu.com/phrase/200603021438435.html" target="_new">计算机</a>产品的生产商/零售商的销售系统，这个销售系统能够通过自己的网站发布产品信息，同时也能将产品目录传送给计算机产品交易市场。销售系统能够在线接受订单(来自自己的Web网站或者来自计算机产品交易市场)，并随后转入内部企业管理系统进行相关的后续处理。</font>
		</p>
		<p>
				<font face="Verdana">      参见图1，这个企业应用可以这种方式架构。该企业应用的核心是产品目录管理和产品定购管理这两个业务逻辑，使用EJB加以实现，并部署在EJB容器中。由于产品目录和定购信息都需要持久化，因此使用JDBC连接数据库，并使用JTA来完成数据库存取事务。</font>
		</p>
		<font face="Verdana">
				<p>
						<img src="http://www.itisedu.com/manage/Upload/image/20063914472692.gif" border="0" />
						<br />图1 J2EE应用示例</p>
				<p>      然后使用JSP/Servlet来实现应用的Web表现：在线产品目录浏览和在线定购。为了将产品目录发送给特定的交易市场，使用JMS实现异步的基于消息的产品目录传输。为了使得更多的其它外部交易市场能够集成产品目录和定购业务，需要使用Web Services技术包装商业逻辑的实现。由于产品定购管理需要由公司内部雇员进行处理，因此需要集成公司内部的用户系统和访问控制服务以方便雇员的使用，使用JACC集成内部的访问控制服务，使用JNDI集成内部的用户目录，并使用JAAS进行访问控制。由于产品订购事务会触发后续的企业ERP系统的相关操作(包括仓储、财务、生产等)，需要使用JCA连接企业ERP。</p>
				<p>      最后为了将这个应用纳入到企业整体的系统管理体系中去，使用Application Client架构了一个管理客户端(与其它企业应用管理应用部署在一台机器上)，并通过JMX管理这个企业应用。<br /><br />from: <a href="http://www.itisedu.com/phrase/200603091447335.html">http://www.itisedu.com/phrase/200603091447335.html</a></p>
		</font>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/74586.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-10-11 16:06 <a href="http://www.blogjava.net/weidagang2046/articles/74586.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Eclipse 3.0 Runtime Adoption Guide</title><link>http://www.blogjava.net/weidagang2046/articles/68330.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Thu, 07 Sep 2006 09:27:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/68330.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/68330.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/68330.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/68330.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/68330.html</trackback:ping><description><![CDATA[
		<blockquote>
				<p>
						<b>Summary</b>
						<br />
				</p>
				<center>
						<b>This is a draft document. </b>
				</center>
				<br />This document details the steps required to <b>adopt</b> the new mechanisms found in the Eclipse 3.0 runtime. In most cases, plug-in developers do not need to follow these steps as the 3.0 runtime supports the 2.1-style API. However, the new runtime offers significant new <i>experimental</i> function. It is this function which is detailed here. 
<p></p><p><b>By Jeff McAffer, IBM OTI Labs</b><br /><font size="-1">January 27, 2004</font></p></blockquote>
		<hr width="100%" />
		<h2>Preamble</h2>
		<p>In this document the terms <i>deprecated</i> and <i>obsolete</i> are used to mean that the code is not part of the Eclipse runtime story going forward. For Eclipse 3.0 this code is still a full-fledged member of the API and is fully supported. </p>
		<h4>
		</h4>
		<p>The new runtime is 99% binary compatible with the original Eclipse runtime. That is, with the exception of the cases listed above, <b>most existing plug-ins will continue to run unchanged.</b></p>
		<p>Having said that, there are costs associated with the compatibilty layer and the full power of the new runtime is not available to legacy plug-ins. Plug-in developers interested in additional performance and function should consider adopting the new APIs and removing their dependence on the compatibility layer. Compatibility code shows up in three places:</p>
		<ul>
				<li>org.eclipse.core.boot - entire plug-in are legacy 
</li>
				<li>org.eclipse.core.runtime.compatibility - entire plug-in are legacy 
</li>
				<li>org.eclipse.core.runtime - various classes and methods are legacy </li>
		</ul>
		<p>The text below gives more detail on the which classes and methods are present for compatibility purposes as well as guidance on how to update your plug-in should you choose to.</p>
		<h3>Plug-ins and bundles</h3>
		<p>The Eclipse runtime has been refactored into two parts; classloading and prerequisite management, and extension/extension-point management. This split allows for natural/seamless adoption of the OSGi framework specification for classloading and prerequisite management. This in turn enables a range of new capabilities in the runtime from dynamic plug-in install/update/uninstall to security and increased configurability.</p>
		<p>While we continue to talk about <i>plug-ins</i>, in the new runtime a plug-in is really a <i>bundle</i> plus some extensions and extension-points. The term <i>bundle</i> is defined by the OSGi framework specification and refers to a collection of types and resources and associated inter-bundle prerequisite information. The <i>extension registry</i> (and related types) is the new form of the plug-in registry (and related types) and details only extension and extension-point information. By-in-large the extension registry API is the same as the relevant plug-in registry API (for more information see <a href="http://dev.eclipse.org/viewcvs/index.cgi/platform-core-home/runtime/adoption.html?rev=1.5#registries">Registries</a>).</p>
		<p>In the Eclipse 2.x runtime, the plug-in object has a number of roles and responsibilities:</p>
		<ul>
				<li>Lifecycle - The <tt>Plugin</tt> class implements method such as startup() and shutdown(). The runtime uses these methods to signal the plug-in that someone is interested in the function it provides. In response, plug-ins typically do a combination of: 
<ul><li>Registration - Hook various event mechanisms (e.g., register listeners) and otherwise make their presence known in the system (e.g., start needed threads). 
</li><li>Initialization - Initialize or prime their data structures and load models so they are ready for use. </li></ul></li>
				<li>Plug-in global data/function - While never explicitly put forth for this role, in common practice plug-in classes have become a place to hang data and function which is effectively global to the plug-in itself. In some cases this data/function is API in others it is internal. For example, the UI plug-in exposes as API methods such as <tt>getDialogSettings()</tt> and <tt>getWorkbench()</tt>. 
</li>
				<li>Context - The standard <tt>Plugin</tt> class provides access to various runtime-provided function such as preferences and logging. </li>
		</ul>
		<p>In the Eclipse 3.0 runtime picture, these roles and responsibilities are factored into distinct objects.</p>
		<dl>
				<dl>
						<dt>
								<b>Bundle</b>
						</dt>
						<dd>Bundles are the OSGi unit of modularity. There is one classloader per bundle and Eclipse-like inter-bundle class loading dependency graphs can be constructed. Bundles have lifecycle for start and stop and the OSGi framework broadcasts bundle related events (e.g., install, resolve, start, stop, uninstall, ...) to interested parties. Unlike the Eclipse <tt>Plugin</tt> class, the OSGi <tt>Bundle</tt> class is not extensible. That is, developers do not have the opportunity to define their own bundle class. 
</dd>
						<dt>
								<b>BundleActivator</b>
						</dt>
						<dd>BundleActivator is an interface defined by the OSGi framework. Each bundle can define a bundle activator class much like a plug-in can define its <tt>Plugin</tt> class. This class is instantiated by the framework and used to implement the <tt>start()</tt> and <tt>stop()</tt> lifecycle processing. There is a major difference however in the nature of this lifecycle processing. In Eclipse it is common (though not recommended) to have the <tt>Plugin</tt> classes do both initialization and registration. In OSGi activators must only do registration. Doing large amounts of initialization (or any other work) in <tt>BundleActivator.start()</tt> threatens the liveness of the system. 
</dd>
						<dt>
								<b>BundleContext</b>
						</dt>
						<dd>BundleContexts are the OSGi mechanism for exposing general system function to individual bundles. Each bundle has a unique and private instance of BundleContext which they can use to access system function (e.g., getBundles() to discover all bundles in the system). 
</dd>
						<dt>
								<b>Plugin</b>
						</dt>
						<dd>The new <tt>Plugin</tt> is very much like the original Eclipse <tt>Plugin</tt> class with the following exceptions: <tt>Plugin</tt> objects are no longer a) required or b) managed by the runtime and various methods have been deprecated (see below). As such, Plugin continues to provide context access. It is essentially a convenience mechanism providing a host of useful function and mechanisms but is no longer absolutely required. Much of the function provided there is also available on the <tt>Platform</tt> class in the runtime. 
<p><tt>Plugin</tt> also implements <tt>BundleActivator</tt>. This recognizes the convenience of having one central object representing the lifecycle and semantic of a plug-in. Note that this does not however sanction the eager initialization of data structures that is common in plug-ins today. We cannot stress enough that plug-ins can be activated because a somewhat peripheral class was referenced during verification of a class in some other plug-in. That is, just because your plug-in has been activated does not necessarily mean that its function is needed. Note also that you are free to define a different <tt>BundleActivator</tt> class or not have a bundle activator at all.</p></dd>
				</dl>
		</dl>
		<p>The steps required to port a 2.x <tt>Plugin</tt> class to Eclipse 3.0 depends on what the class is doing. As outlined above, most startup lifecycle work falls into one of the following categories:</p>
		<dl>
				<dl>
						<dt>
								<b>Initialization</b>
						</dt>
						<dd>Datastructure and model initialization is quite often done in <tt>Plugin.startup()</tt>. The natural/obvious mapping would be to do this work in a <tt>BundleActivator.start()</tt>, that is to leave the function on <tt>Plugin</tt>. <b>This is strongly discouraged.</b> As with 2.x plug-ins, 3.0 plug-ins/bundles may be started for many different reasons in many different circumstances. <br />An actual example from Eclipse 2.0 days illuminates this case. There was a plug-in which initialized a large model requiring the loading of some 11MB of <i>code</i> and many megabytes of data. There were quite common usecases where this plug-in was activated to discover if the project icon presented in the navigator should be decorated with a particular markup. This test did not require any of the initialization done in <tt>startup()</tt> but yet all users, in all usecases had to pay the memory and time penalty for this eager initialization.<br />The alternative approach is to do such initialization in a classic lazy style. For example, rather than having models initialized when the plug-in/bundle is activated, do it when they are actually needed (e.g., in a centralized model accessor method). For many usecases this will amount to nearly the same point in time but for other scenarios this approach will defer initialization (perhaps indefinitely). We recommend taking time while porting 2.1 plug-ins to reconsider the initialization strategy used. 
</dd>
						<dt>
								<b>Registration</b>
						</dt>
						<dd>Plug-in startup is a convenient time to register listeners, services etc. and start background processing threads (e.g., listening on a socket). <tt>Plugin.start()</tt> may a reasonable place to do this work. It may also make sense to defer until some other trigger (e.g., the use of a particular function or data element). 
</dd>
						<dt>
								<b>Plug-in global data</b>
						</dt>
						<dd>Your <tt>Plugin</tt> class can continue to play this role. The main issue is that <tt>Plugin</tt> objects are no longer globally accessible via a system-managed list. In Eclipse 2.x you could discover any plug-in's <tt>Plugin</tt> ojbect via the plug-in registry. This is no longer possible. In most circumstances this type of access is not required. <tt>Plugins</tt> accessed via the registry are more typically used as generic <tt>Plugins</tt> rather than calling domain-specific methods. </dd>
				</dl>
		</dl>
		<h3>
				<a name="registries">
				</a>Registries and the plug-in model</h3>
		<p>In the new runtime there is a separation between the information and structures needed to execute a plug-in and that related to a plug-in's extensions and extension points. The former is defined and managed by the OSGi framework specification. The latter are Eclipse-specific concepts and are added by they Eclipse runtime code. Accordingly, the original plug-in registy and related objects have been split into OSGi <i>bundles</i> and the Eclipse <i>extension registry</i>. </p>
		<p>The parts of <tt>IPluginRegistry</tt> dealing with execution specification (e.g., <tt>IPluginDescriptor, ILibrary</tt>, <tt>IPrequisite</tt>) have been deprecated and the remaining parts related to extensions and extension point have been moved to <tt>IExtensionRegistry</tt>. Further, the so-called model objects related to the plug-in registry as a whole are now deprecated. These types were presented and instantiated by the runtime primarily to support tooling such as PDE. Unfortunately, it was frequently the case that the level of information needed exceeded the runtime's capabilities or interests (e.g., remembering line numbers for plugin.xml elements) and in the end, the potential consumers of the runtime's information had to maintain their own structures anyway.</p>
		<p>In the new runtime we have re-evaluated the facilities provided by the runtime and now provide only those which are either essential for runtime execution or are extraordinarily difficult for others to do. As mentioned above, the plug-in registry model objects have been deprecated as has the plug-in parsing API. The new extensions registry maintains the essential extension-related information. A new <i>state</i> (see <tt>org.eclipse.osgi.service.resolver.State</tt> and friends) structure represents and allows the manipulation of the essential execution-related information. </p>
		<h3>
		</h3>
		<h3>Legacy code</h3>
		<h4>org.eclipse.core.boot.BootLoader (class)</h4>
		<p>BootLoader has been completely deprecated and merged with <tt>org.eclipse.core.runtime.Platform</tt> since it no longer made sense to have a split between boot and runtime. Note that in fact, the org.eclipse.core.boot plug-in has been broken up and all its code moved to either the new runtime or the compatibility layer. </p>
		<h4>org.eclipse.core.boot.IPlatformConfiguration (class)</h4>
		<p>
				<tt>IPlatformConfiguration</tt> has always been a type defined by and for the Ecliipse Install/Update component. With the reorganization of the runtime we are able to repatriate this type to its rightful home. This class remains largely unchanged and has been repackaged as <tt>org.eclipse.update.configurator.IPlatformConfiguration</tt>. The compatibility layer retains a deprecated copy of the original class.</p>
		<p>
		</p>
		<h4>org.eclipse.core.boot.IPlatformRunnable (class)</h4>This class has been deprecated in favour of the identical class org.eclipse.core.runtime.IPlatformRunnable. The was the last remaining use of "boot" in the API and was confusing for people. 
<h3></h3><h4>org.eclipse.core.runtime.IExtension<br />org.eclipse.core.runtime.IExtensionPoint (methods) </h4><ul><li>getDeclaringPlugin() 
<ul><li>This method (on both classes) gives an upward link to the plug-in which declares the extension or extension-point (respectively). The new registry model separates the execution aspects of plug-ins from the extension/extension-point aspects and no longer contains <tt>IPluginDescriptors</tt>. Users of this API should consider the new method <tt>getParentIdentifier()</tt> found on both <tt>IExtension</tt> and <tt>IExtensionPoint</tt>. The returned value can be used directly or as a key in the compatibility <tt>IPluginRegistry</tt> or the OSGi framework's API for bundle discovery (e.g., <tt>BundleContext.getBundles(String)</tt> &lt;XXX point to the Platofrm method&gt;). </li></ul></li></ul><h4></h4><h4>org.eclipse.core.runtime.ILibrary (class)</h4><p>In the original runtime, ILibrary is a representation of execution related attributes. In the new runtime these are now handled by OSGi. While it is possible to maintain some of the API for legacy plug-ins, it is not possible to do so for OSGi-based plug-ins/bundles. Users of ILibrary should consider using Bundle.getHeader(Constants.BUNDLE_CLASSPATH) and processing the results using the ManifestElement helper class.</p><h4></h4><h4>org.eclipse.core.runtime.IPrerequisite (class)</h4><p>In the original runtime, IPrerequisite is a representation of execution related attributes. In the new runtime these are now handled by OSGi. While it is possible to maintain some of the API for legacy plug-ins, it is not possible to do so for OSGi-based plug-ins/bundles. Users of IPrerequiste should consider using Bundle.getHeader(Constants.IMPORT_PACKAGE) or Bundle.getHeader(Constants.REQUIRE_BUNDLE) and processing the results using the ManifestElement helper class.</p><h4></h4><h4>org.eclipse.core.runtime.Platform (methods)</h4><ul><li>getPlugin(String) 
<ul><li>In the new runtime, <tt>Plugin</tt> objects are no longer managed by the runtime and so cannot be accessed via this method. Use &lt;XXX tentative&gt; <tt>Platform.getBundle(String)</tt> instead. Note however that this returns only a generic Bundle object. Those wishing to use domain-specific function on a particular <tt>Plugin</tt> object must arrange for access with the plug-in in question. This is typically done via a singleton pattern (e.g., <tt>ResourcesPlugin.getDefault()</tt>). </li></ul></li><li>getPluginRegistry() 
<ul><li>The plug-in registry object has been deprecated. If you are looking for the registry of extensions and extension points use <tt>Platform.getExtensionRegistry()</tt>. If you are looking to find the static and generic representation of a plug-in, consider using &lt;XXX tentative&gt; <tt>Platform.getBundle(String)</tt>. </li></ul></li></ul><h4>org.eclipse.core.runtime.Plugin (methods)</h4><ul><li>Plugin(IPluginDescriptor) 
<ul><li>The <tt>Plugin</tt> object is no longer created or managed by the runtime and <tt>IPluginDescriptor</tt> is deprecated. <tt>Plugin</tt> class implementors may choose to use their <tt>Plugin</tt> as a bundle activator in which case they must implement (allow for) the default constructor. Users wishing to have a <tt>Plugin</tt> class but not use it as the bundle activator need to implement the appropriate constructor which at a minimum takes the <tt>BundleContext</tt> to be associated with the plug-in. </li></ul></li><li>getPluginDescriptor() 
<ul><li>IPluginDescriptor is now deprecated. Users of this method are looking for 
<ul><li>extension/extension point information: go directly to org.eclipse.core.runtime.IExtensionRegistry 
</li><li>manifest information: get the bundle for the plug-in (getBundle()) and access its manifest headers as required. </li></ul></li></ul></li><li>getPluginPreferences() 
<ul><li>&lt;xxx the preference mechanism is still in play&gt; </li></ul></li><li>savePluginPreferences() 
<ul><li>&lt;xxx the preference mechanism is still in play&gt; </li></ul></li><li>initializeDefaultPluginPreferences() 
<ul><li>&lt;xxx the preference mechanism is still in play&gt; </li></ul></li><li>shutdown() and startup() 
<ul><li>These methods have been replaced with the OSGi standard <tt>start()</tt> and <tt>stop()</tt> methods. Note that the new lifecycle methods are only automatically called if the defining <tt>Plugin</tt> class is registered as the bundle activator in a bundle manifest (MANIFEST.MF) file. See the header Bundle-Activator. 
</li><li>OSGi (and Eclipse) coding practices <b>strongly</b> recommend against doing or triggering significant work in the <tt>start()</tt> methods. Doing excessive work here can damage startup time as well as overall responsiveness. </li></ul></li></ul><h4>org.eclipse.core.runtime.IPluginDescriptor (class)</h4><p>Plug-in descriptors contain methods for a number of different purposes. These are outlined below as well as the steps for updating their uses.</p><dl><dl><dt><b>extension-related</b> (e.g., getExtensions()) 
</dt><dd>Update such cases to use the IExtensionRegistry directly. 
</dd><dt><b>execution-related</b> (e.g., find() and getPluginClassLoader()) 
</dt><dd>Use the related methods found on <tt>org.eclipse.core.runtime.Platform</tt></dd><dt><b>accessors</b> (e.g., getLabel()) 
</dt><dd>Get the bundle associated with the descriptor and access the appropriate manifest header fields. </dd></dl></dl><h4></h4><h4>org.eclipse.core.runtime.IPluginRegistry (class)</h4><p>The contents of the plug-in registry, like the runtime, has been split into two parts, the execution-related and the extension-related. As such, the unified notion of a plug-in registry does not apply. Uses of the IPluginRegistry to discover extensions and extension-points are directly supported by nearly identical API on IExtensionRegistry. Uses of IPluginRegistry to discover IPluginDescriptors should be changed to use the appropriate OSGi structures. See the discussion related to IPluginDescriptor for more details.</p><h4>org.eclipse.core.runtime.model (package)</h4><p>All types in this package are now deprecated. See the discussion on <a href="http://dev.eclipse.org/viewcvs/index.cgi/platform-core-home/runtime/adoption.html?rev=1.5#registries">registries</a> for more information.</p><h3>Updating plugin/fragment files</h3><p>The new runtime is based on an implementation of the OSGi framework specification. The OSGi specification mandates the use of MANIFEST.MF files to define the execution of a plug-in. This conflicts with the Eclipse notion of plugin.xml as far as &lt;runtime&gt; and &lt;requires&gt; elements are concerned. The easiest way to update your plugin.xml or fragment.xml is to use the PDE Migration Tool (PDE Tools-&gt;Convert to OSGi bundle).</p><p>In certain situations the conversion tool is unable to correctly determine all of the packages provided by a plug-in. In particular, if a plug-in declares a library entry for say foo.jar but does not contain foo.jar, the converter is unable to generate the appropriate provides-package entries in the output manifest (this is done by analyzing the supplied jar which, in this case, is not present). Plug-in or fragments which are fully self-contained are converted correctly. </p><p>This tool extracts the execution-specific information from your plugin.xml and puts into the OSGi mandated META-INF/MANIFEST.MF file. All extension-related information is left in the plugin.xml. The manifest is a standard Java Jar file manifest. It contains specifications for the execution elements of our plug-in (e.g., classpath, prerequisites, ...). A table of frequent mapping is included below while the full <a href="http://www.osgi.org/">OSGi specification</a> has enumerates all pre-defined manifest headers.</p><table width="100%" border="1"><tbody><tr><td><div align="center"><b>plugin.xml tag/attribute</b></div></td><td><div align="center"><b>manifest.mf header</b></div></td></tr><tr><td>&lt;plugin id=&gt;</td><td>Bundle-GlobalName</td></tr><tr><td>&lt;plugin version=&gt;</td><td>Bundle-Version</td></tr><tr><td>&lt;plugin name=&gt;</td><td>Bundle-Name</td></tr><tr><td>&lt;plugin provider=&gt;</td><td>Bundle-Vendor</td></tr><tr><td>&lt;plugin class=&gt;</td><td>Bundle-Activator</td></tr><tr><td>&lt;requires&gt;, &lt;import&gt;</td><td>Require-Bundle</td></tr><tr><td>&lt;runtime&gt;, &lt;library&gt;</td><td>Bundle-ClassPath</td></tr></tbody></table><p>The extension/extension-point related information continues to be detailed in the plugin.xml file however the following aspects of the plugin.xml DTD are being deprecated.</p><ul><li>all attributes on the &lt;plugin&gt; tag 
</li><li>&lt;runtime&gt; and its &lt;library&gt; subelement 
</li><li>&lt;requires&gt; and its &lt;import&gt; subelement </li></ul><p>This leaves the plugin.xml containing only the &lt;plugin&gt;, &lt;extension&gt; and &lt;extension-point&gt; tags</p><p>Note also that the form of this content is not specific to a plug-ins vs. fragments. As a result, the fragment.xml file is being deprecated and renamed to plugin.xml with the outlined DTD changes. In most cases fragments do not supply extensions or extension-points so the bulk of the change required is done while moving to the MANIFEST.MF file and the fragment.xml file can be deleted.</p><p>The <tt>plugin.properties</tt> file continues to supply translations for the extension/extension-point information in plugin.xml and also supplies translations for various keys in the MANIFEST.MF.</p><p>The build.properties file remains unchanged.<br /><br />from: <a href="http://dev.eclipse.org/viewcvs/index.cgi/platform-core-home/runtime/adoption.html?rev=1.5">http://dev.eclipse.org/viewcvs/index.cgi/platform-core-home/runtime/adoption.html?rev=1.5</a></p><img src ="http://www.blogjava.net/weidagang2046/aggbug/68330.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-09-07 17:27 <a href="http://www.blogjava.net/weidagang2046/articles/68330.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Eclipse的使用简介及插件开发</title><link>http://www.blogjava.net/weidagang2046/articles/67709.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Mon, 04 Sep 2006 18:19:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/67709.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/67709.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/67709.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/67709.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/67709.html</trackback:ping><description><![CDATA[Eclipse最有魅力的地方就是它的插件体系结构。在这个体系中重要的概念是扩展点（extension points），也就是为插件提供的接口。每一个插件都是在现有的扩展点上开发，并可能还留有自己的扩展点，以便在这个插件上继续开发。<br /><br />由于有了插件，Eclipse系统的核心部分在启动的时候要完成的工作十分简单：启动平台的基础部分和查找系统的插件。在Eclipse中实现的绝大部分功能是由相应的插件完成的，比如WrokBench UI插件完成界面的外观显示，Resource Management插件完成维护或生成项目或文件等资源管理工作（在下面的第二个例子就会用到这个插件），而Version and Configuration Management (<a href="http://dev.21tx.com/language/vc/" target="_blank"><font color="#3366cc">VC</font></a>M)插件则负责完成版本控制功能，等等。虽然以上提到的每一个功能都是绝大多数IDE环境所必备的功能，Eclipse却也把它们都做成了插件模式，甚至用来开发<a href="http://dev.21tx.com/java/" target="_blank"><font color="#3366cc">Java</font></a>程序的开发环境（Java development tooling，JDT）也只不过是Eclipse系统中的一个普通插件而已。整个Eclipse体系结构就象一个大拼图，可以不断的向上加插件，同时，现有插件上还可以再加插件。下面的插件开发示例就是在WorkBench UI插件中的观察窗口扩展点上开发的。<br /><br />本文第一部分介绍过Eclipse的开发界面其中之一就是观察窗口，它通常配合编辑窗口显示一些有用的信息，在这里我们只简单生成一个显示欢迎信息的观察窗口，假设新插件的名子叫Welcome。<br /><br />第一步，先用向导新建一个Java项目。我们可以在菜单栏选择FileàNew，或用工具栏的向导按键，或是在资源窗口用鼠标右键菜单中的New，打开向导对话框，然后用缺省方式创建项目。并在项目中建立一个Welcome.java文件，代码如下：<br /><br /><br />package com.nidapeng.eclipse.plugin;<br />import org.eclipse.swt.widgets.Composite;<br />import org.eclipse.swt.widgets.Label;<br />import org.eclipse.swt.SWT;<br />import org.eclipse.ui.part.ViewPart;<br />public class Welcome extends ViewPart {<br />    Label label;<br />    public Welcome() {<br />    }<br />    public void createPartControl(Composite parent) {<br />        label = new Label(parent, SWT.WRAP);<br />        label.setText("Welcome to Eclipse");<br />    }<br />    public void setFocus() {<br />    }<br />}<br /><br /><br /><br /><br />为使这个程序能正常编译，要配置它的编译环境，即指定所需的CLASSPATH。在Eclipse中可以用几种方法，常用的是两种：第一是在资源窗口或Java包窗口选中该项目，点击鼠标右键，在打开的菜单中选择属性（Properties），之后在属性对话框中选择Java Build PathàLibraries，用Add External JARs功能添加三个包，它们都是Eclipse的现有插件的类包，可以在"你的Eclipse安装路径\plugins"下面的相应路径中找到。分别是org.eclipse.core.runtime插件中的runtime.jar，org.eclipse.swt中的swt.jar和org.eclipse.ui中的workbench.jar。第二种指定CLASSPATH的方法是先将以上提到的三个包直接导入到Eclipse中的某下一个项目中。如果导入到和Welcome.java相同的项目中，则无需进一步指定CLASSPATH，否则需要在项目的属性菜单中选择Java Build PathàProjects，然后选中这三个包所在的项目。<br /><br />在我们的项目中还要生成一个<a href="http://dev.21tx.com/web/xml/" target="_blank"><font color="#3366cc">XML</font></a>文件，它的名字必须plugin.xml。代码如下：<br /><br /><br />&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />&lt;plugin<br />   id="com.nidapeng.eclipse.plugin"<br />   name="Welcome to Eclipse"<br />   version="1.0"<br />   provider-name="Ni Dapeng"&gt;<br />&lt;requires&gt;<br />   &lt;import plugin="org.eclipse.ui"/&gt;<br />&lt;/requires&gt;<br />&lt;runtime&gt;<br />   &lt;library name="welcome.jar"/&gt;<br />&lt;/runtime&gt;<br />&lt;extension<br />      point="org.eclipse.ui.views"&gt;<br />   &lt;category<br />         name="Welcome"<br />         id="com.nidapeng.eclipse.plugin.category1"&gt;<br />  &lt;/category&gt;<br />   &lt;view<br />         name="Welcome to Eclipse"<br />         category="com.nidapeng.eclipse.plugin.category1"<br />         class="com.nidapeng.eclipse.plugin.Welcome"<br />         id="com.nidapeng.eclipse.plugin.view1"&gt;<br />  &lt;/view&gt;<br />&lt;/extension&gt;<br />&lt;/plugin&gt;<br /><br /><br /><br /><br />在plugin.xml中一共有四个主要的标签：plugin，requires，runtime，extension。其中plugin标签的属性提供的是我们要开发的Welcome插件的基本信息，除了name，version，provider-name等，最重要的是id，它要求不能和现有的Eclipse插件id有冲突，因此我们用包名作为插件的id。requires标签中所列出的是需要的插件，这里我们要用到Eclipse Workbench和SWT API，因此导入了org.eclipse.ui插件。runtime标签指明的是我们开发的插件所在JAR包的文件名。extension标签是插件扩展点的信息。org.eclipse.ui.views是Eclipse系统提供的观察窗口扩展点，我们的例子是一个观察窗口（View），这表明我们是要在 org.eclipse.ui.views扩展点上进一步开发。extension中还包括category和view两个标签，在后续的启动Welcome插件步骤中，我们就会知道这两个标签的含义。要注意的是category和view标签的id的唯一性，并且在view的属性中声明了Welcome插件的类名。<br /><br />在Eclipse中为plugin.xml提供了缺省可视化的编辑器，在编写plugin.xml过程中可以借助这个编辑器完成一些工作。如果你直接录入了plugin.xml文件源代码，还可以用这个编辑器校验你的代码：如果编辑器不能正确读入，就表明你的plugin.xml有一些问题。<br /><br />在确认Weclome.java和plugin.xml都正确无误之后，可以用Eclipse菜单栏中的Export命令将Weclome.java导出为JAR文件，它的名子应该和plugin.xml中runtime声明的JAR相一致。同时导出plugin.xml。安装Welcome插件的方法和本文第一部分介绍的安装Tomcat插件方法是一样的：首先在"Eclipse的安装路径\plugins"路径下面建立一个com.nidapeng.eclipse.plugin路径，然后将Weclome.jar和plugin.xml拷到这个路径下。之后必需重新启动Eclipse，在Eclipse启动的时候，它会搜索所有在插件路径下的插件并注册它们（仅仅是注册，只有在需要某个插件的时候，Eclipse才会启动它）。在重新启动的Eclipse的菜单栏中选择PerspectiveàShow ViewàOthers，在打开的对话框中我们会找到在plugin.xml中extension的category标签中声明的name属性：Welcome。在Welcome的支结点中包含了view标签name属性：Welcome to Eclipse。选中它并确认，Welcome窗口就会显示在Eclipse Workbench上的某个位置 。如果在执行了以上操作，但没有显示新窗口，可以再次打开Show View菜单，此时在菜单中应该有新一顶选择：Welcome to Eclipse，然后选中它。<br /><br />上面我们完成了一个观察窗口的插件，但这个操作过程对开发稍微复杂一些的插件就显得不太方便了：每次测试都要将代码打包，发布，再重新启动Eclipse系统！为此Eclipse提供了一个专门为开发插件而做插件（有点绕嘴）：Plug-in Development Environment（PDE）。本文前面曾提到，目前Eclipse的Release或Stable版本缺省提供了这个插件，因此如果安装的Eclipse是这两个版本中的一个就可以直接进行下面的步骤。下面我们再用PDE环境开发一个稍微复杂一些的插件。<br /><br />第一步仍然要新建一个项目，只是在向导中不是用Java项目，而是Plug-in Development中的Plug-in Project。在应用向导生成新项目的时候，要注意两点：第一是PDE的项目名称就是plugin的id，因此要保证它的唯一性，这里我们的项目名是com.nidapeng.eclipse.plugin.pde。其次为了进一步说明Eclipse插件的结构，在Plug-in Code Generators中，选择用向导模板生成一个缺省的插件，如图六：<br /><br /><br /><br />图六 <br /><br />这个用缺省方式生成的插件类对于我们将要的代码并不是必需的，也可以用生成空插件的方式建立我们的项目，这样做只是为进一步说明Eclipse的插件结构。<br /><br />项目生成之后，在我们的项目中会包含一个PdePlugin.java文件，它就是以缺省方式生成的插件类。注意到它继承了AbstractUIPlugin类，而AbstractUIPlugin类实现了org.eclipse.ui.plugin接口。事实上，所有的Eclipse插件都会有一个相应的实现plugin接口的类，这个类将是新插件的主类（类似于有main()函数的Java类），它负责管理插件的生存期。在我们的AbstractUIPlugin继承子类中，可以用singleton模式来保存在Eclipse中的生成的该插件的第一个也是唯一实例，一般来说，在该继承子类中也要实现一个getDefault()方法以返回当前插件的实例。而且，当Eclipse首次使用该插件的时候，这个主类将是第一个被调用的类，因此我们也可以在它的代码中执行一些初始化的工作。而且如果插件需要使用Preferences，Dialogs或Images资源，也可以通过这个类中的相应方法来得到它们的实例，如用其中的getDialogSettings()，getPreferenceStore()，getImageRe<a href="http://dev.21tx.com/corp/gis/" target="_blank"><font color="#3366cc">GIS</font></a>try()方法。<br /><br />但是象前面提到的，PdePlugin.java对下面的例子并不是必需的，我们不用对它进行任何修改。在我们第一个例子中的Weclome插件，根本就没有生成AbstractUIPlugin的继承子类，此时系统会自动为Weclome插件生成一个缺省的主类（类似于Java类构造函数，如果没有声明，系统会指定一个默认的构造函数）。<br /><br />下面的代码是才真正实现了我们新插件的功能，假设这个插件名子是NoticeView：<br /><br /><br />package com.nidapeng.eclipse.plugin.pde;<br />import org.eclipse.core.resources.*;<br />import org.eclipse.core.resources.IResourceChangeEvent;<br />import org.eclipse.core.runtime.CoreException;<br />import java.util.ResourceBundle;<br />import org.eclipse.swt.widgets.Label;<br />import org.eclipse.swt.widgets.Composite;<br />import org.eclipse.ui.part.ViewPart;<br />import org.eclipse.swt.SWT;<br />import org.eclipse.swt.widgets.Display;<br />public class NoticeView extends ViewPart implements　<br />Runnable,IResourceChangeListener ,IResourceDeltaVisitor{<br />    private ResourceBundle resourceBundle;<br />    private Label label;<br />    private Display disp; <br />    private String dispTxt;<br />    public NoticeView() {<br />    ResourcesPlugin.getWorkspace().addResourceChangeListener(this,<br />      IResourceChangeEvent.PRE_CLOSE<br />      | IResourceChangeEvent.PRE_DELETE<br />      | IResourceChangeEvent.PRE_AUTO_BUILD<br />      | IResourceChangeEvent.POST_AUTO_BUILD<br />      | IResourceChangeEvent.POST_CHANGE);<br />    }<br />    public static IWorkspace getWorkspace() {<br />        //ResourcesPlugin插件的主类！<br />         return ResourcesPlugin.getWorkspace();<br />     }<br />    public void createPartControl(Composite parent) {<br />           label = new Label(parent, SWT.WRAP);<br />        label.setText("change your project status...");<br />         disp = Display.getDefault();<br />     }<br />    public void setFocus() {<br />     }<br />    // 实现IResourceChangeListener接口中的resourceChanged方法<br />    public void resourceChanged(IResourceChangeEvent event) {<br />          IResource res = event.getResource();<br />          switch (event.getType()) {<br />               case IResourceChangeEvent.PRE_CLOSE :<br />                    dispTxt = res.getFullPath() + " is about to closed!";<br />                    break;<br />               case IResourceChangeEvent.PRE_DELETE :<br />                    dispTxt = res.getFullPath() + " is about to be deleted!";<br />                   break;<br />               case IResourceChangeEvent.POST_CHANGE :<br />                    try {<br />                     event.getDelta().accept(this);<br />                    } catch (CoreException e) {<br />                         e.printStackTrace();<br />                    }<br />                    break;<br />               case IResourceChangeEvent.PRE_AUTO_BUILD :<br />                    try {<br />                     event.getDelta().accept(this);<br />                    } catch (CoreException e) {    <br />                     e.printStackTrace();<br />                    }<br />                    break;<br />               case IResourceChangeEvent.POST_AUTO_BUILD :<br />                    try {<br />                     event.getDelta().accept(this);<br />                    } catch (CoreException e) {<br />                         e.printStackTrace();<br />                    }<br />                    break;<br />          }<br />          disp.syncExec(this);<br />     }<br />     // 实现IResourceDeltaVisitor接口中的visit方法<br />     public boolean visit(IResourceDelta delta) {<br />          IResource res = delta.getResource();<br />          switch (delta.getKind()) {<br />           case IResourceDelta.ADDED :<br />                dispTxt = "Resource "+res.getFullPath()+" was added.";<br />                break;<br />           case IResourceDelta.REMOVED:<br />                dispTxt = "Resource "+res.getFullPath()+" was removed.";<br />               break;<br />           case IResourceDelta.CHANGED :<br />                dispTxt = "Resource "+res.getFullPath()+" has changed.";<br />               break;<br />          }<br />          return true; // visit the children<br />     }<br />    // 实现Runnable接口中的run方法<br />     public void run() {<br />          try {<br />               label.setText(dispTxt); <br />          } catch (Exception e) {<br />               e.printStackTrace();<br />          }<br />    }<br />}<br /><br /><br /><br /><br />象上面的第一个Welcome插件，这个新插件同样继承了ViewPart，不同的是实现了三个接口：Runnable,IResourceChangeListener ,IResourceDeltaVisitor。其中的Runnable大家应该很熟悉：多线程的接口。而IResourceChangeListener和IResourceDeltaVisitor是Eclipse系统中的资源接口，这里的资源是指Eclipse中的项目或文件等。在下面运行NoticeView插件的过程中你可以通过添加、打开、删除项目或文件来触发这两个接口中的事件，并在我们的观察窗口中显示相关信息。<br /><br />在程序中比较奇怪部分的是在resourceChanged()函数里面，并没有象大家想象的那样直接调用label.setText()方法来显示信息，而是调用了disp.syncExec(this)，其中的disp是Display类型的对象。这是因为resourceChanged()方法运行的线程和lable所在插件运行的Eclipse主线程并不是同一个线程，如果直接调用label.setText()方法，会抛出一个异常。<br /><br />下面还需要对项目中的plugin.xml进行一些改动，主要就是加上扩展点声明：<br /><br /><br />&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />&lt;plugin<br />   id="com.nidapeng.eclipse.plugin.pde"<br />   name="Pde Plugin"<br />   version="1.0.0"<br />   provider-name="NIDAPENG"<br />   class="com.nidapeng.eclipse.plugin.pde.PdePlugin"&gt;<br />&lt;requires&gt;<br />   &lt;import plugin="org.eclipse.core.runtime"/&gt;<br />   &lt;import plugin="org.eclipse.core.resources"/&gt;<br />   &lt;import plugin="org.eclipse.ui"/&gt;<br />&lt;/requires&gt;<br />&lt;runtime&gt;<br />   &lt;library name="pde.jar"/&gt;<br />&lt;/runtime&gt;<br />&lt;extension<br />      id="NoticeView"<br />      name="Notice View"<br />      point="org.eclipse.ui.views"&gt;<br />   &lt;category<br />         name="Notice"<br />         id="com.nidapeng.eclipse.plugin.pde.category1"&gt;<br />   &lt;/category&gt;<br />   &lt;view<br />         name="Notice Resource View"<br />         category="com.nidapeng.eclipse.plugin.pde.category1"<br />         class="com.nidapeng.eclipse.plugin.pde.NoticeView"<br />         id="com.nidapeng.eclipse.plugin.pde.view1"&gt;<br />  &lt;/view&gt;<br />&lt;/extension&gt;<br />&lt;/plugin&gt;<br /><br /><br /><br /><br />这个xml文件和Welcome插件的plugin.xml非常接近，这里就不做过多的说明了。<br /><br />要运行这个插件，可以直接用Eclipse中的运行按钮，因为这个项目是一个Plug-in Project，此时项目会自动以Run-time Workbench方式运行。运行后，会生成一个和当前Eclipse完全一致的平台，在这个平台上可以直接运行NoticeView插件，查看这个插件到底会执行什么功能，也可以用直接Run-time Workbench方式调试插件。这里省去了安装插件，重启动Eclipse等过程，可以看到用PDE开发插件的过程比直接用Java开发环境简洁了很多！<br /><br />Eclipse的开发不仅仅限于插件的开发，它还可以取代Java中的标准<a href="http://dev.21tx.com/java/applet/" target="_blank"><font color="#3366cc">Swing</font></a>，进行基于Java的独立应用程序GUI开发。它带来的好处是显而易见的：高速，资源占用低，跨平台，代码开放，有大公司的支持等等。<br /><br />由于Eclipse目前还在开发阶段，笔者在用它调试程序时发现有些性能还不是十分的稳定，一些地方会遇到奇怪的问题，要求使用者能想一些办法解决。不过，以现在Eclipse的开发速度，相信过不了多久，它的各种功能会逐步完善。目前Eclipse虽然有种种不足，但瑕不掩玉，笔者对Eclipse的总体印象还是非常不错的，运行速度，资源占用都要好于IVJ，操作起来也大多顺手，而且即使在现阶段也很少有意外退出等重大的Bug发生，希望未来的Eclipse能真正达到IVJ的功能，VisualCafe的速度，成为广大程序员开发软件的一大利器！<br /><br />参考资源：<br /><br />www.eclipse.org是Eclipse的资源总站。<br /><br />关于作者 <br />倪大鹏，有五年的<a href="http://dev.21tx.com/" target="_blank"><font color="#3366cc">软件开发</font></a>经验，其中的近四年时间里是在从事与Java相关技术的应用与开发。你可以通过e-mail: ndp@21cn.com与他联系。 <br /><br />from: <a href="http://dev.21tx.com/2003/07/26/17291.html">http://dev.21tx.com/2003/07/26/17291.html</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/67709.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-09-05 02:19 <a href="http://www.blogjava.net/weidagang2046/articles/67709.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DND入门学习</title><link>http://www.blogjava.net/weidagang2046/articles/67707.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Mon, 04 Sep 2006 17:03:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/67707.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/67707.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/67707.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/67707.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/67707.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Drag and Drop是Eclipse为用户在一个或多个SWT应用之间重置部件或数据传输上提供的简单快捷的机制。可以使用在普通SWT应用中，也是学习GEF的基础部分。下面我从以下几个Drag and Drop所必需的部分来简单讲述：DragSource And DropTarget 要实现Drag and Drop，就必须要有DragSource And DropTarget。DragSour...&nbsp;&nbsp;<a href='http://www.blogjava.net/weidagang2046/articles/67707.html'>阅读全文</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/67707.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-09-05 01:03 <a href="http://www.blogjava.net/weidagang2046/articles/67707.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Transfer type to transfer SWT ImageData objects</title><link>http://www.blogjava.net/weidagang2046/articles/67706.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Mon, 04 Sep 2006 16:57:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/67706.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/67706.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/67706.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/67706.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/67706.html</trackback:ping><description><![CDATA[
		<font face="Courier New">
				<font size="2">
						<font color="#3f5fbf">/**<br /></font>
						<font color="#ffffff"> </font>
						<font color="#3f5fbf">* Transfer type to transfer SWT ImageData objects.</font>
						<br />
						<font color="#ffffff"> </font>
						<font color="#3f5fbf">*/</font>
						<br />
						<font color="#ffffff">
						</font>
						<br />
						<font color="#7f0055">
								<b>import </b>
						</font>
						<font color="#000000">java.io.ByteArrayInputStream;</font>
						<br />
						<font color="#7f0055">
								<b>import </b>
						</font>
						<font color="#000000">java.io.ByteArrayOutputStream;</font>
						<br />
						<font color="#7f0055">
								<b>import </b>
						</font>
						<font color="#000000">java.io.DataInputStream;</font>
						<br />
						<font color="#7f0055">
								<b>import </b>
						</font>
						<font color="#000000">java.io.DataOutputStream;</font>
						<br />
						<font color="#7f0055">
								<b>import </b>
						</font>
						<font color="#000000">java.io.IOException;</font>
						<br />
						<font color="#ffffff">
						</font>
						<br />
						<font color="#7f0055">
								<b>import </b>
						</font>
						<font color="#000000">org.eclipse.swt.SWT;</font>
						<br />
						<font color="#7f0055">
								<b>import </b>
						</font>
						<font color="#000000">org.eclipse.swt.dnd.ByteArrayTransfer;</font>
						<br />
						<font color="#7f0055">
								<b>import </b>
						</font>
						<font color="#000000">org.eclipse.swt.dnd.DND;</font>
						<br />
						<font color="#7f0055">
								<b>import </b>
						</font>
						<font color="#000000">org.eclipse.swt.dnd.TransferData;</font>
						<br />
						<font color="#7f0055">
								<b>import </b>
						</font>
						<font color="#000000">org.eclipse.swt.graphics.ImageData;</font>
						<br />
						<font color="#7f0055">
								<b>import </b>
						</font>
						<font color="#000000">org.eclipse.swt.graphics.ImageLoader;</font>
						<br />
						<font color="#ffffff">
						</font>
						<br />
						<font color="#7f0055">
								<b>public class </b>
						</font>
						<font color="#000000">ImageTransfer </font>
						<font color="#7f0055">
								<b>extends </b>
						</font>
						<font color="#000000">ByteArrayTransfer </font>
						<font color="#000000">{</font>
						<br />
						<font color="#ffffff">
						</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#7f0055">
								<b>private static final </b>
						</font>
						<font color="#000000">String TYPENAME = </font>
						<font color="#2a00ff">"imagedata"</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">
						</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#7f0055">
								<b>private static final </b>
						</font>
						<font color="#7f0055">
								<b>int </b>
						</font>
						<font color="#000000">TYPEID = registerType</font>
						<font color="#000000">(</font>
						<font color="#000000">TYPENAME</font>
						<font color="#000000">)</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">
						</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#7f0055">
								<b>private static </b>
						</font>
						<font color="#000000">ImageTransfer _instance = </font>
						<font color="#7f0055">
								<b>new </b>
						</font>
						<font color="#000000">ImageTransfer</font>
						<font color="#000000">()</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">
						</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#7f0055">
								<b>public static </b>
						</font>
						<font color="#000000">ImageTransfer getInstance</font>
						<font color="#000000">() {</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#7f0055">
								<b>return </b>
						</font>
						<font color="#000000">_instance;</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#000000">}</font>
						<br />
						<font color="#ffffff">
						</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#7f0055">
								<b>public </b>
						</font>
						<font color="#7f0055">
								<b>void </b>
						</font>
						<font color="#000000">javaToNative</font>
						<font color="#000000">(</font>
						<font color="#000000">Object object, TransferData transferData</font>
						<font color="#000000">) {</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#7f0055">
								<b>if </b>
						</font>
						<font color="#000000">(</font>
						<font color="#000000">!checkImage</font>
						<font color="#000000">(</font>
						<font color="#000000">object</font>
						<font color="#000000">) </font>
						<font color="#000000">|| !isSupportedType</font>
						<font color="#000000">(</font>
						<font color="#000000">transferData</font>
						<font color="#000000">)) {</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#000000">DND.error</font>
						<font color="#000000">(</font>
						<font color="#000000">DND.ERROR_INVALID_DATA</font>
						<font color="#000000">)</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#000000">}</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#000000">ImageData imdata = </font>
						<font color="#000000">(</font>
						<font color="#000000">ImageData</font>
						<font color="#000000">) </font>
						<font color="#000000">object;</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#7f0055">
								<b>try </b>
						</font>
						<font color="#000000">{</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#3f7f5f">// write data to a byte array and then ask super to convert to</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#3f7f5f">// pMedium</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#000000">ByteArrayOutputStream out = </font>
						<font color="#7f0055">
								<b>new </b>
						</font>
						<font color="#000000">ByteArrayOutputStream</font>
						<font color="#000000">()</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#000000">DataOutputStream writeOut = </font>
						<font color="#7f0055">
								<b>new </b>
						</font>
						<font color="#000000">DataOutputStream</font>
						<font color="#000000">(</font>
						<font color="#000000">out</font>
						<font color="#000000">)</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#000000">ImageLoader loader = </font>
						<font color="#7f0055">
								<b>new </b>
						</font>
						<font color="#000000">ImageLoader</font>
						<font color="#000000">()</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#000000">loader.data = </font>
						<font color="#7f0055">
								<b>new </b>
						</font>
						<font color="#000000">ImageData</font>
						<font color="#000000">[] { </font>
						<font color="#000000">imdata </font>
						<font color="#000000">}</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#000000">loader.save</font>
						<font color="#000000">(</font>
						<font color="#000000">writeOut, SWT.IMAGE_BMP</font>
						<font color="#000000">)</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#000000">writeOut.close</font>
						<font color="#000000">()</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#7f0055">
								<b>byte</b>
						</font>
						<font color="#000000">[] </font>
						<font color="#000000">buffer = out.toByteArray</font>
						<font color="#000000">()</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#7f0055">
								<b>super</b>
						</font>
						<font color="#000000">.javaToNative</font>
						<font color="#000000">(</font>
						<font color="#000000">buffer, transferData</font>
						<font color="#000000">)</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#000000">out.close</font>
						<font color="#000000">()</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#000000">} </font>
						<font color="#7f0055">
								<b>catch </b>
						</font>
						<font color="#000000">(</font>
						<font color="#000000">IOException e</font>
						<font color="#000000">) {</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#000000">}</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#000000">}</font>
						<br />
						<font color="#ffffff">
						</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#7f0055">
								<b>public </b>
						</font>
						<font color="#000000">Object nativeToJava</font>
						<font color="#000000">(</font>
						<font color="#000000">TransferData transferData</font>
						<font color="#000000">) {</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#7f0055">
								<b>if </b>
						</font>
						<font color="#000000">(</font>
						<font color="#000000">!isSupportedType</font>
						<font color="#000000">(</font>
						<font color="#000000">transferData</font>
						<font color="#000000">))</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#7f0055">
								<b>return null</b>
						</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">
						</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#7f0055">
								<b>byte</b>
						</font>
						<font color="#000000">[] </font>
						<font color="#000000">buffer = </font>
						<font color="#000000">(</font>
						<font color="#7f0055">
								<b>byte</b>
						</font>
						<font color="#000000">[]) </font>
						<font color="#7f0055">
								<b>super</b>
						</font>
						<font color="#000000">.nativeToJava</font>
						<font color="#000000">(</font>
						<font color="#000000">transferData</font>
						<font color="#000000">)</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#7f0055">
								<b>if </b>
						</font>
						<font color="#000000">(</font>
						<font color="#000000">buffer == </font>
						<font color="#7f0055">
								<b>null</b>
						</font>
						<font color="#000000">)</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#7f0055">
								<b>return null</b>
						</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">
						</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#000000">ImageData imdata;</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#7f0055">
								<b>try </b>
						</font>
						<font color="#000000">{</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#000000">ByteArrayInputStream in = </font>
						<font color="#7f0055">
								<b>new </b>
						</font>
						<font color="#000000">ByteArrayInputStream</font>
						<font color="#000000">(</font>
						<font color="#000000">buffer</font>
						<font color="#000000">)</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#000000">DataInputStream readIn = </font>
						<font color="#7f0055">
								<b>new </b>
						</font>
						<font color="#000000">DataInputStream</font>
						<font color="#000000">(</font>
						<font color="#000000">in</font>
						<font color="#000000">)</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#000000">imdata = </font>
						<font color="#7f0055">
								<b>new </b>
						</font>
						<font color="#000000">ImageData</font>
						<font color="#000000">(</font>
						<font color="#000000">readIn</font>
						<font color="#000000">)</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#000000">readIn.close</font>
						<font color="#000000">()</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#000000">} </font>
						<font color="#7f0055">
								<b>catch </b>
						</font>
						<font color="#000000">(</font>
						<font color="#000000">IOException ex</font>
						<font color="#000000">) {</font>
						<br />
						<font color="#ffffff">      </font>
						<font color="#7f0055">
								<b>return null</b>
						</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#000000">}</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#7f0055">
								<b>return </b>
						</font>
						<font color="#000000">imdata;</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#000000">}</font>
						<br />
						<font color="#ffffff">
						</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#7f0055">
								<b>protected </b>
						</font>
						<font color="#000000">String</font>
						<font color="#000000">[] </font>
						<font color="#000000">getTypeNames</font>
						<font color="#000000">() {</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#7f0055">
								<b>return new </b>
						</font>
						<font color="#000000">String</font>
						<font color="#000000">[] { </font>
						<font color="#000000">TYPENAME </font>
						<font color="#000000">}</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#000000">}</font>
						<br />
						<font color="#ffffff">
						</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#7f0055">
								<b>protected </b>
						</font>
						<font color="#7f0055">
								<b>int</b>
						</font>
						<font color="#000000">[] </font>
						<font color="#000000">getTypeIds</font>
						<font color="#000000">() {</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#7f0055">
								<b>return new </b>
						</font>
						<font color="#7f0055">
								<b>int</b>
						</font>
						<font color="#000000">[] { </font>
						<font color="#000000">TYPEID </font>
						<font color="#000000">}</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#000000">}</font>
						<br />
						<font color="#ffffff">
						</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#7f0055">
								<b>boolean </b>
						</font>
						<font color="#000000">checkImage</font>
						<font color="#000000">(</font>
						<font color="#000000">Object object</font>
						<font color="#000000">) {</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#7f0055">
								<b>return </b>
						</font>
						<font color="#000000">(</font>
						<font color="#000000">object != </font>
						<font color="#7f0055">
								<b>null </b>
						</font>
						<font color="#000000">&amp;&amp; object </font>
						<font color="#7f0055">
								<b>instanceof </b>
						</font>
						<font color="#000000">ImageData</font>
						<font color="#000000">)</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#000000">}</font>
						<br />
						<font color="#ffffff">
						</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#7f0055">
								<b>protected </b>
						</font>
						<font color="#7f0055">
								<b>boolean </b>
						</font>
						<font color="#000000">validate</font>
						<font color="#000000">(</font>
						<font color="#000000">Object object</font>
						<font color="#000000">) {</font>
						<br />
						<font color="#ffffff">    </font>
						<font color="#7f0055">
								<b>return </b>
						</font>
						<font color="#000000">checkImage</font>
						<font color="#000000">(</font>
						<font color="#000000">object</font>
						<font color="#000000">)</font>
						<font color="#000000">;</font>
						<br />
						<font color="#ffffff">  </font>
						<font color="#000000">}</font>
						<br />
						<font color="#000000">}<br /><br />from: <a href="http://www.java2s.com/Code/Java/SWT-JFace-Eclipse/TransfertypetotransferSWTImageDataobjects.htm">http://www.java2s.com/Code/Java/SWT-JFace-Eclipse/TransfertypetotransferSWTImageDataobjects.htm</a></font>
				</font>
		</font>
<img src ="http://www.blogjava.net/weidagang2046/aggbug/67706.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-09-05 00:57 <a href="http://www.blogjava.net/weidagang2046/articles/67706.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用图形编辑框架创建基于 Eclipse 的应用程序</title><link>http://www.blogjava.net/weidagang2046/articles/67530.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Mon, 04 Sep 2006 02:57:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/67530.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/67530.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/67530.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/67530.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/67530.html</trackback:ping><description><![CDATA[
		<p>本文为您从头到尾地介绍了使用 GEF 的步骤。我们不是完整地完成每个步骤，而是将使用您的应用程序模型的子集，并先使该子集工作。例如，开始我们可能会忽略连接，或者只注重于您应用程序中图形元素类型的子集。</p>
		<p>
				<a name="N10043">
						<span class="atitle">
								<font face="Arial" size="4">GEF 概述</font>
						</span>
				</a>
		</p>
		<p>GEF 假定您拥有一个希望以图形方式显示和编辑的模型。为了做到这一点，GEF 提供了可在 Eclipse 工作台中任何地方使用的查看器（类型为 <code>EditPartViewer</code> ）。象 JFace 查看器一样，GEF 查看器是 SWT 控件上的适配器。但是它们的类似之处仅此而已。GEF 查看器基于模型-视图-控制器（model-view-controller，MVC）体系结构。 </p>
		<p>
				<i>控制器</i>作为视图和模型之间的桥梁（请参阅图 1）。每个控制器（即本文所谓的 <i>EditPart</i>）负责将模型映射到它的视图，也负责对模型进行更改。EditPart 还观察模型并更新视图，以反映模型状态中的变化。EditPart 是一种对象，用户将与这种对象进行交互。稍后将更详细地介绍 EditPart。 </p>
		<p>
				<a name="figure1">
						<b>图 1. 模型-视图-控制器</b>
				</a>
				<br />
				<img height="165" alt="模型-视图-控制器" src="http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/fig1.gif" width="215" />
		</p>
		<p>GEF 提供了两种查看器类型：图形的和基于树的。每种查看器都主管一种不同类型的 <i>视图</i>。图形查看器使用了在 SWT <i>画布（Canvas）</i>上绘制的 <i>图形（figure）</i>。图形是在 Draw2D 插件中定义的，该插件是 GEF 的一部分。TreeViewer 将 SWT 树和 TreeItem 用于其视图。 </p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N10077">
						<span class="atitle">
								<font face="Arial" size="4">第 1 步. 选定自己的模型</font>
						</span>
				</a>
		</p>
		<p>GEF 对于模型一无所知。任何模型类型都可工作，只要它符合下面描述的特性。</p>
		<p>
				<a name="N10080">
						<span class="smalltitle">模型中有什么？</span>
				</a>
		</p>
		<p>所有东西都在模型中。模型是唯一会被持久存储和恢复的东西。您的应用程序应当将所有重要数据都存储在模型中。在编辑、撤销和重做的过程中，模型是唯一保持不变的。随着时间推移，将对图形和 EditPart 进行垃圾收集处理并重新创建。</p>
		<p>当用户与 EditPart 交互时，EditPart 并不直接操作模型。而是创建一个封装了更改的 <i>命令（Command）</i>。命令可用来验证用户的交互，并且提供撤销和重做支持。 </p>
		<p>严格地说，命令概念上也是模型一部分。它们 <i>本身</i>并不是模型，而是一些方法，模型是由这些方法编辑的。命令用于执行用户的所有可撤销的更改。理论上，命令应当只了解模型。它们应当避免引用 EditPart 或图形。类似地，如果可能，命令应当避免调用用户界面（例如弹出式对话框）。 </p>
		<p>
				<a name="N10095">
						<span class="smalltitle">两个模型的故事</span>
				</a>
		</p>
		<p>一个简单的 GEF 应用程序就是用于绘制图的编辑器。（这里 <i>图</i>只意味着图片，而不是类图等）图可以被建模成某些形状。一个形状可能具有位置、颜色等特性，并且可能是多个形状构成的一组结构。这里没有什么可惊讶的，并且前述需求也易于维护（请参阅图 2）。 </p>
		<p>
				<a name="figure2">
						<b>图 2. 一个简单的模型</b>
				</a>
				<br />
				<img height="179" alt="一个简单的模型" src="http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/fig2.gif" width="334" />
		</p>
		<p>另一种常见的 GEF 应用程序是 UML 编辑器，例如类图编辑器。图中的一段重要信息就是 (x, y) 位置，类就出现在该位置上。根据前一节的介绍，您可能会以为模型必须将一个 <i>类</i>描述成具有 <i>x</i>和 <i>y</i>特性。大多数开发人员都不希望由于无意义的属性而“污染”其模型。在这类应用程序中，术语“业务”模型可用于指代基本模型，重要语义的详细信息存储在基本模型中。而特定于图的信息存储在“视图”模型（它指的是业务模型中某样东西的“视图”；在一个图中可多次查看某个对象）中。有时候这种划分甚至会反映在工作空间中，其中不同的资源可能被分别用来持久存储图和业务模型。甚至可能有多个图对应于同一个业务模型（请参阅图 3）。 </p>
		<p>
				<a name="figure3">
						<b>图 3. 划分成业务模型和视图模型的模型</b>
				</a>
				<br />
				<img height="255" alt="划分成业务模型和视图模型的模型" src="http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/fig3.gif" width="552" />
		</p>
		<p>不管您的模型划分成了两个部分，还是划分成了多个资源，对于 GEF 而言这都是无关紧要的。术语模型用于指代整个应用程序模型。屏幕上的一个对象可能对应于模型中的多个对象。GEF 旨在允许开发人员方便地处理这类映射。</p>
		<p>
				<a name="N100D4">
						<span class="smalltitle">通知策略</span>
				</a>
		</p>
		<p>对视图进行更新几乎总是由来自模型的通知而导致的。您的模型必须提供某种通知机制，该机制必须映射到您应用程序中相应的更新。而只读模型或不能进行通知的模型（例如文件系统或远程连接）可能是例外。</p>
		<p>通知策略通常是分布式的（每对象）或集中式的（每域）。域通知器了解到对模型中任何对象的每次更改，然后将这些更改向域侦听器广播。如果您的应用程序使用了这种通知模型，您可能要为每个查看器添加一个域侦听器。当该侦听器接收到更改时，它将查找受影响的 EditPart，然后适当地重新分派该更改。如果您的应用程序使用了分布式通知，那么每个 EditPart 通常都将把自己的侦听器添加到任何一个影响它的模型对象。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N100E0">
						<span class="atitle">
								<font face="Arial" size="4">第 2 步. 定义视图</font>
						</span>
				</a>
		</p>
		<p>下一步是决定将如何使用来自 Draw2D 插件的图形显示您的模型。某些图形可直接用来显示模型的某个对象。例如，Label 图形可用来显示 Image 和 String。有时候，通过组合多个图形、布局管理器和／或边框可以获得期望的结果。最后，您可能要编写自己的图形实现，该实现以特定于您应用程序的方式绘图。</p>
		<p>有关组合或实现图形和布局的更多信息可在 Draw2D 开发人员指南中找到，该指南包含在 GEF SDK 中。</p>
		<p>在和 GEF 一起使用 Draw2D 时，通过遵循下列方针，可以使您的项目更便于管理，并且可以更灵活地更改需求：</p>
		<ul>
				<li>
						<b>不要从头做起</b>。您可以组合所提供的布局管理器以呈现大多数东西。请考虑使用工具栏布局（在垂直或水平方向上）和边框布局的组合来组合多个图形。只有在万不得已时才编写自己的布局管理器。作为参考，请查看 GEF 中提供的调色板。该调色板是使用 Draw2D 中的许多标准图形和布局呈现的。 <br /><br /></li>
				<li>
						<b>保持 EditPart 和图形之间“彻底”的分离</b>。如果您的 EditPart 使用了几个图形、布局和／或边框的复合结构，那么请尽量对 EditPart 隐藏详细信息。让 EditPart 自己构建所有东西是可能的（但不是个好主意）。不过这样做并不会导致控制器和视图之间“彻底”分离。EditPart 非常熟悉图形结构，因此以类似的 EditPart 重用该结构是不可能的。此外，更改外观或结构可能会导致意想不到的错误。 <br /><br />替代方法是，您应当编写自己的 Figure 子类，该子类掩藏了图形结构的详细信息。然后定义这个子类最少数量的 API，EditPart（控制器）用这些 API 来更新视图。这种实践（称为 <i>关注分离（separation of concerns）</i>）可提高重用机会并使错误更少。 <br /><br /></li>
				<li>
						<b>不要从图形引用模型或 EditPart</b>。图形不应该具有对 EditPart 或模型的访问权。在某些情形中，EditPart 可能会将自己作为侦听器添加到图形，但是只会认为它是侦听器，而不是 EditPart。这种去耦合（de-coupling）实践也可以产生更多的重用机会。 <br /><br /></li>
				<li>
						<b>使用内容窗格</b>。有时候您拥有一个容器，该容器将包含其它图形元素，但是您需要在容器四周进行一些装饰。例如，一个 UML 类通常显示为框，其顶部标有类名，可能还有一些原型，而底部是为属性和方法保留的。通过组合多个图形可以做到这一点。第一个图形是类的标题框，而另一个图形被指派为 <i>内容窗格</i>。该图形将最终包含表示属性和方法的图形。稍后在编写 EditPart 实现时，并不一定要表明应该将内容窗格用作所有子元素的父元素。 </li>
		</ul>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N10120">
						<span class="atitle">
								<font face="Arial" size="4">第 3 步. 编写您的 EditPart - 控制器</font>
						</span>
				</a>
		</p>
		<p>接下来我们将利用控制器（即 EditPart）将模型和视图联接起来。该步骤在 GEF 中放置“框架”。所提供的类是抽象的，因此客户实际上必须编写代码。结果证明，生成子类不仅是人们熟悉的方法，而且可能也是将模型映射到视图最灵活和简单的方法。</p>
		<p>所提供的用于生成子类的基本实现有三种。对于出现在树查看器中的 EditPart 使用 <code>AbstractTreeEditPart</code> 。图形查看器中的继承 <code>AbstractGraphicalEditPart</code> 和 <code>AbstractConnectionEditPart</code> 。本文将着重讨论图形 EditPart。相同的原理同样适用于树查看器。 </p>
		<p>
				<a name="N10138">
						<span class="smalltitle">EditPart 生命周期</span>
				</a>
		</p>
		<p>在编写您的 EditPart 之前，了解它们来自哪里，以及当不再需要它们时怎样处理它们，这是有帮助的。每个查看器都配置有一个用于创建 EditPart 的工厂（factory）。当您设置查看器的内容时，通过提供表示该查看器输入的模型对象，可以做到这一点。输入通常是最顶部的模型对象，通过该对象可遍历其它所有对象。然后查看器可以使用自己的工厂来构造用于该输入对象的 <i>内容 EditPart</i>。之后，查看器中的每个 EditPart 将填充和管理其自己的子 EditPart（和连接 EditPart），当需要新的 EditPart 时，将委派给 EditPart 工厂，直到填充该查看器。当用户添加新的模型对象时，包含这些对象的 EditPart 将通过构造相应的 EditPart 做出响应。请注意，视图的构造与 EditPart 的构造是同时进行的。因此，构造每个 EditPart 并将它添加到它的父 EditPart 之后，视图（不管是图形还是树项）也会发生同样的过程。 </p>
		<p>一旦用户除去与某些 EditPart 对应的模型对象，就丢弃这些 EditPart。如果用户撤销了一个删除操作，那么用于表示被恢复对象而重新创建的 EditPart 与原先的 EditPart 是不同的。这就是为什么 EditPart 不能包含长期信息，以及为什么不应由命令引用的原因。</p>
		<p>
				<a name="N10147">
						<span class="smalltitle">您的第一个 EditPart：内容 EditPart</span>
				</a>
		</p>
		<p>您编写的第一个 EditPart 是对应于图本身的 EditPart。这个 EditPart 称为查看器的 <i>内容</i>。它对应于模型中最顶部的元素，并且其父元素为查看器的 <i>根</i>EditPart（请参阅图 4）。根通过提供各种图形层（例如连接层和句柄层等）以及可能会在查看器级别提供的视图缩放或其它功能，为内容打下基础。请注意，根的功能不依赖于任何模型对象，GEF 为根提供了几个现成的实现。 </p>
		<p>
				<a name="figure4">
						<b>图 4. 查看器中的 EditPart</b>
				</a>
				<br />
				<img height="231" alt="查看器中的 EditPart" src="http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/fig4.gif" width="170" />
		</p>
		<p>内容的图形不是很有趣，并且它通常只是一个空面板，该面板将包含图的子图。它的图形应该为不透明类型（opaque），并且应当利用布局管理器进行初始化，该布局管理器将对图的子图进行布局。但是，它将拥有结构。图的直系子图是由返回的子模型对象列表确定的。清单 1 显示了一个样本内容 EditPart，它创建了一个不透明类型的图形，后者将使用 XYLayout 定位其子图。</p>
		<br />
		<a name="N1016B">
				<b>清单 1. 内容 EditPart 的初始实现</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console" size="2">public class DiagramContentsEditPart extends AbstractGraphicalEditPart {
    protected IFigure createFigure() {
        Figure f = new Figure();
        f.setOpaque(true);
        f.setLayoutManager(new XYLayout());
        return f;
    }

    protected void createEditPolicies() {
        ...
    }

    protected List getModelChildren() {
        return ((MyModelType)getModel()).getDiagramChildren();
    }
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>要确定图上的项，需要实现方法 <code>getModelChildren()</code> 。该方法返回子模型对象的列表（例如图中的节点）。超类将使用这个模型对象列表来创建对应的 EditPart。新创建的 EditPart 被添加到部件的子 EditPart 列表。这样又会将每个子 EditPart 的图形添加到示例图。缺省情况下，将返回一个空的列表，这表明没有子对象。 </p>
		<p>
				<a name="N1017C">
						<span class="smalltitle">其它图形 EditPart</span>
				</a>
		</p>
		<p>其余的 EditPart（表示图中的项）可能拥有要以图形方式显示的数据。它们可能还拥有自己的结构，例如连接或自己的子 EditPart。许多 GEF 应用程序利用由标签注明的图标之间的连接来描述这些图标。让我们假定您的 EditPart 将使用 Label 作为它的图形，并且您的模型提供了名称、图标以及与标签之间的连接。清单 2 显示了实现这种类型 EditPart 的第一次尝试。</p>
		<br />
		<a name="N10185">
				<b>清单 2. “节点”EditPart 的初始实现</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console" size="2">public class MyNodeEditPart extends AbstractGraphicalEditPart {
    protected IFigure createFigure() {
        return new Label();
    }
    protected void createEditPolicies() {
        ...
    }
    protected List getModelSourceConnections() {
        MyModel node = (MyModel)getModel();
        return node.getOutgoingConnections();
    }
    protected List getModelTargetConnections() {
        MyModel node = (MyModel)getModel();
        return node.getIncomingConnections();
    }
    protected void refreshVisuals() {
        MyModel node = (MyModel)getModel();
        Label label = (Label)getFigure();
        label.setText(node.getName());
        label.setIcon(node.getIcon());

        Rectangle r = new Rectangle(node.x, node.y, -1, -1);
        ((GraphicalEditPart) getParent()).setLayoutConstraint(this, label, r);
    }
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>这里覆盖了一个新方法 <code>refreshVisuals()</code> 。当需要利用来自模型的数据更新图形时，就调用该方法。在本案例中，模型的名称和图标被反映在标签中。但更为重要的是，该标签是通过将其布局约束传递给父元素来定位的。在内容 EditPart 中，我们使用了 XY 布局管理器。该布局使用 Rectangle 约束来确定在何处放置子图形。宽和高的值为“-1”，表明应当为该图提供理想的大小。 </p>
		<table cellspacing="0" cellpadding="0" width="40%" align="right" border="0">
				<tbody>
						<tr>
								<td width="10">
										<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" />
								</td>
								<td>
										<table cellspacing="0" cellpadding="5" width="100%" border="1">
												<tbody>
														<tr>
																<td bgcolor="#eeeeee">
																		<a name="N10198">
																				<b>技巧 #1</b>
																		</a>
																		<br />
																		<p>决不要使用 <code>setBounds(...)</code> 方法来放置图形。使用诸如 XYLayout 之类的布局管理器确保会正确地更新滚动条。另外，XYLayout 还处理将相对约束转换成绝对位置的工作，并且可用它来将图形自动调整为理想的大小（换而言之，如果约束的宽和高为 -1）。 </p>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<p>方法 <code>refreshVisuals()</code> 仅在 EditPart 初始化的过程中调用一次，并且决不会被再次调用。在响应模型通知时，应用程序负责根据需要再次调用 <code>refreshVisuals() </code>以更新图形。要改进性能，您可能希望将每个模型属性的代码分解成其自己的方法（或者是一个带有“开关（switch）”的方法）。这样，当模型发出通知时，可以运行最少的代码以便只刷新那些发生更改的内容。 </p>
		<p>另一个有趣的区别是连接支持的代码。与 <code>getModelChildren()</code> 类似， <code>getModelSourceConnections()</code> 和 <code>getModelTargetConnections()</code> 应当返回表示节点之间连接的模型对象。超类在必要时创建对应的 EditPart，并将它们添加到源和目标连接 EditPart 列表。请注意，连接是由每端的节点引用的，而其 EditPart 只需创建一次。GEF 确保只创建一次连接，该工作是通过首先检查查看器中是否已经存在连接来完成的。 </p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N101C0">
						<span class="atitle">
								<font face="Arial" size="4">建立连接</font>
						</span>
				</a>
		</p>
		<p>编写连接 EditPart 实现没有太大的区别。首先生成 <code>AbstractConnectionEditPart</code> 的子类。跟前面一样，可以实现 <code>refreshVisuals()</code> ，以将属性从模型映射到图形。连接可能还拥有约束，尽管这些约束与前面的约束略有不同。这里，连接路由器使用约束来使连接转向（bend）。此外，连接 EditPart 的图形必须是 Draw2D <code>Connection</code> ，它引入了另一个需求：连接锚（connection anchor）。 </p>
		<p>连接必须由 <code>ConnectionAnchor</code> “锚定”在两端。因此，必须在连接 EditPart 中或在节点实现中表明使用哪些锚。缺省情况下，GEF 假定节点 EditPart 将通过实现 <code>NodeEditPart </code>接口而提供锚。这样假定的一个原因是，锚的选择取决于各端上的节点正在使用的图形。连接 EditPart 不应了解节点正在使用的图形的任何内容。另一个原因是，当用户创建连接时，连接 EditPart 是不存在的，因此节点必须能够自己显示反馈。作为清单 2 的延续，我们在清单 3 中添加了必要的锚支持。 </p>
		<br />
		<a name="N101E0">
				<b>清单 3. 将锚支持添加到“节点”EditPart</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console" size="2">public class MyNodeEditPart
    extends AbstractGraphicalEditPart
    implements NodeEditPart
{
    ...
    public ConnectionAnchor getSourceConnectionAnchor(ConnectionEditPart connection) {
        return new ChopboxAnchor(getFigure());
    }
    public ConnectionAnchor getSourceConnectionAnchor(Request request) {
        return new ChopboxAnchor(getFigure());
    }
    public ConnectionAnchor getTargetConnectionAnchor(ConnectionEditPart connection) {
        return new ChopboxAnchor(getFigure());
    }
    public ConnectionAnchor getTargetConnectionAnchor(Request request) {
        return new ChopboxAnchor(getFigure());
    }

    ...
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<table cellspacing="0" cellpadding="0" width="40%" align="right" border="0">
				<tbody>
						<tr>
								<td width="10">
										<font face="Lucida Console" size="2">
												<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" />
										</font>
								</td>
								<td>
										<table cellspacing="0" cellpadding="5" width="100%" border="1">
												<tbody>
														<tr>
																<td bgcolor="#eeeeee">
																		<a name="N101EC">
																				<b>技巧 #2</b>
																		</a>
																		<br />
																		<p>别忘记真正实现 <code>NodeEditPart</code> 接口。否则您的方法将永远不会被调用。 </p>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<p>以连接（connection）为参数的方法是对现有连接 EditPart 设置锚时使用的方法。其它两个方法以请求（request）作为参数。这些方法是用户创建新连接时的编辑过程中使用的。对于本示例，所有情形都将返回 chopbox 锚。chopbox 锚只查找线与节点图形的边框相交的点。</p>
		<p>实现连接 EditPart 是比较简单的。请注意，甚至无需创建图形，因为缺省的 <code>PolylineConnection</code> 创建适合于大多数场合（请参阅清单 4）。 </p>
		<br />
		<a name="N10204">
				<b>清单 4. 初始的连接 EditPart 实现</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console" size="2">public class MyConnectionEditPart extends AbstractConnectionEditPart {
    protected void createEditPolicies() {
        ...
    }

    protected void refreshVisuals() {
        PolylineConnection figure = (PolylineConnection)getFigure();
        MyConnection connx = (MyConnection)getModel();
        figure.setForegroundColor(MagicHelper.getConnectionColor(connx));
        figure.setRoutingConstraint(MagicHelper.getConnectionBendpoints(connx));
    }
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<table cellspacing="0" cellpadding="0" width="40%" align="right" border="0">
				<tbody>
						<tr>
								<td width="10">
										<font face="Lucida Console" size="2">
												<img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" />
										</font>
								</td>
								<td>
										<table cellspacing="0" cellpadding="5" width="100%" border="1">
												<tbody>
														<tr>
																<td bgcolor="#eeeeee">
																		<a name="N10210">
																				<b>技巧 #3</b>
																		</a>
																		<br />
																		<p>最重要的是了解何时使用以及何时不使用 ConnectionEditPart。当用户可以选择某些东西并可与之进行交互的时候，就使用连接 EditPart。它可能与模型中的某个对象直接相关，并且通常可由自己删除。</p>
																		<p>如果您只是拥有一个需要绘制直线的节点或容器，那么只须用图形的 paint 方法绘制直线，或者组合一个图形，该图形包含 Polyline 图形。</p>
																		<p>连接始终都必须拥有源和目标。如果您需要一个连接，该连接可以在没有源或没有目标的情况下存在，那么比较好的方法是只继承 <code>AbstractGraphicalEditPart</code> ，并使用连接图形。 </p>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<p>
				<a name="N10224">
						<span class="smalltitle">侦听模型</span>
				</a>
		</p>
		<p>创建 EditPart 之后，它应该开始侦听来自模型的更改通知。由于 GEF 是与模型无关的，因此所有应用程序都必须添加自己的侦听器，并处理产生的通知。接收到通知时，处理程序可以调用某个提供的方法来强制进行一次刷新。例如，如果删除了一个子模型对象，那么调用 <code>refreshChildren()</code> 将导致对应的 EditPart 及其图形被除去。对于简单的属性更改，可以使用 <code>refreshVisuals()</code> 。正如我们前面提及的，可将该方法分解成几个部分，从而避免没有必要地更新每个显示的属性。 </p>
		<p>添加侦听器但却忘记除去它们是导致内存泄漏的常见原因。出于这个原因，添加和除去侦听器的地方应该在 API 中清晰地注明。您的 EditPart 必须继承 <code>activate()</code> ，以便添加稍后必须除去的任何侦听器。通过继承 <code>deactivate()</code> 可除去那些相同的侦听器。清单 5 显示了向节点 EditPart 实现添加的模型通知内容。 </p>
		<br />
		<a name="N10240">
				<b>清单 5. 侦听“节点”EditPart 中的模型更改</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console" size="2">public class MyNodeEditPart
extends AbstractGraphicalEditPart
    implements NodeEditPart, ModelListener
{
    ...

    public void activate() {
        super.activate();
        ((MyModel)getModel()).addModelListener(this);
    }

    public void deactivate() {
        ((MyModel)getModel()).removeModelListener(this);
        super.deactivate();
    }

    public void modelChanged(ModelEvent event) {
        if (event.getChange().equals("outgoingConnections"))
            refreshSourceConnections();
        else if (event.getChange().equals("incomingConnections"))
            refreshTargetConnections();
        else if (event.getChange().equals("icon")
          || event.getChange().equals("name"))
            refreshVisuals();
    }

    ...
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<p>
				<a name="N1024A">
						<span class="smalltitle">编辑模型</span>
				</a>
		</p>
		<p>到目前为止，我们已经讲解了如何创建 EditPart，它们如何创建自己的可视图（visual）以及当模型发生变化时它们如何自我更新。除此之外，EditPart 也是对模型进行更改的主要参与者。当命令的请求被发送给 EditPart 时就会发生这种情况。请求还用来要求 EditPart 显示诸如在鼠标拖动期间发生的反馈。EditPart 支持、阻止或忽略给定的请求。所支持或阻止的请求类型决定了 EditPart 的行为。</p>
		<p>到目前为止，侧重点都是将模型的结构和特性映射到视图。结果表明，这基本上是您在 EditPart 类自身中所做的所有工作。其行为是由一组名为 <i>EditPolicies</i> 的可插入的助手类决定的。在所提供的示例中，我们忽略了方法 <code>createEditPolicies()</code> 。一旦您实现该方法，您就几乎已经完成了您的 EditPart。当然，您仍需要提供编辑策略，该策略知道如何修改您应用程序的模型。 </p>
		<p>因为编辑行为是可插入的，所以在开发各种 EditPart 实现时，可以根据将模型映射到视图和处理模型更新这种任务，来创建类层次结构。</p>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N10260">
						<span class="atitle">
								<font face="Arial" size="4">第 4 步. 将所有内容组合在一起</font>
						</span>
				</a>
		</p>
		<p>现在，您已经完成了以图形方式显示您的模型所需的所有部分。对于最后的组装，我们将使用 <code>IEditorPart</code> 。但是，也可以在视图、对话框或者可以放置控件的几乎任何地方使用 GEF 的查看器。对于本步骤，您必须拥有自己的 UI 插件，它将为正在打开的资源定义编辑器和文件扩展名。可在同一个插件或一个不同的插件中定义您的模型。您还需要一个预填充的模型，因为目前还没有编辑功能。 </p>
		<p>提供样本模型数据的方法有几种。对于本示例而言，当编辑器打开时，我们将在代码中创建模型，从而忽略了文件的实际内容。要做到这一点，我们假定已经存在一个测试工厂。或者，您可以创建一个示例向导，该向导用数据对资源进行预填充（普通向导只创建空图）。最后，您可以利用文本编辑器以手工方式编写文档的内容。</p>
		<p>既然您有了样本模型，那么让我们创建将显示模型的编辑器部件。有一种快速的方法，就是生成子类或复制 GEF 的 <code>GraphicalEditor</code> 。该类创建 <code>ScrollingGraphicalViewer</code> 的一个实例，并且构造一个画布来充当编辑器的控件。它是一个很方便的类，用来帮助您开始使用 GEF；一个可以正确工作的 Eclipse 编辑器需要考虑很多其它事情，例如不利的团队环境（pessimistic team environment）和要删除或移动资源等。 </p>
		<p>清单 6 显示了一个样本编辑器实现。有几个必须实现的抽象方法。出于本文所讨论范围的限制，我们将忽略模型持久性和标记。要让您的图出现在图形查看器中，您必须做两件事情。首先，利用自己的 EditPart 工厂配置查看器，以从第 3 步构造 EditPart。然后，将图模型对象传递给查看器。</p>
		<br />
		<a name="N1027E">
				<b>清单 6. 实现您的编辑器部件（Editor Part）</b>
		</a>
		<br />
		<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1">
				<tbody>
						<tr>
								<td>
										<pre>
												<code class="section">
														<font face="Lucida Console" size="2">public class MyEditor extends GraphicalEditor {
    public MyEditor() {
        setEditDomain(new DefaultEditDomain(this));
    }

    protected void configureGraphicalViewer() {
        super.configureGraphicalViewer(); //Sets the viewer's background to System "white"
        getGraphicalViewer().setEditPartFactory(new MyGraphicalEditpartFactory());
    }

    protected void initializeGraphicalViewer() {
        getGraphicalViewer().setContents(MagicHelper.constructSampleDiagram());
    }
    public void doSave(IProgressMonitor monitor) {
        ...
    }
    public void doSaveAs() {
        ...
    }
    public void gotoMarker(IMarker marker) {
        ...
    }
    public boolean isDirty() {
        ...
    }
    public boolean isSaveAsAllowed() {
        ...
    }
}
</font>
												</code>
										</pre>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<font face="Lucida Console" size="2">
												<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
												<br />
												<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
										</font>
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<font face="Lucida Console" size="2">
												<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
												<br />
										</font>
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<font face="Lucida Console" size="2">
																				<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																				<br />
																		</font>
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="N10288">
						<span class="atitle">
								<font face="Arial" size="4">接下来的步骤</font>
						</span>
				</a>
		</p>
		<p>我们已经经历了从仅拥有一个模型到在图形编辑器中显示该模型的全过程。但是我们只打下了基础。我们简要提及了编辑策略。通过阅读与 GEF SDK 一起提供的开发人员文档，可以获得有关编辑策略的更多信息。还可从 GEF 的主页（请参阅文章结尾的 <a href="http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/index.html#resources"><font color="#996699">参考资料</font></a>）获得一个示例，该示例演示了如何使用各种编辑策略类型。 </p>
		<p>GEF 还提供了一个调色板。该调色板显示了一组工具，用于在图中创建对象。用户可以激活这些工具，或者使用本机拖放直接从该调色板拖动项。它还支持让用户定制内容。</p>
		<p>在 GEF 中还可以使用几个 JFace 操作。应用程序可以在菜单、工具栏或上下文菜单中使用诸如撤销、对齐和删除之类的操作。</p>
		<p>最后，您的应用程序应当支持大纲（outline）视图和特性（properties）视图。大纲视图用于导航和有限的编辑用途。GEF 的 TreeViewer 和／或概述（overview）窗口可以在这里使用。特性表（property sheet）允许用户查看和编辑任何当前选定项的详细特性。</p>
		<p>为了显示所选择的项并允许用户进行更改，您必须将编辑策略添加到 EditPart。请参阅 GEF 主页上的示例，并参阅与 GEF SDK 一起提供的开发人员文档。</p>
		<p>有关 GEF 和 Eclipse 工作台提供的其它功能的详细信息不在本文的讨论范畴之内，但是您可能有兴趣对它们稍做了解：</p>
		<ul>
				<li>
						<b>调色板</b>。该工具的调色板是用于在图中创建新对象的 <i>实际</i>方法。GEF 包含了一个功能丰富的调色板，它支持拖放、多绘图程序和布局设置，如果应用程序希望，它甚至可支持让用户定制内容。 
</li>
				<li>
						<b>操作栏</b>。编辑器（Editor）和视图（View）可以为工具栏、菜单和上下文菜单提供操作（Action）。GEF 提供了几个可重用的操作实现，但是将它们显示在什么地方取决于应用程序。 
</li>
				<li>
						<b>特性表</b>。特性表可用于显示所选项特性的详细信息。GEF 允许您在 EditPart 上或在模型中添加特性表支持。 
</li>
				<li>
						<b>大纲</b>。大纲视图通常用于显示图的结构化表示，但是一般来说，它可用于任何工作。GEF 的 TreeViewer 通常在大纲视图中使用。 </li>
		</ul>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="resources">
						<span class="atitle">
								<font face="Arial" size="4">参考资料 </font>
						</span>
				</a>
		</p>
		<ul>
				<li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www.ibm.com/developerworks/library/os-gef/index.html"><font color="#5c81a7">英文原文</font></a>. <br /><br /></li>
				<li>请在 <a href="http://www.eclipse.org/gef"><font color="#996699">GEF 主页</font></a>上下载插件并查找有关 GEF 的其它信息。 <br /><br /><br /><br /></li>
				<li>
						<a href="news://news.eclipse.org/eclipse.tools.gef">
								<font color="#5c81a7">GEF 新闻组</font>
						</a>是查找答案和进行提问的极佳场所。 <br /><br /><br /><br /></li>
				<li>
						<a href="http://www.eclipse.org/">
								<font color="#5c81a7">Eclipse 项目</font>
						</a>主管着 Eclipse 工作台、GEF 和其它开放源码技术。 <br /><br /><br /><br /></li>
				<li>请在 <i>developerWorks</i>上浏览 <a href="http://www.ibm.com/developerworks/views/opensource/articles.jsp?sort_order=desc&amp;expand=&amp;sort_by=Date&amp;show_abstract=false&amp;view_by=Eclipse&amp;search_by="><font color="#5c81a7">针对开发人员的其它 Eclipse 文章</font></a>。 <br /><br /><br /></li>
		</ul>
		<br />
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td>
										<img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" />
										<br />
										<img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" />
								</td>
						</tr>
				</tbody>
		</table>
		<table class="no-print" cellspacing="0" cellpadding="0" align="right">
				<tbody>
						<tr align="right">
								<td>
										<img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										<br />
										<table cellspacing="0" cellpadding="0" border="0">
												<tbody>
														<tr>
																<td valign="center">
																		<img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" />
																		<br />
																</td>
																<td valign="top" align="right">
																		<a class="fbox" href="http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/index.html#main">
																				<b>
																						<font color="#996699">回页首</font>
																				</b>
																		</a>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
		<br />
		<br />
		<p>
				<a name="author">
						<span class="atitle">
								<font face="Arial" size="4">关于作者</font>
						</span>
				</a>
		</p>
		<table cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td colspan="3">
										<font face="Arial" size="4">
												<img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="100%" />
										</font>
								</td>
						</tr>
						<tr valign="top" align="left">
								<td>
										<p>
												<font face="Arial" size="4">
												</font>
										</p>
								</td>
								<td>
										<font face="Arial" size="4">
												<img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="4" />
										</font>
								</td>
								<td width="100%">
										<p>Randy Hudson 是北卡罗来纳州 Research Triangle Park 的 IBM 软件工程师。作为图形编辑框架（Graphical Editing Framework，GEF）的技术领导，他帮助将这个曾经是内部的项目转变成了开放源码技术。他目前的工作侧重于可用性、图形编辑、图形布局和边缘路由（edge routing）。可以通过 <a href="mailto:buchu@nc.rr.com"><font color="#5c81a7">buchu at nc.rr.com</font></a>与 Randy 联系。 </p>
								</td>
						</tr>
				</tbody>
		</table>
		<br />from: <a href="http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/index.html">http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/index.html</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/67530.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-09-04 10:57 <a href="http://www.blogjava.net/weidagang2046/articles/67530.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Eclipse插件开发之新手入门</title><link>http://www.blogjava.net/weidagang2046/articles/65087.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Tue, 22 Aug 2006 07:28:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/65087.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/65087.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/65087.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/65087.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/65087.html</trackback:ping><description><![CDATA[现在在Internet上已经可以见到不少的Eclipse插件开发的入门文章，这里我写本文的目的主要是将我自己的体会和最开始的学习告诉给大家。 同时也希望本文能使用最为简单的方法来让大家了解开发Eclipse插件的基础。需要注意的是，要学习Eclipse的插件开发，你需要：<br /><br />　　会使用Eclipse来开发Java应用程序 <br /><br />　　了解插件这个词的概念 <br /><br />　　了解一些XML的知识 本文是一个入门的文章，只是向大家说明开发一个插件的简单步骤，同时了解在开发插件时涉及到的技术面会有哪些。 <br /><br />　　<font color="#990000"><strong>Eclipse SDK概述</strong></font><br /><br />　　我们通常使用的Eclipse也就是我们这里所说的Eclipse SDK，这个SDK中包括了很多的内容，如下图所示：<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img onerror="this.src='http://www.yesky.com/image20010518/188289.jpg';" hspace="3" src="http://dev.yesky.com/image20010518/188289.jpg" align="center" vspace="1" border="1" /></div></td></tr></tbody></table><br />　　运行时核心(Eclipse Platform) - SDK必须一个Eclipse Platform，它自身不具有任何对最终用户有意义的功能， 它是一个加载所有插件的基础平台。也就是Eclipse的运行时最小集合了。 <br /><br />　　Java 开发工具(JDT) - 我们所有的有关Java的开发部分都是由这个插件来完成了，它形成了对于Java最为基础的编辑、 编译、运行、调试、发布的环境。 <br /><br />　　插件开发者环境(PDE) - 开发插件的插件，我们如果要开发插件哪么我们就会发现所有的工作环境都是由它来提供的。 它提供了用来自动创建、处理、调试和部署插件的工具。 <br /><br />　　我们将来要开发的插件都是由平台来加载和运行，而PDE则是开发插件的开发环境，JDT则是开发插件时的Java代码的开发环境。<br /><br />　　<font color="#990000"><strong>创建插件项目</strong></font><br /><br />　　<strong>设置引用项目</strong><br /><br />　　开发插件时需要大量的外部库，这些外部库主要是现有的Eclipse中各个插件所提供的库。 为了开发方便，我们先将这些外部库由一个项目统一引用。 <br /><br />　　从资源透视图中，使用文件＞导入...＞外部插件和段。 <br />　　在下一步中选择抽取源归档并在项目中创建源文件夹。 <br />　　到显示称为选择的屏幕，选择 org.eclipse.ui，然后单击完成按钮。 <br /><br />　　<strong>创建项目</strong><br /><br />　　在Eclipse需要创建一个空的插件项目，为了让我们更好的理解插件中各个文件的来源，我们从一个空白的插件项目开始：<br /><br />　　1) 打开新建项目...向导（文件＞新建＞项目...）并从插件开发类别中选择插件项目。 <br /><br />　　2) 将com.huangdong.examples.helloworld用作项目的名称。缺省情况下，向导还会将com.huangdong.examples.helloworld设置为标识。<br /><br />　　3) 最终，确保在插件代码生成器页面上选择了创建空白插件项目。 <br /><br />　　4) 当询问您是否想切换到“插件开发”透视图时，回答是。 <br /><br />　　5) 选择com.huangdong.examples.helloWorld项目并打开属性对话框。 <br /><br />　　6) 在Java构建路径属性中，选择项目选项卡，并选择项目org.eclipse.ui。这些包含了项目需要的导入类。 <br /><br />　　7) 重建项目。<br /><strong><font color="#990000">创建一个插件内容<br /><br /></font></strong>　　<strong>创建一个新的小视图</strong><br /><br />　　下面我们为该项目加入一个很简单的视图：<br /><br />　　1) 在项目的src目录下创建包com.huangdong.examples.helloworld。 <br /><br />　　2) 在此包中创建称为HelloWorldView的新类其超类为org.eclipse.ui.part.ViewPart。 <br /><br />　　在HelloWorldView中加入以下代码：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e6e4dd" border="1"><tbody><tr><td>package com.huangdong.examples.helloworld;<br /><br />import org.eclipse.swt.SWT;<br />import org.eclipse.swt.widgets.Composite;<br />import org.eclipse.swt.widgets.Label;<br />import org.eclipse.ui.part.ViewPart;<br /><br />public class HelloWorldView extends ViewPart {<br /><br />　Label label;<br /><br />　public void createPartControl(Composite parent) {<br />　　label = new Label(parent, SWT.WRAP);<br />　　label.setText("Hello World");<br />　}<br /><br />　public void setFocus() {}<br />}</td></tr></tbody></table><br />　　我们为该类定义了一个变量lable，在createPartControl方法中初始化并设置了一个显示的字符串。<br /><br />　　<strong>护展扩展点</strong><br /><br />　　让Eclipse添加这个视图，需要扩展org.eclipse.ui.views扩展点。所有的这些需要在plugin.xml中进行描述。该清单文件描述插件，包括插件的代码所在的位置以及正在添加的扩展。<br /><br />　　将以下内容复制到plugin.xml中：<br /><br /><table bordercolor="#ffcc66" width="90%" align="center" bgcolor="#e6e4dd" border="1"><tbody><tr><td>＜?xml version="1.0" encoding="UTF-8"?＞<br />＜plugin id="com.huangdong.examples.helloworld"<br />　name="com.huangdong.examples.helloworld"<br />　version="1.0.0"<br />　provider-name="HuangDong"＞<br /><br />＜runtime＞<br />　＜library name="helloworld.jar"/＞<br />＜/runtime＞<br />＜requires＞<br />　＜import plugin="org.eclipse.ui"/＞<br />＜/requires＞<br /><br />＜extension point="org.eclipse.ui.views"＞<br />＜category<br />　name="Hello"<br />　id="com.huangdong.examples.helloworld.hello"＞<br />＜/category＞<br />＜view<br />　　name="Hello Greetings"<br />　　category="com.huangdong.examples.helloworld.hello"<br />　　class="com.huangdong.examples.helloworld.HelloWorldView"<br />　　id="com.huangdong.examples.helloworld.helloworldview"＞<br />＜/view＞<br />＜/extension＞<br /><br />＜/plugin＞<br /></td></tr></tbody></table><br />　　在plugin域中定义了插件的名称、标识和版本。 同时在runtime域中定义了插件代码将打包于helloworld.jar文件中。 在requires域中定义了该插件所要使用的依赖插件，由于我们要使用SWT API和工作台所以列示了org.eclipse.ui。 最后，在extension中说明了要们要扩展org.eclipse.ui.views扩展点。 首先我们在category中定义了视图的类别，在工作台的显示视图对话框中，可以使用类别来将相关的视图集中在一起。我们定义的类别名为“Hello”。 同时也定义了我们的视图，名为“Hello Greetings”，这个视图将会显示在“显示视图”对话框和视图的标题栏中，这里我们还通过class标识来说明了实现这个视图的最终类。 <br /><br />　　通过plugin.xml的定义，Eclipse才会真正的找到插件可以做的行为，以及这些行为最终实现的具体Java类。<br /><br />　　在插件清单文件中使用了许多标识。 个别扩展点通常会定义需要标识的配置参数（例如，以上用于视图扩展点的类别标识）。 我们还要定义插件标识。通常，应该对所有标识都使用 Java 包名前缀，以便确保所有已安装的插件都是唯一的。 <br /><br />　　在前缀后面使用的特定名称完全由您自己决定。 然而，如果插件标识前缀刚好与其中一个包的名称相同，则应该避免在该包中使用类名。 否则，将很难分辨您正在查看标识名还是类名。 <br /><br />　　还应该避免对不同的扩展配置参数使用相同的标识。 在上述清单中，已经使用了公共标识前缀（com.huangdong.examples.helloworld），但是，我们的所有标识都是唯一的。 此命名方法可以帮助我们阅读文件并了解哪些标识是相关的。 <br /><br />　　<font color="#990000"><strong>运行和测试插件</strong></font><br /><br />　　运行插件是一件很简单的事，这些在PDE中给我们提供了很好的支持。 只需要在菜单中选择运行＞运行为＞运行时工作台，在运行时会弹出一个重复插件的提示框，可以按确定跳过，不必在意。 这样会启动一个已经安装好插件的Eclipse。 <br /><br />　　启动后在菜单中选择窗口＞显示视图＞其它，在显示视图对话框中会有一个分类为Hello，点开Hello分类会看到Hello Greetings，选择后点确定按钮。在最下面的视图中可以见到以下界面：<br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img onerror="this.src='http://www.yesky.com/image20010518/188290.gif';" hspace="3" src="http://dev.yesky.com/image20010518/188290.gif" align="center" vspace="1" border="1" /></div></td></tr></tbody></table><br />　　到这里，如果你看到了这个图，哪么恭喜你，你的第一个Eclipse插件成功运行了。<br /><br />from: <a href="http://dev.yesky.com/SoftChannel/72342371961929728/20041022/1867558_1.shtml">http://dev.yesky.com/SoftChannel/72342371961929728/20041022/1867558_1.shtml</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/65087.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-08-22 15:28 <a href="http://www.blogjava.net/weidagang2046/articles/65087.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开发一个调试JSP的Eclipse插件</title><link>http://www.blogjava.net/weidagang2046/articles/64736.html</link><dc:creator>weidagang2046</dc:creator><author>weidagang2046</author><pubDate>Mon, 21 Aug 2006 03:11:00 GMT</pubDate><guid>http://www.blogjava.net/weidagang2046/articles/64736.html</guid><wfw:comment>http://www.blogjava.net/weidagang2046/comments/64736.html</wfw:comment><comments>http://www.blogjava.net/weidagang2046/articles/64736.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/weidagang2046/comments/commentRss/64736.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/weidagang2046/services/trackbacks/64736.html</trackback:ping><description><![CDATA[本文通过开发一个JSP 编辑器插件的示例，介绍了 Eclipse 中设置 JSP 断点的方法，以及如何远程调试 JSP。作为基础知识，本文的前两部分描述了 JAVA Debug 和 JSR-45 的基本原理。 <br /><br />　　环境要求: 本文的代码是在 Eclipse3.0.0，JDK1.4.2 和 Tomcat5.0.5 上测试过的。<br /><br />　　<b>JAVA 调试框架（JPDA）简介</b><br /><br />　　JPDA 是一个多层的调试框架，包括 JVMDI、JDWP、JDI 三个层次。JAVA 虚拟机提供了 JPDA 的实现。其开发工具作为调试客户端，可以方便的与虚拟机通讯，进行调试。Eclipse 正是利用 JPDA 调试 JAVA 应用，事实上，所有 JAVA 开发工具都是这样做的。SUN JDK 还带了一个比较简单的调试工具以及示例。<br /><ul><li>JVMDI 定义了虚拟机需要实现的本地接口 
</li><li>JDWP 定义了JVM与调试客户端之间的通讯协议 <br /><br />调试客户端和JVM 既可以在同一台机器上，也可以远程调试。JDK 会包含一个默认的实现 jdwp.dll，JVM 允许灵活的使用其他协议代替 JDWP。SUN JDK 有两种方式传输通讯协议：Socket 和共享内存(后者仅仅针对 Windows)，一般我们都采用 Socket 方式。<br /><br />你可以用下面的参数，以调试模式启动JVM<br /><br /><table bordercolor="#cccccc" width="90%" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre>  -Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n 
  -Xrunjdwp     JVM 加载 jdwp.dll  
   transport=dt_socket   使用 Socket 传输 
   address      表示调试端口号 
   server=y     表示 JVM 作为服务器，建立 Socket 
   suspend=n    表示启动过程中，JVM 不会挂起去等待调试客户端连接</pre></td></tr></tbody></table><br /></li><li>JDI 则是一组JAVA接口 <br /><br />如果是一个 JAVA 的调试客户端，只要实现 JDI 接口，利用JDWP协议，与虚拟机通讯，就可以调用JVMDI了。<br /></li></ul>　　下图为 JPDA 的基本架构：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre> 
                          Components                        Debugger Interface 
              
                /    |-----------------------| 
               /     |     VM       | 
 debuggee ----(      |-----------------------|  &lt;------- JVMDI - Java VM Debug Interface 
               \     |   back-end     | 
                \    |-----------------------| 
                /           | 
  comm channel -(           |  &lt;--------------- JDWP - Java Debug Wire Protocol 
                \           | 
                     |---------------------| 
                     | front-end      | 
                     |---------------------|  &lt;------- JDI - Java Debug Interface 
                     |      UI      | 
                     |---------------------| 
             </pre></td></tr></tbody></table><br />　　参见：<a href="http://java.sun.com/j2se/1.4.2/docs/guide/jpda/architecture.html">http://java.sun.com/j2se/1.4.2/docs/guide/jpda/architecture.html</a><br /><br />　　Eclipse作为一个基于 JAVA 的调试客户端，利用 org.eclipse.jdt.debug Plugin 提供了JDI 的具体实现。JDI 接口主要包含下面 4 个包<br /><pre>com.sun.jdi  
com.sun.jdi.connect  
com.sun.jdi.event  
com.sun.jdi.request </pre>　　本文不对 JDI 进行深入阐述，这里重点介绍 JDI 中与断点相关的接口。<br /><ul><li>com.sun.jdi <br /><br />主要是JVM(VirtualMachine) 线程(ThreadReference) 调用栈(StackFrame) 以及类型、实例的描述。利用这组接口，调试客户端可以用类似类反射的方式，得到所有类型的定义，动态调用 Class 的方法。 
</li><li>com.sun.jdi.event <br /><br />封装了JVM 产生的事件， JVM 正是将这些事件通知给调试客户端的。例如 BreakpointEvent 就是 JVM 执行到断点的时候，发出的事件；ClassPrepareEvent就是 Class 被加载时发出的事件。 <br /><br /></li><li>com.sun.jdi.request <br /><br />封装了调试客户端可以向 JVM发起的请求。例如 BreakpointRequest 向 JVM 发起一个添加断点的请求；ClassPrepareRequest 向 JVM 注册一个类加载请求，JVM 在加载指定 Class 的时候，就会发出一个 ClassPrepareEvent 事件。 </li></ul>　　<b>JSR-45规范</b><br /><br />　　JSR-45(Debugging Support for Other Languages)为那些非 JAVA 语言写成，却需要编译成 JAVA 代码，运行在 JVM 中的程序，提供了一个进行调试的标准机制。也许字面的意思有点不好理解，什么算是非 JAVA 语言呢？其实 JSP 就是一个再好不过的例子，JSR-45 的样例就是一个 JSP。<br /><br />　　JSP的调试一直依赖于具体应用服务器的实现，没有一个统一的模式，JSR-45 针对这种情况，提供了一个标准的模式。我们知道，JAVA 的调试中，主要根据行号作为标志，进行定位。但是 JSP 被编译为 JAVA 代码之后，JAVA 行号与 JSP 行号无法一一对应，怎样解决呢？<br /><br />　　JSR-45 是这样规定的：JSP 被编译成 JAVA 代码时，同时生成一份 JSP 文件名和行号与 JAVA 行号之间的对应表(SMAP)。JVM 在接受到调试客户端请求后，可以根据这个对应表(SMAP)，从 JSP 的行号转换到 JAVA 代码的行号；JVM 发出事件通知前, 也根据对应表(SMAP)进行转化，直接将 JSP 的文件名和行号通知调试客户端。<br /><br />　　我们用 Tomcat 5.0 做个测试，有两个 JSP，Hello.jsp 和 greeting.jsp，前者 include 后者。Tomcat会将他们编译成 JAVA 代码(Hello_jsp.java)，JAVA Class(Hello_jsp.class) 以及 JSP 文件名/行号和 JAVA 行号之间的对应表(SMAP)。<br /><br />　　Hello.jsp: <br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre> 
          1    &lt;HTML&gt;     
          2    &lt;HEAD&gt;     
          3    &lt;TITLE&gt;Hello Example&lt;/TITLE&gt;     
          4    &lt;/HEAD&gt;     
          5    &lt;BODY&gt;     
          6    &lt;%@ include file="greeting.jsp" %&gt;     
          7    &lt;/BODY&gt;     
          8    &lt;/HTML&gt;   
          </pre></td></tr></tbody></table><br />　　greeting.jsp: <br /><br />1 Hello There!&lt;P&gt; 2 Goodbye on &lt;%= new java.util.Date() %&gt; <br /><br />　　JSP 编译后产生的Hello_jsp.java 如下:<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre>Hello_jsp.java: 
1      package org.apache.jsp; 
2 
3      import javax.servlet.*; 
4      import javax.servlet.http.*; 
5      import javax.servlet.jsp.*; 
6       
7      public final class Hello_jsp extends org.apache.jasper.runtime.HttpJspBase 
8          implements org.apache.jasper.runtime.JspSourceDependent { 
9       
10        private static java.util.Vector _jspx_dependants; 
11       
12        static { 
13          _jspx_dependants = new java.util.Vector(1); 
14          _jspx_dependants.add("/greeting.jsp"); 
15        } 
16       
17        public java.util.List getDependants() { 
18          return _jspx_dependants; 
19        } 
20       
21  public void _jspService(HttpServletRequest request, HttpServletResponse response) 
22              throws java.io.IOException, ServletException { 
23       
24          JspFactory _jspxFactory = null; 
25          PageContext pageContext = null; 
26          HttpSession session = null; 
27          ServletContext application = null; 
28          ServletConfig config = null; 
29          JspWriter out = null; 
30          Object page = this; 
31          JspWriter _jspx_out = null; 
32       
33       
34          try { 
35            _jspxFactory = JspFactory.getDefaultFactory(); 
36            response.setContentType("text/html"); 
37            pageContext = _jspxFactory.getPageContext(this, request, response, 
38               null, true, 8192, true); 
39            application = pageContext.getServletContext(); 
40            config = pageContext.getServletConfig(); 
41            session = pageContext.getSession(); 
42            out = pageContext.getOut(); 
43            _jspx_out = out; 
44       
45            out.write("&lt;HTML&gt;    \r\n"); 
46            out.write("&lt;HEAD&gt;    \r\n"); 
47            out.write("&lt;TITLE&gt;Hello Example"); 
48            out.write("&lt;/TITLE&gt;    \r\n"); 
49            out.write("&lt;/HEAD&gt;    \r\n"); 
50            out.write("&lt;BODY&gt;    \r\n"); 
51            out.write("Hello There!"); 
52            out.write("&lt;P&gt;    \r\nGoodbye on "); 
53            out.write(String.valueOf( new java.util.Date() )); 
54            out.write("  \r\n"); 
55            out.write("    \r\n"); 
56            out.write("&lt;/BODY&gt;    \r\n"); 
57            out.write("&lt;/HTML&gt;  \r\n"); 
58          } catch (Throwable t) { 
59            if (!(t instanceof javax.servlet.jsp.SkipPageException)){ 
60              out = _jspx_out; 
61              if (out != null &amp;&amp; out.getBufferSize() != 0) 
62                out.clearBuffer(); 
63              if (pageContext != null) pageContext.handlePageException(t); 
64            } 
65          } finally { 
66     if (_jspxFactory != null) _jspxFactory.releasePageContext ( pageContext); 
67          } 
68        } 
69      }</pre></td></tr></tbody></table><br />　　Tomcat 又将这个 JAVA 代码编译为 Hello_jsp.class，他们位于： $Tomcat_install_path$\work\Standalone\localhost\_ 目录下。但是 JSP 文件名/行号和 JAVA 行号的对应表(以下简称SMAP) 在哪里呢？答案是，它保存在 Class 中。如果用 UltraEdit 打开这个 Class 文件，就可以找到 SourceDebugExtension 属性，这个属性用来保存 SMAP。<br /><br />　　JVM 规范定义了 ClassFile 中可以包含 SourceDebugExtension 属性，保存 SMAP：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre>SourceDebugExtension_attribute { 
  u2 attribute_name_index; 
  u4 attribute_length; 
  u1 debug_extension[attribute_length]; 
} 
</pre></td></tr></tbody></table><br />　　我用 javassist 做了一个测试(javassist可是一个好东东，它可以动态改变Class的结构，JBOSS 的 AOP就利用了javassist，这里我们只使用它读取ClassFile的属性)<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre> 
public static void main(String[] args) throws Exception{ 
   String[]files = { 
　　　"E:\\Tomcat5_0_5\\work\\Catalina\\localhost\\_\\org\\apache\\jsp\\Hello_jsp.class", 
   }; 
                 
　for(int k = 0; k &lt; files.length; k++){ 
  　 String file = files[k]; 
  　 System.out.println("Class : " + file); 
  　 ClassFile classFile = new ClassFile(new DataInputStream(new FileInputStream(file))); 
  　         
  　 AttributeInfo attributeInfo = classFile.getAttribute("SourceDebugExtension"); 
  　 System.out.println("attribute name :" + attributeInfo.getName() + "]\n\n"); 
  　 byte[]bytes = attributeInfo.get(); 
  　 String str = new String(bytes); 
  　 System.out.println(str);    
   } 
} 
</pre></td></tr></tbody></table><br />　　这段代码显示了SourceDebugExtension 属性，你可以看到SMAP 的内容。编译JSP后，SMAP 就被写入 Class 中, 你也可以利用 javassist 修改 ClassFile 的属性。<br /><br />　　下面就是 Hello_jsp.class 中保存的 SMAP 内容:<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre>SMAP 
E:\Tomcat5_0_5\work\Catalina\localhost\_\org\apache\jsp\Hello_jsp.java 
JSP 
*S JSP 
*F 
+ 0 Hello.jsp 
/Hello.jsp 
+ 1 greeting.jsp 
/greeting.jsp 
*L 
1:45 
2:46 
3:47 
3:48 
4:49 
5:50 
1#1:51 
1:52 
2:53 
7#0:56 
8:57 
*E</pre></td></tr></tbody></table><br />　　首先注明JAVA代码的名称：Hello_jsp.java，然后是 stratum 名称： JSP。随后是两个JSP文件的名称 ：Hello.jsp、greeting.jsp。两个JSP文件共10行，产生的Hello_jsp共69行代码。最后也是最重要的内容就是源文件文件名/行号和目标文件行号的对应关系(*L 与 *E之间的部分)<br /><br />　　在规范定义了这样的格式：<br /><br />　　源文件行号 # 源文件代号,重复次数 : 目标文件开始行号,目标文件行号每次增加的数量 <br />(InputStartLine # LineFileID , RepeatCount : OutputStartLine , OutputLineIncrement)<br /><br />　　源文件行号(InputStartLine) 目标文件开始行号(OutputStartLine) 是必须的。下面是对这个SMAP具体的说明：<br /><br /><table cellspacing="0" cellpadding="5" width="100%" bgcolor="#eeeeee" border="1"><tbody><tr><td><pre> 
1:45  2:46  3:47  3:48  4:49  5:50(没有源文件代号，默认为Hello.jsp) 
　　　　　　　　　　　　　　　　　　　开始行号 　　结束行号 
Hello.jsp:    1 -&gt;  Hello_jsp.java:    45 
              2 -&gt;                     46 
              3 -&gt;                     47           48 
              4 -&gt;                     49 
              5 -&gt;                     50 

1#1:51  1:52  2:53(1#1表示 greeting.jsp 的第1行) 
greeting.jsp:    1 -&gt;  Hello_jsp.java:       51           52 
                 2 -&gt;                     53  
         
7#0:56  8:57(7#0表示 Hello.jsp 的第7行) 
Hello.jsp:     7 -&gt;  Hello_jsp.java:       56 
               8 -&gt;                     57 
</pre></td></tr></tbody></table>Eclipse 提供了 TextEditor，作为文本编辑器的父类。由于 Editor 的开发不是本文的重点，不做具体论述。我们可以利用 Eclipse 的 Plugin 项目向导，生成一个简单的 JSP 编辑器：<br /><br />　　(1)点击 File 菜单，New -&gt; Project -&gt; Plug-in Project ；<br /><br />　　(2)输入项目名称 JSP_DEBUG，下一步；<br /><br />　　(3)输入 plugin ID ： com.jsp.debug <br />　　　　Plugin Class name ： com.jsp.debug.JSP_DebugPlugin<br /><br />　　(4)选择用模板创建<br /><br />　　使用 Plug-in with editor，输入<br /><br />　　Java Package Name ：com.jsp.editors<br /><br />　　Editor Class Name ：JSPEditor<br /><br />　　File extension ：jsp<br /><br />　　一个 jsp editor 就产生了。<br /><br />　　运行这个Plugin，新建一个JAVA项目，新建一个 Hello.jsp 和 greeting.jsp，在 Navigator 视图双击 jsp，这个editor就打开了。<br /><br />　　<b>在JSP编辑器中设置断点</b><br /><br />　　在编辑器中添加断点的操作方式有两种，一种是在编辑器左侧垂直标尺上双击，另一种是在左侧垂直标尺上点击鼠标右键，选择菜单"添加/删除断点"。<br /><br />　　在 Eclipse 的实现中，添加断点实际上就是为 IFile 添加一个marker ，类型是IBreakpoint.BREAKPOINT_MARKER，然后将断点注册到 BreakpointManager。<br /><br />　　BreakpointManager 将产生一个 BreakpointRequest，通知正在运行的JVM Target，如果此时还没有启动 JVM，会在 JVM 启动的时候，将所有断点一起通知 JVM Target。<br /><br />　　添加断点使用一个 AbstractRulerActionDelegate，重载 createAction 方法，返回一个 IAction ManageBreakpointRulerAction动作：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre>public class ManageBreakpointRulerActionDelegate extends AbstractRulerActionDelegate{ 
 protected IAction createAction(ITextEditor editor, IVerticalRulerInfo rulerInfo) { 
  return new ManageBreakpointRulerAction(rulerInfo, editor); 
 } 
} 
</pre></td></tr></tbody></table><br />　　为了将 ManageBreakpointRulerActionDelegate 添加到文本编辑器左侧标尺的鼠标右键菜单，并且能够处理左侧标尺的鼠标双击事件，在 plugin.xml 中加入定义。<br /><br />　　处理双击事件：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre>&lt;extension  point="org.eclipse.ui.editorActions"&gt; 
　&lt;editorContribution 
      targetID="com.jiaoly.editors.JSPEditor" 
      id="com.jiaoly.debug.ManageBreakpointRulerActionDelegate"&gt; 
　　　&lt;action 
         label="添加/删除断点" 
         class="com.jiaoly.debug.ManageBreakpointRulerActionDelegate" 
         actionID="RulerDoubleClick" 
         id="com.jiaoly.debug.ManageBreakpointRulerActionDelegate"&gt; 
　　　&lt;/action&gt; 
　&lt;/editorContribution&gt; 
&lt;/extension&gt; 
       </pre></td></tr></tbody></table><br />　　添加右键菜单：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre>&lt;extension point="org.eclipse.ui.popupMenus"&gt; 
 &lt;viewerContribution 
     targetID="#TextRulerContext" 
     id="com.jiaoly.debug.ManageBreakpointRulerActionDelegate"&gt; 
     &lt;action 
       label="添加/删除断点" 
       class="com.jiaoly.debug.ManageBreakpointRulerActionDelegate" 
       menubarPath="addition" 
       id="com.jiaoly.debug.ManageBreakpointRulerActionDelegate"&gt; 
    &lt;/action&gt; 
 &lt;/viewerContribution&gt; 
&lt;/extension&gt; 
</pre></td></tr></tbody></table><br />　　ManageBreakpointRulerAction 是实际添加断点的Action，实现了 IUpdate 接口，这个Action的工作，就是判断当前选中行是否存在断点类型的 Marker，如果不存在创建一个，如果存在，将它删除。<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre>public class ManageBreakpointRulerAction extends Action implements IUpdate{ 
      
     private IVerticalRulerInfo rulerInfo; 
     private ITextEditor textEditor; 
     
     private String BPmarkerType ;     //当点Marker的类型 
     private List allMarkers;       //当前鼠标点击行所有的Marker 
     private String addBP;   //Action 的显示名称 
     
public ManageBreakpointRulerAction(IVerticalRulerInfo ruler, ITextEditor editor){ 
     this.rulerInfo = ruler; 
     this.textEditor = editor; 
     BPmarkerType = IBreakpoint.BREAKPOINT_MARKER; 
     addBP = "添加/删除断点"; //$NON-NLS-1$ 
     setText(this.addBP); 
} 
     
public void update() { 
  this.allMarkers = this.fetchBPMarkerList();  
} 
     
public void run(){ 
 if(this.allMarkers.isEmpty()) 
   this.addMarker(); 
 else 
   this.removeMarkers(this.allMarkers); 
} 
} 
  </pre></td></tr></tbody></table><br />　　update 方法会在点击时首先调用，这时就可以收集当前选中行是否有marker了（调用fetchBPMarkerList方法)，如果有，就保存在 变量allMarkers 中。由于ManageBreakpointRulerAction每一次都产生一个新的实例，因此不会产生冲突。<br /><br />　　下面是update的调用栈，可以看出，update方法是在鼠标点击事件中被调用的：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre> 
ManageBreakpointRulerAction.update() line: 55 
ManageBreakpointRulerActionDelegate(AbstractRulerActionDelegate).update() line: 114 
ManageBreakpointRulerActionDelegate(AbstractRulerActionDelegate).mouseDown(MouseEvent) line: 139 
</pre></td></tr></tbody></table><br />　　updae被调用后，会执行 run 方法，就可以根据 allMarkers.isEmpty() 确定要删除还是添加 marker 了。<br /><br />　　添加断点的时候，首先利用 IVerticalRulerInfo，获取鼠标点击的行号，根据行号，从 Document 模型中取得该行的描述IRegion，得到开始字符位置和结束字符位置，创建一个 JSP 断点。<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre> 
  protected void addMarker() { 
  IEditorInput editorInput= this.getTextEditor().getEditorInput(); 
   
  IDocument document= this.getDocument(); 
  //the line number of the last mouse button activity 
  int rulerLine= this.getRulerInfo().getLineOfLastMouseButtonActivity(); 

  try{ 
   int lineNum = rulerLine + 1; 
   if(lineNum &gt; 0){ 
       //Returns a description of the specified line 
    IRegion iregion = document.getLineInformation(lineNum - 1); 
    int charStart = iregion.getOffset(); 
    int charEnd = (charStart + iregion.getLength()) - 1; 
    JSPDebugUtility.createJspLineBreakpoint(this.getResource(),  
             lineNum, charStart, charEnd); 
   } 
  }catch(CoreException coreexception){ 
   coreexception.printStackTrace(); 
  } 
  catch(BadLocationException badlocationexception){ 
   badlocationexception.printStackTrace(); 
  }   
 } 
 </pre></td></tr></tbody></table><br />　　注册 JSP 断点为支持 JSR-45 规范，Eclipse 中提供了 JavaStratumLineBreakpoint。不过它目前是一个 internal 的实现，在以后的版本中不能保证不作修改。这里为了简单起见，直接从 JavaStratumLineBreakpoint 继承。<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre> 
        public class JSPBreakpoint extends JavaStratumLineBreakpoint { 
        public JSPBreakpoint(IResource resource, String stratum, String sourceName, 
                String sourcePath, String classNamePattern, int lineNumber, 
                int charStart, int charEnd, int hitCount, boolean register, 
                Map attributes) throws DebugException { 
            super(resource, stratum, sourceName, sourcePath, classNamePattern, 
                    lineNumber, charStart, charEnd, hitCount, register, attributes); 
        } 
    } 
</pre></td></tr></tbody></table><br />　　查看 JavaStratumLineBreakpoint 的源代码可以知道，创建 JavaStratumLineBreakpoint 的时候做了两件事情：<br /><br />　　(1) 创建断点类型的 marker, 并且设置了marker的属性resource.createMarker(markerType);<br /><br />　　(2) 将断点注册到断点管理器<br /><br />　　DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(this); 断点管理器负责产生一个 BreakpointRequest，通知正在运行的JVM Target 如果此时还没有启动 JVM，会在 JVM 启动的时候，将所有断点一起通知 JVM Target。<br /><br />　　下面是 JavaStratumLineBreakpoint 构造函数中的代码：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre> 
    IWorkspaceRunnable wr= new IWorkspaceRunnable() { 
   public void run(IProgressMonitor monitor) throws CoreException { 
    // create the marker 
    setMarker(resource.createMarker(markerType));     
    // modify pattern 
    String pattern = classNamePattern; 
    if (pattern != null &amp;&amp; pattern.length() == 0) { 
     pattern = null; 
    } 
    // add attributes 
    addLineBreakpointAttributes(attributes, getModelIdentifier(), true,  
          lineNumber, charStart, charEnd); 
    addStratumPatternAndHitCount(attributes, stratum, sourceName,  
sourcePath, pattern, hitCount); 
    // set attributes 
    ensureMarker().setAttributes(attributes); 
     
    register(register); 
   } 
  }; 
  run(null, wr); 
     
    protected void register(boolean register) throws CoreException { 
  if (register) { 
   DebugPlugin.getDefault().getBreakpointManager().addBreakpoint(this); 
  } else { 
   setRegistered(false); 
  } 
 } 
  </pre></td></tr></tbody></table><br />　　移除断点的时候，根据 marker 找到相应的 IBreakpoint，从 BreakpointManager 中移除 BreakpointManager 会自动删除 marker，通知 JVM Target。<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre>breakpointManager  = DebugPlugin.getDefault().getBreakpointManager(); 
IBreakpoint breakpoint = breakpointManager.getBreakpoint(IMarker); 
breakpointManager.removeBreakpoint(breakpoint, true); 
        </pre></td></tr></tbody></table><br />　　JSPBreakpoint 重载了父类的addToTarget(JDIDebugTarget target) 方法。重载这个方法的目的是根据不同的应用服务器，设置不同的 referenceTypeName和sourcePath。我们知道，每种应用服务器编译 JSP 产生Java Class 名称的规则都不相同，例如Tomcat编译Hello.jsp 产生的Java 类名为 org.apache.jsp. Hello_jsp，而WebSphere6.0 却是 com.ibm._jsp._Hello。只有确定服务器类型，才能知道referenceTypeName 和souecePath应该是什么。目前通过启动 JVM 时target 名称来判断应用服务器类型： String targetString = target.getLaunch().getLaunchConfiguration().getName(); 如果targetString 包含 Tomcat ,就认为是 Tomcat。<br /><br />　　产生 referenceTypeName 后首先创建一个 ClassPrepareRequest 通知，然后从vm中取出所有的classes，如果是当前的 Class，再创建一个添加断点通知。之所以这样做，是因为有可能这个 Class 还没有被 JVM 加载，直接通知 JVM 没有任何意义。在 Class 被加载的时候，JVM 会通知 Eclipse，这个时候，才产生添加断点通知。需要指出的是，本文示例代码获取 referenceTypeName 的方法不是很完善：<br /><br />　　(1) 仅仅实现了Tomcat 读者有兴趣可以实现更多的Web容器，例如 JBoss3 以上，WebSphere6.0<br /><br />　　(2) 一些特殊情况没有处理例如 路径名为package的jsp，路径名或文件名带有数字的jsp<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre> 
  public void addToTarget(JDIDebugTarget target) throws CoreException { 
  IMarker marker = this.getMarker(); 
     IResource resource = marker.getResource(); 
     String targetString = target.getLaunch().getLaunchConfiguration().getName(); 
  IJSPNameUtil util = JSPDebugUtility.getJSPNameUtil(targetString); 
      
  // pre-notification 
  fireAdding(target); 
     
  String referenceTypeName; 
  try { 
   referenceTypeName = getPattern(); 
   //如果没有设置 Pattern, 根据 Server 的类型, 产生新的 Pattern  
   if(referenceTypeName == null ||  
      "".equals(referenceTypeName.trim()) || 
      "*".equals(referenceTypeName.trim())){ 
       referenceTypeName = util.referenceTypeName(resource); 
   } 
    
  } catch (CoreException e) { 
   JDIDebugPlugin.log(e); 
   return; 
  } 
   
  this.ensureMarker().setAttribute(TYPE_NAME, referenceTypeName); 
  String sourcePath = util.sourcePath(resource); 
  this.ensureMarker().setAttribute(JSPBreakpoint.SOURCE_PATH, sourcePath); 
   
  String classPrepareTypeName= referenceTypeName; 
   
  //如果这时 class 还没有被加载, 注册一个 ClassPrepareRequest 请求 
  // 
  //当 class 加载的时候, 首先会触发 JavaBreakpoint 的 handleClassPrepareEvent 方法 
  //调用 createRequest(target, event.referenceType()) --&gt; newRequest() --&gt; 
  //    createLineBreakpointRequest() 创建 enable或disable 断点的请求 
  // 
  //  设置 enable/disable 动作在 configureRequest() --&gt; updateEnabledState(request) 方法中 
  //  根据 getMarker().getAttribute(ENABLED, false) 确定断点是否有效 
   
  registerRequest(target.createClassPrepareRequest(classPrepareTypeName), target); 
   
  // create breakpoint requests for each class currently loaded 
  VirtualMachine vm = target.getVM(); 
  if (vm == null) { 
   target.requestFailed("Unable_to_add_breakpoint_-_VM_disconnected._1"),  
   null);   } 
  List classes = null; 
  try { 
   classes= vm.allClasses(); 
  } catch (RuntimeException e) { 

   target.targetRequestFailed("JavaPatternBreakpoint.0"), e);  
  } 
  if (classes != null) { 
   Iterator iter = classes.iterator(); 
   while (iter.hasNext()) { 
    ReferenceType type= (ReferenceType)iter.next(); 
    if (installableReferenceType(type, target)) { 
     createRequest(target, type); 
    } 
   } 
  } 
 }     
</pre></td></tr></tbody></table>现在我们可以调试 JSP 了。<br /><br />　　(1)运行 JSP_DEBUG plugin<br /><br />　　首先在 run -&gt; run 中添加一个 Run-time Workbench，点击 run 按钮，Eclipse 的Plugin开发环境会启动一个新的Eclipse，这个新启动的 Eclipse 中，我们创建的 JSP_DEBUG plugin 就可以使用了。新建 一个 JAVA 项目 Test （注意，一定要是JAVA项目），新建一个 Hello.jsp 和 greeting.jsp，打开Hello.jsp，在编辑器左侧标尺双击，就出现了一个断点。<br /><br />　　(2)以 Debug 模式启动Tomcat：<br /><br />　　windows 开始 -&gt; 运行，键入 cmd，启动一个命令行窗口：<br /><br />　　cd E:\Tomcat5_0_5\bin <br /><br />　　(我的 Tomcat 安装在 E:\Tomcat5_0_5 目录，JDK 安装在 D:\j2sdk1.4.2)<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre> 
    D:\j2sdk1.4.2\bin\java   
-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8888,server=y,<br />     suspend=n -Djava.endorsed.dirs="..\common\endorsed" 
-classpath "D:\j2sdk1.4.2\lib\tools.jar;..\bin\bootstrap.jar"  
-Dcatalina.base=".."  
-Dcatalina.home=".."  
-Djava.io.tmpdir="..\temp"  
org.apache.catalina.startup.Bootstrap  start 
</pre></td></tr></tbody></table><br />　　-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,address=8888,server=y,suspend=n 表示以调试方式启动，端口号是 8888 classpath中要加入 D:\j2sdk1.4.2\lib\tools.jar，因为我是 Tomcat5.0.5，如果是5.5就不需要了。<br /><br />　　(3) 测试Hello.jsp<br /><br />　　将 Hello.jsp 和 greeting.jsp 拷贝到 E:\Tomcat5_0_5\webapps\ROOT 目录，从浏览器访问 Hello.jsp http://localhost:8000/Hello.jsp。成功的话就可以继续下面的工作了，如果失败，检查你的Tomcat设置。<br /><br />　　(4)启动远程调试<br /><br />　　在 Eclipse 中启动远程调试，将 Eclipse 作为一个 Debug 客户端，连接到 Tomcat 。在 Java 透视图中，点击 Run -&gt; Debug ，添加一个 Remote Java Application，名称是 Start Tomcat Server(不能错，因为我们要根据这个名称，判断当前的 Web Server 类型)<br /><br />　　project是创建的 Test 项目<br /><br />　　Port 为 8888，和启动 Tomcat 时设置的一样<br /><br /><img height="424" alt="" src="http://dev.yesky.com/imagelist/06/02/459743558u5s.jpg" width="553" border="0" /><br /><br />　　点击 Debug 按钮，就可以连接到 Tomcat 上了。切换到 Debug 透视图，在Debug 视图中，能够看到所有 Tomcat 中线程的列表。<br /><br />　　(5)调试Hello.jsp<br /><br />　　为 Hello.jsp 添加断点，然后从浏览器访问Hello.jsp，就可以在断点处挂起了。你可以使用单步执行，也可以在Variables视图查看jsp中的变量信息。<br /><br />　　由于 Eclipse 自身的实现，现在的 JSP Editor 有一个问题，单步执行到 include jsp 行后，会从Hello.jsp的1行再次执行。这是因为 Eclipse JDT Debug视图缓存了 StackFrame 中已经打开的Editor，StackFrame不改变时，不会再重新计算当前调试的是否是其他Resource。本来应该打开 greeting.jsp的，现在却从 Hello.jsp 的第 1 行开始执行了。<br /><br />　　<b>结束语</b><br /><br />　　很多集成开发环境都支持 JSP 的调试，在 Eclipse 中也有 MyEclipse 这样的插件完成类似的功能。但是在 JSR-45 规范产生前，每种应用服务器对 JSP Debug 的实现是不一样的，例如 WebSphere 5 就是在 JSP 编译产生的 JAVA 代码中加入了两个数组，表示源文件和行号的对应信息。Tomcat 率先实现了 JSR-45 规范，WebSphere 6.0 现在也采取这种模式， 有兴趣的话，可以查看 WebSphere 6.0 编译的 Class，和 Tomcat 不一样，SMAP 文件会和java代码同时产生。<br /><br />　　但是启动server前，需要设置 JVM 参数 was.debug.mode = true<br /><br />　　同时在 ibm-web-ext.xmi 中设置 <br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td><pre>&lt;jspAttributes xmi:id="JSPAttribute_0" name="keepgenerated" value="true"/&gt; 
&lt;jspAttributes xmi:id="JSPAttribute_1" name="createDebugClassfiles" value="true"/&gt; 
&lt;jspAttributes xmi:id="JSPAttribute_2" name="debugEnabled" value="true"/&gt; 
     </pre></td></tr></tbody></table><br />　　利用本文的基本原理，我们也可以开发其他基于 JAVA 脚本语言的编辑器（例如 Groovy），为这个编译器加入 Debug 的功能。<br /><br />from: <a href="http://dev.yesky.com/206/2292206_2.shtml">http://dev.yesky.com/206/2292206_2.shtml</a><img src ="http://www.blogjava.net/weidagang2046/aggbug/64736.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/weidagang2046/" target="_blank">weidagang2046</a> 2006-08-21 11:11 <a href="http://www.blogjava.net/weidagang2046/articles/64736.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>