﻿<?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-鱼儿水中游，兔子草上飞-文章分类-java技术</title><link>http://www.blogjava.net/sinoly/category/19655.html</link><description>-- Open my Mind </description><language>zh-cn</language><lastBuildDate>Wed, 28 Feb 2007 04:12:33 GMT</lastBuildDate><pubDate>Wed, 28 Feb 2007 04:12:33 GMT</pubDate><ttl>60</ttl><item><title>免费的晚餐--google技术学习（转）</title><link>http://www.blogjava.net/sinoly/articles/97411.html</link><dc:creator>sinoly</dc:creator><author>sinoly</author><pubDate>Fri, 02 Feb 2007 01:00:00 GMT</pubDate><guid>http://www.blogjava.net/sinoly/articles/97411.html</guid><wfw:comment>http://www.blogjava.net/sinoly/comments/97411.html</wfw:comment><comments>http://www.blogjava.net/sinoly/articles/97411.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sinoly/comments/commentRss/97411.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sinoly/services/trackbacks/97411.html</trackback:ping><description><![CDATA[
		<p>    作者：<a href="/calvin/"><font color="#366900">江南白衣</font></a>，原文出处： <a id="Editor_Edit_hlEntryLink" title="view: 免费的晚餐--google技术学习" href="/calvin/archive/2007/01/31/96844.html" target="_blank"><font color="#002c99">http://www.blogjava.net/calvin/archive/2007/01/31/96844.html</font></a> ，转载请保留出处。<br /><br />    如果说Google的搜索引擎是免费的早餐，Gmail们是免费的午餐的话，</p>
		<p>    <a href="http://labs.google.com/papers/"><font color="#366900">http://labs.google.com/papers/</font></a> 就是Google给开发人员们的一份免费的晚餐。</p>
		<p>    不过，咋看着一桌饭菜可能不知道从哪吃起，在自己不熟悉的领域啃英文也不是一件愉快的事情。<br /></p>
		<h2>一、一份PPT与四份中文翻译 </h2>
		<p>    幸好，有一位面试google不第的老兄，自我爆发搞了一份Google Interal的PPT：</p>
		<p>    <a href="http://cbcg.net/talks/googleinternals/index.html"><font color="#366900">http://cbcg.net/talks/googleinternals/index.html</font></a>，大家鼠标点点就能跟着他匆匆过一遍google的内部架构。</p>
		<p>   然后又有崮崮山路上走9遍(<a href="http://sharp838.mblogger.cn/"><font color="#366900">http://sharp838.mblogger.cn</font></a>)与美人他爹(<a href="http://my.donews.com/eraera/"><font color="#366900">http://my.donews.com/eraera/</font></a>)，翻译了其中最重要的四份论文：</p>
		<ul>
				<li> <a href="http://avindev.googlepages.com/mapreduce.doc"><font color="#366900">《MapRedue:在超大集群上的简易数据处理》</font></a>--Simplified Data Processing on Large Clusters 
</li>
				<li>
						<a href="http://avindev.googlepages.com/gfs.doc">
								<font color="#366900">《The Google File System》</font>
						</a>
				</li>
				<li>
						<a href="http://avindev.googlepages.com/sawzall.doc">
								<font color="#366900">《海量数据分析：Sawzall并行处理》</font>
						</a>--Interpreting the Data: Parallel Analysis with Sawzall 
</li>
				<li>
						<a href="http://my.donews.com/eraera/2006/09/26/swogzstwtqdnwlfrzgsljctkjsbrtuiumxzj/">
								<font color="#366900">《Bigtable:结构化数据的分布存储系统》</font>
						</a>--A Distributed Storage System for Structured Data<br /></li>
		</ul>
		<h2>二、Google帝国的技术基石</h2>
		<p>     Google帝国，便建立在大约45万台的Server上，其中大部分都是"cheap x86 boxes"。而这45万台Server，则建立于下面的key infrastructure：</p>
		<h2>     1.GFS(Google File System):</h2>
		<p>     GFS是适用于大规模分布式数据处理应用的分布式文件系统，是Google一切的基础，它基于普通的硬件设备，实现了容错的设计与极高的性能。     </p>
		<p>     李开复说：Google最厉害的技术是它的storage。我认为学计算机的学生都应该看看<a href="http://avindev.googlepages.com/gfs.doc"><font color="#366900">这篇文章</font></a>（再次感谢翻译的兄弟)。    <br />     </p>
		<p>     它以64M为一个Chunk(Block)，每个Chunk至少存在于三台机器上，交互的简单过程见:<br />     <br />     <img src="http://cbcg.net/talks/googleinternals/pix/gfs.png" /></p>
		<h2>     2.MapReduce</h2>
		<p>    MapReduce是一个分布式处理海量数据集的编程模式，让程序自动分布到一个由普通机器组成的超大集群上并发执行。像Grep-style job，日志分析等都可以考虑采用它。<br /><br />    MapReduce的run-time系统会解决输入数据的分布细节，跨越机器集群的程序执行调度，处理机器的失效，并且管理机器之间的通讯请求。这样的模式允许程序员可以不需要有什么并发处理或者分布式系统的经验，就可以处理超大的分布式系统得资源。</p>
		<p>     我自己接触MapReduce是<a href="http://lucene.apache.org/"><font color="#366900">Lucene</font></a>-&gt;<a href="http://lucene.apache.org/nutch"><font color="#366900">Nutch</font></a>-&gt;<a href="http://lucene.apache.org/hadoop"><font color="#366900">Hadoop</font></a>的路线。<br />     <a href="http://lucene.apache.org/hadoop"><font color="#366900">Hadoop</font></a>是Lucene之父Doug Cutting的又一力作，是Java版本的分布式文件系统与Map/Reduce实现。<br />     Hadoop的文档并不详细，再看一遍<a href="http://avindev.googlepages.com/mapreduce.doc"><font color="#366900">Google这篇中文版的论文</font></a>，一切清晰很多(又一次感谢翻译的兄弟)。    <br /><br />     孟岩也有一篇很清晰的博客：<a href="http://www.mengyan.org/blog/archives/2006/11/15/138.html"><font color="#366900">Map Reduce - the Free Lunch is not over?</font></a></p>
		<h2>     3.BigTable </h2>
		<p>     BigTable 是Google Style的数据库，使用结构化的文件来存储数据。<br />     虽然不支持关系型数据查询，但却是建立GFS/MapReduce基础上的，分布式存储大规模结构化数据的方案。</p>
		<p>     BigTable是一个稀疏的，多维的，排序的Map，每个Cell由行关键字，列关键字和时间戳三维定位．Cell的内容是一个不解释的字符串。<br />     比如下表存储每个网站的内容与被其他网站的反向连接的文本。<br />     反向的URL com.cnn.www(<a href="http://www.cnn.com/"><font color="#366900">www.cnn.com</font></a>)是行的关键字；contents列存储网页内容，每个内容有一个时间戳；因为有两个反向连接，所以archor列族有两列:anchor:cnnsi.com和anchhor:my.look.ca，列族的概念，使得表可以横向扩展，archor的列数并不固定。</p>
		<p>   <img src="  http://i106.photobucket.com/albums/m261/beyond-the-sea/Bigtable_Figure1.jpg" temp_src="  http://i106.photobucket.com/albums/m261/beyond-the-sea/Bigtable_Figure1.jpg" /></p>
		<p>    为了并发读写，热区，HA等考虑，BigTable当然不会存在逗号分割的文本文件中，，是存储在一种叫SSTable的数据库结构上，并有BMDiff和Zippy两种不同侧重点的压缩算法。</p>
		<h2>4.Sawzall</h2>
		<p>    Sawzall是一种建立在MapReduce基础上的领域语言，可以被认为是分布式的awk。它的程序控制结构(if,while)与C语言无异，但它的领域语言语义使它完成相同功能的代码与MapReduce的C++代码相比简化了10倍不止。</p>
		<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee">
				<span style="COLOR: #008080">1</span> <span style="COLOR: #000000">   proto </span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">cvsstat.proto</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000"><br /></span><span style="COLOR: #008080">2</span> <span style="COLOR: #000000">   submits: table sum[hour: </span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">] of count: </span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000">;<br /></span><span style="COLOR: #008080">3</span> <span style="COLOR: #000000">   log: ChangelistLog </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> input;<br /></span><span style="COLOR: #008080">4</span> <span style="COLOR: #000000">   hour: </span><span style="COLOR: #0000ff">int</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">=</span><span style="COLOR: #000000"> hourof(log.time)<br /></span><span style="COLOR: #008080">5</span> <span style="COLOR: #000000">   emit submits[hour] </span><span style="COLOR: #000000">&lt;-</span><span style="COLOR: #000000"> </span><span style="COLOR: #000000">1</span><span style="COLOR: #000000">;</span></div>
		<p>     <br />     天书吗?慢慢看吧。</p>
		<p>     我们这次是统计在每天24小时里CVS提交的次数。<br />     首先它的变量定义类似Pascal  (i:int=0; 即定义变量i，类型为int，初始值为0)</p>
		<p>     1:引入cvsstat.proto协议描述，作用见后。<br />     2:定义int数组submits 存放统计结果，用hour作下标。<br />     3.循环的将文件输入转换为ChangelistLog 类型，存储在log变量里，类型及转换方法在前面的cvsstat.proto描述。<br />     4.取出changlog中的提交时间log.time的hour值。<br />     5.emit聚合，在sumits结果数组里，为该hour的提交数加1，然后自动循环下一个输入。 </p>
		<p>     居然读懂了，其中1、2步是准备与定义，3、4步是Map，第5步是Reduce。<br /></p>
		<h2>三. 小结：</h2>
		<p>  本文只是简单的介绍Google的技术概貌，大家知道以后除了可作谈资外没有任何作用，我们真正要学习的骨血，是论文里如何解决高并发，高可靠性等的设计思路和细节.....<br /></p>
<img src ="http://www.blogjava.net/sinoly/aggbug/97411.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sinoly/" target="_blank">sinoly</a> 2007-02-02 09:00 <a href="http://www.blogjava.net/sinoly/articles/97411.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA性能优化－通用篇（转）</title><link>http://www.blogjava.net/sinoly/articles/97408.html</link><dc:creator>sinoly</dc:creator><author>sinoly</author><pubDate>Fri, 02 Feb 2007 00:59:00 GMT</pubDate><guid>http://www.blogjava.net/sinoly/articles/97408.html</guid><wfw:comment>http://www.blogjava.net/sinoly/comments/97408.html</wfw:comment><comments>http://www.blogjava.net/sinoly/articles/97408.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sinoly/comments/commentRss/97408.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sinoly/services/trackbacks/97408.html</trackback:ping><description><![CDATA[
		<div align="left">“通用篇”讨论的问题适合于大多数Java应用。 
<p>　　1.1 不用new关键词创建类的实例 </p><p>　　用new关键词创建类的实例时，构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口，我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。 </p><p>　　在使用设计模式(Design Pattern)的场合，如果用Factory模式创建对象，则改用clone()方法创建新的对象实例非常简单。例如，下面是Factory模式的一个典型实现： </p><p>public static Credit getNewCredit() <br />　　{ </p><p>　　　return new Credit(); </p><p>　　} </p><p> </p><p><br />　　改进后的代码使用clone()方法，如下所示： </p><p>private static Credit BaseCredit = new Credit(); <br />　　public static Credit getNewCredit() </p><p>　　{ </p><p>　　　return (Credit) BaseCredit.clone(); </p><p>　　} </p><p><br />　　 上面的思路对于数组处理同样很有用。 </p><p><br />　　1.2 使用非阻塞I/O </p><p>　　版本较低的JDK不支持非阻塞I/O API。为避免I/O阻塞，一些应用采用了创建大量线程的办法(在较好的情况下，会使用一个缓冲池)。这种技术可以在许多必须支持并发I/O流的应用中见到，如Web服务器、报价和拍卖应用等。然而，创建Java线程需要相当可观的开销。 </p><p>　　JDK 1.4引入了非阻塞的I/O库(java.nio)。如果应用要求使用版本较早的JDK，在这里有一个支持非阻塞I/O的软件包。 </p><p>　　1.3 慎用异常 </p><p>　　异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地 (Native)方法，fillInStackTrace()方法检查堆栈，收集调用跟踪信息。只要有异常被抛出，VM就必须调整调用堆栈，因为在处理过程中创建了一个新的对象。 </p><p>　　异常只能用于错误处理，不应该用来控制程序流程。 </p><p>　　1.4 不要重复初始化变量 </p><p>　　默认情况下，调用类的构造函数时， Java会把变量初始化成确定的值：所有的对象被设置成null，整数变量(byte、short、int、long)设置成0，float和 double变量设置成0.0，逻辑值设置成false。当一个类从另一个类派生时，这一点尤其应该注意，因为用new关键词创建一个对象时，构造函数链中的所有构造函数都会被自动调用。 </p><p>　　1.5 尽量指定类的final修饰符 </p><p>　　带有final修饰符的类是不可派生的。在Java核心API中，有许多应用final的例子，例如java.lang.String。为String类指定final防止了人们覆盖length()方法。 </p><p>　　另外，如果指定一个类为final，则该类所有的方法都是final。Java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50%。 </p><p>　　1.6 尽量使用局部变量 </p><p>　　调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中，速度较快。其他变量，如静态变量、实例变量等，都在堆(Heap)中创建，速度较慢。另外，依赖于具体的编译器/JVM，局部变量还可能得到进一步优化。请参见《尽可能使用堆栈变量》。 </p><p>　　1.7 乘法和除法 </p><p>　　考虑下面的代码： </p><p>　　for (val = 0; val &lt; 100000; val +=5) </p><p>　　{ </p><p>　　　alterX = val * 8; </p><p>　　　myResult = val * 2; </p><p>　　} </p><p>　　用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码： </p><p>　　for (val = 0; val &lt; 100000; val += 5) </p><p>　　{ </p><p>　　　alterX = val &lt;&lt; 3; </p><p>　　　myResult = val &lt;&lt; 1; </p><p>　　} </p><p> </p>　　修改后的代码不再做乘以8的操作，而是改用等价的左移3位操作，每左移1位相当于乘以2。相应地，右移1位操作相当于除以2。值得一提的是，虽然移位操作速度快，但可能使代码比较难于理解，所以最好加上一些注释。</div>
<img src ="http://www.blogjava.net/sinoly/aggbug/97408.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sinoly/" target="_blank">sinoly</a> 2007-02-02 08:59 <a href="http://www.blogjava.net/sinoly/articles/97408.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java中实现图片裁剪(转)</title><link>http://www.blogjava.net/sinoly/articles/97406.html</link><dc:creator>sinoly</dc:creator><author>sinoly</author><pubDate>Fri, 02 Feb 2007 00:58:00 GMT</pubDate><guid>http://www.blogjava.net/sinoly/articles/97406.html</guid><wfw:comment>http://www.blogjava.net/sinoly/comments/97406.html</wfw:comment><comments>http://www.blogjava.net/sinoly/articles/97406.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sinoly/comments/commentRss/97406.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sinoly/services/trackbacks/97406.html</trackback:ping><description><![CDATA[
		<h1 style="TEXT-ALIGN: center" align="center">
				<span lang="EN-US">Java</span>
				<span style="FONT-FAMILY: 宋体">如何截取图片</span>
				<span lang="EN-US">
						<?XML:NAMESPACE PREFIX = O /?>
						<o:p>  <br /></o:p>
				</span>
		</h1>
		<div style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; PADDING-BOTTOM: 1pt; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: 1pt solid">
		</div>
		<p class="MsoNormal" style="MARGIN-LEFT: 105pt">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; FONT-FAMILY: 'Arial Black'">Author</span>
				</b>
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt">
								<span>     </span>:<span>      </span><span style="COLOR: navy">zhyiwww</span><o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal" style="MARGIN-LEFT: 105pt">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; FONT-FAMILY: 'Arial Black'">E-Mail</span>
				</b>
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt">
								<span>      </span>:<span>      </span><span style="COLOR: navy"><a href="mailto:zhyiwww@163.com"><span style="COLOR: navy; TEXT-DECORATION: none">zhyiwww@163.com</span></a></span><o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal" style="MARGIN-LEFT: 105pt">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; FONT-FAMILY: 'Arial Black'">Date</span>
				</b>
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt">
								<span>          </span>:<span>      </span><span style="COLOR: navy">2007-1-30</span><o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal" style="MARGIN-LEFT: 105pt">
				<b>
						<span style="FONT-SIZE: 12pt; COLOR: blue; FONT-FAMILY: 宋体">转载请注明出处</span>
				</b>
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: blue; FONT-FAMILY: 'Arial Black'">www.BlogJava.net/zhyiwww<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: fuchsia; FONT-FAMILY: 'Arial Black'">                                (copyright by @ zhangyi)</span>
				</b>
				<span lang="EN-US">
						<o:p>  <br /></o:p>
				</span>
		</p>
		<div style="BORDER-RIGHT: medium none; PADDING-RIGHT: 0cm; BORDER-TOP: medium none; PADDING-LEFT: 0cm; PADDING-BOTTOM: 0cm; BORDER-LEFT: medium none; PADDING-TOP: 0cm; BORDER-BOTTOM: 1pt solid">
		</div>
		<p class="MsoNormal">
				<b>
						<span style="FONT-FAMILY: 宋体">下面是我的一段代码，实现如何截取图片的：</span>
						<span lang="EN-US">
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<span lang="EN-US">
						<o:p> </o:p>
				</span>
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>       </span>// </span>
				</b>
				<b>
						<span style="FONT-SIZE: 12pt; COLOR: green; FONT-FAMILY: 宋体">图片源</span>
				</b>
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>       </span>private static final String SRC_FILE="org//zy//demo//jdk//base//image//car1.jpg";<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>      </span>// </span>
				</b>
				<b>
						<span style="FONT-SIZE: 12pt; COLOR: green; FONT-FAMILY: 宋体">目标图片</span>
				</b>
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>       </span>private static final String DEST_FILE="c://a.jpg";<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<span lang="EN-US">
						<o:p> </o:p>
				</span>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">/**<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>       </span>
								<span> </span>* </span>
				</b>
				<b>
						<span style="FONT-SIZE: 12pt; COLOR: green; FONT-FAMILY: 宋体">读取图像文件</span>
				</b>
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>       </span>
								<span> </span>* </span>
				</b>
				<b>
						<span style="FONT-SIZE: 12pt; COLOR: green; FONT-FAMILY: 宋体">用</span>
				</b>
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">ImageReader<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>       </span>
								<span> </span>* @param imgPath<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>       </span>
								<span> </span>* @throws IOException <o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>       </span>
								<span> </span>*/<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>       </span>public void readUsingImageReader(String imgPath) throws IOException{<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>// </span>
				</b>
				<b>
						<span style="FONT-SIZE: 12pt; COLOR: green; FONT-FAMILY: 宋体">取得图片读入器</span>
				</b>
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>Iterator readers = ImageIO.getImageReadersByFormatName("jpg");<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>System.out.println(readers);<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>ImageReader reader = (ImageReader)readers.next();<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>System.out.println(reader);<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>// </span>
				</b>
				<b>
						<span style="FONT-SIZE: 12pt; COLOR: green; FONT-FAMILY: 宋体">取得图片读入流</span>
				</b>
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>InputStream source=this.parseImagePath(ImageDemo.SRC_FILE);<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>ImageInputStream iis = ImageIO.createImageInputStream(source);<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>reader.setInput(iis, true);<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>// </span>
				</b>
				<b>
						<span style="FONT-SIZE: 12pt; COLOR: green; FONT-FAMILY: 宋体">图片参数</span>
				</b>
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>ImageReadParam param = reader.getDefaultReadParam();<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>int imageIndex = 0;<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>int half_width = reader.getWidth(imageIndex)/2;<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>int half_height = reader.getHeight(imageIndex)/2;<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">//<span>            </span>Rectangle rect = new Rectangle(60, 60, half_width, half_height); <o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>Rectangle rect = new Rectangle(60, 60, 100, 100);<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>param.setSourceRegion(rect);<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>       </span>
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>BufferedImage bi = reader.read(0,param);<span>              </span><o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>ImageIO.write(bi, "jpg", this.initDestFile());<span>           </span><o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>              </span>
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<span>       </span>}<o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<o:p> </o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span style="FONT-SIZE: 12pt; COLOR: green; FONT-FAMILY: 宋体">我的源图片是：</span>
				</b>
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<?XML:NAMESPACE PREFIX = V /?>
								<v:shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f">
										<v:stroke joinstyle="miter">
										</v:stroke>
										<v:formulas>
												<v:f eqn="if lineDrawn pixelLineWidth 0">
												</v:f>
												<v:f eqn="sum @0 1 0">
												</v:f>
												<v:f eqn="sum 0 0 @1">
												</v:f>
												<v:f eqn="prod @2 1 2">
												</v:f>
												<v:f eqn="prod @3 21600 pixelWidth">
												</v:f>
												<v:f eqn="prod @3 21600 pixelHeight">
												</v:f>
												<v:f eqn="sum @0 0 1">
												</v:f>
												<v:f eqn="prod @6 1 2">
												</v:f>
												<v:f eqn="prod @7 21600 pixelWidth">
												</v:f>
												<v:f eqn="sum @8 21600 0">
												</v:f>
												<v:f eqn="prod @7 21600 pixelHeight">
												</v:f>
												<v:f eqn="sum @10 21600 0">
												</v:f>
										</v:formulas>
										<v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect">
										</v:path>
										<o:lock v:ext="edit" aspectratio="t">
										</o:lock>
								</v:shapetype>
								<v:shape id="_x0000_i1025" style="WIDTH: 449.25pt; HEIGHT: 318.75pt" type="#_x0000_t75">
										<v:imagedata src="file:///C:\DOCUME~1\zhangyi\LOCALS~1\Temp\msohtml1\01\clip_image001.png" o:title="a">
										</v:imagedata>
								</v:shape>&lt;!--[if !vml]--&gt; <img height="567" alt="img0.jpg" src="http://www.blogjava.net/images/blogjava_net/zhyiwww/img/img0.jpg" width="800" border="0" /><br />&lt;!--[endif]--&gt; <o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<o:p> </o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span style="FONT-SIZE: 12pt; COLOR: green; FONT-FAMILY: 宋体">上面的程序运行后截得的图片如下：</span>
				</b>
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<o:p>
								</o:p>
						</span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<v:shape id="_x0000_i1026" style="WIDTH: 300pt; HEIGHT: 212.25pt" type="#_x0000_t75">
										<v:imagedata src="file:///C:\DOCUME~1\zhangyi\LOCALS~1\Temp\msohtml1\01\clip_image003.jpg" o:title="a0.9358084919675186">
										</v:imagedata>
								</v:shape>&lt;!--[if !vml]--&gt; <img height="283" alt="img1.jpg" src="http://www.blogjava.net/images/blogjava_net/zhyiwww/img/img1.jpg" width="400" border="0" /><br />&lt;!--[endif]--&gt; <o:p></o:p></span>
				</b>
		</p>
		<p class="MsoNormal">
				<b>
						<span lang="EN-US" style="FONT-SIZE: 12pt; COLOR: green">
								<o:p> </o:p>
						</span>
				</b>
		</p>
<img src ="http://www.blogjava.net/sinoly/aggbug/97406.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sinoly/" target="_blank">sinoly</a> 2007-02-02 08:58 <a href="http://www.blogjava.net/sinoly/articles/97406.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java RMI Tutorial（收录）</title><link>http://www.blogjava.net/sinoly/articles/97169.html</link><dc:creator>sinoly</dc:creator><author>sinoly</author><pubDate>Thu, 01 Feb 2007 03:29:00 GMT</pubDate><guid>http://www.blogjava.net/sinoly/articles/97169.html</guid><wfw:comment>http://www.blogjava.net/sinoly/comments/97169.html</wfw:comment><comments>http://www.blogjava.net/sinoly/articles/97169.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/sinoly/comments/commentRss/97169.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/sinoly/services/trackbacks/97169.html</trackback:ping><description><![CDATA[
		<h4 class="TextColor1" id="subjcns!574028854B4D636A!151" style="MARGIN-BOTTOM: 0px">远程方法调用入门指南（Java RMI Tutorial）</h4>
		<div id="msgcns!574028854B4D636A!151">
				<br />
				<div>
						<div>
								<h1>
										<font color="#000000">Java R</font>
										<a>
												<font color="#000000">MI Tutorial</font>
										</a>
								</h1>
								<h2>远程方法调用入门指南</h2>
								<div>
										<a>
										</a>
										<h3>
												<a>
														<font color="#0066a7">Stephen Suen</font>
												</a>
										</h3>
								</div>
								<p>Copyright © 2005 Stephen Suen. All rights reserved. 
</p>
								<div>
										<div>
												<p>
														<a>
														</a>
												</p>
												<p>Java 远程方法调用（Remote Method Invocation, RMI）使得运行在一个 Java 虚拟机（Java Virtual Machine, JVM）的对象可以调用运行另一个 JVM 之上的其他对象的方法，从而提供了程序间进行远程通讯的途径。RMI 是 J2EE 的很多分布式技术的基础，比如 RMI-IIOP 乃至 EJB。本文是 RMI 的一个入门指南，目的在于帮助读者快速建立对 Java RMI 的一个感性认识，以便进行更深层次的学习。事实上，如果你了解 RMI 的目的在于更好的理解和学习 EJB，那么本文就再合适不过了。通过本文所了解的 RMI 的知识和技巧，应该足够服务于这个目的了。 
</p>
												<p> </p>
										</div>
								</div>
						</div>
						<div>
						</div>
						<div>
								<h1>
										<a>
												<font color="#0066a7">1. 简介</font>
										</a>
								</h1>
								<p>我们知道远程过程调用（Remote Procedure Call, RPC）可以用于一个进程调用另一个进程（很可能在另一个远程主机上）中的<span><i>过程</i></span>，从而提供了过程的分布能力。Java 的 RMI 则在 RPC 的基础上向前又迈进了一步，即提供分布式<span><i> 对象</i></span>间的通讯，允许我们获得在远程进程中的对象（称为远程对象）的引用（称为远程引用），进而通过引用调用远程对象的方法，就好像该对象是与你的客户端代码同样运行在本地进程中一样。RMI 使用了术语"方法"（Method）强调了这种进步，即在分布式基础上，充分支持面向对象的特性。 
</p>
								<p>RMI 并不是 Java 中支持远程方法调用的唯一选择。在 RMI 基础上发展而来的 RMI-IIOP（Java Remote Method Invocation over the Internet Inter-ORB Protocol），不但继承了 RMI 的大部分优点，并且可以兼容于 CORBA。J2EE 和 EJB 都要求使用 RMI-IIOP 而不是 RMI。尽管如此，理解 RMI 将大大有助于 RMI-IIOP 的理解。所以，即便你的兴趣在 RMI-IIOP 或者 EJB，相信本文也会对你很有帮助。另外，如果你现在就对 API 感兴趣，那么可以告诉你，RMI 使用 <font color="red">java.rmi</font> 包，而 RMI-IIOP 则既使用 <font color="red">java.rmi</font> 也使用扩展的 <font color="red">javax.rmi</font> 包。 
</p>
								<p>本文的随后内容将仅针对 Java RMI。</p>
						</div>
						<div>
								<h1>
										<a>
												<font color="#0066a7">2. 分布式对象</font>
										</a>
								</h1>
								<p>在学习 RMI 之前，我们需要了解一些基础知识。首先需要了解所谓的分布式对象（Distributed Object）。分布式对象是指一个对象可以被远程系统所调用。对于 Java 而言，即对象不仅可以被同一虚拟机中的其他客户程序（Client）调用，也可以被运行于其他虚拟机中的客户程序调用，甚至可以通过网络被其他远程主机之上的客户程序调用。 
</p>
								<p>下面的图示说明了客户程序是如何调用分布式对象的： 
</p>
								<div>
										<p>
												<img src="http://storage.msn.com/x1pGg9EMswqL--KyzGRGdhRjrq64UqttMM_PJCibCwwE4IdiYXO1md4zsjx996jvYuxnudo29e0GI0ry5z7KlGOWTxiA7t3Y3D5evWkQ8tZPfazDTvArq--pvMKaUlc__RNmlIlOq5vY-H0Kl2N47CivQ" />
										</p>
								</div>
								<p>从图上我们可以看到，分布式对象被调用的过程是这样的： 
</p>
								<p>
								</p>
								<ol>
										<li>
												<p>客户程序调用一个被称为 Stub （有时译作存根，为了不产生歧义，本文将使用其英文形式）的客户端代理对象。该代理对象负责对客户端隐藏网络通讯的细节。Stub 知道如何通过网络套接字（Socket）发送调用，包括如何将调用参数转换为适当的形式以便传输等。 </p>
										</li>
										<li>
												<p>Stub 通过网络将调用传递到服务器端，也就是分布对象一端的一个被称为 Skeleton 的代理对象。同样，该代理对象负责对分布式对象隐藏网络通讯的细节。Skeleton 知道如何从网络套接字（Socket）中接受调用，包括如何将调用参数从网络传输形式转换为 Java 形式等。 </p>
										</li>
										<li>
												<p>Skeleton 将调用传递给分布式对象。分布式对象执行相应的调用，之后将返回值传递给 Skeleton，进而传递到 Stub，最终返回给客户程序。</p>
										</li>
								</ol>
								<p>这个场景基于一个基本的法则，即行为的定义和行为的具体实现相分离。如图所示，客户端代理对象 Stub 和分布式对象都实现了相同的接口，该接口称为远程接口（Remote Interface）。正是该接口定义了行为，而分布式对象本身则提供具体的实现。对于 Java RMI 而言，我们用接口（<font color="red">interface</font>）定义行为，用类（<font color="red">class</font>）定义实现。 </p>
						</div>
						<div>
								<h1>
										<a>
												<font color="#0066a7">3. RMI 架构</font>
										</a>
								</h1>
								<p>RMI 的底层架构由三层构成： 
</p>
								<p>
								</p>
								<ul>
										<li>
												<p>首先是 Stub/Skeleton 层。该层提供了客户程序和服务程序彼此交互的接口。 </p>
										</li>
										<li>
												<p>然后是远程引用（Remote Reference）层。这一层相当于在其之上的 Stub/Skeleton 层和在其之下的传输协议层之前的中间件，负责处理远程对象引用的创建和管理。 </p>
										</li>
										<li>
												<p>最后是传输协议（Transport Protocol） 层。该层提供了数据协议，用以通过线路传输客户程序和远程对象间的请求和应答。</p>
										</li>
								</ul>
								<p>这些层之间的交互可以参照下面的示意图： 
</p>
								<div>
										<p>
												<img src="http://storage.msn.com/x1pGg9EMswqL--KyzGRGdhRjrq64UqttMM_PJCibCwwE4LYc9TR44YWI1mMQE5aQp9ot9OjPBDKWlfamAjUBUEv9B78DuzO6uk_6mbUokjRpiHZnuy_Vhk1YzAUlzbLdD0LfHsABLa8zbukz397rrHcWccZDZQyoGBG" />
										</p>
								</div>
								<p>和其它分布式对象机制一样，Java RMI 的客户程序使用客户端的 Stub 向远程对象请求方法调用；服务器对象则通过服务器端的 Skeleton 接受请求。我们深入进去，来看看其中的一些细节。 
</p>
								<div>
										<blockquote>
												<p>
														<b>注意: </b>事实上，在 Java 1.2 之后，RMI 不再需要 Skeleton 对象，而是通过 Java 的反射机制（Reflection）来完成对服务器端的远程对象的调用。为了便于说明问题，本文以下内容仍然基于 Skeleton 来讲解。</p>
										</blockquote>
								</div>
								<p>当客户程序调用 Stub 时，Stub 负责将方法的参数转换为序列化（Serialized）形式，我们使用一个特殊的术语，即编列（Marshal）来指代这个过程。编列的目的是将这些参数转换为可移植的形式，从而可以通过网络传输到远程的服务对象一端。不幸的是，这个过程没有想象中那么简单。这里我们首先要理解一个经典的问题，即方法调用时，参数究竟是传值还是传引用呢？对于 Java RMI 来说，存在四种情况，我们将分别加以说明。 
</p>
								<p>
								</p>
								<ul>
										<li>
												<p>对于基本的原始类型（整型，字符型等等），将被自动的序列化，以传值的方式编列。 </p>
										</li>
										<li>
												<p>对于 Java 的对象，如果该对象是可序列化的（实现了 <font color="red">java.io.Serializable</font> 接口），则通过 Java 序列化机制自动地加以序列化，以传值的方式编列。对象之中包含的原始类型以及所有被该对象引用，且没有声明为 <font color="red">transient</font> 的对象也将自动的序列化。当然，这些被引用的对象也必须是可序列化的。 </p>
										</li>
										<li>
												<p>绝大多数内建的 Java 对象都是可序列化的。 对于不可序列化的 Java 对象（<font color="red">java.io.File</font> 最典型），或者对象中包含对不可序列化，且没有声明为 <font color="red">transient</font> 的其它对象的引用。则编列过程将向客户程序抛出异常，而宣告失败。 </p>
										</li>
										<li>
												<p>客户程序可以调用远程对象，没有理由禁止调用参数本身也是远程对象（实现了 <font color="red">java.rmi.Remote</font> 接口的类的实例）。此时，RMI 采用一种<span><i>模拟的</i></span>传引用方式（当然不是传统意义的传引用，因为本地对内存的引用到了远程变得毫无意义），而不是将参数直接编列复制到远程。这种情况下，交互的双方发生的戏剧性变化值得我们注意。参数是远程对象，意味着该参数对象可以远程调用。当客户程序指定远程对象作为参数调用服务器端远程对象的方法时，RMI 的运行时机制将向服务器端的远程对象发送作为参数的远程对象的一个 Stub 对象。这样服务器端的远程对象就可以回调（Callback）这个 Stub 对象的方法，进而调用在客户端的远程对象的对应方法。通过这种方法，服务器端的远程对象就可以修改作为参数的客户端远程对象的内部状态，这正是传统意义的传引用所具备的特性。是不是有点晕？这里的关键是要明白，在分布式环境中，所谓服务器和客户端都是相对的。被请求的一方就是服务器，而发出请求的一方就是客户端。 </p>
										</li>
								</ul>
								<p>在调用参数的编列过程成功后，客户端的远程引用层从 Stub 那里获得了编列后的参数以及对服务器端远程对象的远程引用（参见 <font color="red">java.rmi.server.RemoteRef</font> API）。该层负责将客户程序的请求依据底层的 RMI 数据传输协议转换为传输层请求。在 RMI 中，有多种的可能的传输机制，比如点对点（Point-to-Point）以及广播（Multicast）等。不过，在当前的 JMI 版本中只支持点对点协议，即远程引用层将生成唯一的传输层请求，发往指定的唯一远程对象（参见 <font color="red">java.rmi.server.UnicastRemoteObject</font> API）。 
</p>
								<p>在服务器端，服务器端的远程引用层接收传输层请求，并将其转换为对远程对象的服务器端代理对象 Skeleton 的调用。Skeleton 对象负责将请求转换为对实际的远程对象的方法调用。这是通过与编列过程相对的反编列（Unmarshal）过程实现的。所有序列化的参数被转换为 Java 形式，其中作为参数的远程对象（实际上发送的是远程引用）被转换为服务器端本地的 Stub 对象。 
</p>
								<p>如果方法调用有返回值或者抛出异常，则 Skeleton 负责编列返回值或者异常，通过服务器端的远程引用层，经传输层传递给客户端；相应地，客户端的远程引用层和 Stub 负责反编列并最终将结果返回给客户程序。 
</p>
								<p>整个过程中，可能最让人迷惑的是远程引用层。这里只要明白，本地的 Stub 对象是如何产生的，就不难理解远程引用的意义所在了。远程引用中包含了其所指向的远程对象的信息，该远程引用将用于构造作为本地代理对象的 Stub 对象。构造后，Stub 对象内部将维护该远程引用。真正在网络上传输的实际上就是这个远程引用，而不是 Stub 对象。</p>
						</div>
						<div>
								<h1>
										<a>
												<font color="#0066a7">4. RMI 对象服务</font>
										</a>
								</h1>
								<p>在 RMI 的基本架构之上，RMI 提供服务与分布式应用程序的一些对象服务，包括对象的命名/注册（Naming/Registry）服务，远程对象激活（Activation）服务以及分布式垃圾收集（Distributed Garbage Collection, DGC）。作为入门指南，本文将指介绍其中的命名/注册服务，因为它是实战 RMI 所必备的。其它内容请读者自行参考其它更加深入的资料。 
</p>
								<p>在前一节中，如果你喜欢刨根问底，可能已经注意到，客户端要调用远程对象，是通过其代理对象 Stub 完成的，那么 Stub 最早是从哪里得来的呢？RMI 的命名/注册服务正是解决这一问题的。当服务器端想向客户端提供基于 RMI 的服务时，它需要将一个或多个远程对象注册到本地的 RMI 注册表中（参见<font color="red">java.rmi.registry.Registry</font> API）。每个对象在注册时都被指定一个将来用于客户程序引用该对象的名称。客户程序通过命名服务（参见 <font color="red">java.rmi.Naming</font> API），指定类似 URL 的对象名称就可以获得指向远程对象的远程引用。在 <font color="red">Naming</font> 中的 <font color="red">lookup()</font> 方法找到远程对象所在的主机后，它将检索该主机上的 RMI 注册表，并请求所需的远程对象。如果注册表发现被请求的远程对象，它将生成一个对该远程对象的远程引用，并将其返回给客户端，客户端则基于远程引用生成相应的 Stub 对象，并将引用传递给调用者。之后，双方就可以按照我们前面讲过的方式进行交互了。 
</p>
								<div>
										<blockquote>
												<p>
														<b>注意: </b>RMI 命名服务提供的 <font color="red">Naming</font> 类并不是你的唯一选择。RMI 的注册表可以与其他命名服务绑定，比如 JNDI，这样你就可以通过 JNDI 来访问 RMI 的注册表了。</p>
										</blockquote>
								</div>
						</div>
						<div>
								<h1>
										<a>
												<font color="#0066a7">5. 实战 RMI</font>
										</a>
								</h1>
								<p>理论离不开实践，理解 RMI 的最好办法就是通过例子。开发 RMI 的分布式对象的大体过程包括如下几步： 
</p>
								<p>
								</p>
								<ol>
										<li>
												<p>定义远程接口。这一步是通过扩展 <font color="red">java.rmi.Remote</font> 接口，并定义所需的业务方法实现的。 </p>
										</li>
										<li>
												<p>定义远程接口的实现类。即实现上一步所定义的接口，给出业务方法的具体实现逻辑。 </p>
										</li>
										<li>
												<p>编译远程接口和实现类，并通过 RMI 编译器 <b>rmic</b> 基于实现类生成所需的 Stub 和 Skeleton 类。</p>
										</li>
								</ol>
								<p>RMI 中各个组件之间的关系如下面这个示意图所示： 
</p>
								<div>
										<p>
												<img src="http://storage.msn.com/x1pGg9EMswqL--KyzGRGdhRjrq64UqttMM_PJCibCwwE4JxA-aX-SZD0hjjnO9OG_VQQMz7HdUngz_LHForXB2I2mTv509Lxg9BFNumeHviSDXogOWKhYd19lHUSUiy6q91gU2yB69bThJI89K6hoqnkg" />
										</p>
								</div>
								<p>回忆我们上一节所讲的，Stub 和 Skeleton 负责代理客户和服务器之间的通讯。但我们并不需要自己生成它们，相反，RMI 的编译器 <b>rmic</b> 可以帮我们基于远程接口和实现类生成这些类。当客户端对象通过命名服务向服务器端的 RMI 注册表请求远程对象时，RMI 将自动构造对应远程对象的 Skeleton 实例对象，并通过 Skeleton 对象将远程引用返回给客户端。在客户端，该远程引用将用于构造 Stub 类的实例对象。之后，Stub 对象和 Skeleton 对象就可以代理客户对象和远程对象之间的交互了。 
</p>
								<p>我们的例子展现了一个简单的应用场景。服务器端部署了一个计算引擎，负责接受来自客户端的计算任务，在服务器端执行计算任务，并将结果返回给客户端。客户端将发送并调用计算引擎的计算任务实际上是计算指定精度的 π 值。 
</p>
								<div>
										<blockquote>
												<p>
														<b>重要: </b>本文的例子改编自 <a href="http://java.sun.com/docs/books/tutorial/rmi/index.html"><font color="#0066a7">The Java™ Tutorial Trail:RMI</font></a>。所有权利属于相应的所有人。</p>
										</blockquote>
								</div>
						</div>
						<div>
								<h1>
										<a>
												<font color="#0066a7">6. 定义远程接口</font>
										</a>
								</h1>
								<p>定义远程接口与非分布式应用中定义接口的方法没有太多的区别。只要遵守下面两个要求： 
</p>
								<p>
								</p>
								<ul>
										<li>
												<p>远程接口必须直接或者间接的扩展自 <font color="red">java.rmi.Remote</font> 接口。远程接口还可以在扩展该接口的基础上，同时扩展其它接口，只要被扩展的接口的所有方法与远程接口的所有方法一样满足下一个要求。 </p>
										</li>
										<li>
												<p>在远程接口或者其超接口（Super-interface）中声明的方法必须满足下列对<span><i>远程方法</i></span>的要求： 
</p>
												<p>
												</p>
												<ul>
														<li>
																<p>远程方法必须声明抛出 <font color="red">java.rmi.RemoteException</font> 异常，或者该异常的超类（Superclass），比如 <font color="red">java.io.IOException</font> 或者 <font color="red">java.lang.Exception</font> 异常。在此基础上，远程方法可以声明抛出应用特定的其它异常。 </p>
														</li>
														<li>
																<p>在远程方法声明中，作为参数或者返回值的远程对象，或者包含在其它非远程对象中的远程对象，必须声明为其对应的远程接口，而不是实际的实现类。</p>
														</li>
												</ul>
										</li>
								</ul>
								<ul>
								</ul>
								<div>
										<blockquote>
												<p>
														<b>注意: </b>在 Java 1.2 之前，上面关于抛出异常的要求更严格，即必须抛出 <font color="red">java.rmi.RemoteExcption</font>，不允许类似 <font color="red">java.io.IOException</font> 这样的超类。现在之所以放宽了这一要求，是希望可以使定义既可以用于远程对象，也可以用于本地对象的接口变得容易一些（想想 EJB 中的本地接口和远程接口）。当然，这并没有使问题好多少，你还是必须声明异常。不过，一种观点认为这不是问题，强制声明异常可以使开发人员保持清醒的头脑，因为远程对象和本地对象在调用时传参的语意是不同的。本地对象是传引用，而远程对象主要是传值，这意味对参数内部状态的修改产生的结果是不同的。 </p>
										</blockquote>
								</div>
								<p>对于第一个要求，<font color="red">java.rmi.Remote</font> 接口实际上没有任何方法，而只是用作标记接口。RMI 的运行环境依赖该接口判断对象是否是远程对象。第二个要求则是因为分布式应用可能发生任何问题，比如网络问题等等。 
</p>
								<p>
										<a>
												<font color="#0066a7">例 1</font>
										</a>列出了我们的远程接口定义。该接口只有一个方法：<font color="red">executeTask()</font> 用以执行指定的计算任务，并返回相应的结果。注意，我们用后缀 <tt>Remote</tt> 表明接口是远程接口。 
</p>
								<div>
										<a>
										</a>
										<p>
												<b>例 1. ComputeEngineRemote 远程接口</b>
												<a>
												</a>
										</p>
										<blockquote>
												<pre>package rmitutorial;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface ComputeEngineRemote extends Remote {
    public Object executeTask(Task task) throws RemoteException;    
}</pre>
										</blockquote>
								</div>
								<p>
										<a>
												<font color="#0066a7">例 2</font>
										</a>列出了计算任务接口的定义。该接口也只有一个方法：<font color="red">execute()</font> 用以执行实际的计算逻辑，并返回结果。注意，该接口不是远程接口，所以没有扩展 <font color="red">java.rmi.Remote</font> 接口；其方法也不必抛出 <font color="red">java.rmi.RemoteException</font> 异常。但是，因为它将用作远程方法的参数，所以扩展了 <font color="red">java.io.Serializable</font> 接口。 
</p>
								<div>
										<a>
										</a>
										<p>
												<b>例 2. Task 接口</b>
												<a>
												</a>
										</p>
										<blockquote>
												<pre>package rmitutorial;

import java.io.Serializable;

public interface Task extends Serializable {
    Object execute();
}</pre>
										</blockquote>
								</div>
						</div>
						<div>
								<h1>
										<a>
												<font color="#0066a7">7. 实现远程接口</font>
										</a>
								</h1>
								<p>接下来，我们将实现前面定义的远程接口。<a><font color="#0066a7">例 3</font></a>给出了实现的源代码。 
</p>
								<div>
										<a>
										</a>
										<p>
												<b>例 3. ComputeEngine 实现</b>
												<a>
												</a>
										</p>
										<blockquote>
												<pre>package rmitutorial;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class ComputeEngine extends UnicastRemoteObject 
        implements ComputeEngineRemote {
    
    public ComputeEngine() throws RemoteException {
        super();
    }
    
    public Object executeTask(Task task) throws RemoteException {
        return task.execute();
    }
}</pre>
										</blockquote>
								</div>
								<p>类 <font color="red">ComputeEngine</font> 实现了之前定义的远程接口，同时继承自 <font color="red">java.rmi.server.UnicastRemoteObject</font> 超类。<font color="red">UnicastRemoteObject</font> 类是一个便捷类，它实现了我们前面所讲的基于 TCP/IP 的点对点通讯机制。远程对象都必须从该类扩展（除非你想自己实现几乎所有 <font color="red">UnicastRemoteObject</font> 的方法）。在我们的实现类的构造函数中，调用了超类的构造函数（当然，即使你不显式的调用这个构建函数，它也一样会被调用。这里这样做，只是为了突出强调这种调用而已）。该构造函数的最重要的意义就是调用 <font color="red">UnicastRemoteObject</font> 类的 <font color="red">exportObject() </font>方法。导出（Export）对象是指使远程对象准备就绪，可以接受进来的调用的过程。而这个过程的最重要内容就是建立服务器套接字，监听特定的端口，等待客户端的调用请求。</p>
						</div>
						<div>
								<h1>
										<a>
												<font color="#0066a7">8. 引导程序</font>
										</a>
								</h1>
								<p>为了让客户程序可以找到我们的远程对象，就需要将我们的远程对象注册到 RMI 的注册表。这个过程有时被称为"引导"过程（Bootstrap）。我们将为此编写一个独立的引导程序负责创建和注册远程对象。<a><font color="#0066a7">例 4</font></a> 给出了引导程序的源代码。 
</p>
								<div>
										<a>
										</a>
										<p>
												<b>例 4. 引导程序</b>
												<a>
												</a>
										</p>
										<blockquote>
												<pre>package rmitutorial;

import java.rmi.Naming;
import java.rmi.RMISecurityManager;

public class Bootstrap {
    
    public static void main(String[] args) throws Exception {
        String name = "ComputeEngine";
        
        ComputeEngine engine = new ComputeEngine();
        System.out.println("ComputerEngine exported");
        
        Naming.rebind(name, engine);
        System.out.println("ComputeEngine bound");
    }
}
</pre>
										</blockquote>
								</div>
								<p>可以看到，我们首先创建了一个远程对象（同时导出了该对象），之后将该对象绑定到 RMI 注册表中。<font color="red">Naming</font> 的 <font color="red">rebind()</font> 方法接受一个 URL 形式的名字作绑定之用。其完整格式如下：<a></a></p>
								<blockquote>
										<p>
												<a>
														<font color="#0066a7">protocol://host:port/object</font>
												</a>
										</p>
								</blockquote>
								<p>其中，协议（Protocol）默认为 <span>rmi</span>；主机名默认为 <span>localhost</span>；端口默认为 <tt>1099</tt>。注意，JDK 中提供的默认 <font color="red">Naming</font> 实现只支持 <span>rmi</span> 协议。在我们的引导程序里面只给出了对象绑定的名字，而其它部分均使用缺省值。</p>
						</div>
						<div>
								<h1>
										<a>
												<font color="#0066a7">9. 客户端程序</font>
										</a>
								</h1>
								<p>
										<a>
												<font color="#0066a7">例 5</font>
										</a>给出了我们的客户端程序。该程序接受两个参数，分别是远程对象所在的主机地址和希望获得的 π 值的精度。 
</p>
								<div>
										<a>
										</a>
										<p>
												<b>例 5. Client.java</b>
												<a>
												</a>
										</p>
										<blockquote>
												<pre>package rmitutorial;

import java.math.BigDecimal;
import java.rmi.Naming;

public class Client {
    public static void main(String args[]) throws Exception {
            String name = "rmi://" + args[0] + "/ComputeEngine";
            ComputeEngineRemote engineRemote = 
                    (ComputeEngineRemote)Naming.lookup(name);
            Pi task = new Pi(Integer.parseInt(args[1]));
            BigDecimal pi = (BigDecimal)(engineRemote.executeTask(task));
            System.out.println(pi);
    }
}</pre>
										</blockquote>
								</div>
								<div>
										<a>
										</a>
										<p>
												<b>例 6. Pi.java</b>
												<a>
												</a>
										</p>
										<blockquote>
												<pre>package rmitutorial;

import java.math.*;

public class Pi implements Task {
    
    private static final BigDecimal ZERO =
            BigDecimal.valueOf(0);
    private static final BigDecimal  ONE =
            BigDecimal.valueOf(1);
    private static final BigDecimal FOUR =
            BigDecimal.valueOf(4);
    
    private static final int roundingMode =
            BigDecimal.ROUND_HALF_EVEN;
    
    private int digits;
    
    public Pi(int digits) {
        this.digits = digits;
    }
    
    public Object execute() {
        return computePi(digits);
    }
    
    public static BigDecimal computePi(int digits) {
        int scale = digits + 5;
        BigDecimal arctan1_5 = arctan(5, scale);
        BigDecimal arctan1_239 = arctan(239, scale);
        BigDecimal pi = arctan1_5.multiply(FOUR).subtract(
                arctan1_239).multiply(FOUR);
        return pi.setScale(digits,
                BigDecimal.ROUND_HALF_UP);
    }

    public static BigDecimal arctan(int inverseX,
            int scale) {
        BigDecimal result, numer, term;
        BigDecimal invX = BigDecimal.valueOf(inverseX);
        BigDecimal invX2 =
                BigDecimal.valueOf(inverseX * inverseX);
        
        numer = ONE.divide(invX, scale, roundingMode);
        
        result = numer;
        int i = 1;
        do {
            numer =
                    numer.divide(invX2, scale, roundingMode);
            int denom = 2 * i + 1;
            term =
                    numer.divide(BigDecimal.valueOf(denom),
                    scale, roundingMode);
            if ((i % 2) != 0) {
                result = result.subtract(term);
            } else {
                result = result.add(term);
            }
            i++;
        } while (term.compareTo(ZERO) != 0);
        return result;
    }
}</pre>
										</blockquote>
								</div>
						</div>
						<div>
								<h1>
										<a>
												<font color="#0066a7">10. 编译示例程序</font>
										</a>
								</h1>
								<p>编译我们的示例程序和编译其它非分布式的应用没什么区别。只是编译之后，需要使用 RMI 编译器，即 <b>rmic</b> 生成所需 Stub 和 Skeleton 实现。使用 <b>rmic</b> 的方式是将我们的远程对象的实现类（不是远程接口）的全类名作为参数来运行 <b>rmic</b> 命令。参考下面的示例：<a></a></p>
								<blockquote>
										<pre>E:\classes\rmic rmitutorial.ComputeEngine</pre>
								</blockquote>
								<p>编译之后将生成 <font color="red">rmitutorial.ComputeEngine_Skel</font> 和 <font color="red">rmitutorial.ComputeEngine_Stub</font> 两个类。</p>
						</div>
						<div>
								<h1>
										<a>
												<font color="#0066a7">11. 运行示例程序</font>
										</a>
								</h1>
								<p>远程对象的引用通常是通过 RMI 的注册表服务以及 <font color="red">java.rmi.Naming</font> 接口获得的。远程对象需要导出（注册）相应的远程引用到注册表服务，之后注册表服务就可以监听并服务于客户端对远程对象引用的请求。标准的 Sun Java SDK 提供了一个简单的 RMI 注册表服务程序，即 <b>rmiregistry</b> 用于监听特定的端口，等待远程对象的注册，以及客户端对这些远程对象引用的检索请求。 
</p>
								<p>在运行我们的示例程序之前，首先要启动 RMI 的注册表服务。这个过程很简单，只要直接运行 <b>rmiregistry</b> 命令即可。缺省的情况下，该服务将监听 <tt>1099</tt> 端口。如果需要指定其它的监听端口，可以在命令行指定希望监听的端口（如果你指定了其它端口，需要修改示例程序以适应环境）。如果希望该程序在后台运行，在 Unix 上可以以如下方式运行（当然，可以缺省端口参数）： <a></a></p>
								<blockquote>
										<pre>$ rmiregistry 1099 &amp;</pre>
								</blockquote>
								<p>在 Windows 操作系统中可以这样运行：<a></a></p>
								<blockquote>
										<pre>C:\&gt; start rmiregistry 1099</pre>
								</blockquote>
								<p>我们的 <font color="red">rmitutorial.Bootstrap</font> 类将用于启动远程对象，并将其绑定在 RMI 注册表中。运行该类后，远程对象也将进入监听状态，等待来自客户端的方法调用请求。<a></a></p>
								<blockquote>
										<pre>$ java rmitutorial.Bootstrap
ComputeEngine exported
ComputeEngine bound</pre>
								</blockquote>
								<p>启动远程对象后，打开另一个命令行窗口，运行客户端。命令行的第一个参数为 RMI 注册表的地址，第二个参数为期望的 π 值精度。参考下面的示例：<a></a></p>
								<blockquote>
										<pre>$ java rmitutorial.Client localhost 50
3.14159265358979323846264338327950288419716939937511</pre>
								</blockquote>
						</div>
						<div>
								<h1>
										<a>
												<font color="#0066a7">12. 其它信息</font>
										</a>
								</h1>
								<p>在演示示例程序时，我们实际上是在同一主机上运行的服务器和客户端，并且无论是服务器和客户端所需的类都在相同的类路径上，可以同时被服务器和客户端所访问。这忽略了 Java RMI 的一个重要细节，即动态类装载。因为 RMI 的特性（包括其它几个特性）并不适用于 J2EE 的 RMI-IIOP 和 EJB 技术，所以，本文将不作详细介绍，请读者自行参考本文给出的参考资料。不过，为了让好奇的读者不至于过分失望，这里简单介绍一下动态类装载的基本思想。 
</p>
								<p>RMI 运行时系统采用动态类装载机制来装载分布式应用所需的类。如果你可以直接访问应用所涉及的所有包括服务器端客户端在内的主机，并且可以把分布式应用所需的所有类都安装在每个主机的 <code>CLASSPATH</code> 中（上面的示例就是极端情况，所有的东西都在本地主机），那么你完全不必关心 RMI 类装载的细节。显然，既然是分布式应用，情况往往正相反。对于 RMI 应用，客户端需要装载客户端自身所需的类，将要调用的远程对象的远程接口类以及对应的 Stub 类；服务器端则要装载远程对象的实现类以及对应的 Skeleton 类（Java 1.2 之后不需要 Skeleton 类）。RMI 在处理远程调用涉及的远程引用，参数以及返回值时，可以将一个指定的 URL 编码到流中。交互的另一端可以通过 该 URL 获得处理这些对象所需的类文件。这一点类似于 Applet 中的 CODEBASE 的概念，交互的两端通过 HTTP 服务器发布各自控制的类，允许交互的另一端动态下载这些类。以我们的示例为例，客户端不必部署 <font color="red">ComputeEngine_Stub </font>的类文件，而可以通过服务器端的 HTTP 服务器获得类文件。同样，服务器端也不需要客户端实现的定制任务 <font color="red">Pi</font> 的类文件。 
</p>
								<p>注意，这种动态类装载将需要交互的两端加载定制的安全管理器（参见 <font color="red">java.rmi.RMISecurityManager</font> API），以及对应的策略文件。</p>
						</div>
						<div>
								<h1>
										<a>
												<font color="#0066a7">13. 参考资料</font>
										</a>
								</h1>
								<p>
										<font color="#0066a7">
										</font>
								</p>
								<ul>
										<li>
												<p>
														<a href="http://java.sun.com/docs/books/tutorial/rmi/index.html">
																<font color="#0066a7">The Java™ Tutorial Trail:RMI</font>
														</a>
												</p>
										</li>
										<li>
												<p>David Flanagan, Jim Farley, William Crawford and Kris Magnusson, 1999, ISBN 1-56592-483-5E, O'Reilly, <span><i>Java™ Enterprise in a Nutshell</i></span></p>
										</li>
										<li>
												<p>Ed Roman, Scott Ambler and Tyler Jewell 2002, ISBN 0-471-41711-4, John Wiley &amp;Sons, Inc., <span><i>Matering Enterprise JavaBeans™</i></span> , Second Edition</p>
										</li>
								</ul>
						</div>
				</div>
		</div>
<img src ="http://www.blogjava.net/sinoly/aggbug/97169.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/sinoly/" target="_blank">sinoly</a> 2007-02-01 11:29 <a href="http://www.blogjava.net/sinoly/articles/97169.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>