﻿<?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-庄周梦蝶-随笔分类-源码解读</title><link>http://www.blogjava.net/killme2008/category/21285.html</link><description>生活、程序、未来</description><language>zh-cn</language><lastBuildDate>Tue, 22 May 2012 15:22:13 GMT</lastBuildDate><pubDate>Tue, 22 May 2012 15:22:13 GMT</pubDate><ttl>60</ttl><item><title>如何熟悉一个开源项目？</title><link>http://www.blogjava.net/killme2008/archive/2012/05/22/378885.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Tue, 22 May 2012 15:12:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2012/05/22/378885.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/378885.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2012/05/22/378885.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/378885.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/378885.html</trackback:ping><description><![CDATA[<br />&nbsp; &nbsp; 你有个任务，需要用到某个开源项目;或者老大交代你一个事情，让你去了解某个东西。怎么下手呢？如何开始呢？我的习惯是这样：<br /><br />1.首先，查找和阅读该项目的博客和资料，通过google你能找到某个项目大体介绍的博客，快速阅读一下就能对项目的目的、功能、基本使用有个大概的了解。<br /><br />2.阅读项目的文档，重点关注类似<strong>Getting started、Example</strong>之类的文档，从中学习如何下载、安装、甚至基本使用该项目所需要的知识。<br /><br />3.如果该项目有提供现成的example工程，首先尝试按照开始文档的介绍运行example，如果运行顺利，那么恭喜你顺利开了个好头;如果遇到问题，首先尝试在项目的<strong>FAQ</strong>等文档里查找答案，再次，可以将问题（例如异常信息）当成关键词去搜索，查找相关的解决办法，你遇到了，别人一般也会遇到，热心的朋友会记录下解决的过程;最后，可以将问题提交到项目的邮件列表，请大家帮你看看。<strong>在没有成功运行example之前，不要尝试修改example。<br /><br /></strong>4.运行了第一个example之后，尝试根据你的理解和需要修改example，测试高级功能等。<br /><br />5.在了解基本使用后，需要开始深入的了解该项目。例如项目的配置管理、高级功能以及最佳实践。通常一个运作良好的项目会提供一份从浅到深的用户指南，你并不需要从头到尾阅读这份指南，根据时间和兴趣，特别是你自己任务的需要，重点阅读部分章节并做笔记（推荐evernote）。<br /><br />6.如果时间允许，尝试从源码构建该项目。通常开源项目都会提供一份构建指南，指导你如何搭建一个用于开发、调试和构建的环境。尝试构建一个版本。<br /><br />7.如果时间允许并且有兴趣，可以尝试阅读源码：<br />（1）阅读源码之前，查看该项目是否提供架构和设计文档，阅读这些文档可以了解该项目的大体设计和结构，读源码的时候不会无从下手。<br />（2）阅读源码之前，一定要能构建并运行该项目，有个直观感受。<br />（3）阅读源码的第一步是抓主干，尝试理清一次正常运行的代码调用路径，这可以通过debug来观察运行时的变量和行为。修改源码加入日志和打印可以帮助你更好的理解源码。<br />（4）适当画图来帮助你理解源码，在理清主干后，可以将整个流程画成一张流程图或者标准的UML图，帮助记忆和下一步的阅读。<br />（5）挑选感兴趣的&#8220;枝干&#8221;代码来阅读，比如你对网络通讯感兴趣，就阅读网络层的代码，深入到实现细节，如它用了什么库，采用了什么设计模式，为什么这样做等。如果可以，debug细节代码。<br />（6）阅读源码的时候，重视单元测试，尝试去运行单元测试，基本上一个好的单元测试会将该代码的功能和边界描述清楚。<br />（7）在熟悉源码后，发现有可以改进的地方，有精力、有意愿可以向该项目的开发者提出改进的意见或者issue，甚至帮他修复和实现，参与该项目的发展。<br /><br />8.通常在阅读文档和源码之后，你能对该项目有比较深入的了解了，但是该项目所在领域，你可能还想搜索相关的项目和资料，看看有没有其他的更好的项目或者解决方案。在广度和深度之间权衡。<br /><br />&nbsp; &nbsp; 以上是我个人的一些习惯，我自己也并没有完全按照这个来，但是按照这个顺序，基本上能让你比较高效地学习和使用某个开源项目。<img src ="http://www.blogjava.net/killme2008/aggbug/378885.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2012-05-22 23:12 <a href="http://www.blogjava.net/killme2008/archive/2012/05/22/378885.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Storm源码浅析之topology的提交</title><link>http://www.blogjava.net/killme2008/archive/2011/12/01/364112.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Thu, 01 Dec 2011 13:48:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2011/12/01/364112.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/364112.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2011/12/01/364112.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/364112.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/364112.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 原文：<a href="http://www.blogjava.net/killme2008/archive/2011/11/17/364112.html">http://www.blogjava.net/killme2008/archive/2011/11/17/364112.html</a><br />&nbsp;&nbsp;&nbsp; 作者：dennis (killme2008@gmail.com)<br />&nbsp;&nbsp;&nbsp; 转载请注明出处。<br /><br />&nbsp;&nbsp;&nbsp; 最近一直在读twitter开源的这个分布式流计算框架&#8212;&#8212;storm的源码，还是有必要记录下一些比较有意思的地方。我按照storm的主要概念进行组织，并且只分析我关注的东西，因此称之为浅析。&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br /><br />一、介绍<br />&nbsp;&nbsp;&nbsp; Storm的开发语言主要是Java和Clojure，其中Java定义骨架，而Clojure编写核心逻辑。源码统计结果：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">180</span><span style="color: #000000; ">&nbsp;text&nbsp;files.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">177</span><span style="color: #000000; ">&nbsp;unique&nbsp;files.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">7</span><span style="color: #000000; ">&nbsp;files&nbsp;ignored.<br /><br />http:</span><span style="color: #008000; ">//</span><span style="color: #008000; ">cloc.sourceforge.net&nbsp;v&nbsp;1.55&nbsp;&nbsp;T=1.0&nbsp;s&nbsp;(171.0&nbsp;files/s,&nbsp;46869.0&nbsp;lines/s)</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">-------------------------------------------------------------------------------</span><span style="color: #000000; "><br />Language&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;files&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;blank&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;comment&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;code<br /></span><span style="color: #000000; ">-------------------------------------------------------------------------------</span><span style="color: #000000; "><br />Java&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">125</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">5010</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">2414</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">25661</span><span style="color: #000000; "><br />Lisp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">33</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">732</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">283</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">4871</span><span style="color: #000000; "><br />Python&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">7</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">742</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">433</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">4675</span><span style="color: #000000; "><br />CSS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">12</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">45</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">1837</span><span style="color: #000000; "><br /><a title="" href="http://www.ruby-lang.org">ruby</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">22</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">104</span><span style="color: #000000; "><br />Bourne&nbsp;Shell&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">0</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">6</span><span style="color: #000000; "><br />Javascript&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">15</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">6</span><span style="color: #000000; "><br /></span><span style="color: #000000; ">-------------------------------------------------------------------------------</span><span style="color: #000000; "><br />SUM:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">171</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">6519</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">3190</span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">37160</span><span style="color: #000000; "><br /></span><span style="color: #000000; ">-------------------------------------------------------------------------------</span></div><br />&nbsp; &nbsp; Java代码25000多行，而Clojure(Lisp)只有4871行，说语言不重要再次证明是扯淡。<br />&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />二、Topology和Nimbus &nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; Topology是storm的核心理念，将spout和bolt组织成一个topology，运行在storm集群里，完成实时分析和计算的任务。这里我主要想介绍下topology部署到storm集群的大概过程。提交一个topology任务到Storm集群是通过StormSubmitter.submitTopology方法提交：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">StormSubmitter.submitTopology(name,&nbsp;conf,&nbsp;builder.createTopology());</span></div>&nbsp;&nbsp;&nbsp; 我们将topology打成jar包后，利用bin/storm这个python脚本，执行如下命令：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">bin</span><span style="color: #000000; ">/</span><span style="color: #000000; ">storm&nbsp;jar&nbsp;xxxx.jar&nbsp;com.taobao.MyTopology&nbsp;args</span></div>&nbsp;&nbsp;&nbsp; 将jar包提交给storm集群。storm脚本会启动JVM执行Topology的main方法，执行submitTopology的过程。而submitTopology会将jar文件上传到nimbus，上传是通过socket传输。在storm这个python脚本的jar方法里可以看到：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">def</span><span style="color: #000000; ">&nbsp;jar(jarfile,&nbsp;klass,&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">args):&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;exec_storm_class(&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;klass,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jvmtype</span><span style="color: #000000; ">=</span><span style="color: #800000; ">"</span><span style="color: #800000; ">-client</span><span style="color: #800000; ">"</span><span style="color: #000000; ">,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;extrajars</span><span style="color: #000000; ">=</span><span style="color: #000000; ">[jarfile,&nbsp;CONF_DIR,&nbsp;STORM_DIR&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">/bin</span><span style="color: #800000; ">"</span><span style="color: #000000; ">],&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;args</span><span style="color: #000000; ">=</span><span style="color: #000000; ">args,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong>prefix</strong></span><strong><span style="color: #000000; ">=</span><span style="color: #800000; ">"</span><span style="color: #800000; ">export&nbsp;STORM_JAR=</span><span style="color: #800000; ">"</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;jarfile&nbsp;</span><span style="color: #000000; ">+</span><span style="color: #000000; ">&nbsp;</span><span style="color: #800000; ">"</span><span style="color: #800000; ">;</span><span style="color: #800000; ">"</span></strong><span style="color: #000000; "><strong>)</strong> <br /></span></div>&nbsp;&nbsp;&nbsp;&nbsp; 将jar文件的地址设置为环境变量STORM_JAR，这个环境变量在执行submitTopology的时候用到：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; ">StormSubmitter.java&nbsp;</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">private</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">static</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;submitJar(Map&nbsp;conf)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">if</span><span style="color: #000000; ">(submittedJar</span><span style="color: #000000; ">==</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LOG.info(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Jar&nbsp;not&nbsp;uploaded&nbsp;to&nbsp;master&nbsp;yet.&nbsp;Submitting&nbsp;jar<img src="http://www.blogjava.net/Images/dot.gif" alt="" /></span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;localJar&nbsp;</span><span style="color: #000000; ">=</span><strong><span style="color: #000000; ">&nbsp;System.getenv(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">STORM_JAR</span><span style="color: #000000; ">"</span></strong><span style="color: #000000; "><strong>)</strong>;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;submittedJar&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;submitJar(conf,&nbsp;localJar);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">else</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LOG.info(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">Jar&nbsp;already&nbsp;uploaded&nbsp;to&nbsp;master.&nbsp;Not&nbsp;submitting&nbsp;jar.</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>&nbsp;&nbsp;&nbsp; 通过环境变量找到jar包的地址，然后上传。利用环境变量传参是个小技巧。<br /><br />&nbsp;&nbsp;&nbsp; 其次，nimbus在接收到jar文件后，存放到数据目录的inbox目录，<strong>nimbus数据目录的结构</strong>：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">-</span><span style="color: #000000; ">nimbus<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">inbox<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">stormjar</span><span style="color: #000000; ">-</span><span style="color: #000000; ">57f1d694</span><span style="color: #000000; ">-</span><span style="color: #000000; ">2865</span><span style="color: #000000; ">-</span><span style="color: #000000; ">4b3b</span><span style="color: #000000; ">-</span><span style="color: #000000; ">8a7c</span><span style="color: #000000; ">-</span><span style="color: #000000; ">99104fc0aea3.jar<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">stormjar</span><span style="color: #000000; ">-</span><span style="color: #000000; ">76b4e316</span><span style="color: #000000; ">-</span><span style="color: #000000; ">b430</span><span style="color: #000000; ">-</span><span style="color: #000000; ">4215</span><span style="color: #000000; ">-</span><span style="color: #000000; ">9e26</span><span style="color: #000000; ">-</span><span style="color: #000000; ">4f33ba4ee520.jar<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">stormdist<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">storm</span><span style="color: #000000; ">-</span><span style="color: #000000; ">id<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">stormjar.jar<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">stormconf.ser<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">-</span><span style="color: #000000; ">stormcode.ser</span></div>&nbsp;&nbsp;&nbsp;&nbsp; 其中inbox用于存放提交的jar文件，每个jar文件都重命名为stormjar加上一个32位的UUID。而stormdist存放的是启动topology后生成的文件，每个topology都分配一个唯一的id，ID的规则是&#8220;name-计数-时间戳&#8221;。启动后的topology的jar文件名命名为storm.jar ,而它的配置经过java序列化后存放在stormconf.ser文件，而stormcode.ser是将topology本身序列化后存放的文件。<strong>这些文件在部署的时候，supervisor会从这个目录下载这些文件，然后在supervisor本地执行这些代码。</strong><br />&nbsp;&nbsp;&nbsp; 进入重点，topology任务的分配过程(zookeeper路径说明忽略root):<br />1.在zookeeper上创建/taskheartbeats/{storm id} 路径，用于任务的心跳检测。storm对zookeeper的一个重要应用就是利用zk的临时节点做存活检测。task将定时刷新节点的时间戳，然后nimbus会检测这个时间戳是否超过timeout设置。<br />2.从topology中获取bolts,spouts设置的并行数目以及全局配置的最大并行数，然后产生task id列表，如[1 2 3 4]<br />3.在zookeeper上创建/tasks/{strom id}/{task id}路径，并存储task信息<br />4.开始分配任务（内部称为assignment)， 具体步骤：<br />&nbsp;(1)从zk上获得已有的assignment(新的toplogy当然没有了）<br />&nbsp;(2)查找所有可用的slot，所谓slot就是可用的worker，在所有supervisor上配置的多个worker的端口。<br />&nbsp;(3)将任务均匀地分配给可用的worker，这里有两种情况：<br />&nbsp;(a)task数目比worker多，例如task是[1 2 3 4],可用的slot只有[host1:port1 host2:port1]，那么最终是这样分配<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">{</span><span style="color: #000000; ">1</span><span style="color: #000000; ">:&nbsp;[host1:port1]&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;:&nbsp;[host2:port1]<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">3</span><span style="color: #000000; ">&nbsp;:&nbsp;[host1:port1]&nbsp;</span><span style="color: #000000; ">4</span><span style="color: #000000; ">&nbsp;:&nbsp;[host2:port1]}</span></div>，可以看到任务平均地分配在两个worker上。<br />(b)如果task数目比worker少，例如task是[1 2]，而worker有[host1:port1 host1:port2 host2:port1 host2:port2]，那么首先会将woker排序，<strong>将不同host间隔排列</strong>，保证task不会全部分配到同一个worker上，也就是将worker排列成<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">[host1:port1&nbsp;host2:port1&nbsp;host1:port2&nbsp;host2:port2]</span></div>，然后分配任务为<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #000000; ">{</span><span style="color: #000000; ">1</span><span style="color: #000000; ">:&nbsp;host1:port1&nbsp;,&nbsp;</span><span style="color: #000000; ">2</span><span style="color: #000000; ">&nbsp;:&nbsp;host2:port2}</span></div><br />(4)记录启动时间<br />(5)判断现有的assignment是否跟重新分配的assignment相同，如果相同，不需要变更，否则更新assignment到zookeeper的/assignments/{storm id}上。<br />5.启动topology，所谓启动，只是将zookeeper上/storms/{storm id}对应的数据里的active设置为true。<br />6.nimbus会检查task的心跳，如果发现task心跳超过超时时间，那么会重新跳到第4步做re-assignment。<br /><img src ="http://www.blogjava.net/killme2008/aggbug/364112.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2011-12-01 21:48 <a href="http://www.blogjava.net/killme2008/archive/2011/12/01/364112.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>高性能EL——Fel探秘，兼谈EL</title><link>http://www.blogjava.net/killme2008/archive/2011/09/17/358863.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Sat, 17 Sep 2011 04:52:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2011/09/17/358863.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/358863.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2011/09/17/358863.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/358863.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/358863.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; Fel是最近javaeye比较火的关键词，这是由网友<a href="http://lotusyu.iteye.com/">lotusyu</a>开发的一个高性能的EL，从作者给出的数据来看，性能非常优异，跟前段时间温少开源的<a href="http://www.iteye.com/topic/993292">Simple EL</a>有的一拼。首先要说，这是个好现象，国内的开源项目越来越多，可以看出开发者的水平是越来越高了，比如我最近还看到有人开源的类似kestel的轻量级MQ&#8212;&#8212;<a href="http://code.google.com/p/fqueue/">fqueue</a>也非常不错，有兴趣可以看下我的分析《<a href="../archive/2011/09/16/358827.html">fqueue初步分析</a>》。<br />
<br />


<p>&nbsp;&nbsp;&nbsp; 进入正文，本文是尝试分析下Fel的实现原理，以及优缺点和<a href="http://code.google.com/p/aviator/">aviator</a>&#8212;&#8212;我自己开源的EL之间的简单比较。</p>


<p>&nbsp; &nbsp; Fel的实现原理跟Simple 
EL是类似，都是使用template生成中间代码&#8212;&#8212;也就是普通的java代码，然后利用javac编译成class，最后运行，当然，这个过程都是动
态的。JDK6已经引入了编译API，在此之前的版本可以调用sun的类来编译，因为javac其实就是用java实现的。回到Fel里
面，FelCompiler15就是用 <em><strong>com.sun.tools.javac.Main</strong></em>来编译，而FelCompiler16用标准的<a href="http://download.oracle.com/javase/6/docs/api/javax/tools/JavaCompiler.html">javax.tools.JavaCompiler</a>来编译的。</p>


<p> &nbsp; &nbsp; 文法和语法解释这块是使用antlr这个parse generator生成的，这块不多说，有兴趣可以看下antlr，整体一个运行的过程是这样：</p>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;expression&nbsp;string&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;antlr&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;AST&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;comiple&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;java&nbsp;source&nbsp;template&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;java&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;</span><span style="color: #000000; ">-&gt;</span><span style="color: #000000; ">&nbsp;Expression&nbsp; <br />
</span></div>

<p>&nbsp;&nbsp;&nbsp; 这个思路我在实现<a href="http://code.google.com/p/aviator/">aviator</a>之前就想过，但是后来考虑到API需要用的sun独有的类，而且要求classpath必须有tools.jar这个依赖包，就放弃了这个思路，还是采用ASM生成字节码的方式。题外，velocity的优化可以采用这个思路，我们有这么一个项目是这么做的，也准备开源了。</p>

<p>&nbsp;</p>

<p>&nbsp;&nbsp;&nbsp; 看看Fel生成的中间代码，例如a+b这样的一个简单的表达式，假设我一开始不知道a和b的类型，编译是这样：</p>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;FelEngine&nbsp;fel&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;FelEngineImpl();&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;Expression&nbsp;exp&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000;">&nbsp; </span><span style="color: #000000; ">fel.compile(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a+b</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">);&nbsp; <br /></span></div>
<p> </p>

<p>&nbsp;&nbsp;&nbsp; 我稍微改了下FEL的源码，让它打印中间生成的java代码，a+b生成的中间结果为：</p>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">package</span><span style="color: #000000; ">&nbsp;com.greenpineyu.fel.compile;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.greenpineyu.fel.common.NumberUtil;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.greenpineyu.fel.Expression;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.greenpineyu.fel.context.FelContext;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;org.apache.commons.lang.ObjectUtils;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;org.apache.commons.lang.StringUtils;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Fel_0&nbsp;&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;Expression{&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Object&nbsp;eval(FelContext&nbsp;context)&nbsp;{&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;java.lang.Object&nbsp;var_1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(java.lang.Object)context.get(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">b</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">b&nbsp;&nbsp;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;java.lang.Object&nbsp;var_0&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(java.lang.Object)context.get(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">a&nbsp;&nbsp;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;(ObjectUtils.toString(var_0))</span><span style="color: #000000; ">+</span><span style="color: #000000; ">(ObjectUtils.toString(var_1));&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; <br />
</span></div>

<p>&nbsp;&nbsp;&nbsp;&nbsp; 可见，FEL对表达式解析和解释后，利用template生成这么一个普通的java类，而a和b都从context中获取并转化为<strong>Object</strong>类型，这里没有做任何判断就直接认为a和b是要做字符串相加，然后拼接字符串并返回。</p>

<p>&nbsp;</p>

<p>&nbsp;&nbsp;&nbsp;&nbsp; 问题出来了，因为没有在编译的时候传入context（我们这里是null），FEL会将a和b的类型默认都为java.lang.Object，a+b解释为字符串拼接。但是运行的时候，我完全可以传入a和b都为数字，那么结果就非常诡异了：</p>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FelEngine&nbsp;fel&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;FelEngineImpl();&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;Expression&nbsp;exp&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;fel.compile(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a+b</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">);&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;Map</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String,&nbsp;Object</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;env</span><span style="color: #000000; ">=</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;HashMap</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String,&nbsp;Object</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">();&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;env.put(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">);&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;env.put(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">b</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">3.14</span><span style="color: #000000; ">);&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(exp.eval(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;MapContext(env)));&nbsp; <br />
</span></div>

<p>输出：</p>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">13.14</span><span style="color: #000000; ">&nbsp; <br />
</span></div>


<p>&nbsp;&nbsp;&nbsp; 1+3.14的结果，作为字符串拼接就是13.14，而不是我们想要的4.14。如果将表达式换成a*b，就完全运行不了</p>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;com.greenpineyu.fel.exception.CompileException:&nbsp;</span><span style="color: #0000FF; ">package</span><span style="color: #000000; ">&nbsp;com.greenpineyu.fel.compile;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.greenpineyu.fel.common.NumberUtil;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.greenpineyu.fel.Expression;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.greenpineyu.fel.context.FelContext;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;org.apache.commons.lang.ObjectUtils;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;org.apache.commons.lang.StringUtils;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Fel_0&nbsp;&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;Expression{&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Object&nbsp;eval(FelContext&nbsp;context)&nbsp;{&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;java.lang.Object&nbsp;var_1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(java.lang.Object)context.get(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">b</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">b&nbsp;&nbsp;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;java.lang.Object&nbsp;var_0&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(java.lang.Object)context.get(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a</span><span style="color: #000000; ">"</span><span style="color: #000000; ">);&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">a&nbsp;&nbsp;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;(var_0)</span><span style="color: #000000; ">*</span><span style="color: #000000; ">(var_1);&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;[Fel_0.java:</span><span style="color: #000000; ">14</span><span style="color: #000000; ">:&nbsp;运算符&nbsp;</span><span style="color: #000000; ">*</span><span style="color: #000000; ">&nbsp;不能应用于&nbsp;java.lang.Object,java.lang.Object]&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;com.greenpineyu.fel.compile.FelCompiler16.compileToClass(FelCompiler16.java:</span><span style="color: #000000; ">113</span><span style="color: #000000; ">)&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;com.greenpineyu.fel.compile.FelCompiler16.compile(FelCompiler16.java:</span><span style="color: #000000; ">87</span><span style="color: #000000; ">)&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;com.greenpineyu.fel.compile.CompileService.compile(CompileService.java:</span><span style="color: #000000; ">66</span><span style="color: #000000; ">)&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;com.greenpineyu.fel.FelEngineImpl.compile(FelEngineImpl.java:</span><span style="color: #000000; ">62</span><span style="color: #000000; ">)&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;TEst.main(TEst.java:</span><span style="color: #000000; ">14</span><span style="color: #000000; ">)&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;Exception&nbsp;in&nbsp;thread&nbsp;</span><span style="color: #000000; ">"</span><span style="color: #000000; ">main</span><span style="color: #000000; ">"</span><span style="color: #000000; ">&nbsp;java.lang.NullPointerException&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;TEst.main(TEst.java:</span><span style="color: #000000; ">18</span><span style="color: #000000; ">)&nbsp; <br />
</span></div>
<p>&nbsp;</p>

<p>&nbsp;&nbsp;&nbsp; 这个问题对于Simple EL同样存在，如果没有在编译的时候能确定变量类型，这无法生成正确的中间代码，导致运行时出错，并且有可能造成非常诡异的bug。</p>

<p>&nbsp;</p>

<p>&nbsp;&nbsp;&nbsp; 这个问题的本质是<strong>因为Fel和Simple EL没有自己的类型系统，他们都是直接使用java的类型的系统，并且必须在编译的时候确定变量类型，才能生成高效和正确的代码，我们可以将它们称为&#8220;强类型的EL&#8220;。</strong></p>

<p>&nbsp;</p>


<p>&nbsp;&nbsp;&nbsp; 现在让我们在编译的时候给a和b加上类型，看看生成的中间代码：</p>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;FelEngine&nbsp;fel&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;FelEngineImpl();&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;fel.getContext().set(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">);&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;fel.getContext().set(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">b</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">3.14</span><span style="color: #000000; ">);&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;Expression&nbsp;exp&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;fel.compile(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a+b</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">);&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;Map</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String,&nbsp;Object</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">&nbsp;env&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;HashMap</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">String,&nbsp;Object</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">();&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;env.put(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">1</span><span style="color: #000000; ">);&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;env.put(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">b</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #000000; ">3.14</span><span style="color: #000000; ">);&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(exp.eval(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;MapContext(env)));&nbsp; <br />
</span></div>
<p>&nbsp;&nbsp;&nbsp; 查看中间代码：</p>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">package</span><span style="color: #000000; ">&nbsp;com.greenpineyu.fel.compile;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.greenpineyu.fel.common.NumberUtil;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.greenpineyu.fel.Expression;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;com.greenpineyu.fel.context.FelContext;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;org.apache.commons.lang.ObjectUtils;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">import</span><span style="color: #000000; ">&nbsp;org.apache.commons.lang.StringUtils;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">class</span><span style="color: #000000; ">&nbsp;Fel_0&nbsp;&nbsp;</span><span style="color: #0000FF; ">implements</span><span style="color: #000000; ">&nbsp;Expression{&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Object&nbsp;eval(FelContext&nbsp;context)&nbsp;{&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">double</span><span style="color: #000000; ">&nbsp;var_1&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;((java.lang.Number)context.get(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">b</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)).doubleValue();&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">b&nbsp;&nbsp;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">double</span><span style="color: #000000; ">&nbsp;var_0&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;((java.lang.Number)context.get(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">a</span><span style="color: #000000; ">"</span><span style="color: #000000; ">)).doubleValue();&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">//</span><span style="color: #008000; ">a&nbsp;&nbsp;</span><span style="color: #008000; "><br /></span><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;(var_0)</span><span style="color: #000000; ">+</span><span style="color: #000000; ">(var_1);&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp; <br />
</span></div>

<p>可以看到这次将a和b都强制转为double类型了，做数值相加，结果也正确了：</p>
<div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><span style="color: #000000; ">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000; ">4.140000000000001</span><span style="color: #000000; ">&nbsp; <br />
</span></div>
<p> </p>

<p>&nbsp;&nbsp;&nbsp; Simple EL我没看过代码，这里猜测它的实现也应该是类似的，也应该有同样的问题。</p>


<p>&nbsp;&nbsp;&nbsp; 相比来说，<a href="http://code.google.com/p/aviator/">aviator</a>这是<strong>一个弱类型的EL</strong>，<strong>在编译的时候不对变量类型做任何假设，而是在运行时做类型判断和自动转化</strong>。过去提过，我给aviator的定位是一个介于EL和script之间的东西，<strong>它有自己的类型系统</strong>。
例如，3这个数字，在java里可能是long,int,short,byte，而aviator统一为AviatorLong这个类型。为了在这两个类
型之间做适配，就需要做很多的判断和box,unbox操作。这些判断和转化都是运行时进行的，因此aviator没有办法做到Fel这样的高效，但是已
经做到至少跟groovy这样的弱类型脚本语言一个级别，也超过了JXEL这样的纯解释EL，具体可以看这个<a href="http://code.google.com/p/aviator/wiki/Performance">性能测试</a>。</p>

<p>&nbsp;</p>

<p>&nbsp;&nbsp; 强类型还是弱类型，这是一个选择问题，如果你能在运行前就确定变量的类型，那么使用Fel应该可以达到或者接近于原生java执行的效率，但是失去了灵活性；如果你无法确定变量类型，则只能采用弱类型的EL。</p>

<p>&nbsp;</p>

<p>&nbsp;&nbsp; 
EL涌现的越来越多，这个现象有点类似消息中间件领域，越来越多面向特定领域的轻量级MQ的出现，而不是原来那种大而笨重的通用MQ大行其道，一方面是互
联网应用的发展，需求不是通用系统能够满足的，另一方面我认为也是开发者素质的提高，大家都能造适合自己的轮子。从EL这方面来说，我也认为会有越来越多
特定于领域的，优点和缺点一样鲜明的EL出现，它们包含设计者自己的目标和口味，选择很多，就看取舍。</p><img src ="http://www.blogjava.net/killme2008/aggbug/358863.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2011-09-17 12:52 <a href="http://www.blogjava.net/killme2008/archive/2011/09/17/358863.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>fqueue初步分析</title><link>http://www.blogjava.net/killme2008/archive/2011/09/16/358827.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Fri, 16 Sep 2011 12:10:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2011/09/16/358827.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/358827.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2011/09/16/358827.html#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/358827.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/358827.html</trackback:ping><description><![CDATA[<br />&nbsp;&nbsp;&nbsp; fqueue是国产的一个类似memcacheq,kestrel这样的支持memcached协议的轻量级开源MQ。它的项目主页：<br /><a href="http://code.google.com/p/fqueue/downloads/list" target="_blank">http://code.google.com/p/<wbr>fqueue/downloads/list</a>，介绍和特点都可以看主页，我就不废话了。<br /><br />&nbsp;&nbsp;&nbsp; 今天老大提到， co了源码看了下，写个初步分析报告。<br /><br />&nbsp;&nbsp;&nbsp; 首先是它的存储层，主要是一个FQueue这么一个抽象队列，内部实现是FSQueue，也就是基于文件的FIFO队列。这个队列是多个文件组成的。每个文件默认大小在150M，超过即切换一个新文件来写。读的时候如果读到尾部，则查找下一个文件进行读取。数据文件名以idb为后缀，并且从编号1开始递增，除了数据文件外，每个队列还有个db为后缀的索引文件，记录当前写和读的数据文件编号和偏移量。目录结构大概是这样：<br />&nbsp;&nbsp;&nbsp; --fqueue<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; --fqueuedata_1.idb<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; --fqueuedata_2.idb<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; --&#8230;&#8230;<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; --icqueue.db<br /><br />&nbsp;&nbsp;&nbsp; 文件的存储比较有特色，采用<a href="http://download.oracle.com/javase/1.4.2/docs/api/java/nio/MappedByteBuffer.html">MappedByteBuffer</a>做文件读写，<a href="http://download.oracle.com/javase/1.4.2/docs/api/java/nio/MappedByteBuffer.html">MappedByteBuffer</a>是java nio引入的文件内存映射方案，读写性能极高，但是也有一定的问题，比如说内存占用，以及数据刷入设备的不确定性和关闭问题。在fqueue中，每隔10毫秒会强制force一次buffer，将修改过的数据刷入设备。对于关闭问题，则采用那个技巧，示例代码：<br /><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">/**</span><span style="color: #008000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;关闭索引文件<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000; ">*/</span><span style="color: #000000; "><br />&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">void</span><span style="color: #000000; ">&nbsp;close()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mappedByteBuffer.force();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AccessController.doPrivileged(</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;PrivilegedAction</span><span style="color: #000000; ">&lt;</span><span style="color: #000000; ">Object</span><span style="color: #000000; ">&gt;</span><span style="color: #000000; ">()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">public</span><span style="color: #000000; ">&nbsp;Object&nbsp;run()&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">try</span><span style="color: #000000; ">&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Method&nbsp;getCleanerMethod&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;mappedByteBuffer.getClass().getMethod(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">cleaner</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Class[</span><span style="color: #000000; ">0</span><span style="color: #000000; ">]);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getCleanerMethod.setAccessible(</span><span style="color: #0000FF; ">true</span><span style="color: #000000; ">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sun.misc.Cleaner&nbsp;cleaner&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;(sun.misc.Cleaner)&nbsp;getCleanerMethod.invoke(mappedByteBuffer,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">new</span><span style="color: #000000; ">&nbsp;Object[</span><span style="color: #000000; ">0</span><span style="color: #000000; ">]);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cleaner.clean();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(Exception&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.error(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">close&nbsp;logindexy&nbsp;file&nbsp;error:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;e);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000FF; ">return</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fc.close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dbRandFile.close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mappedByteBuffer&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fc&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;dbRandFile&nbsp;</span><span style="color: #000000; ">=</span><span style="color: #000000; ">&nbsp;</span><span style="color: #0000FF; ">null</span><span style="color: #000000; ">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000FF; ">catch</span><span style="color: #000000; ">&nbsp;(IOException&nbsp;e)&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log.error(</span><span style="color: #000000; ">"</span><span style="color: #000000; ">close&nbsp;logindex&nbsp;file&nbsp;error:</span><span style="color: #000000; ">"</span><span style="color: #000000; ">,&nbsp;e);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;}</span></div><br />&nbsp;&nbsp;&nbsp; 利用反射，并且使用了sun特有的类，不具有可移植性。MappedByteBuffer还有一个问题是map的代价比较高，可能在切换文件的时候fqueue会有一定程度的阻塞现象。<br /><br />&nbsp;&nbsp;&nbsp; 存储的性能，我在我的机器测试了下，似乎没有作者宣称的那么高，我的机器是5400转的普通SATA盘，写入1K数据的平均QPS在8000左右。我估计fqueue的性能跟磁盘有很大关系，如果使用15000转的SAS盘应该能有很大改观。<br /><br />&nbsp;&nbsp;&nbsp; 网络层直接使用了jmemcached的实现，<a href="http://code.google.com/p/jmemcache-daemon/">jmemcached</a>是一个java实现的memcached，通常用于单元测试之类。看情况fqueue也支持memcached的二进制协议了。网络框架使用了netty3，这些就不多说了。自己看都明白。额外提一下，作者做的单元测试使用了<a href="http://code.google.com/p/xmemcached">xmemcached</a>，咔咔，广而告之。<br /><br />&nbsp;&nbsp;&nbsp; 总体来说fqueue是一个整体上很清爽和轻量级的MQ实现，适合一些特定的场景，至于性能，我们下周准备做个压测，到时候再谈吧。<img src ="http://www.blogjava.net/killme2008/aggbug/358827.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2011-09-16 20:10 <a href="http://www.blogjava.net/killme2008/archive/2011/09/16/358827.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>最近关注过的一些项目</title><link>http://www.blogjava.net/killme2008/archive/2011/01/20/343284.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Thu, 20 Jan 2011 15:23:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2011/01/20/343284.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/343284.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2011/01/20/343284.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/343284.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/343284.html</trackback:ping><description><![CDATA[<br />
&nbsp;&nbsp;&nbsp; 最近因为空闲时间有一些，所以去看了不少开源项目，大部分东西如果看过不记录下来，其实还是相当于没看，所以想想还是有必要摘要记录一下。<br />
<br />
&nbsp;&nbsp;&nbsp; 首先是去了解了<a href="http://hadoop.apache.org/zookeeper/">zookeeper</a>这个项目，基于paxos算法的分布式服务组件，同事对此有非常深入的研究和介绍，具体可以看我们的<a href="http://rdc.taobao.com/team/jm/archives/category/rpc-soa">团队Blog</a>。令我感慨的是这么一个非常难以理解的算法，却用一个简单的树状目录模型表达出来，并且在这个模型的基础上衍生出种种应用：集群感知、分布式锁、分布式队列、分布式并发原语等等，具体可以看文档给出的<a href="http://hadoop.apache.org/zookeeper/docs/r3.3.2/recipes.html">recipes</a>。在实现这些应用的时候，突出强调的是避免网络风暴，例如分布式锁的实现，竞争创建子节点，节点序列号最小的获取锁，其他节点等待，但是等待在什么条件上是有讲究的，如果所有节点都等待最小节点的删除事件，那么当最小节点释放锁的时候，就需要广播消息给所有其他等待的节点；换一个思路，如果每个等待节点只是等待比它序列号小的节点上，那么就可以避免这种广播风暴，变成一个顺序唤醒的过程。因此尽管有了zookeeper帮助实现分布式这些服务，但是要实现好仍然有一定难度，具体可以参考官方例子。我本来萌生了基于zookeeper实现一套封装好的类似j.u.c的服务框架，后来在邮件列表发现已经有人搞了这么一个基础类库放在github上:<a href="https://github.com/openUtility/menagerie">https://github.com/openUtility/menagerie</a> 。不过我没有继续深入了，有兴趣的朋友可以瞧瞧。<br />
<br />
&nbsp;&nbsp;&nbsp; 然后又去看了我们淘宝开源的<a title="TimeTunnel" href="http://code.taobao.org/project/view/411/">TimeTunnel</a>。TimeTunnel你可以理解成一个消息中间件，它整个设计跟我们的产品相当接近，但是两者的目的完全不同，tt强调的是高吞吐量，而notify强调的则是可靠性。TT的通讯层直接采用Facebook的thrift，并且利用zookeeper做集群管理和路由。TT的代码质量很好，有兴趣可以拉出来看一下，并且对zookeeper的应用也是一个典型的案例。TT在高可用性上的方案也很有特色，所有的服务器节点形成一个环，两两相互主辅备份，一个节点挂了，后续节点仍然可以提供服务直到主节点回来，有点类似一致性哈希的概念。节点的主从关系和顺序也是通过zookeeper保证。消息顺序的实现是通过称为router的路由到固定节点做传输，router默认是策略不是固定而是RR。TT的数据存储优先放在内存，并设置了一个内存状况监视的组件，当发现内存放不下的时候，swap到磁盘文件缓存，实现类似内存换页的功能。正常情况数据都应该在内存，当然如果可靠级别要求高的话可以先存磁盘再传输。TT目前仍然还是比较适合传输日志这样的文本增量数据，并且提供了TailFile这样的python脚本帮你做这个事情，这个脚本可以通过checkpoint做断点续传。在学习这个项目的时候，发现文档有很大问题，要么错误，要么遗漏，并且代码也不是最新的，我估计开源出来外面的人用的还不太多，希望慢慢能搞的更好一些。<br />
<br />
&nbsp;&nbsp;&nbsp; 跟TT类似，另一个追求高吞吐量的MQ是linkedin开源的<a title="kafka" href="http://sna-projects.com/kafka/">kafka</a>。Kafka就跟这个名字一样，设计非常独特。首先，kafka的开发者们认为不需要在内存里缓存什么数据，操作系统的文件缓存已经足够完善和强大，只要你不搞随机写，顺序读写的性能是非常高效的。kafka的数据只会顺序append，数据的删除策略是累积到一定程度或者超过一定时间再删除。Kafka另一个独特的地方是将消费者信息保存在客户端而不是MQ服务器，这样服务器就不用记录消息的投递过程，每个客户端都自己知道自己下一次应该从什么地方什么位置读取消息，消息的投递过程也是采用客户端主动pull的模型，这样大大减轻了服务器的负担。Kafka还强调减少数据的序列化和拷贝开销，它会将一些消息组织成Message Set做批量存储和发送，并且客户端在pull数据的时候，尽量以zero-copy的方式传输，利用sendfile（对应java里的FileChannel.transferTo/transferFrom）这样的高级IO函数来减少拷贝开销。可见，kafka是一个精心设计，特定于某些应用的MQ系统，这种偏向特定领域的MQ系统我估计会越来越多，垂直化的产品策略值的考虑。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 在此期间，我还重新去看了activemq和hornetq的存储实现，从实现上大家都大同小异，append log + data file的模式。Activemq采用异步队列写来提高吞吐量，而Hornetq干脆就直接利用JNI调用原生aio来实现高性能。在搜索Java的aio实现的时候，碰巧发现Mina的沙箱里有个aioj的实现，源码在：<a href="https://svn.apache.org/repos/asf/mina/sandbox/mheath/aioj/">https://svn.apache.org/repos/asf/mina/sandbox/mheath/aioj/</a> 。我测试了完全可用，也尝试改造我们的磁盘存储组件，可惜提升不多，估计不从整个设计上调整服务器，不大可能从aio上获益。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 最近也重新看起了clojure的一些开源项目，clojure的开源资源在github上也非常丰富，有待挖掘，下次有机会再尝试介绍一二。<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; <br />
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/343284.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2011-01-20 23:23 <a href="http://www.blogjava.net/killme2008/archive/2011/01/20/343284.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Selector.wakeup实现注记</title><link>http://www.blogjava.net/killme2008/archive/2010/10/22/335861.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Fri, 22 Oct 2010 02:35:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2010/10/22/335861.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/335861.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2010/10/22/335861.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/335861.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/335861.html</trackback:ping><description><![CDATA[<br />
&nbsp;&nbsp;&nbsp; NIO中的Selector封装了底层的系统调用，其中wakeup用于唤醒阻塞在select方法上的线程，它的实现很简单，在linux上就是创建一个管道并加入poll的fd集合，wakeup就是往管道里写一个字节，那么阻塞的poll方法有数据可读就立即返回。证明这一点很简单，strace即可知道：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;SelectorTest&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;main(String[]&nbsp;args)&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Exception&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Selector&nbsp;selector&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;Selector.open();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector.wakeup();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
&nbsp;&nbsp;&nbsp;&nbsp;
使用strace调用,只关心write的系统调用<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><span style="color: #000000;">sudo&nbsp;strace&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">f&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">e&nbsp;write&nbsp;java SelectorTest<br />
</span></div>
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 输出：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><span style="color: #000000;">Process&nbsp;</span><span style="color: #000000;">29181</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29182</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29183</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29184</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29185</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29186</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29187</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29188</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29189</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29190</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29191</span><span style="color: #000000;">&nbsp;attached<br />
[pid&nbsp;</span><span style="color: #000000;">29181</span><span style="color: #000000;">]&nbsp;write(</span><span style="color: #000000;">36</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">\1</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;"><br />
Process&nbsp;</span><span style="color: #000000;">29191</span><span style="color: #000000;">&nbsp;detached<br />
Process&nbsp;</span><span style="color: #000000;">29184</span><span style="color: #000000;">&nbsp;detached<br />
Process&nbsp;</span><span style="color: #000000;">29181</span><span style="color: #000000;">&nbsp;detached<br />
</span></div>
<br />
&nbsp;&nbsp;&nbsp; 有的同学说了，怎么证明这个write是wakeup方法调用的，而不是其他方法呢，这个很好证明，我们多调用几次：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;SelectorTest&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;main(String[]&nbsp;args)&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Exception&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Selector&nbsp;selector&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;Selector.open();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector.wakeup();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector.selectNow();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector.wakeup();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector.selectNow();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector.wakeup();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
<br />
&nbsp;&nbsp;&nbsp; 修改程序调用三次wakeup，心细的朋友肯定注意到我们还调用了两次selectNow，这是因为在两次成功的select方法之间调用wakeup多次都只算做一次，为了显示3次write，这里就每次调用前select一下将前一次写入的字节读到，同样执行上面的strace调用，输出：<br />
<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><span style="color: #000000;">Process&nbsp;</span><span style="color: #000000;">29303</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29304</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29305</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29306</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29307</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29308</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29309</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29310</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29311</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29312</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29313</span><span style="color: #000000;">&nbsp;attached<br />
[pid&nbsp;</span><span style="color: #000000;">29303</span><span style="color: #000000;">]&nbsp;write(</span><span style="color: #000000;">36</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">\1</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;"><br />
[pid&nbsp;</span><span style="color: #000000;">29303</span><span style="color: #000000;">]&nbsp;write(</span><span style="color: #000000;">36</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">\1</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;"><br />
[pid&nbsp;</span><span style="color: #000000;">29303</span><span style="color: #000000;">]&nbsp;write(</span><span style="color: #000000;">36</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">\1</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;"><br />
Process&nbsp;</span><span style="color: #000000;">29313</span><span style="color: #000000;">&nbsp;detached<br />
Process&nbsp;</span><span style="color: #000000;">29309</span><span style="color: #000000;">&nbsp;detached<br />
Process&nbsp;</span><span style="color: #000000;">29306</span><span style="color: #000000;">&nbsp;detached<br />
Process&nbsp;</span><span style="color: #000000;">29303</span><span style="color: #000000;">&nbsp;detached<br />
</span></div>
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 果然是3次write的系统调用，都是写入一个字节，如果我们去掉selectNow，那么三次wakeup还是等于一次：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;SelectorTest&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;main(String[]&nbsp;args)&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Exception&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Selector&nbsp;selector&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;Selector.open();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector.wakeup();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector.wakeup();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;selector.wakeup();<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}<br />
</span></div>
&nbsp; <br />
&nbsp;&nbsp; 输出：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><span style="color: #000000;">Process&nbsp;</span><span style="color: #000000;">29331</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29332</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29333</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29334</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29335</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29336</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29337</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29338</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29339</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29340</span><span style="color: #000000;">&nbsp;attached<br />
Process&nbsp;</span><span style="color: #000000;">29341</span><span style="color: #000000;">&nbsp;attached<br />
[pid&nbsp;</span><span style="color: #000000;">29331</span><span style="color: #000000;">]&nbsp;write(</span><span style="color: #000000;">36</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">\1</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;"><br />
Process&nbsp;</span><span style="color: #000000;">29341</span><span style="color: #000000;">&nbsp;detached<br />
Process&nbsp;</span><span style="color: #000000;">29337</span><span style="color: #000000;">&nbsp;detached<br />
Process&nbsp;</span><span style="color: #000000;">29334</span><span style="color: #000000;">&nbsp;detached<br />
Process&nbsp;</span><span style="color: #000000;">29331</span><span style="color: #000000;">&nbsp;detached<br />
</span></div>
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; wakeup方法的API说明没有欺骗我们。wakeup方法的API还告诉我们，如果当前Selector没有阻塞在select方法上，那么本次wakeup调用会在下一次select阻塞的时候生效，这个道理很简单，wakeup方法写入一个字节，下次poll等待的时候立即发现可读并返回，因此不会阻塞。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 具体到源码级别，在linux平台上的wakeup方法其实调用了pipe创建了管道，wakeup调用了<span id="cls"><a title="EPollArrayWrapper" href="http://www.docjar.com/docs/api/sun/nio/ch/EPollArrayWrapper.html">EPollArrayWrapper</a>的</span>interrupt方法：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;interrupt()&nbsp;<br />
<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;interrupt(outgoingInterruptFD);<br />
}</span></div>
<br />
&nbsp;&nbsp;&nbsp; 实际调用的是interrupt(fd)的native方法，查看EPollArrayWrapper.c可见清晰的write系统调用：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><span style="color: #000000;"><br />
JNIEXPORT&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;JNICALL<br />
Java_sun_nio_ch_EPollArrayWrapper_interrupt(JNIEnv&nbsp;</span><span style="color: #000000;">*</span><span style="color: #000000;">env,&nbsp;jobject&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">,&nbsp;jint&nbsp;fd)<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;fakebuf[</span><span style="color: #000000;">1</span><span style="color: #000000;">];<br />
&nbsp;&nbsp;&nbsp;&nbsp;fakebuf[</span><span style="color: #000000;">0</span><span style="color: #000000;">]&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(write(fd,&nbsp;fakebuf,&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">)&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JNU_ThrowIOExceptionWithLastError(env,</span><span style="color: #000000;">"</span><span style="color: #000000;">write&nbsp;to&nbsp;interrupt&nbsp;fd&nbsp;failed</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
}</span></div>
&nbsp;&nbsp;&nbsp; 写入一个字节的fakebuf。有朋友问起这个问题，写个注记在此。strace充分利用对了解这些细节很有帮助。<br />
&nbsp; <br />
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/335861.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2010-10-22 10:35 <a href="http://www.blogjava.net/killme2008/archive/2010/10/22/335861.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Kilim实现浅析（一）</title><link>http://www.blogjava.net/killme2008/archive/2010/09/17/332281.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Fri, 17 Sep 2010 04:05:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2010/09/17/332281.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/332281.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2010/09/17/332281.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/332281.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/332281.html</trackback:ping><description><![CDATA[<br />
&nbsp;&nbsp;&nbsp; Kilim是一个Java的actor框架，让你可以在JVM里使用基于协程的actor模型，bluedavy曾经介绍过，这里不再赘言。这篇blog的目的在于分析下kilim实现的基本原理，看看怎么在JVM上实现协程。<br />
<br />
&nbsp;&nbsp;&nbsp; 在一些语言层面上支持协程的语言，如lua、ruby，都是直接在VM级别支持协程，VM帮你做context的保存和恢复。JVM没有提供这样的指令来保存和恢复方法栈的状态，因此kilim的实现还是需要在bytecode级别做文章。首先，试想下，如果是你来实现协程，你会怎么做？协程的两个基本原语resume和yield，resume运行协程，yield让出执行权，下次resume的时候会从yield的地方重新执行，并且context保持不变。可见，你需要做这么几个事情：<br />
1、在yield的时候保存当前context。<br />
2、在resume的时候恢复context，并根据pc计数来决定从哪里恢复执行。<br />
3、半协程的实现来说，还需要一个调度器来调度所有协程。<br />
4、为了做到用户代码透明，可能需要某种手段去修改用户代码，自动帮你做上面三个事情。<br />
<br />
&nbsp;&nbsp;&nbsp; kilim的实现就是干了这么几个事情：<br />
1、利用字节码增强，将普通的java代码转换为支持协程的代码。<br />
2、在调用pausable方法的时候，如果pause了就保存当前方法栈的State，停止执行当前协程，将控制权交给调度器<br />
3、调度器负责调度就绪的协程<br />
4、协程resume的时候，自动恢复State，根据协程的pc计数跳转到上次执行的位置，继续执行。<br />
<br />
<br />
&nbsp;&nbsp;&nbsp; 下面是来自kilim文档的一个例子，同一段代码，在字节码增前前后的变化：<br />
<img alt="" src="http://www.blogjava.net/images/blogjava_net/killme2008/kilim.png" height="619" width="833" /><br />
&nbsp;&nbsp;&nbsp; <br />
左边是原始代码，右边是通过字节码增强后的代码。其中h方法是pausable的，也就是说可能被暂停阻塞的，g方法因为调用了h这个方法也变成了pausable。<br />
<br />
首先看，原始的h方法是没有传入任何参数的，增强后的代码，多了个参数Fiber，指向当前的协程，同样，g方法本来只有一个参数n，现在在后面也多了个Fiber类型的参数，同样是指向当前执行的协程。<br />
<br />
其次，在原始的g方法里，一旦调用，马上进入一个for循环。但是在增强后的代码，多了个switch派发的过程，这就是前面提到的，根据当前的Fiber的pc计数，跳转到上一次执行的地方执行。如果是第一次resume，也就是启动协程，那么就将初始循环的i设置为0，进入原始代码的循环部分。Fiber有一个pc计数，称为程序计数器，用于指向恢复context的时候需要跳转到位置。<br />
<br />
第三，在g方法里调用h这个可被暂停阻塞的方法的时候，在h方法前后多了一些调用:<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">f.down();<br />
h(f);<br />
f.up();</span></div>
<br />
kilim的Fiber将每个pauseable方法的调用组织成一个栈，每个pauseable方法都有一个activation frame，翻译过来可以称为活动栈帧，这个栈帧记录了当前的栈的State，注意这个栈跟java本身的方法调用栈区分开来，一个是VM层面的，一个是kilim框架层面的。这里的down方法就是将栈向下延伸，表示将调用一个pauseable方法，并且设置当前State和pc计数。<br />
调用了down之后，才是调用实际的h方法，最后还要调用一次up，顾名思义，就是说一次pauseable方法调用完成，fiber的活动栈要递增一层，回到上一层。但是h方法调用可能出现四种情况：<br />
1、正常的顺利返回，没有状态需要恢复，所谓NOT_PAUSING__NO_STATE<br />
2、也是正常返回，有状态需要恢复，也就是NOT_PAUSING__HAS_STATE<br />
3、h方法暂停阻塞，当前没有保存状态，需要保存状态，这是第一次暂停的时候，称为PAUSING__NO_STATE<br />
4、h方法暂停阻塞，当前已经有状态，不需要保存状态，这是第一次暂停之后的resume再次暂停,称为PAUSING__HAS_STATE，通常不需要处理什么。<br />
<br />
第四，可以看到，在up之后，就要根据up返回的上述4种状态执行不同的逻辑：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(f.isPausing){<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">第一次暂停，没有状态</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #000000;">!</span><span style="color: #000000;">f.hasState){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">new一个State_I2，并保存i和n</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f.state&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;State_I2(i,n);<br />
&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">记录pc，还记的前面的switch吗？</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;f.pc&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;H1;<br />
&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br />
}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(f.hasState)<br />
&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">正常返回，有状态需要恢复，恢复i和n</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;State_I2&nbsp;st&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(State_I2)&nbsp;f.state;<br />
&nbsp;&nbsp;&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;st.i1;&nbsp;n&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;st.i2;<br />
}</span></div>
<br />
<br />
这里没有处理NOT_PAUSING__NO_STATE和PAUSING__HAS_STATE，因为这两种情况在这里不需要处理。<br />
<br />
<br />
&nbsp;&nbsp;&nbsp; 通过上面的分析，我想大家对kilim的实现应该已经有一个很基本的认识。下一步，我们分析一个实际的代码例子，查看整个运作流程。<br />
&nbsp;
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/332281.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2010-09-17 12:05 <a href="http://www.blogjava.net/killme2008/archive/2010/09/17/332281.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>LinkedList的局限</title><link>http://www.blogjava.net/killme2008/archive/2010/09/16/332168.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Thu, 16 Sep 2010 05:51:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2010/09/16/332168.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/332168.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2010/09/16/332168.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/332168.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/332168.html</trackback:ping><description><![CDATA[<br />
&nbsp;&nbsp;&nbsp; java.util.LinkedList是双向链表，这个大家都知道，比如Java的基础面试题喜欢问ArrayList和LinkedList的区别，在什么场景下用。大家都会说LinkedList随机增删多的场景比较合适，而ArrayList的随机访问多的场景比较合适。更进一步，我有时候会问，LinkedList.remove(Object)方法的时间复杂度是什么？有的人回答对了，有的人回答错了。回答错的应该是没有读过源码。<br />
<br />
&nbsp;&nbsp;&nbsp; 理论上说，双向链表的删除的时间复杂度是O(1)，你只需要将要删除的节点的前节点和后节点相连，然后将要删除的节点的前节点和后节点置为null即可，<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #008000;">//</span><span style="color: #008000;">伪代码</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">node.prev.next</span><span style="color: #000000;">=</span><span style="color: #000000;">node.next;<br />
node.next.prev</span><span style="color: #000000;">=</span><span style="color: #000000;">node.prev;<br />
node.prev</span><span style="color: #000000;">=</span><span style="color: #000000;">node.next</span><span style="color: #000000;">=</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;</span></div>
<br />
这个操作的时间复杂度可以认为是O(1)级别的。但是LinkedList的实现是一个通用的数据结构，因此没有暴露内部的节点Entry对象，remove(Object)传入的Object其实是节点存储的value，这里还需要一个查找过程：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;remove(Object&nbsp;o)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(o</span><span style="color: #000000;">==</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(Entry</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">E</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;e&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;header.next;&nbsp;e&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;header;&nbsp;e&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;e.next)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(e.element</span><span style="color: #000000;">==</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remove(e);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">查找节点Entry</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(Entry</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">E</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;e&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;header.next;&nbsp;e&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;header;&nbsp;e&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;e.next)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(o.equals(e.element))&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">删除节点</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remove(e);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br />
<br />
&nbsp;&nbsp;&nbsp; 删除节点的操作就是刚才伪代码描述的：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;E&nbsp;remove(Entry</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">E</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;E&nbsp;result&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;e.element;<br />
&nbsp;&nbsp;&nbsp;&nbsp;e.previous.next&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;e.next;<br />
&nbsp;&nbsp;&nbsp;&nbsp;e.next.previous&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;e.previous;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.next&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;e.previous&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.element&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;size</span><span style="color: #000000;">--</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;modCount</span><span style="color: #000000;">++</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;result;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br />
&nbsp;&nbsp;&nbsp; 因此，显然，LinkedList.remove(Object)方法的时间复杂度是O(n)+O(1)，结果仍然是O(n)的时间复杂度,而非推测的O(1)复杂度。最坏情况下要删除的元素是最后一个，你都要比较N-1次才能找到要删除的元素。<br />
<br />
&nbsp;&nbsp;&nbsp; 既然如此，说LinkedList适合随机删减有个前提，链表的大小不能太大，如果链表元素非常多，调用remove(Object)去删除一个元素的效率肯定有影响，一个简单测试，插入100万数据，随机删除1000个元素：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;List</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Integer</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;list&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;"> LinkedList</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Integer</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;count&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1000000</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;count;&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.add(i);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;Random&nbsp;rand</span><span style="color: #000000;">=</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Random();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;start</span><span style="color: #000000;">=</span><span style="color: #000000;">System.nanoTime();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i</span><span style="color: #000000;">=</span><span style="color: #000000;">0</span><span style="color: #000000;">;i</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">1000</span><span style="color: #000000;">;i</span><span style="color: #000000;">++</span><span style="color: #000000;">){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">这里要强制转型为Integer，否则调用的是remove(int)</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.remove((Integer)rand.nextInt(count));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println((System.nanoTime()</span><span style="color: #000000;">-</span><span style="color: #000000;">start)</span><span style="color: #000000;">/</span><span style="color: #000000;">Math.pow(</span><span style="color: #000000;">10</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">9</span><span style="color: #000000;">));</span></div>
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 在我的机器上耗时近9.5秒，删除1000个元素耗时9.5秒，是不是很恐怖？注意到上面的注释，产生的随机数强制转为Integer对象，否则调用的是remove(int)方法，而非remove(Object)。如果我们调用remove(int)根据索引来删除:<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i</span><span style="color: #000000;">=</span><span style="color: #000000;">0</span><span style="color: #000000;">;i</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">1000</span><span style="color: #000000;">;i</span><span style="color: #000000;">++</span><span style="color: #000000;">){<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;list.remove(rand.nextInt(list.size()</span><span style="color: #000000;">-</span><span style="color: #000000;">1</span><span style="color: #000000;">));<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
&nbsp;&nbsp;&nbsp; 随机数范围要递减，防止数组越界，换成remove(int)效率提高不少，但是仍然需要2.2秒左右（包括了随机数产生开销）。这是因为remove(int)的实现很有技巧，它首先判断索引位置在链表的前半部分还是后半部分，如果是前半部分则从head往前查找，如果在后半部分，则从head往后查找（LinkedList的实现是一个环）：<br />
<div style="background-color: #eeeeee; font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Entry</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">E</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;e&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;header;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(index&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;(size&nbsp;</span><span style="color: #000000;">&gt;&gt;</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">))&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">前一半，往前找</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;=</span><span style="color: #000000;">&nbsp;index;&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;e.next;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">后一半，往后找</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;size;&nbsp;i&nbsp;</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;index;&nbsp;i</span><span style="color: #000000;">--</span><span style="color: #000000;">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;e.previous; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 最坏情况下要删除的节点在中点左右，查找的次数仍然达到n/2次，但是注意到这里没有比较的开销，并且比remove(Object)最坏情况下n次查找还是好很多。<br />
<br />
&nbsp;&nbsp;&nbsp; 总结下，LinkedList的两个remove方法，remove(Object)和remove(int)的时间复杂度都是O(n)，在链表元素很多并且没有索引可用的情况下，LinkedList也并不适合做随机增删元素。在对性能特别敏感的场景下，还是需要自己实现专用的双向链表结构，真正实现O(1)级别的随机增删。更进一步，jdk5引入的ConcurrentLinkedQueue是一个非阻塞的线程安全的双向队列实现，同样有本文提到的问题，有兴趣可以测试一下在大量元素情况下的并发随机增删，效率跟自己实现的特定类型的线程安全的链表差距是惊人的。<br />
<br />
&nbsp;&nbsp;&nbsp; 题外，ArrayList比LinkedList更不适合随机增删的原因是多了一个数组移动的动作，假设你删除的元素在m，那么除了要查找m次之外，还需要往前移动n-m-1个元素。<br />
<br />
<br />
&nbsp;&nbsp;&nbsp;
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/332168.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2010-09-16 13:51 <a href="http://www.blogjava.net/killme2008/archive/2010/09/16/332168.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Rabbitmq的网络层浅析</title><link>http://www.blogjava.net/killme2008/archive/2009/11/29/304079.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Sun, 29 Nov 2009 04:00:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2009/11/29/304079.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/304079.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2009/11/29/304079.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/304079.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/304079.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 最近在锋爷的建议下开始读rabbitmq的源码，锋爷说这个项目已经很成熟，并且代码也很有借鉴和学习的意义，在自己写erlang代码之前看看别人是怎么写的，可以少走弯路，避免养成一些不好的习惯，学习一些最佳实践。读了一个星期，这个项目果然非常棒，代码也写的非常清晰易懂，一些细节的处理上非常巧妙，比如我这里想分享的网络层一节。<br />
&nbsp;&nbsp;&nbsp; Rabbitmq是一个MQ系统，也就是消息中间件，它实现了AMQP 0.8规范，简单来说就是一个TCP的广播服务器。AMQP协议，你可以类比JMS，不过JMS仅仅是java领域内的API规范，而AMQP比JMS更进一步，它有自己的wire-level protocol，有一套可编程的协议，中立于语言。简单介绍了Rabbitmq之后，进入正题。<br />
&nbsp;&nbsp;&nbsp; Rabbitmq充分利用了Erlang的分布式、高可靠性、并发等特性，首先看它的一个结构图：<br />
<div id="yv_." style="text-align: left;"><img style="width: 648px; height: 486px;" src="http://docs.google.com/File?id=ddxkntwd_135zpwntjg2_b" alt="" /><br />
<br />
这张图展现了Rabbitmq的主要组件和组件之间的关系，具体到监控树的结构，我画了一张图：<br />
<br />
<div id="kahs" style="text-align: left;"><img style="width: 645px; height: 470px;" src="http://docs.google.com/File?id=ddxkntwd_131d9bnbtcm_b" alt="" /><br />
<br />
<br />
</div>
<br />
</div>
<br />
<br />
&nbsp;&nbsp;&nbsp; <strong>顶层是rabbit_sup
supervisor，它至少有两个子进程，一个是rabbit_tcp_client_sup，用来监控每个connection的处理进程
rabbit_reader的supervisor;rabbit_tcp_listener_sup是监控tcp_listener和
tcp_acceptor_sup的supervisor，tcp_listener里启动tcp服务器，监听端口，并且通过tcp_acceptor_sup启动N个tcp_accetpor，tcp_acceptor发起accept请求，等待客户端连接;tcp_acceptor_sup负责监控这些acceptor。这张图已经能给你一个大体的印象。</strong><br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 讲完大概，进入细节，说说几个我觉的值的注意的地方：<br />
1、<strong>tcp_accepto.erl,r对于accept采用的是异步方式</strong>，利用<strong>prim_inet:async_accept/2</strong>方
法，此模块没有被文档化，是otp库内部使用,通常来说没必要使用这一模块，gen_tcp:accept/1已经足够，不过rabbitmq是广播程
序，因此采用了异步方式。使用async_accept，需要打patch，以使得socket好像我们从gen_tcp:accept/1得到的一样：<br />
<br />
handle_info({inet_async, LSock, Ref, {ok, Sock}},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; State = #state{callback={M,F,A}, sock=LSock, ref=Ref}) -&gt;<br />
&nbsp;&nbsp;&nbsp; %%这里做了patch<br />
&nbsp;&nbsp;&nbsp; %% patch up the socket so it looks like one we got from<br />
&nbsp;&nbsp;&nbsp; %% gen_tcp:accept/1 <br />
&nbsp;&nbsp;&nbsp; <strong>{ok, Mod} = inet_db:lookup_socket(LSock),<br />
&nbsp;&nbsp;&nbsp; inet_db:register_socket(Sock, Mod),<br />
</strong><br />
&nbsp;&nbsp;&nbsp; try<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %% report<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {Address, Port}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = inet_op(fun () -&gt; inet:sockname(LSock) end),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {PeerAddress, PeerPort} = inet_op(fun () -&gt; inet:peername(Sock) end),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error_logger:info_msg("accepted TCP connection on ~s:~p from ~s:~p~n",<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [inet_parse:ntoa(Address), Port,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inet_parse:ntoa(PeerAddress), PeerPort]),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %% 调用回调模块，将Sock作为附加参数<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; apply(M, F, A ++ [Sock])<br />
&nbsp;&nbsp;&nbsp; catch {inet_error, Reason} -&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gen_tcp:close(Sock),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error_logger:error_msg("unable to accept TCP connection: ~p~n",<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Reason])<br />
&nbsp;&nbsp;&nbsp; end,<br />
<br />
&nbsp;&nbsp;&nbsp; %% 继续发起异步调用<br />
&nbsp;&nbsp;&nbsp; case prim_inet:async_accept(LSock, -1) of<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {ok, NRef} -&gt; {noreply, State#state{ref=NRef}};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Error -&gt; {stop, {cannot_accept, Error}, none}<br />
&nbsp;&nbsp;&nbsp; end;<br />
%%处理错误情况<br />
handle_info({inet_async, LSock, Ref, {error, closed}},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; State=#state{sock=LSock, ref=Ref}) -&gt;<br />
&nbsp;&nbsp;&nbsp; %% It would be wrong to attempt to restart the acceptor when we<br />
&nbsp;&nbsp;&nbsp; %% know this will fail.<br />
&nbsp;&nbsp;&nbsp; {stop, normal, State};<br />
<br />
2、<strong>rabbitmq内部是使用了多个并发acceptor</strong>，这在高并发下、大量连接情况下有效率优势，<strong>类似java现在的nio框架采用多个reactor类似</strong>，查看tcp_listener.erl:<br />
<br />
init({IPAddress, Port, SocketOpts,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ConcurrentAcceptorCount, AcceptorSup,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {M,F,A} = OnStartup, OnShutdown, Label}) -&gt;<br />
&nbsp;&nbsp;&nbsp; process_flag(trap_exit, true),<br />
&nbsp;&nbsp;&nbsp; case gen_tcp:listen(Port, SocketOpts ++ [{ip, IPAddress},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {active, false}]) of<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {ok, LSock} -&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %%创建ConcurrentAcceptorCount个并发acceptor<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<strong> lists:foreach(fun (_) -&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {ok, _APid} = supervisor:start_child(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AcceptorSup, [LSock])<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lists:duplicate(ConcurrentAcceptorCount, dummy)),</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {ok, {LIPAddress, LPort}} = inet:sockname(LSock),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error_logger:info_msg("started ~s on ~s:~p~n",<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Label, inet_parse:ntoa(LIPAddress), LPort]),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %%调用初始化回调函数<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; apply(M, F, A ++ [IPAddress, Port]),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {ok, #state{sock = LSock,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; on_startup = OnStartup, on_shutdown = OnShutdown, <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; label = Label}};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {error, Reason} -&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; error_logger:error_msg(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "failed to start ~s on ~s:~p - ~p~n",<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Label, inet_parse:ntoa(IPAddress), Port, Reason]),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {stop, {cannot_listen, IPAddress, Port, Reason}}<br />
&nbsp;&nbsp;&nbsp; end.<br />
<br />
这里有一个技巧，如果要循环N次执行某个函数F，可以通过lists:foreach结合lists:duplicate(N,dummy)来处理。<br />
<br />
<strong>lists:foreach(fun(_)-&gt; F() end,lists:duplicate(N,dummy)).</strong><br />
<br />
3、<strong>simple_one_for_one策略的使用</strong>，可以看到对于tcp_client_sup和tcp_acceptor_sup都采用了simple_one_for_one策略，而非普通的one_fo_one，这是为什么呢？<br />
这牵扯到simple_one_for_one的几个特点：<br />
<strong>1)simple_one_for_one内部保存child是使用dict，而其他策略是使用list</strong>，因此simple_one_for_one更适合child频繁创建销毁、需要大量child进程的情况，具体来说例如网络连接的频繁接入断开。<br />
<strong>2)使用了simple_one_for_one后，无法调用terminate_child/2 delete_child/2 restart_child/2 <br />
<br />
</strong>3)start_child/2
对于simple_one_for_one来说，不必传入完整的child
spect，传入参数list，会自动进行<strong>参数合并</strong>。<strong>在一个地方定义好child
spec之后，其他地方只要start_child传入参数即可启动child进程，简化child都是同一类型进程情况下的编程</strong>。<br />
<br />
在
rabbitmq中，tcp_acceptor_sup的子进程都是tcp_acceptor进程，在tcp_listener中是启动了
ConcurrentAcceptorCount个tcp_acceptor子进程，通过supervisor:start_child/2方法：<br />
<br />
%%创建ConcurrentAcceptorCount个并发acceptor<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lists:foreach(fun (_) -&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {ok, _APid} = <strong>supervisor:start_child(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AcceptorSup, [</strong><strong>LSock])</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lists:duplicate(ConcurrentAcceptorCount, dummy)),<br />
<br />
注意到，这里调用的start_child只传入了<strong>LSock</strong>一个参数，另一个参数CallBack是在定义child spec的时候传入的，参见tcp_acceptor_sup.erl:<br />
init(Callback) -&gt;<br />
&nbsp;&nbsp;&nbsp; {ok, {{simple_one_for_one, 10, 10},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [{tcp_acceptor, {tcp_acceptor, start_link, [<strong>Callback</strong>]},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; transient, brutal_kill, worker, [tcp_acceptor]}]}}.<br />
<br />
Erlang内部自动为simple_one_for_one做了<strong>参数合并</strong>，最后调用的是tcp_acceptor的init/2:<br />
<br />
init({<strong>Callback</strong>, <strong>LSock</strong>}) -&gt;<br />
&nbsp;&nbsp;&nbsp; case prim_inet:async_accept(LSock, -1) of<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {ok, Ref} -&gt; {ok, #state{callback=Callback, sock=LSock, ref=Ref}};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Error -&gt; {stop, {cannot_accept, Error}}<br />
&nbsp;&nbsp;&nbsp; end.<br />
<br />
对于tcp_client_sup的情况类似，tcp_client_sup监控的子进程都是rabbit_reader类型，在
rabbit_networking.erl中启动tcp_listenner传入的处理connect事件的回调方法是是
rabbit_networking:start_client/1:<br />
<br />
start_tcp_listener(Host, Port) -&gt;<br />
&nbsp;&nbsp;&nbsp; start_listener(Host, Port, "TCP Listener",<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %回调的MFA<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<strong>?MODULE, start_client, []</strong>}).<br />
<br />
start_client(Sock) -&gt;<br />
&nbsp;&nbsp;&nbsp; {ok, Child} = supervisor:start_child(rabbit_tcp_client_sup, []),<br />
&nbsp;&nbsp;&nbsp; ok = rabbit_net:controlling_process(Sock, Child),<br />
&nbsp;&nbsp;&nbsp; Child ! {go, Sock},<br />
&nbsp;&nbsp;&nbsp; Child.<br />
<br />
start_client调用了supervisor:start_child/2来动态启动rabbit_reader进程。<br />
<br />
4、<strong>协议的解析，消息的读取</strong>这部分也非常巧妙，这一部分主要在rabbit_reader.erl中，对于协议的解析没有采用gen_fsm，而是实现了一个巧妙的状态机机制,核心代码在mainloop/4中：<br />
%启动一个连接<br />
start_connection(Parent, Deb, ClientSock) -&gt;<br />
&nbsp;&nbsp;&nbsp; process_flag(trap_exit, true),<br />
&nbsp;&nbsp;&nbsp; {PeerAddressS, PeerPort} = peername(ClientSock),<br />
&nbsp;&nbsp;&nbsp; ProfilingValue = setup_profiling(),<br />
&nbsp;&nbsp;&nbsp; try <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rabbit_log:info("starting TCP connection ~p from ~s:~p~n",<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [self(), PeerAddressS, PeerPort]),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %延时发送握手协议<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a title="" href="http://www.erlang.org">Erlang</a>:send_after(?HANDSHAKE_TIMEOUT * 1000, self(),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; handshake_timeout),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %进入主循环，更换callback模块，魔法就在这个switch_callback<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mainloop(Parent, Deb, switch_callback(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #v1{sock = ClientSock,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; connection = #connection{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; user = none,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timeout_sec = ?HANDSHAKE_TIMEOUT,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame_max = ?FRAME_MIN_SIZE,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; vhost = none},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; callback = uninitialized_callback,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; recv_ref = none,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; connection_state = pre_init},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>%%注意到这里，handshake就是我们的回调模块，8就是希望接收的数据长度，AMQP协议头的八个字节。</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; handshake, 8))<br />
<br />
魔法就在switch_callback这个方法上：<br />
<strong>switch_callback(OldState, NewCallback, Length) -&gt;<br />
&nbsp;&nbsp;&nbsp; %发起一个异步recv请求，请求Length字节的数据<br />
&nbsp;&nbsp;&nbsp; Ref = inet_op(fun () -&gt; rabbit_net:async_recv(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; OldState#v1.sock, Length, infinity) end),<br />
&nbsp;&nbsp;&nbsp; %更新状态，替换ref和处理模块<br />
&nbsp;&nbsp;&nbsp; OldState#v1{callback = NewCallback,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; recv_ref = Ref}.</strong><br />
<br />
异步接收Length个数据，如果有，erlang会通知你处理。处理模块是什么概念呢？其实就是一个状态的概念，表示当前协议解析进行到哪一步，起一个label的作用，看看mainloop/4中的应用：<br />
<br />
mainloop(Parent, Deb, State = #v1{sock= Sock, recv_ref = Ref}) -&gt;<br />
&nbsp;&nbsp;&nbsp; %%?LOGDEBUG("Reader mainloop: ~p bytes available, need ~p~n", [HaveBytes, WaitUntilNBytes]),<br />
&nbsp;&nbsp;&nbsp; receive<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %%接收到数据，交给handle_input处理,注意handle_input的第一个参数就是callback<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {inet_async, Sock, Ref, {ok, Data}} -&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %handle_input处理<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {State1, Callback1, Length1} =<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <strong>handle_input(State#v1.callback, Data,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; State#v1{recv_ref = none}),</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %更新回调模块，再次发起异步请求，并进入主循环<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mainloop(Parent, Deb,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; switch_callback(State1, Callback1, Length1));<br />
<br />
<br />
handle_input有多个分支，每个分支都对应一个处理模块，例如我们刚才提到的握手协议：<br />
<br />
%handshake模块，注意到第一个参数，第二个参数就是我们得到的数据<br />
handle_input(<strong>handshake</strong>, &lt;&lt;"AMQP",1,1,ProtocolMajor,ProtocolMinor&gt;&gt;,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; State = #v1{sock = Sock, connection = Connection}) -&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; %检测协议是否兼容<br />
&nbsp;&nbsp;&nbsp; case check_version({ProtocolMajor, ProtocolMinor},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {?PROTOCOL_VERSION_MAJOR, ?PROTOCOL_VERSION_MINOR}) of<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; true -&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {ok, Product} = application:get_key(id),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {ok, Version} = application:get_key(vsn),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %兼容的话，进入connections start，协商参数<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ok = send_on_channel0(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sock,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #'connection.start'{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; version_major = ?PROTOCOL_VERSION_MAJOR,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; version_minor = ?PROTOCOL_VERSION_MINOR,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; server_properties =<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [{list_to_binary(K), longstr, list_to_binary(V)} ||<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {K, V} &lt;-<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [{"product",&nbsp;&nbsp;&nbsp;&nbsp; Product},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {"version",&nbsp;&nbsp;&nbsp;&nbsp; Version},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {"platform",&nbsp;&nbsp;&nbsp; "<a title="" href="http://www.erlang.org">Erlang</a>/OTP"},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {"copyright",&nbsp;&nbsp; ?COPYRIGHT_MESSAGE},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {"information", ?INFORMATION_MESSAGE}]],<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mechanisms = &lt;&lt;"PLAIN AMQPLAIN"&gt;&gt;,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; locales = &lt;&lt;"en_US"&gt;&gt; }),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {State#v1{connection = Connection#connection{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; timeout_sec = ?NORMAL_TIMEOUT},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; connection_state = starting},<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; frame_header, 7};<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; %否则，断开连接，返回可以接受的协议<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; false -&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw({bad_version, ProtocolMajor, ProtocolMinor})<br />
&nbsp;&nbsp;&nbsp; end;<br />
<br />
&nbsp;&nbsp;&nbsp; 其他协议的处理也是类似，通过动态替换callback的方式来模拟状态机做协议的解析和数据的接收，真的很巧妙！让我们体会到Erlang的魅力，FP的魅力。<br />
<br />
<strong>5、序列图：</strong><br />
1）tcp server的启动过程：<br />
<div id="kauz" style="text-align: left;"><img style="width: 648px; height: 491.195px;" src="http://docs.google.com/File?id=ddxkntwd_134f36cc8hm_b" alt="" /></div>
<br />
2）一个client连接上来的处理过程：<br />
<div id="d.1n" style="text-align: left;"><img style="width: 648px; height: 750.115px;" src="http://docs.google.com/File?id=ddxkntwd_133gn88c5cs_b" alt="" /></div>
<br />
<br />
&nbsp;&nbsp;&nbsp;
小结：从上面的分析可以看出,rabbitmq的网络层是非常健壮和高效的，通过层层监控，对每个可能出现的风险点都做了考虑，并且利用了prim_net模块做异步IO处理。分层也是很清晰，将业务处理模块隔离到client_sup监控下的子进程，将网络处理细节和业务逻辑分离。在协议的解析和业务处理上虽然没有采用gen_fsm，但是也实现了一套类似的状态机机制，通过动态替换Callback来模拟状态的变迁，非常巧妙。如果你要实现一个tcp server，强烈推荐从rabbitmq中扣出这个网络层，你只需要实现自己的业务处理模块即可拥有一个高效、健壮、分层清晰的TCP服务器。<br /><img src ="http://www.blogjava.net/killme2008/aggbug/304079.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2009-11-29 12:00 <a href="http://www.blogjava.net/killme2008/archive/2009/11/29/304079.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java nio的一个严重BUG</title><link>http://www.blogjava.net/killme2008/archive/2009/09/28/296826.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Mon, 28 Sep 2009 11:27:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2009/09/28/296826.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/296826.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2009/09/28/296826.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/296826.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/296826.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 这个BUG会在linux上导致cpu 100%，使得nio server/client不可用，具体的详情可以看这里<a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933">http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933</a> 。令人失望的是这个BUG直到jdk 6u4才解决，sun的拖沓让人难以相信。这个BUG在server端容易出现，因为server端有频繁地接入断开连接。<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; 使用jdk 6u4之前版本的nio框架都有这个隐患，除非你的框架很好地处理了这个可能的隐患。Grizzly的处理方式比较简单，也就是BUG报告里面提到的方式，在SelectionKey.cancel()之后马上进行了一次select调用将fd从poll(epoll)中移除：<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">this</span><span style="color: #000000;">.selectionKey.cancel();<br />
</span><span style="color: #0000ff;">try</span><span style="color: #000000;">&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;cancel&nbsp;key,then&nbsp;select&nbsp;now&nbsp;to&nbsp;remove&nbsp;file&nbsp;descriptor</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.selector.selectNow();<br />
&nbsp;}&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(IOException&nbsp;e)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onException(e);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log.error(</span><span style="color: #000000;">"</span><span style="color: #000000;">Selector&nbsp;selectNow&nbsp;fail</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;e);<br />
}</span></div>
<br />
&nbsp;&nbsp;&nbsp; 实际上这样的解决方式还是留有隐患的，因为key的取消和这个selectNow操作很可能跟Selector.select操作并发地在进行，在两个操作之间仍然留有一个极小的时间窗口可能发生这个BUG。因此，你需要更安全地方式处理这个问题，jetty的处理方式是这样，连续的select(timeout)操作没有阻塞并返回0，并且次数超过了一个指定阀值，那么就遍历整个key set，将key仍然有效并且interestOps等于0的所有key主动取消掉；如果在这次修正后，仍然继续出现select(timeout)不阻塞并且返回0的情况，那么就重新创建一个新的Selector，并将Old Selector的有效channel和对应的key转移到新的Selector上，<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span style="color: #0000ff;">long</span><span style="color: #000000;">&nbsp;before</span><span style="color: #000000;">=</span><span style="color: #000000;">now;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;selected</span><span style="color: #000000;">=</span><span style="color: #000000;">selector.select(wait);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;now&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;System.currentTimeMillis();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_idleTimeout.setNow(now);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_timeout.setNow(now);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Look&nbsp;for&nbsp;JVM&nbsp;bugs<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;</span><span style="color: #008000; text-decoration: underline;">http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(__JVMBUG_THRESHHOLD</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">0</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;selected</span><span style="color: #000000;">==</span><span style="color: #000000;">0</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;wait</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">__JVMBUG_THRESHHOLD&nbsp;</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;(now</span><span style="color: #000000;">-</span><span style="color: #000000;">before)</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">(wait</span><span style="color: #000000;">/</span><span style="color: #000000;">2</span><span style="color: #000000;">)&nbsp;)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_jvmBug</span><span style="color: #000000;">++</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(_jvmBug</span><span style="color: #000000;">&gt;=</span><span style="color: #000000;">(__JVMBUG_THRESHHOLD2))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_lastJVMBug</span><span style="color: #000000;">=</span><span style="color: #000000;">now;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;BLOODY&nbsp;SUN&nbsp;BUG&nbsp;!!!&nbsp;&nbsp;Try&nbsp;refreshing&nbsp;the&nbsp;entire&nbsp;selector.</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;Selector&nbsp;new_selector&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;Selector.open();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(SelectionKey&nbsp;k:&nbsp;selector.keys())<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #000000;">!</span><span style="color: #000000;">k.isValid()&nbsp;</span><span style="color: #000000;">||</span><span style="color: #000000;">&nbsp;k.interestOps()</span><span style="color: #000000;">==</span><span style="color: #000000;">0</span><span style="color: #000000;">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">continue</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;SelectableChannel&nbsp;channel&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;k.channel();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;Object&nbsp;attachment&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;k.attachment();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(attachment</span><span style="color: #000000;">==</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addChange(channel);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addChange(channel,attachment);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_selector.close();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_selector</span><span style="color: #000000;">=</span><span style="color: #000000;">new_selector;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_jvmBug</span><span style="color: #000000;">=</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(_jvmBug</span><span style="color: #000000;">==</span><span style="color: #000000;">__JVMBUG_THRESHHOLD&nbsp;</span><span style="color: #000000;">||</span><span style="color: #000000;">&nbsp;_jvmBug</span><span style="color: #000000;">==</span><span style="color: #000000;">__JVMBUG_THRESHHOLD1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Cancel&nbsp;keys&nbsp;with&nbsp;0&nbsp;interested&nbsp;ops</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(SelectionKey&nbsp;k:&nbsp;selector.keys())<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(k.isValid()</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">k.interestOps()</span><span style="color: #000000;">==</span><span style="color: #000000;">0</span><span style="color: #000000;">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;k.cancel();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_jvmBug</span><span style="color: #000000;">=</span><span style="color: #000000;">0</span><span style="color: #000000;">;</span></div>
<br />
<br />
&nbsp;&nbsp;&nbsp; 这个方案能比较好的在jdk 6u4之前的版本上解决这个BUG可能导致的问题。Mina和Netty没有看到有处理这个BUG的代码，如果我看错了，请留言告诉我。Yanf4j一直采用的是grizzly的方式，准备加上jetty的处理方案。当然，最简单的方案就是升级你的JDK :D<br />
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/296826.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2009-09-28 19:27 <a href="http://www.blogjava.net/killme2008/archive/2009/09/28/296826.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>初识Kestrel</title><link>http://www.blogjava.net/killme2008/archive/2009/09/15/295119.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Tue, 15 Sep 2009 03:34:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2009/09/15/295119.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/295119.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2009/09/15/295119.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/295119.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/295119.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; <a href="http://github.com/robey/kestrel">Kestrel</a>是一个<a href="http://www.scala-lang.org/">scala</a>写的twitter开源的消息中间件，特点是高性能、小巧（2K行代码）、持久存储（记录日志到journal）并且可靠（支持可靠获取）。Kestrel的前身是Ruby写的<a href="http://rubyforge.org/projects/starling/">Starling</a>项目，后来twitter的开发人员尝试用scala重新实现。它的代码非常简洁并且优雅，推荐一读。<br />
<br />
&nbsp;&nbsp;&nbsp; Kestrel采用的协议是<a href="http://www.danga.com/memcached/">memcached</a>的文本协议，但是并不完全支持所有<a href="http://www.danga.com/memcached/">memcached</a>协议，也不是完全兼容现有协议。标准的协议它仅支持GET、SET、FLUSH_ALL、STATS，扩展的协议有:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SHUTDOWN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;关闭kestrel&nbsp;server&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RELOAD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;动态重新加载配置文件<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DUMP_CONFIG&nbsp;&nbsp;&nbsp;&nbsp;dump配置文件<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FLUSH queueName&nbsp;&nbsp; flush某个队列<br />
</span></div>
<br />
&nbsp;&nbsp;&nbsp; 每个key对应都是一个队列。标准memcached文本协议的支持上也没有完全兼容，<strong>SET不支持flag，因此现有大多数基于flag做序列化的memcached client都无法存储任意java类型到kestrel</strong>；FLUSH_ALL返回"Flushed all queues.\r\n"而不是"OK\r\n"。<br />
<br />
&nbsp;&nbsp;&nbsp; GET协议支持<strong>阻塞获取和可靠获取</strong>，都是在key上作文章，例如你要获取queue1的消息，并且在没有消息的时候等待一秒钟，如果有消息马上返回，超时时间后还没有就返回空，kestrel允许你通过发送<br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "GET&nbsp;queue1/t=1000\r\n"</span></div>
<br />
来阻塞获取。本来的key应该queue1，这里变成了"queue1/t=1000"，因此如果你使用的client有对返回的key和发送的key做校验，那么可能就认为kestrel返回错误。<br />
&nbsp;&nbsp;&nbsp; 什么是可靠获取呢？默认的GET是从队列中获取消息后，server端就将该消息从队列中移除，客户端需要自己保证不把这个消息丢失掉，也就是说这里是类似JMS规范中的自动应答（auto-acknowledge)，如果客户端在处理这个消息的时候异常崩溃或者在接收消息数据的时候连接断开，那么可能导致这个消息永久丢失。Kestrel的可靠获取就是类似JMS规范中的CLIENT_ACK mode，客户端获取消息后，server将这个消息从队列移除并正常发送给客户端，如果这时候客户端崩溃或者连接断开，那么server将不会确认该消息被消费并且"un-get"这个消息，重新放到队列头部，那么当client重新连接上来的时候还可以获取这个消息；只有当server收到客户端的明确确认消息成功的时候，才将消息移除。这个功能也是通过key做手脚，<br />
<br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">"GET&nbsp;queue1/open\r\n"&nbsp;&nbsp;&nbsp;开始一次可靠获取<br />
"GET&nbsp;queue1/close\r\n"&nbsp;&nbsp;确认消费成功</span></div>
<br />
<br />
你要关闭前一次可靠获取开启新的一次，还可以这样调用<br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">"GET&nbsp;queue1/close/open\r\n"</span></div>
<br />
<br />
&nbsp;&nbsp;&nbsp; 要注意的是每个连接的client<strong>只能有一个正在执行的可靠获取</strong>，关闭一个没有开启的reliable fetch或者在执行一次reliable fetch再次open一个新的获取都将直接返回空。<br />
<br />
&nbsp;&nbsp;&nbsp; 从kestrel的协议方面，我们可以学习到的一点就是<strong>在做一份协议的时候，如果有多种不同语言的client的话，应该尽量用通用协议，通用协议通常都已经有很多成熟的client可以使用，避免了为私有协议开发不同语言的client；并且我们可以在通用协议上作扩展，例如kestrel在key上面做的花样，通过给key附加不同的属性即可实现一些特殊功能</strong>。<br />
<br />
&nbsp;&nbsp;&nbsp; <a href="http://code.google.com/p/xmemcached/">XMemcachedClient</a>默认是无法支持kestrel对memcached的协议的扩展，也就是说无法支持阻塞获取、可靠获取和flush_all，这是因为<a href="http://code.google.com/p/xmemcached/">xmemcached</a>会对返回的key和发送的key做校验，如果不相等就认为解码错误；并且由于kestrel不支持flag，因此无法存储java序列化类型；另外一个问题是，xmemcached(spymemcached)都会将连续的GET协议合并成一个bulk get协议，而kestrel也并不支持bulk get，所以需要关闭这个优化，这个可以通过下列代码关闭：<br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">memcachedClient.setOptimizeGet(false);</span></div>
<br />
Spymemcached似乎不提供这个选项。为了解决序列化问题，我添加了一个新的<strong>KestrelCommandFactory</strong>，使用这个CommandFactory后，将默认关闭get优化，并且不对GET返回的key做校验从而支持阻塞获取和可靠获取，并且将在存储的数据之前加上4个字节的flag(整型)，因此可以支持存储任意可序列化类型。但是有一些应用只需要存储字符串类型和原生类型，这是为了在不同语言的client之间保持可移植（如存储json数据），那么就不希望在数据之前加上这个flag，关闭这个功能可以通过<br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">memcachedClient.setPrimitiveAsString(true);<br />
</span></div>
<br />
方法来设置，所有的原生类型都将调用toString转成字符串来存储，字符串前不再自动附加flag。<br />
<br />
&nbsp;&nbsp;&nbsp; KestrelCommandFactory已经提交到<a href="http://xmemcached.googlecode.com/svn/trunk/">svn trunk</a>，预计在xmemcached 1.2.0-RC2的时候发布。<br />
<br />
&nbsp;&nbsp;&nbsp; 使用KestrelCommandFactory对kestrel做的性能测试，server和client都跑在linux上，jdk6,单线程单client连续push消息<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 消息个数&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 消息长度&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 是否启用journal&nbsp;&nbsp; 时间&nbsp;&nbsp; &nbsp; &nbsp; TPS(/s)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 500000 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 256&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 否&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 123.0s&nbsp;&nbsp;&nbsp;&nbsp; 4065<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 500000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 1024 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 否&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; 126.3s &nbsp; &nbsp; 3959<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 500000 &nbsp; &nbsp; &nbsp; &nbsp;&nbsp; 4096&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 否&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 120.6s&nbsp;&nbsp;&nbsp;&nbsp; 4145<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 500000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4096&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 是&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 122.1s&nbsp;&nbsp;&nbsp;&nbsp; 4095<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 500000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8192&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 是&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 121.2s&nbsp;&nbsp;&nbsp;&nbsp; 4125<br />
<br />
从数据上来看比官方数据好很多，可能机器配置不同。是否启用journal带来的影响似乎很小，写文件都是append，还是比较高效的。<br />
<br />
kestrel的项目主页&nbsp; <a href="http://github.com/robey/kestrel">http://github.com/robey/kestrel</a><br />
kestrel的wiki页&nbsp;&nbsp;&nbsp; <a href="http://wiki.github.com/robey/kestrel">http://wiki.github.com/robey/kestrel</a><br />
xmemcached项目主页&nbsp; <a title="http://code.google.com/p/xmemcached/" href="http://code.google.com/p/xmemcached/">http://code.google.com/p/xmemcached/</a><br />
<br />
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/295119.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2009-09-15 11:34 <a href="http://www.blogjava.net/killme2008/archive/2009/09/15/295119.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java.util.HashMap源码要点浅析</title><link>http://www.blogjava.net/killme2008/archive/2009/04/15/265721.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Wed, 15 Apr 2009 04:33:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2009/04/15/265721.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/265721.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2009/04/15/265721.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/265721.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/265721.html</trackback:ping><description><![CDATA[1、散列表要解决的一个问题就是散列值的冲突问题，通常是两种方法：链表法和开放地址法。链表法就是将相同hash值的对象组织成一个链表放在hash值对应的槽位；开放地址法是通过一个探测算法，当某个槽位已经被占据的情况下继续查找下一个可以使用的槽位。java.util.HashMap采用的链表法的方式，链表是单向链表，因此在删除过程中要自己维持prev节点，我想不采用双向链表是从节省空间考虑。一个典型的查找过程：<br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">for</span><span style="color: rgb(0, 0, 0);">&nbsp;(Entry</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;e&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;table[indexFor(hash,&nbsp;table.length)];<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e&nbsp;</span><span style="color: rgb(0, 0, 0);">!=</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;e.next)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;k;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);">&nbsp;(e.hash&nbsp;</span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);">&nbsp;hash&nbsp;</span><span style="color: rgb(0, 0, 0);">&amp;&amp;</span><span style="color: rgb(0, 0, 0);"><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((k&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;e.key)&nbsp;</span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);">&nbsp;key&nbsp;</span><span style="color: rgb(0, 0, 0);">||</span><span style="color: rgb(0, 0, 0);">&nbsp;(key&nbsp;</span><span style="color: rgb(0, 0, 0);">!=</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 0);">&amp;&amp;</span><span style="color: rgb(0, 0, 0);">&nbsp;key.equals(k))))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);">&nbsp;e;<br />
&nbsp;}</span></div>
&nbsp;&nbsp; HashMap采用链表法而不是开放地址法，猜想可能的原因是从实用角度出发，对空间和时间效率做出的折中选择。采用开放地址法，无论是线性探测或者二次探测都可能造成群集现象，而双重散列会要求散列表的装填程度比较低的情况下会有比较好的查找效率，容易造成空间的浪费。<br />
<br />
2、什么是负载因子？负载因子a定义为<br />
&nbsp;&nbsp;&nbsp;&nbsp; a=散列表的实际元素数目(n)/ 散列表的容量(m)<br />
<br />
负载因子衡量的是一个散列表的空间的使用程度，负载因子越大表示散列表的装填程度越高，反之愈小。对于使用链表法的散列表来说，查找一个元素的平均时间是 O(1+a)，因此如果负载因子越大，对空间的利用更充分，然而后果是查找效率的降低；如果负载因子太小，那么散列表的数据将过于稀疏，对空间造成严重浪费。<br />
<br />
回到HashMap的实现，HashMap中的loadFactor其实定义的就是该map对象允许的最大的负载因子，如果超过这个系数将重新resize。这个是通过threshold字段来判断，看threshold的计算：<br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">threshold&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);">)(capacity&nbsp;</span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);">&nbsp;loadFactor);</span></div>
<br />
结合上面的负载因子的定义公式可知，threshold就是在此loadFactor和capacity对应下允许的最大元素数目，超过这个数目就重新resize，以降低实际的负载因子。默认的的负载因子0.75是对空间和时间效率的一个平衡选择。注意到的一点是resize的规模是现有 capacity的两倍：<br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);">&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);">&nbsp;(size</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 0);">&gt;=</span><span style="color: rgb(0, 0, 0);">&nbsp;threshold)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resize(</span><span style="color: rgb(0, 0, 0);">2</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 0);">*</span><span style="color: rgb(0, 0, 0);">&nbsp;table.length);</span></div>
&nbsp;<br />
3、可能你也注意到了，java.util.HashMap对key的hash值多做了一步处理，而不是直接使用hashCode：<br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);"><br />
</span><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);">&nbsp;hash(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);">&nbsp;h)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h&nbsp;</span><span style="color: rgb(0, 0, 0);">^=</span><span style="color: rgb(0, 0, 0);">&nbsp;(h&nbsp;</span><span style="color: rgb(0, 0, 0);">&gt;&gt;&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 0);">20</span><span style="color: rgb(0, 0, 0);">)&nbsp;</span><span style="color: rgb(0, 0, 0);">^</span><span style="color: rgb(0, 0, 0);">&nbsp;(h&nbsp;</span><span style="color: rgb(0, 0, 0);">&gt;&gt;&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 0);">12</span><span style="color: rgb(0, 0, 0);">);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);">&nbsp;h&nbsp;</span><span style="color: rgb(0, 0, 0);">^</span><span style="color: rgb(0, 0, 0);">&nbsp;(h&nbsp;</span><span style="color: rgb(0, 0, 0);">&gt;&gt;&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 0);">7</span><span style="color: rgb(0, 0, 0);">)&nbsp;</span><span style="color: rgb(0, 0, 0);">^</span><span style="color: rgb(0, 0, 0);">&nbsp;(h&nbsp;</span><span style="color: rgb(0, 0, 0);">&gt;&gt;&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 0);">4</span><span style="color: rgb(0, 0, 0);">);<br />
&nbsp;&nbsp;}</span></div>
<br />
这个处理的原因在于HashMap的容量总是采用2的p次幂，而取index（槽位）的方法是<br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 255);">static</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);">&nbsp;indexFor(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);">&nbsp;h,&nbsp;</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);">&nbsp;length)&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);">&nbsp;h&nbsp;</span><span style="color: rgb(0, 0, 0);">&amp;</span><span style="color: rgb(0, 0, 0);">&nbsp;(length</span><span style="color: rgb(0, 0, 0);">-</span><span style="color: rgb(0, 0, 0);">1</span><span style="color: rgb(0, 0, 0);">);<br />
&nbsp;}<br />
</span></div>
<br />
这一运算等价于对length取模，也就是<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; h % 2^p<br />
返回的将是h的p个最低位组成的数字，我们假设hash输入是符合简单一致散列，然而这一假设并不能推论出hash的p个最低位也会符合简单一致散列，也许h的这p个最低位相同的几率很大，那么冲突的几率就非常大了。优秀的散列函数应该需要考虑所有的位。<br />
<br />
因此为了防止这些&#8220;坏&#8221;的散列函数造成效率的降低，HashMap预先对hash值做了处理以考虑到所有的位，根据注释也可以知道。这个处理我看不懂，留待高人解释，也许来自于某本算法书也不一定。<br />
<br />
4、我们知道java.util.HashMap不是线程安全的，因此如果在使用迭代器的过程中有其他线程修改了map，那么将抛出ConcurrentModificationException，这就是所谓fail-fast策略（速错），这一策略在源码中的实现是通过 modCount域，modCount顾名思义就是修改次数，对HashMap内容的修改都将增加这个值，那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount，<br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);"><br />
&nbsp;HashIterator()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;expectedModCount&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;modCount;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);">&nbsp;(size&nbsp;</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 0);">0</span><span style="color: rgb(0, 0, 0);">)&nbsp;{&nbsp;</span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">&nbsp;advance&nbsp;to&nbsp;first&nbsp;entry</span><span style="color: rgb(0, 128, 0);"><br />
</span><span style="color: rgb(0, 0, 0);">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Entry[]&nbsp;t&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;table;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">while</span><span style="color: rgb(0, 0, 0);">&nbsp;(index&nbsp;</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">&nbsp;t.length&nbsp;</span><span style="color: rgb(0, 0, 0);">&amp;&amp;</span><span style="color: rgb(0, 0, 0);">&nbsp;(next&nbsp;</span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);">&nbsp;t[index</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">])&nbsp;</span><span style="color: rgb(0, 0, 0);">==</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">null</span><span style="color: rgb(0, 0, 0);">)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />
</span></div>
<br />
在迭代过程中，判断modCount跟expectedModCount是否相等，如果不相等就表示已经有其他线程修改了map<br />
<div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: rgb(0, 0, 0);"><br />
&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">final</span><span style="color: rgb(0, 0, 0);">&nbsp;Entry</span><span style="color: rgb(0, 0, 0);">&lt;</span><span style="color: rgb(0, 0, 0);">K,V</span><span style="color: rgb(0, 0, 0);">&gt;</span><span style="color: rgb(0, 0, 0);">&nbsp;nextEntry()&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">if</span><span style="color: rgb(0, 0, 0);">&nbsp;(modCount&nbsp;</span><span style="color: rgb(0, 0, 0);">!=</span><span style="color: rgb(0, 0, 0);">&nbsp;expectedModCount)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: rgb(0, 0, 255);">throw</span><span style="color: rgb(0, 0, 0);">&nbsp;</span><span style="color: rgb(0, 0, 255);">new</span><span style="color: rgb(0, 0, 0);">&nbsp;ConcurrentModificationException();<br />
</span></div>
&nbsp;注意到modCount声明为volatile，保证线程之间修改的可见性。<br />
&nbsp;<br />
<br />
<br />
<br />
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/265721.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2009-04-15 12:33 <a href="http://www.blogjava.net/killme2008/archive/2009/04/15/265721.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java字符串的最大长度</title><link>http://www.blogjava.net/killme2008/archive/2009/01/15/251368.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Wed, 14 Jan 2009 17:37:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2009/01/15/251368.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/251368.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2009/01/15/251368.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/251368.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/251368.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 在cpp中为了可移植性，string的长度是string::size_type，突然就想知道java允许的最大字符串长度为多少。看String的源码：<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;String<br />
&nbsp;&nbsp;</span><span style="color: #000000;">110</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;java.io.Serializable,&nbsp;Comparable</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">String</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">,&nbsp;CharSequence<br />
&nbsp;&nbsp;</span><span style="color: #000000;">111</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;{<br />
&nbsp;&nbsp;</span><span style="color: #000000;">112</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;">&nbsp;The&nbsp;value&nbsp;is&nbsp;used&nbsp;for&nbsp;character&nbsp;storage.&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;</span><span style="color: #000000;">113</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">char</span><span style="color: #000000;">&nbsp;value[];<br />
&nbsp;&nbsp;</span><span style="color: #000000;">114</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;</span><span style="color: #000000;">115</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;">&nbsp;The&nbsp;offset&nbsp;is&nbsp;the&nbsp;first&nbsp;index&nbsp;of&nbsp;the&nbsp;storage&nbsp;that&nbsp;is&nbsp;used.&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;</span><span style="color: #000000;">116</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;offset;<br />
&nbsp;&nbsp;</span><span style="color: #000000;">117</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;</span><span style="color: #000000;">118</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;">&nbsp;The&nbsp;count&nbsp;is&nbsp;the&nbsp;number&nbsp;of&nbsp;characters&nbsp;in&nbsp;the&nbsp;String.&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br />
&nbsp;&nbsp;</span><span style="color: #000000;">119</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;count;<br />
</span></div>
&nbsp;&nbsp; String内部是以char数组的形式存储，数组的长度是int类型，那么String允许的最大长度就是Integer.MAX_VALUE了。又由于java中的字符是以16位存储的，因此大概需要4GB的内存才能存储最大长度的字符串。不过这仅仅是对字符串变量而言，如果是字符串字面量(string literals)，如&#8220;abc"、"1a2b"之类写在代码中的字符串literals，那么允许的最大长度取决于字符串在常量池中的存储大小，也就是字符串在class格式文件中的存储格式：<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">CONSTANT_Utf8_info&nbsp;{<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u1&nbsp;tag;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u2&nbsp;length;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;u1&nbsp;bytes[length];<br />
}</span></div>
<br />
&nbsp;&nbsp;&nbsp; u2是无符号的16位整数，因此理论上允许的string literal的最大长度是2^16-1=65535。然而实际测试表明，允许的最大长度仅为65534，超过就编译错误了，有兴趣可以写段代码试试，估计是length还不能为0。<br />
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/251368.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2009-01-15 01:37 <a href="http://www.blogjava.net/killme2008/archive/2009/01/15/251368.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ruby的对象模型</title><link>http://www.blogjava.net/killme2008/archive/2007/09/29/149452.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Sat, 29 Sep 2007 01:43:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/09/29/149452.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/149452.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/09/29/149452.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/149452.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/149452.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; Ruby的对象模型，包含在下面这张图中：<br />
<img alt="" src="http://www.javaeye.com/topics/download/2fb4a83c-1a28-4aa0-aee0-4f77aafb8c2c" /><br />
<br />
&nbsp;&nbsp;&nbsp; 首先要知道，Ruby中的类也是对象，类相比于其他对象特殊的地方在于能够产生对象，既然类是对象，那么它显然也有类，也就是所谓类的类，这个类的类在Ruby中就是类的metaclass，图中的(OtherClass)，(OtherClass)就是类OtherClass的klass(C层次）,(OtherClass)存储了类的方法(类方法）和类的实例变量，并且是唯一的且不可实例化。在Ruby层次上我们想操作(otherclass)应该类似：<br />
&nbsp;&nbsp; <br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;OtherClass<br />
&nbsp;&nbsp;<br />
end<br />
</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&lt;&lt;</span><span style="color: #000000;">OtherClass<br />
&nbsp;&nbsp;attr_accessor:name&nbsp;</span><span style="color: #008000;">#</span><span style="color: #008000;">name是OtherClass的实例变量</span><span style="color: #008000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;</span><span style="color: #0000ff;">def</span><span style="color: #000000;">&nbsp;test<br />
&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;</span><span style="color: #800000;">'</span><span style="color: #800000;">hello</span><span style="color: #800000;">'</span><span style="color: #000000;"><br />
&nbsp;&nbsp;end<br />
end<br />
OtherClass.name</span><span style="color: #000000;">=</span><span style="color: #800000;">'</span><span style="color: #800000;">1</span><span style="color: #800000;">'</span><span style="color: #000000;"><br />
p&nbsp;OtherClass.name<br />
OtherClass.test</span></div>
&nbsp;&nbsp;&nbsp; 图中的instance是OtherClass的一个实例，那么显然instance的class是OtherClass，可是图中的(instance)又是什么呢？(instance)就是对象的singleton类，singleton类这个名称怪怪的，不过每个对象只能有一个singleton类的角度上说也可以理解。看看下面的例子：<br />
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;OtherClass<br />
end<br />
instance</span><span style="color: #000000;">=</span><span style="color: #000000;">OtherClass.new<br />
</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&lt;&lt;</span><span style="color: #000000;">instance<br />
&nbsp;&nbsp;</span><span style="color: #0000ff;">def</span><span style="color: #000000;">&nbsp;test<br />
&nbsp;&nbsp;&nbsp;&nbsp;p&nbsp;</span><span style="color: #800000;">"</span><span style="color: #800000;">a.test</span><span style="color: #800000;">"</span><span style="color: #000000;"><br />
&nbsp;&nbsp;end<br />
&nbsp;&nbsp;attr_accessor:name<br />
end<br />
instance.test<br />
instance.name</span><span style="color: #000000;">=</span><span style="color: #800000;">"</span><span style="color: #800000;">dennis</span><span style="color: #800000;">"</span><span style="color: #000000;"><br />
p&nbsp;instance.name</span></div>
<br />
&nbsp;&nbsp;&nbsp;&nbsp;
instance通过OtherClass.new创建，但是此时(instance)还不存在，这与(OtherClass)情况不同，每个类一经创建就有一个metaclass，而对象就不一样，只有当你通过class&lt;&lt;instance 语法创建的时候，(instance)才被创建。注意test方法和name变量都将是instance对象特有的，类OtherClass并没有改变。观察下，发现(instance)继承于OtherClass，引出类的metaclass与对象的singleton类的又一个区别：类的metaclass继承自父类的metaclass，而对象的singleton类则是继承于对象的class。<br />
&nbsp;&nbsp;&nbsp; 那么当我们调用instance.class的时候，怎么不返回(instance)?这是c ruby在底层做了处理，instance的class在c ruby层次是(instance),当查找的时候忽略了singleton类以及下面将要谈到的include模块的代理类，沿着继承链上查找：<br />
86  VALUE<br />
87  rb_obj_class(obj)<br />
88      VALUE obj;<br />
89  {<br />
90      return rb_class_real(CLASS_OF(obj));<br />
91  }<br />
<br />
76  VALUE<br />
77  rb_class_real(cl)<br />
78      VALUE cl;<br />
79  {<br />
80      while (FL_TEST(cl, FL_SINGLETON) || TYPE(cl) == T_ICLASS) {<br />
81          cl = RCLASS(cl)-&gt;super;<br />
82      }<br />
83      return cl;<br />
84  }<br />
<br />
(object.c)<br />
<br />
核心代码就是：<br />
while (FL_TEST(cl, FL_SINGLETON) || TYPE(cl) == T_ICLASS) {<br />
&nbsp;
cl = RCLASS(cl)-&gt;super;<br />
&nbsp;}<br />
&nbsp;&nbsp;&nbsp; 其中FL_TEST(cl,FL_SINGLETON)用于测试是否是singleton类，而TYPE(cl)==TL_ICLASS是否是包含模块的代理类，TL_ICLASS的I就是include的意思。<br />
&nbsp;&nbsp;&nbsp; 图中类OtherClass继承Object，这个是显而易见的，不再多说。而Object、Class和Module这三个类是没办法通过API创建的，称为元类，他们的之间的关系如图所示，Object的class是Class,Module继承Object,而Class又继承Module，因此Class.kind_of? Object返回true,这个问题类似先有鸡，还是先有蛋的问题，是先有Object？还是先有Class?而c ruby的解决办法是不管谁先有，创建Object开始，接着创建Module和Class，然后分别创建它们的metaclass，从此整个Ruby的对象模型开始运转。<br />
<br />
1243  rb_cObject = boot_defclass("Object", 0);<br />
1244  rb_cModule = boot_defclass("Module", rb_cObject);<br />
1245  rb_cClass =  boot_defclass("Class",  rb_cModule);<br />
1246<br />
1247  metaclass = rb_make_metaclass(rb_cObject, rb_cClass);<br />
1248  metaclass = rb_make_metaclass(rb_cModule, metaclass);<br />
1249  metaclass = rb_make_metaclass(rb_cClass, metaclass);<br />
<br />
(object.c)<br />
<br />
那么当我们调用Class.class发生了什么？Class的klass其实指向的是(Class)，可根据上面的代码，我们知道会忽略这个(Class)，继续往上找就是(Module),同理找到(Object)，而(Object)继承自Class,显然Class的类仍然是Class，Class的类的类也是Class,多么有趣。同理，Object.class和Module.class都将是Class类。<br />
<br />
&nbsp;&nbsp;&nbsp; 再来看看include模块时发生的故事。include模块的过程如下图所示：<br />
<img src="http://www.blogjava.net/images/blogjava_net/killme2008/ch_class_include.png" alt="" border="0" /><br />
include模块，本质上是在对象或者类的klass和super之间插入了一个代理类iclass,这个代理类的方法表(m_table)和变量表(iv_table)分别指向了被包含的模块的方法表和变量表（通过指针，因此当包含的Module变化的时候，对象或者类也能相应变化），那么在查找类或者对象的class的时候，上面已经说明将忽略这些代理类。<br />
<br />
<br />
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/149452.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-09-29 09:43 <a href="http://www.blogjava.net/killme2008/archive/2007/09/29/149452.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Ruby变量在c ruby中的存储</title><link>http://www.blogjava.net/killme2008/archive/2007/09/20/146799.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Thu, 20 Sep 2007 08:17:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/09/20/146799.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/146799.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/09/20/146799.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/146799.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/146799.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 读完ruby hacking guide第6章，彻底总结下：<br />
1.在Ruby中，类也是一个对象，因此有实例变量。类的实例变量、类变量、常量都是存储在RClass struct的iv_tbl中，<br />
struct RClass {<br />
&nbsp;&nbsp;&nbsp; struct RBasic basic;<br />
&nbsp;&nbsp;&nbsp; struct st_table *iv_tbl;<br />
&nbsp;&nbsp;&nbsp; struct st_table *m_tbl;<br />
&nbsp;&nbsp;&nbsp; VALUE super;<br />
};<br />
iv_tbl的类型是st_table，我在<a href="http://www.blogjava.net/killme2008/archive/2007/09/18/146234.html">这里</a>用java实现了一下。<br />
<br />
2.用户自定义类的对象（ruby层次声明的类的对象)的实例变量存储在RObject struct的iv_tbl中，<br />
struct RObject {<br />
&nbsp; struct RBasic basic;<br />
&nbsp; struct st_table *iv_tbl;<br />
&nbsp;};<br />
调用方法，本质上是一个查表操作。buildin的几个类，比如String、Array、Hash等(在c层次上实现的类），它们的结构并没有iv_table，这是从节省内存空间的角度考虑，它们的实例变量存储在一张全局的st_table中。这张表比较特别，其中的每一个对应的值又是一个st_table，也就是一个&#8220;二元结构&#8221;，第一层结构是类名与实例变量表的映射，第二层才是实例变量名与实际值的映射。<br />
<br />
3.全局变量存储在一张全局的st_table中，这个表的键就是变量名ID，由于全局变量允许通过alias来设置别名，因此这张全局表中真正存储的是下面这个struct<br />
<br />
334  struct global_entry {<br />
335      struct global_variable *var;<br />
336      ID id;<br />
337  };<br />
<br />
324  struct global_variable {<br />
325      int   counter;      /* 引用计数 */<br />
326      void *data;         /* 变量值 */<br />
327      VALUE (*getter)();  /* 取值函数 */<br />
328      void  (*setter)();  /* 设置函数 */<br />
329      void  (*marker)();  /* 标记函数 */<br />
330      int block_trace;<br />
331      struct trace_var *trace;<br />
332  };<br />
(variable.c)<br />
<br />
当不同变量名（通过别名声明）指向的是同一个全局变量，其实它们指向的是同一个struct global_variable。<br />
<br />
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/146799.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-09-20 16:17 <a href="http://www.blogjava.net/killme2008/archive/2007/09/20/146799.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入下Ruby中的String</title><link>http://www.blogjava.net/killme2008/archive/2007/09/12/144417.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Wed, 12 Sep 2007 01:43:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/09/12/144417.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/144417.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/09/12/144417.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/144417.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/144417.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; Ruby语言中的String是mutable的，不像java、C#中的String是immutable的。比如<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str1="abc"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str2="abc"<br />
在java中，对于字面量的字符串，jvm内部维持一张表，因此如果在java中，str1和str2是同一个String对象。而在Ruby中，str1和str2是完全不同的对象。同样，在java中对于String对象的操作都将产生一个新的对象，而Ruby则是操纵同一个对象，比如:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str="abc"<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; str.concat("cdf")<br />
此时str就是"abccdf"。Ruby对String是怎么处理的呢？我们只谈谈c ruby中的实现，有兴趣的先看看这篇文章《<a href="http://dreamhead.blogbus.com/logs/3299713.html">管窥Ruby——对象基础</a>》。在ruby.h中我们可以看到String对象的结构，Ruby中的对象（包括类也是对象）都是一个一个的struct，String也不能例外：<br />
struct RString {<br />
&nbsp;&nbsp;&nbsp; struct RBasic basic;<br />
&nbsp;&nbsp;&nbsp; long len;<br />
&nbsp;&nbsp;&nbsp; char *ptr;<br />
&nbsp;&nbsp;&nbsp; union {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; long capa;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VALUE shared;<br />
&nbsp;&nbsp;&nbsp; } aux;<br />
};<br />
//ruby.h<br />
<br />
&nbsp;&nbsp;&nbsp; 显然，len是String的长度；ptr是一个char类型的指针，指向实际的字符串；然后是一个联合，这个稍后再说。如果你看看ruby.h可以发现，几乎所有定义的对象结构都有一个struct RBasic。显然,struct RBasic包含由所有对象结构体共享的一些重要信息的。看看RBasic:<br />
<br />
struct RBasic {
<br />
&nbsp;unsigned long flags; <br />
&nbsp;VALUE klass; <br />
};<br />
其中的flags是一个多用途的标记，大多数情况下用于记录结构体的类型，ruby.h中预定义了一些列的宏，比如T_STRING（表示struct RString)，T_ARRAY(表示struct RArray）等。Klass是一个VALUE类型，VALUE也是unsigned long，可以地将它当成指针（一个指针4字节，绰绰有余了）,它指向的是一个Ruby对象，这里以后再深入。<br />
&nbsp;&nbsp;&nbsp; 那么联合aux中的capa和shared是干什么用的呢？因为Ruby的String是可变的，可变意味着len可以改变，我们需要每次都根据len的变换来增减内存（使用c中的realloc()函数），这显然是一个很大的开销，解决办法就是预留一定的空间，ptr指向的内存大小略大于len，这样就不需要频繁调用realloc了，aux.capa就是一个长度，包含额外的内存大小。那么aux.shared是干什么的呢？这是一个VALUE类型，说明它是指向某个对象。aux.shared其实是用于加快字符串的创建速度，在一个循环中：<br />
<pre>while true do  # 无限重复<br />
a = "str"        # 以&#8220;str&#8221;为内容创建字符串，赋值给a<br />
a.concat("ing")  # 为a所指向的对象添加&#8220;ing&#8221;<br />
p(a)             # 显示&#8220;string&#8221;<br />
end<br />
每次都重新创建一个"str"对象，内部就是重复创建一个char[],这是相当奢侈，aux.shared就是用于共享char[],<br />
以字面量创建的字符串会共享一个char[],当要发生变化时，将字符串复制到一个非共享的内存中，变化针对这<br />
个新拷贝进行，这就是所谓的&#8220;copy-on-write"技术。解释了String的内部构造，貌似还没有介绍String是怎么<br />
实现mutable，我们写一个Ruby扩展测试下，我们想写这样一个Ruby类：<br />
class Test<br />
def test<br />
str="str"<br />
str.concat("ing")<br />
end<br />
end<br />
对应的c语言代码就是：<br />
</pre>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br />
<br />
Code highlighting produced by Actipro CodeHighlighter (freeware)<br />
http://www.CodeHighlighter.com/<br />
<br />
--><span style="color: #000000;">#include</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">stdio.h</span><span style="color: #000000;">&gt;</span><span style="color: #000000;"><br />
#include&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;"><a title="" href="http://www.ruby-lang.org">ruby</a>.h</span><span style="color: #000000;">"</span><span style="color: #000000;"><br />
<br />
</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;VALUE&nbsp;t_test(VALUE&nbsp;self)<br />
{<br />
&nbsp;&nbsp;VALUE&nbsp;str;<br />
&nbsp;&nbsp;str</span><span style="color: #000000;">=</span><span style="color: #000000;">rb_str_new2(</span><span style="color: #000000;">"</span><span style="color: #000000;">str</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;printf(</span><span style="color: #000000;">"</span><span style="color: #000000;">before&nbsp;concat:&nbsp;str:%p,&nbsp;str.aux.shared:%p,&nbsp;str.ptr:%s</span><span style="color: #000000;">"</span><span style="color: #000000;">n</span><span style="color: #000000;">"</span><span style="color: #000000;">,str,</span><span style="color: #000000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(RSTRING(str)</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">aux).shared,RSTRING(str)</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">ptr);<br />
&nbsp;&nbsp;rb_str_cat2(str,</span><span style="color: #000000;">"</span><span style="color: #000000;">ing</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br />
&nbsp;&nbsp;printf(</span><span style="color: #000000;">"</span><span style="color: #000000;">after&nbsp;concat:&nbsp;str:%p,&nbsp;str.aux.shared:%p,&nbsp;str.ptr:%s</span><span style="color: #000000;">"</span><span style="color: #000000;">n</span><span style="color: #000000;">"</span><span style="color: #000000;">,</span><span style="color: #000000;"><br />
</span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;str,(RSTRING(str)</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">aux).shared,RSTRING(str)</span><span style="color: #000000;">-&gt;</span><span style="color: #000000;">ptr);<br />
&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;self;<br />
}<br />
VALUE&nbsp;cTest;<br />
</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;Init_string_hack(){<br />
&nbsp;&nbsp;cTest</span><span style="color: #000000;">=</span><span style="color: #000000;">rb_define_class(</span><span style="color: #000000;">"</span><span style="color: #000000;">Test</span><span style="color: #000000;">"</span><span style="color: #000000;">,rb_cObject);<br />
&nbsp;&nbsp;rb_define_method(cTest,</span><span style="color: #000000;">"</span><span style="color: #000000;">test</span><span style="color: #000000;">"</span><span style="color: #000000;">,t_test,</span><span style="color: #000000;">0</span><span style="color: #000000;">);<br />
<br />
}<br />
//string_hack.c<br />
</span></div>
<pre>   rb_define_class函数定义了一个类Test,rb_define_method将t_test方法以test的名称添加到Test类。在<br />
t_test中，通过rb_str_new2每次生成一个RString结构，然后通过rb_str_cat2将str与"ing"连接起来，添加<br />
了一些打印用于跟踪。利用mkmf产生Makefile,写一个extconf.rb<br />
require 'mkmf'<br />
create_makefile("string_hack");<br />
执行ruby extconf.rb，将产生一个Makefile,执行make，生成一个string_hack.so的链接库。扩展写完了，通过<br />
ruby调用：<br />
require 'string_hack"<br />
t=Test.new<br />
(1..3).each{|i| t.test}<br />
输出：<br />
before concat: str:0x40098a40, str.aux.shared:0x3, str.ptr:str<br />
after concat: str:0x40098a40, str.aux.shared:0x8, str.ptr:string<br />
before concat: str:0x40098a2c, str.aux.shared:0x3, str.ptr:str<br />
after concat: str:0x40098a2c, str.aux.shared:0x8, str.ptr:string<br />
before concat: str:0x40098a18, str.aux.shared:0x3, str.ptr:str<br />
after concat: str:0x40098a18, str.aux.shared:0x8, str.ptr:string<br />
从结果可以看出，在str concat之前之后，str指向的位置没有改变，改变的仅仅是str中ptr指向的字符串的值<br />
，看看rb_str_cat2函数的实现就一目了然了：<br />
VALUE rb_str_cat(str, ptr, len)<br />
&nbsp;&nbsp;&nbsp; VALUE str;<br />
&nbsp;&nbsp;&nbsp; const char *ptr;<br />
&nbsp;&nbsp;&nbsp; long len;<br />
{<br />
&nbsp;&nbsp;&nbsp; if (len &lt; 0) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rb_raise(rb_eArgError, "negative string size (or size too big)");<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; if (FL_TEST(str, STR_ASSOC)) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rb_str_modify(str);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; REALLOC_N(RSTRING(str)-&gt;ptr, char, RSTRING(str)-&gt;len+len);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memcpy(RSTRING(str)-&gt;ptr + RSTRING(str)-&gt;len, ptr, len);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RSTRING(str)-&gt;len += len;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RSTRING(str)-&gt;ptr[RSTRING(str)-&gt;len] = '"0'; /* sentinel */<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return str;<br />
&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; return rb_str_buf_cat(str, ptr, len);<br />
}<br />
VALUE rb_str_cat2(str, ptr)<br />
&nbsp;&nbsp;&nbsp; VALUE str;<br />
&nbsp;&nbsp;&nbsp; const char *ptr;<br />
{<br />
&nbsp;&nbsp;&nbsp; return rb_str_cat(str, ptr, strlen(ptr));<br />
}<br />
//string.c<br />
<br />
</pre>
<br />
<br /><img src ="http://www.blogjava.net/killme2008/aggbug/144417.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-09-12 09:43 <a href="http://www.blogjava.net/killme2008/archive/2007/09/12/144417.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>对org.springframework.beans.CachedIntrospectionResults的再次解读</title><link>http://www.blogjava.net/killme2008/archive/2007/06/26/126282.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Tue, 26 Jun 2007 02:39:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/06/26/126282.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/126282.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/06/26/126282.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/126282.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/126282.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 这个类在spring2.01前没有被改写,spring2.06似乎已经改写了,还未看源码。不过这不是我所在意的问题。我在《<a id="viewpost1_TitleUrl" class="postTitle2" href="http://www.blogjava.net/killme2008/archive/2007/04/16/110889.html">org.springframework.beans简单解读</a>》中的对这个类的理解是不正确的。我们先看看Guillaume Poirier对这个类中为什么使用WeakHashMap的解释：<br><br>WeakHashMap
is implemented with WeakReference for keys, and strong reference for
values. That means if the value has a strong reference on the key, then
the key cannot be garbage collected until the WeakHashMap is ready for
collection. However, if the value has no strong reference on its key,
then being in the WeakHashMap won't prevent the key and value from
being garbage collected if it is otherwise ready. The WeakHashMap knows
when to remove the key (and the value with it) by using the
notification provided by the java.lang.ref package. For more
information on this, see: <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ref/package-summary.html" target="_blank" class="ilink">http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ref/package-summary.html</a><br><br>So
the problem here with the CachedIntrospectionResults is that it uses
BeanInfo and PropertyDescriptor that both have strong reference to the
class (indirectly by a reference on Methods of the class). That will be
solved with JDK 1.5 that uses a combinaison of Weak and Soft Reference
to the Class and Method objects, but for 1.4.2, there's not really any
better solution than to flush the Introspector's cache and/or use
WeakReference on CachedIntrospectionResults. Using WeakReference on the
CachedIntrospectionResults is safer, but decrease performance, and in
such case a manual Instrospector.flushFromCaches(Class) must be used,
so that the Instrospector does not keep a strong reference on the
BeanInfo.<br><br>When a webapp is hot-redeployed, a new ClassLoader is
created to load the webapp, and the old one is thrown away, expected to
be garbage collected. For the collection to happen, the server must
clear any strong reference to the ClassLoader or its classes, and also
the webapp must make sure that any code in parent ClassLoaders (or
siblings) clear any reference it might have to any of the webapp's
class.<br><br>&nbsp;&nbsp;&nbsp; 照他的说法和参考《深入JVM》一书，对Class有强引用的有：ClassLoader，java.beans.BeanInfo，java.beans.PropertyDescriptor,java.lang.reflect.Method。因为在这个缓存中使用Class作为key，而Value是CachedIntrospectionResults，CachedIntrospectionResults中持有BeanInfo和Method的引用，这两个都对Class对象有强引用（这一点据说在jdk5中已经修改，被改成软引用和弱引用的组合,而在jdk1.4.2需要这样的处理），导致在web应用关闭或者热部署的时候，旧的ClassLoader和它引用的类不能被回收，因此使用弱引用包装CachedIntrospectionResults对象作为Value。web应用关闭或者热部署的时候，会new新的ClassLoader用于装载类，这就是CachedIntrospectionResults判断缓存是否safe的根据所在，判断要缓存的Class引用的ClassLoader是否相同。<br>&nbsp;&nbsp;&nbsp; 当使用JavaBean的内省时，使用Introspector，jdk会自动缓存内省的信息（BeanInfo），这一点可以理解，因为内省通过反射的代价是高昂的。当ClassLoader关闭的时候，Introspector的缓存持有BeanInfo的信息，而BeanInfo持有Class的强引用，这将导致ClassLoader和它引用的Class等对象不能被垃圾收集器回收，因此在关闭前，需要手工清除Introspector中的缓存，调用Introspector.flushFromCaches，这就是CachedIntrospectionResults中当得到BeanInfo后为什么要执行下面这段代码的原因：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this</span><span style="color: #000000;">.beanInfo&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;Introspector.getBeanInfo(clazz);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Immediately&nbsp;remove&nbsp;class&nbsp;from&nbsp;Introspector&nbsp;cache,&nbsp;to&nbsp;allow&nbsp;for&nbsp;proper<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;garbage&nbsp;collection&nbsp;on&nbsp;class&nbsp;loader&nbsp;shutdown&nbsp;-&nbsp;we&nbsp;cache&nbsp;it&nbsp;here&nbsp;anyway,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;in&nbsp;a&nbsp;GC-friendly&nbsp;manner.&nbsp;In&nbsp;contrast&nbsp;to&nbsp;CachedIntrospectionResults,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Introspector&nbsp;does&nbsp;not&nbsp;use&nbsp;WeakReferences&nbsp;as&nbsp;values&nbsp;of&nbsp;its&nbsp;WeakHashMap!</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class&nbsp;classToFlush&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;clazz;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">do</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Introspector.flushFromCaches(classToFlush);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classToFlush&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;classToFlush.getSuperclass();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">while</span><span style="color: #000000;">&nbsp;(classToFlush&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">);</span></div>
<br>说到这里，spring中有一个比较少人注意的Listener——org.springframework.web.util.IntrospectorCleanupListener,这个类的说明如下：<br><br>它是一个在web应用关闭的时候,清除JavaBeans Introspector缓存的监听器.在web.xml中注册这个listener.可以保证在web 应用关闭的时候释放与掉这个web 应用相关的class loader 和由它加载的类<br>&nbsp;<br>如果你使用了JavaBeans Introspector来分析应用中的类,系统级Introspector 缓冲中会保留这些类的hard引用。结果在你的web应用关闭的时候,这些类以及web 应用相关的class loader没有被垃圾收集器回收.<br>&nbsp;<br>不幸的是,清除Introspector的唯一方式是刷新整个缓存。这是因为我们没法判断哪些是属于你的应用的引用.所以删除被缓冲的introspection会导致把这台server上的所有应用的introspection（内省）结果都删掉.<br>&nbsp;<br>需要注意的是,<span style="font-weight: bold;">spring容器托管的bean不需要使用这个监听器.因为spring它自己的introspection所使用的缓冲在分析完一个类之后会被马上从javaBeans Introspector缓冲中清除掉(上面提到的代码说明了这一点）</span>。<br><br>一般的应用基本不会直接用到JavaBean的内省方法，所以一般不用考虑遇到此类内省资源泄露，但是，很多的类库或者框架（比如struts,Quartz）没有清除Introspector。这个Listener就是为它们&#8220;擦屁股&#8221;的。<span style="font-weight: bold;">请注意，这个监听器需要注册在web.xml中的所有应用监听器之前（比如ContentLoaderListener之前）</span>。<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">listener</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">&lt;</span><span style="color: #800000;">listener-class</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">org.springframework.web.util.IntrospectorCleanupListener</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">listener-class</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;"><br></span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">listener</span><span style="color: #0000ff;">&gt;</span></div>
<br>&nbsp;&nbsp;&nbsp; 参考资料：<br>&nbsp;《深入Java虚拟机》<br>&nbsp;《<a href="http://bbs.cjsdn.net/post/view?bid=1&amp;id=103188&amp;age=0">Class对象什么时候被回收？</a>》<br>&nbsp;《<a href="http://www.springframework.org/docs/api/org/springframework/web/util/IntrospectorCleanupListener.html">Spring API Doc</a>》<br>&nbsp; 《<a href="http://calvin.redsaga.com/viewthread.php?tid=391">ss目前的设计有引起内存泄露而导致down机的隐患</a>》
<br>&nbsp;以及一篇非常好的解释java引用类的文章《<a href="http://java.chinaitlab.com/oop/716371.html">Java对象的强、软、弱和虚引用</a>》<br><br><br><br><br>   <img src ="http://www.blogjava.net/killme2008/aggbug/126282.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-06-26 10:39 <a href="http://www.blogjava.net/killme2008/archive/2007/06/26/126282.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring源码分析：实现AOP（转载）</title><link>http://www.blogjava.net/killme2008/archive/2007/04/24/113094.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Tue, 24 Apr 2007 01:37:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/04/24/113094.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/113094.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/04/24/113094.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/113094.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/113094.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 这两天一直在读spring1.2的AOP实现源码，AOP实现原理说起来很简单，对于实现业务接口的对象使用java代理机制实现，而对于一般的类使用cglib库实现，但spring的实现还是比较复杂的，不过抓住了本质去看代码就容易多了。发现一篇04年写的<a href="http://starrynight.blogdriver.com/starrynight/162536.html">《spring源码分析：实现AOP》</a>，倒是不用自己再写了，04年的时候已经有很多人研读过spring的源码，而那时的我还在学校，对java半懂不懂的状态，就算到现在也不敢说真的懂了，继续学习、努力。文章如下：<br><br>&nbsp;&nbsp;&nbsp;
<div><strong> 我的问题</strong> </div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
为了完成公司应用开发平台的设计，这几天一直在研究Spring的扩展机制。Spring的核心无疑是BeanFactory，
ApplicationContext和AOP。在&#8220;Spring
AOP编程&#8221;教程的例子中，是由ProxyFactoryBean来实现的。问题来了，普通的bean和FactoryBean的配置完全是一样的。那
么，BeanFactory是如何区分普通的Bean和用作Proxy的FactoryBean的？ProxyFactoryBean又是怎样实现AOP
功能的？（本文还很不完善，我会继续修改。）</div>
<div>&nbsp;</div>
<div><strong> FactoryBean的职责</strong> <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FactoryBean在Spring中被当成一种特殊的bean，通过实现FactoryBean接口进行扩展。FactoryBean的职责是：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; l.封装了创建对象或查找对象的逻辑。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2.提供了一个中间层，用于支持AOP。</div>
<div><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们来看一个LocalStatelessSessionProxyFactoryBean的例子。首先，定义Stateless EJB的代理，id为ejbServiceProxy：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="ejbServiceProxy" class="LocalStatelessSessionProxyFactoryBean"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="jndiName"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;value&gt;myEjb&lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="businessInterface"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;com.mycompany.MyBusinessInterface&lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</div>
<div>&nbsp;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 然后，再将这个业务逻辑服务对象注入客户程序：<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean id="myAction" class = "samples.myAction"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="myService"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;ref bean="ejbServiceProxy"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/property&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;</div>
<div><br>&nbsp;&nbsp;&nbsp;&nbsp; 这样，客户程序并不知道myService的实现细节，Spring使用FactoryBean完成了两者之间的解耦。</div>
<div>&nbsp;</div>
<div><strong> 准备代码分析环境</strong> </div>
<div>&nbsp;&nbsp;&nbsp;&nbsp; 1.&nbsp;安装Eclipse和Spring IDE。<br>&nbsp;&nbsp;&nbsp;&nbsp; 2.&nbsp;下载Spring framework源代码，并导入Eclipse。<br>&nbsp;&nbsp;&nbsp;&nbsp; 3.&nbsp;在类路径创建log4j.properties配置文件，设置如下：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log4j.rootLogger=DEBUG, stdout<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log4j.appender.stdout=org.apache.log4j.ConsoleAppender<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;log4j.appender.stdout.layout=org.apache.log4j.PatternLayout<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log4j.appender.stdout.layout.ConversionPattern=%d{SSS} %p %c{2} - %m%n<br>&nbsp;&nbsp;&nbsp;&nbsp; 4.&nbsp;编写TestCase，跟踪Console窗口的debug信息。</div>
<br>
<div><strong> FactoryBean源代码分析</strong> <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
如果bean实现了FactoryBean接口，BeanFactory将把它作为一个bean工厂，而不是直接作为普通的bean。正常情况下，
BeanFactory的getBean（"bean"）返回FactoryBean生产的bean实例，如果要返回FactoryBean本身的实例，
使用getBean（"&amp;bean"）的调用方式。</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在分析ProxyFactoryBean之前，我们先分析BeanFactory，它是Spring Framework的基础。我们看看它是如何分别处理普通的Bean和FactoryBean的。</div>
<div>&nbsp;</div>
<div><strong> &nbsp;&nbsp;BeanFactory分析</strong> </div>
<div><strong> </strong> &nbsp;</div>
<div><strong> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BeanFactory类图</strong> <br></div>
<div align="center"><img src="http://starrynight.blogdriver.com/diary/starrynight/inc/spring_class1.gif" width="510"></div>
<div>&nbsp;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
如以上的类图所示，XmlBeanFactory继承了AbstactBeanFactory抽象类。AbstactBeanFactory类中使用了
Template
Method设计模式，其中的模板方法为getBeanDefinition（）和createBean（）两个抽象方法。其中
AbstractAutowireCapableBeanFactory类实现了getBeanDefinition（）方法，
DefaultAutowireCapableBeanFactory类实现了getBeanDefinition（）方法。当调用getBean（）方
法时，AbstractBeanFactory类定义的逻辑分别调用了这两个模板方法。</div>
<div><br><strong> &nbsp;&nbsp;&nbsp;&nbsp; BeanFactory类的调用顺序<br></strong> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们暂时不使用ApplicationContext，以简化分析过程。我在这里使用了&#8220;Spring AOP编程&#8221;的例子，请参照该教程阅读。首先，编写测试用例，代码如下：</div>
<div><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public class AopTest extends TestCase {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XmlBeanFactory factory = null;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected void setUp() throws Exception {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.setUp();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InputStream is = new FileInputStream("testaop.xml");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;factory = new XmlBeanFactory(is);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void testGetBean() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Bean bean = (Bean)factory.getBean("bean");</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; assertNotNull(bean);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bean.theMethod();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</div>
<div>&nbsp;</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
1.&nbsp;首先，XmlBeanFactory使用XmlBeanDefinitionReader读入testaop.xml配置文件，后者用
XmlBeanDefinitionParser和DefaultXmlBeanDefinitionParser进行分析，从中得到
BeanDefinition的信息，并保存在XmlBeanDefinitionReader的BeanDefinitionRegistry变量里。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2.&nbsp;客户程序调用getBean方法时，AbstractBeanFactory首先使用transFormedBeanName方法分析传入的Bean名称，判断客户程序需要FactoryBean本身，还是它所创建的Bean对象。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
3.&nbsp;接下来，如果bean被定义为singleton模式，AbstractBeanFactory调用createBean方法根据
BeanDefinition信息实例化bean类，然后将该bean实例传给getObjectForSharedInstance方法并返回
getObjectForSharedInstance的返回对象。GetObjectForSharedInstance方法摘要如类图所示，首先判断
bean是否继承了FactoryBean。如果是，返回FactoryBean的getObject方法（下节我将详细分析使用
ProxyFactoryBean如何实现AOP）；如果不是，返回bean对象。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 4.&nbsp;如果bean被定义为prototype模式，每次客户程序请求都会生成新的bean实例，因此，createBean方法直接实例化bean对象并返回。</div>
<div>&nbsp;</div>
<div><strong> &nbsp;&nbsp;ProxyFactoryBean如何实现AOP</strong> </div>
<strong> </strong>
<div><br>&nbsp;&nbsp;&nbsp;&nbsp; <strong> ProxyFactoryBean类图</strong> </div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FactoryBean接口如下图所示，共有三个方法getObject，getObjectType，和isSingleton。ProxyFactoryBean实现了FactoryBean接口，它的相关类图如下：</div>
<div>&nbsp;</div>
<div align="center"><img src="http://starrynight.blogdriver.com/diary/starrynight/inc/spring_class2.gif"></div>
<div align="center"><br>&nbsp;</div>
<div><strong> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;实现AOP的过程</strong> <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
如上图所示，ProxyFactoryBean类继承了AdvisedSupport类，后者继承了ProxyConfig类并定义了操作advisor
和interceptor的接口，以支持AOP。当BeanFactory实例化ProxyFactoryBean时，根据配置文件的定义将关于
advice，pointcut，advisor，所代理的接口和接口实现类的所有信息传给ProxyFactoryBean。</div>
<div>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
当客户程序调用BeanFactory的getBean方法时，ProxyFactory使用JdkDynamicAopProxy实例化
BeanImpl类，并用JdkDynamicAopProxy的invoke方法执行advice。至于执行advice的时机，由
ProxyFactoryBean调用RegexpMethodPointcutAdvisor进行判断。</div>
<br><br>  <img src ="http://www.blogjava.net/killme2008/aggbug/113094.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-04-24 09:37 <a href="http://www.blogjava.net/killme2008/archive/2007/04/24/113094.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>spring IOC容器实现探讨</title><link>http://www.blogjava.net/killme2008/archive/2007/04/20/112160.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Fri, 20 Apr 2007 03:59:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/04/20/112160.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/112160.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/04/20/112160.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/112160.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/112160.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;&nbsp; spring IOC容器的实现，一开始我被复杂的接口和类所掩埋，看不清整体的思路和设计，踟蹰于代码丛林中，摸不清前进的方向。一开始我就决定只研读以xml文件做配置文件的XmlFactoryBean的具体实现为主要目标，渐渐地有了点感觉，用UML把spring中的bean工厂体系展现出来之后就更清晰了，让你不得不感叹设计的精巧和复杂。本文只是我个人对spring...&nbsp;&nbsp;<a href='http://www.blogjava.net/killme2008/archive/2007/04/20/112160.html'>阅读全文</a><img src ="http://www.blogjava.net/killme2008/aggbug/112160.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-04-20 11:59 <a href="http://www.blogjava.net/killme2008/archive/2007/04/20/112160.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>org.springframework.beans简单解读</title><link>http://www.blogjava.net/killme2008/archive/2007/04/16/110889.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Mon, 16 Apr 2007 02:23:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/04/16/110889.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/110889.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/04/16/110889.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/110889.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/110889.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 这个包的说明是说主要是包括用于操作JavaBean的类和接口，将被大部分spring包使用。在读这个包的代码前，我特意将JavaBean规范读了一遍。JavaBean规范不仅仅是getter、setter，定义了一个完整的轻量级组件模型，事件、方法、属性、持久化等等支持均包含在内。JavaBean规范很明显是学习Delphi的组件模型，sun希望通过它来形成一个java组件的市场，可惜结果不如人意，JavaBean在GUI方面并未形成一个类似delphi控件市场；随着spring等轻量级框架的流行，而EJB重量级的组件模型被越来越多的人放弃，JavaBean反而在服务端模型方面占据了主流
。废话不提，这个包的核心接口和类就是BeanWrapper和BeanWrapperImpl，顾名思义，这个接口就是用于包装JavaBean的行为，诸如设置和获取属性，设置属性编辑器等（PropertyEditor）。看下这个包的核心类图：<br><img src="http://www.blogjava.net/images/blogjava_net/killme2008/spring-bean.jpg" border="0"><br>BeanWrapper接口继承了PropertyAccessor（用于属性访问和设置）和PropertyEditorRegistry（属性编辑器的获取和设置）,而BeanWrapperImpl除了实现BeanWrapper接口外还继承自PropertyEditorRegistrySupport 类。在PropertyEditorRegistrySupport
类中可以看到spring默认设置的一系列自定义PropertyEditor。比如：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;registerDefaultEditors()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.defaultEditors&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;HashMap(</span><span style="color: #000000;">32</span><span style="color: #000000;">);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Simple&nbsp;editors,&nbsp;without&nbsp;parameterization&nbsp;capabilities.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;The&nbsp;JDK&nbsp;does&nbsp;not&nbsp;contain&nbsp;a&nbsp;default&nbsp;editor&nbsp;for&nbsp;any&nbsp;of&nbsp;these&nbsp;target&nbsp;types.</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.defaultEditors.put(Class.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ClassEditor());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.defaultEditors.put(File.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;FileEditor());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.defaultEditors.put(InputStream.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;InputStreamEditor());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.defaultEditors.put(Locale.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;LocaleEditor());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.defaultEditors.put(Properties.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;PropertiesEditor());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.defaultEditors.put(Resource[].</span><span style="color: #0000ff;">class</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ResourceArrayPropertyEditor());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.defaultEditors.put(String[].</span><span style="color: #0000ff;">class</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;StringArrayPropertyEditor());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.defaultEditors.put(URL.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;URLEditor());<br><br>。。。。。。。</span></div>
<br>&nbsp;&nbsp;&nbsp; PropertyEditor的概念就是属性编辑器，或者说属性转换器，比如我们在spring的配置文件中设置某个bean的class，这是一个字符串，怎么转换为一个Class对象呢？通过上面注册的ClassEditor，看看这个类是怎么实现的：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;ClassEditor&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;PropertyEditorSupport&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;ClassLoader&nbsp;classLoader;<br><br></span><span style="color: #008000;"></span><span style="color: #000000;"></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp; </span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Create&nbsp;a&nbsp;default&nbsp;ClassEditor,&nbsp;using&nbsp;the&nbsp;given&nbsp;ClassLoader.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;classLoader&nbsp;the&nbsp;ClassLoader&nbsp;to&nbsp;use<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;(or&nbsp;&lt;code&gt;null&lt;/code&gt;&nbsp;for&nbsp;the&nbsp;thread&nbsp;context&nbsp;ClassLoader)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;ClassEditor(ClassLoader&nbsp;classLoader)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.classLoader&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(classLoader&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">?</span><span style="color: #000000;">&nbsp;classLoader&nbsp;:&nbsp;Thread.currentThread().getContextClassLoader());<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setAsText(String&nbsp;text)&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;IllegalArgumentException&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(StringUtils.hasText(text))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">try</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //调用辅助类，得到Class对象<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setValue(ClassUtils.forName(text.trim(),&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.classLoader));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(ClassNotFoundException&nbsp;ex)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;IllegalArgumentException(</span><span style="color: #000000;">"</span><span style="color: #000000;">Class&nbsp;not&nbsp;found:&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;ex.getMessage());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setValue(</span><span style="color: #0000ff;">null</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;String&nbsp;getAsText()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class&nbsp;clazz&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(Class)&nbsp;getValue();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(clazz&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">""</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(clazz.isArray())&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;clazz.getComponentType().getName()&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;ClassUtils.ARRAY_SUFFIX;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;clazz.getName();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></span></div>
&nbsp;&nbsp;&nbsp; 代码已经解释了一切，继承javabean的PropertyEditorSupport，自己实现转换即可。这个包另外就是定义了一个完整的异常体系，值的我们参考。另外一个值的注意的地方是CachedIntrospectionResults类的实现，这个类使用了单例模式，它的作用在于缓存JavaBean反省(Introspect）得到的信息，因为每次使用Introspector对获取JavaBean信息是个不小的性能开支。缓存使用的是WeakHashMap，而不是HashMap，看看spring的解释：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Map&nbsp;keyed&nbsp;by&nbsp;class&nbsp;containing&nbsp;CachedIntrospectionResults.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Needs&nbsp;to&nbsp;be&nbsp;a&nbsp;WeakHashMap&nbsp;with&nbsp;WeakReferences&nbsp;as&nbsp;values&nbsp;to&nbsp;allow<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;for&nbsp;proper&nbsp;garbage&nbsp;collection&nbsp;in&nbsp;case&nbsp;of&nbsp;multiple&nbsp;class&nbsp;loaders.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;Map&nbsp;classCache&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;Collections.synchronizedMap(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;WeakHashMap());</span></div>
<br>因为缓存使用的key是bean的Class对象（以保证唯一性），因此在应用存在多个class loaders的时候，为了保证垃圾收集的进行，不出现内存泄露而采用WeakHashMap，为了理解这一点，我用JProfiler测试了自定义ClassLoader情况下，内存堆的使用情况，从快照上看。在使用HashMap的情况下，因为测试的bean的Class对象被载入它的ClassLoader以及java.beans.BeanInfo，java.beans.PropertyDescriptor,java.lang.reflect.Method这四个对象强引用，而导致不可回收。而在使用WeakHashMap时，判断当载入bean的ClassLoader和载入CachedIntrospectionResults的ClassLoader是不同的时候，使用弱引用包装缓存对象，当垃圾收集起发现弱引用时将马上清除弱引用对象，该弱引用也将加入一个队列，而WeakHashMap将定时检查这个队列，当有新的弱引用达到时（意味着已经被回收）就清除相应的键值。请看：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;isCacheSafe(Class&nbsp;clazz)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">CachedIntrospectionResults的ClassLoader</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClassLoader&nbsp;cur&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;CachedIntrospectionResults.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">.getClassLoader();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">载入bean的ClassLoader</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClassLoader&nbsp;target&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;clazz.getClassLoader();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(target&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">||</span><span style="color: #000000;">&nbsp;cur&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;target)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">while</span><span style="color: #000000;">&nbsp;(cur&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cur&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;cur.getParent();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(cur&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;target)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;CachedIntrospectionResults&nbsp;forClass(Class&nbsp;beanClass)&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;BeansException&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"><img src="http://www.blogjava.net/Images/dot.gif"><br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;cacheSafe&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;isCacheSafe(beanClass);<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(cacheSafe)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classCache.put(beanClass,&nbsp;results);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br></span><span style="color: #008000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</span><span style="color: #008000;">弱引用&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; classCache.put(beanClass,&nbsp;new&nbsp;WeakReference(results));</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"><img src="http://www.blogjava.net/Images/dot.gif"></span></div>
<br>&nbsp;&nbsp;&nbsp; 不知道我的理解是否有误，如果有误，请不吝指出，谢谢。<br><br>    <img src ="http://www.blogjava.net/killme2008/aggbug/110889.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-04-16 10:23 <a href="http://www.blogjava.net/killme2008/archive/2007/04/16/110889.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>org.springframework.core.enums类图</title><link>http://www.blogjava.net/killme2008/archive/2007/04/11/109925.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Wed, 11 Apr 2007 07:57:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/04/11/109925.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/109925.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/04/11/109925.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/109925.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/109925.html</trackback:ping><description><![CDATA[&nbsp; &nbsp; 这个包按照说明是：Interfaces and classes for type-safe enum support on JDK &gt;= 1.3。提供类型安全的枚举类型。代码也是相当简单，枚举类型又分为静态类型和通用类型。静态类型其实跟jdk1.5引进的enum类型类似，都是以int类型做code，比如声明一个Dog类型：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Dog&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;StaticLabeledEnum&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #000000;"> </span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;Dog(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;code,&nbsp;String&nbsp;name)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">(code,&nbsp;name);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></span></div>
<br>然后就可以这样声明枚举类型了：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;Dog&nbsp;BORDER_COLLIE&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Dog(</span><span style="color: #000000;">13</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Border&nbsp;Collie</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;Dog&nbsp;WHIPPET&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Dog(</span><span style="color: #000000;">14</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Whippet</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;Dog&nbsp;GOLDEN_RETRIEVER&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Dog(</span><span style="color: #000000;">11</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;must&nbsp;set&nbsp;type&nbsp;to&nbsp;be&nbsp;recognized&nbsp;as&nbsp;a&nbsp;"Dog"</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Class&nbsp;getType()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;Dog.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;String&nbsp;getLabel()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Golden&nbsp;Retriever</span><span style="color: #000000;">"</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br></span></div>
<br>同时有一个静态枚举类型的处理类用于提取信息：StaticLabeledEnumResolver
——这个类继承自抽象类AbstractCachingLabeledEnumResolver，而抽象类实现了接口LabeledEnumResovler，看看这个接口就知道所谓处理类是干什么的了：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">interface</span><span style="color: #000000;">&nbsp;LabeledEnumResolver&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">获取某个类中声明的枚举类型，这些类型&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">必须是LabeledEnum的子类</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Set&nbsp;getLabeledEnumSet(Class&nbsp;type)&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;IllegalArgumentException;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Map&nbsp;getLabeledEnumMap(Class&nbsp;type)&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;IllegalArgumentException;<br>&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; //根据code获取枚举<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;LabeledEnum&nbsp;getLabeledEnumByCode(Class&nbsp;type,&nbsp;Comparable&nbsp;code)&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;IllegalArgumentException;<br><br>&nbsp;&nbsp;&nbsp; //根据lable获取枚举<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;LabeledEnum&nbsp;getLabeledEnumByLabel(Class&nbsp;type,&nbsp;String&nbsp;label)&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;IllegalArgumentException;<br><br>}<br></span></div>
<br>StaticLabeledEnumResolver
使用了单例模式，同时AbstractCachingLabeledEnumResolver定义了一个模板法方法并使用：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">abstract</span><span style="color: #000000;">&nbsp;Set&nbsp;findLabeledEnums(Class&nbsp;type);</span></div>
也是一个Template Method模式应用的例子。<br><br>所谓通用性的枚举类型，是指不定义成static，并且可以灵活使用其他类型做code的枚举，比如spring已经内置的3种：ShortCodedLabeledEnum
,StringCodeLabeledEnum和LetterCodeLabeledEnum，这些类都继承自AbstractLabeledEnum,类名已经显示了它们的用途，不再细说。这个包完整的类图如下：<br><img  src="http://www.blogjava.net/images/blogjava_net/killme2008/spring-enum.jpg" border="0">&nbsp;<br><br><img src ="http://www.blogjava.net/killme2008/aggbug/109925.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-04-11 15:57 <a href="http://www.blogjava.net/killme2008/archive/2007/04/11/109925.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>org.springframework.core简单分析</title><link>http://www.blogjava.net/killme2008/archive/2007/04/10/109675.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Tue, 10 Apr 2007 08:56:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/04/10/109675.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/109675.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/04/10/109675.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/109675.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/109675.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 这个包的类主要用于spring框架的异常处理和一些核心的助手类（与框架具体部分无关的）。<br>&nbsp;&nbsp;&nbsp; 这个包中主要应用到了简单工厂模式，用于判断jdk版本，根据jdk版本不同提供不同的集合类、当前方法栈信息等。我们来看看是如何判断当前用户的jdk版本的：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;"></span><span style="color: #008000;"></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;org.springframework.core;<br><br></span><span style="color: #008000;"></span><span style="color: #008000;"></span><span style="color: #000000;"></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;JdkVersion&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;JAVA_13&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;JAVA_14&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;JAVA_15&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;String&nbsp;javaVersion;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;majorJavaVersion&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;JAVA_13;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;javaVersion&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;System.getProperty(</span><span style="color: #000000;">"</span><span style="color: #000000;">java.version</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;should&nbsp;look&nbsp;like&nbsp;"1.4.1_02"</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(javaVersion.indexOf(</span><span style="color: #000000;">"</span><span style="color: #000000;">1.4.</span><span style="color: #000000;">"</span><span style="color: #000000;">)&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">1</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;majorJavaVersion&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;JAVA_14;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(javaVersion.indexOf(</span><span style="color: #000000;">"</span><span style="color: #000000;">1.5.</span><span style="color: #000000;">"</span><span style="color: #000000;">)&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">1</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;majorJavaVersion&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;JAVA_15;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;else&nbsp;leave&nbsp;as&nbsp;1.3&nbsp;default</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Return&nbsp;the&nbsp;full&nbsp;Java&nbsp;version&nbsp;string,&nbsp;as&nbsp;returned&nbsp;by<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&lt;code&gt;System.getProperty("java.version")&lt;/code&gt;.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;String&nbsp;getJavaVersion()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;javaVersion;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Get&nbsp;the&nbsp;major&nbsp;version&nbsp;code.&nbsp;This&nbsp;means&nbsp;we&nbsp;can&nbsp;do&nbsp;things&nbsp;like<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;&lt;code&gt;if&nbsp;(getMajorJavaVersion()&nbsp;&lt;&nbsp;JAVA_14)&lt;/code&gt;.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@return</span><span style="color: #008000;">&nbsp;a&nbsp;code&nbsp;comparable&nbsp;to&nbsp;the&nbsp;JAVA_XX&nbsp;codes&nbsp;in&nbsp;this&nbsp;class<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@see</span><span style="color: #008000;">&nbsp;#JAVA_13<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@see</span><span style="color: #008000;">&nbsp;#JAVA_14<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@see</span><span style="color: #008000;">&nbsp;#JAVA_15<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;getMajorJavaVersion()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;majorJavaVersion;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></span></div>
<br>直接获取系统的java.version属性来进行jdk版本的判断。而CollectionFactory依据这个类来创建不同的集合类型，如果是jdk1.4就优先使用jdk1.4的集合框架，再次选择Commons Collections，最后才不得已就使用jdk1.3的集合框架，这里比较有趣的是判断Commons Collections的方法就是尝试Class.forName一个Commons集合框架中的对象，如果成功，当然证明classpath有commons-collections.jar包：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Check&nbsp;whether&nbsp;JDK&nbsp;1.4+&nbsp;collections&nbsp;and/or<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">&nbsp;Commons&nbsp;Collections&nbsp;3.x&nbsp;are&nbsp;available.</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(JdkVersion.getMajorJavaVersion()&nbsp;</span><span style="color: #000000;">&gt;=</span><span style="color: #000000;">&nbsp;JdkVersion.JAVA_14)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info(</span><span style="color: #000000;">"</span><span style="color: #000000;">JDK&nbsp;1.4+&nbsp;collections&nbsp;available</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">try</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class.forName(COMMONS_COLLECTIONS_CLASS_NAME);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;commonsCollections3xAvailable&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info(</span><span style="color: #000000;">"</span><span style="color: #000000;">Commons&nbsp;Collections&nbsp;3.x&nbsp;available</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(ClassNotFoundException&nbsp;ex)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;commonsCollections3xAvailable&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br>然后就是一系列的getXXXIfPossible（）方法用以获取最优版本的集合类型，比如getLinkedHashMapIfPossible()：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;Map&nbsp;createLinkedMapIfPossible(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;initialCapacity)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(JdkVersion.getMajorJavaVersion()&nbsp;</span><span style="color: #000000;">&gt;=</span><span style="color: #000000;">&nbsp;JdkVersion.JAVA_14)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">Creating&nbsp;[java.util.LinkedHashMap]</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;Jdk14CollectionFactory.createLinkedHashMap(initialCapacity);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(commonsCollections3xAvailable)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">Creating&nbsp;[org.apache.commons.collections.map.LinkedMap]</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;CommonsCollectionFactory.createLinkedMap(initialCapacity);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.debug(</span><span style="color: #000000;">"</span><span style="color: #000000;">Falling&nbsp;back&nbsp;to&nbsp;[java.util.HashMap]&nbsp;for&nbsp;linked&nbsp;map</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;HashMap(initialCapacity);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
其中的Jdk14CollectionFactory
和CommonsCollectionFactory
也都是工厂类。可以看到，一个优秀的通用框架对于版本的兼容性非常重视。<br><br>&nbsp;&nbsp;&nbsp; 这个包中另外一个需要注意的就是用于spring AOP功能实现的辅助类——ControlFlow。ControlFlow按照rod johnson的说法就是用于获取当前调用的方法栈的具体信息。ControlFlow是一个接口，拥有3个方法用于判断当前方法栈的位置：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">interface</span><span style="color: #000000;">&nbsp;ControlFlow&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;查找当前方法调用是否则在某类中<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;clazz&nbsp;the&nbsp;clazz&nbsp;to&nbsp;look&nbsp;for<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;under(Class&nbsp;clazz);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;查找当前方法调用是否则在某类的某个方法中<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;according&nbsp;to&nbsp;the&nbsp;current&nbsp;stack&nbsp;trace.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;clazz&nbsp;the&nbsp;clazz&nbsp;to&nbsp;look&nbsp;for<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;methodName&nbsp;the&nbsp;name&nbsp;of&nbsp;the&nbsp;method&nbsp;to&nbsp;look&nbsp;for<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;under(Class&nbsp;clazz,&nbsp;String&nbsp;methodName);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;当前栈帧是否包含传入的记号<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;token&nbsp;the&nbsp;token&nbsp;to&nbsp;look&nbsp;for<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;underToken(String&nbsp;token);<br><br>}</span></div>
<br>然后根据jdk版本的不同采用不同的方式实现这个接口：Jdk14ControlFlow和Jdk13ControlFlow。这是典型的<span style="font-weight: bold;">策略模式</span>的应用。需要注意的是，这两个具体类的是放在工厂类ControlFlowFactory中作为内部类实现的:<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">abstract</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;ControlFlowFactory&nbsp;{<br>&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"><img src="http://www.blogjava.net/Images/dot.gif"><br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Jdk13ControlFlow&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;ControlFlow&nbsp;{<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"><img src="http://www.blogjava.net/Images/dot.gif"><br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Jdk14ControlFlow&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;ControlFlow&nbsp;{<br>&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"><img src="http://www.blogjava.net/Images/dot.gif"><br>}</span></div>
<br>在这里，我们可以学到的东西就如何去判断当前方法栈的信息？jdk1.4之前只能通过对StackTrace的字符串进行分析，而jdk1.4引入了java.lang.StackTraceElement用于获取当前方法调用所处的栈帧的信息，看看spring的使用方法，相当简单：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;Jdk14ControlFlow&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;ControlFlow&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;StackTraceElement[]&nbsp;stack;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;Jdk14ControlFlow()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.stack&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Throwable().getStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Searches&nbsp;for&nbsp;class&nbsp;name&nbsp;match&nbsp;in&nbsp;a&nbsp;StackTraceElement.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;under(Class&nbsp;clazz)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.notNull(clazz,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Class&nbsp;must&nbsp;not&nbsp;be&nbsp;null</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;className&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;clazz.getName();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;stack.length;&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.stack[i].getClassName().equals(className))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Searches&nbsp;for&nbsp;class&nbsp;name&nbsp;match&nbsp;plus&nbsp;method&nbsp;name&nbsp;match<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;in&nbsp;a&nbsp;StackTraceElement.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;under(Class&nbsp;clazz,&nbsp;String&nbsp;methodName)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.notNull(clazz,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Class&nbsp;must&nbsp;not&nbsp;be&nbsp;null</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.notNull(methodName,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">Method&nbsp;name&nbsp;must&nbsp;not&nbsp;be&nbsp;null</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;className&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;clazz.getName();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.stack.length;&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.stack[i].getClassName().equals(className)&nbsp;</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.stack[i].getMethodName().equals(methodName))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">true</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Leave&nbsp;it&nbsp;up&nbsp;to&nbsp;the&nbsp;caller&nbsp;to&nbsp;decide&nbsp;what&nbsp;matches.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Caller&nbsp;must&nbsp;understand&nbsp;stack&nbsp;trace&nbsp;format,&nbsp;so&nbsp;there's&nbsp;less&nbsp;abstraction.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">boolean</span><span style="color: #000000;">&nbsp;underToken(String&nbsp;token)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(token&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">false</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;StringWriter&nbsp;sw&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;StringWriter();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Throwable().printStackTrace(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;PrintWriter(sw));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;stackTrace&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;sw.toString();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;stackTrace.indexOf(token)&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">1</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></span></div>
<br>获取当前栈帧的信息，对于一般的java开发者没有什么意义，对于AOP的实现和框架开发者可能有比较重要的作用，我还未研读spring的aop部分，不敢妄言，留待以后解答，如果您已经研读过这部分代码，不吝赐教。<br><br>这个包另外的一个特点就是将java的反射API演示了一遍，特别是Constant.java（用于提取某个类public static final定义的常量）和ReflectiveVisitorHelper
（反射助手类），对于学习java反射技术也有不小的帮助。<br><br>  <img src ="http://www.blogjava.net/killme2008/aggbug/109675.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-04-10 16:56 <a href="http://www.blogjava.net/killme2008/archive/2007/04/10/109675.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>org.springframework.core.styler包解读</title><link>http://www.blogjava.net/killme2008/archive/2007/04/06/108946.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Fri, 06 Apr 2007 07:18:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/04/06/108946.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/108946.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/04/06/108946.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/108946.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/108946.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 这个包的说明是：Support for styling values as Strings, with ToStringCreator as central class.<br>这个包简单来说就是提供一个pretty-printing功能的辅助类，而ToStringCreator就是用于产生一个可以输出经过美化的value信息的toString()方法。使用方法参照spring的Test可以看到是这样：&nbsp;&nbsp;&nbsp;<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int</span><span style="color: #000000;">[]&nbsp;integers&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">[]&nbsp;{&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">3</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">4</span><span style="color: #000000;">&nbsp;};<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;str&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ToStringCreator(integers).toString();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span style="color: #000000;">"</span><span style="color: #000000;">[@</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;ObjectUtils.getIdentityHexString(integers)&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;array&lt;Integer&gt;[0,&nbsp;1,&nbsp;2,&nbsp;3,&nbsp;4]]</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;str);</span></div>
或者写个简单例子感受下：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;[]&nbsp;a</span><span style="color: #000000;">=</span><span style="color: #000000;">{</span><span style="color: #000000;">1</span><span style="color: #000000;">,</span><span style="color: #000000;">2</span><span style="color: #000000;">,</span><span style="color: #000000;">3</span><span style="color: #000000;">,</span><span style="color: #000000;">4</span><span style="color: #000000;">,</span><span style="color: #000000;">5</span><span style="color: #000000;">,</span><span style="color: #000000;">6</span><span style="color: #000000;">,</span><span style="color: #000000;">7</span><span style="color: #000000;">,</span><span style="color: #000000;">8</span><span style="color: #000000;">,</span><span style="color: #000000;">9</span><span style="color: #000000;">};<br>System.out.println(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ToStringCreator(a).toString());</span></div>
&nbsp;&nbsp;&nbsp;&nbsp; <br>输出：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">[@18558d2&nbsp;array</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">Integer</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">[</span><span style="color: #000000;">1</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">3</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">4</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">5</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">6</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">7</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">8</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">9</span><span style="color: #000000;">]]</span></div>
<br>&nbsp;&nbsp;&nbsp; 如果你接触过ruby，你应该很熟悉Object.inpsect这个功能，这里通过ToStringCreator包装的toString()方法也是产生类似的能够清晰显示对象内部结构信息的方法。spring应该是使用这些辅助类来报告清晰的错误信息或者提示信息。<br>&nbsp;&nbsp;&nbsp; 看看这个包的UML类图：<br><img src="http://www.blogjava.net/images/blogjava_net/killme2008/style.jpg" border="0"><br>&nbsp; &nbsp; 首先，你需要理解ToStringStyler和ValueStyle两个接口，ToStringStyler定义了描述一个输入的Value信息的基本模板方法：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">interface</span><span style="color: #000000;">&nbsp;ToStringStyler&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Style&nbsp;a&nbsp;&lt;code&gt;toString()&lt;/code&gt;'ed&nbsp;object&nbsp;before&nbsp;its&nbsp;fields&nbsp;are&nbsp;styled.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;buffer&nbsp;the&nbsp;buffer&nbsp;to&nbsp;print&nbsp;to<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;obj&nbsp;the&nbsp;object&nbsp;to&nbsp;style<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;styleStart(StringBuffer&nbsp;buffer,&nbsp;Object&nbsp;obj);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Style&nbsp;a&nbsp;&lt;code&gt;toString()&lt;/code&gt;'ed&nbsp;object&nbsp;after&nbsp;it's&nbsp;fields&nbsp;are&nbsp;styled.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;buffer&nbsp;the&nbsp;buffer&nbsp;to&nbsp;print&nbsp;to<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;obj&nbsp;the&nbsp;object&nbsp;to&nbsp;style<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;styleEnd(StringBuffer&nbsp;buffer,&nbsp;Object&nbsp;obj);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Style&nbsp;a&nbsp;field&nbsp;value&nbsp;as&nbsp;a&nbsp;string.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;buffer&nbsp;the&nbsp;buffer&nbsp;to&nbsp;print&nbsp;to<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;fieldName&nbsp;the&nbsp;he&nbsp;name&nbsp;of&nbsp;the&nbsp;field<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;value&nbsp;the&nbsp;field&nbsp;value<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;styleField(StringBuffer&nbsp;buffer,&nbsp;String&nbsp;fieldName,&nbsp;Object&nbsp;value);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Style&nbsp;the&nbsp;given&nbsp;value.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;buffer&nbsp;the&nbsp;buffer&nbsp;to&nbsp;print&nbsp;to<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;value&nbsp;the&nbsp;field&nbsp;value<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;styleValue(StringBuffer&nbsp;buffer,&nbsp;Object&nbsp;value);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Style&nbsp;the&nbsp;field&nbsp;separator.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;buffer&nbsp;buffer&nbsp;to&nbsp;print&nbsp;to<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;styleFieldSeparator(StringBuffer&nbsp;buffer);<br><br>}</span></div>
&nbsp;&nbsp;&nbsp; 这是典型的Template Method模式，而两个接口ToStringStyler、ValueStyler和它们的相应实现DefaultToStringStyler、DefaultValueStyler又是策略模式(Strategy)的应用体现。ValueStyler和DefaultValueStyler之间不仅仅是策略模式，同时也是visitor模式，请看DefaultValueStyler中一系列重载的visit方法，这些visit方法访问不同类型Value的内部结构并构造pretty格式的String返回，提供给ToStringStyler使用。<br>&nbsp;&nbsp;&nbsp; ToStringCreator是ToStringStyler的客户，它使用ToStringStyler调用产生优美格式打印，而ToStringStyler
其实又是使用ValueStyler是访问每个不同类型的子元素并返回优美格式的String。实现的相当精巧和灵活：<br>&nbsp;&nbsp;&nbsp;
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;ToStringCreator(Object&nbsp;obj,&nbsp;ToStringStyler&nbsp;styler)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Assert.notNull(obj,&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">The&nbsp;object&nbsp;to&nbsp;be&nbsp;styled&nbsp;is&nbsp;required</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.object&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;obj;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.styler&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(styler&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">?</span><span style="color: #000000;">&nbsp;styler&nbsp;:&nbsp;DEFAULT_TO_STRING_STYLER);<br></span><span style="color: #008000;">//</span><span style="color: #008000;">开始&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.styler.styleStart(this.buffer,&nbsp;this.object);</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;}<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;ToStringCreator&nbsp;append(String&nbsp;fieldName,&nbsp;</span><span style="color: #0000ff;">byte</span><span style="color: #000000;">&nbsp;value)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;append(fieldName,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Byte(value));<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"><img src="http://www.blogjava.net/Images/dot.gif">一系列不同类型的append方法<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;String&nbsp;toString()&nbsp;{<br></span><span style="color: #008000;">//</span><span style="color: #008000;">结束，并返回优美格式的String&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.styler.styleEnd(this.buffer,&nbsp;this.object);</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.buffer.toString();<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br> <img src ="http://www.blogjava.net/killme2008/aggbug/108946.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-04-06 15:18 <a href="http://www.blogjava.net/killme2008/archive/2007/04/06/108946.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JUnit源码分析(四)——从Decorator模式说起</title><link>http://www.blogjava.net/killme2008/archive/2007/04/06/108894.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Fri, 06 Apr 2007 04:39:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/04/06/108894.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/108894.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/04/06/108894.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/108894.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/108894.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 其实我这系列小文，名为源码分析，其实是自己读《设计模式》的读书笔记。Decorator模式在java的IO库中得到应用，java的IO库看起来复杂，其实理解了Decorator模式再回头看可以很好理解并使用。<br>&nbsp;&nbsp;&nbsp; Decorator模式，也就是装饰器模式，是对象结构型模式之一。<br><br>1.意图：动态地给一个对象添加一些额外的职责。给对象添加功能，我们首先想到的是继承，但是如果每增一个功能都需要继承，类的继承体系将无可避免地变的庞大和难以理解。面向对象设计的原则：优先使用组合，而非继承，继承的层次深度最好不过三。<br><br>2.适用场景：<br>1）在不影响其他对象的情况下，以动态、透明的方式给单个对象添加额外的责任<br>2）处理可以撤销的职责<br>3）为了避免类的数目爆炸，或者不能采用生成子类的方法进行扩展时<br><br>3.UML图和协作：<br><br><img src="http://www.blogjava.net/images/blogjava_net/killme2008/ShowImg.jpg" border="0"><br><br>Component——定义一个对象接口，可以给这些对象动态地添加职责<br><br>ConcreteComponent——定义一个对象，可以给这个对象添加职责<br><br>Decorator——维持一个指向Component的引用，并定义一个与Component一致的接口，作为装饰类的父类<br><br>ConcreteDecorator——具体装饰类<br><br>4.效果：<br>1）与静态继承相比，Decorator可以动态添加职责，更为灵活<br>2）避免产生复杂的类，通过动态添加职责，而不是一次性提供一个万能的接口<br>3）缺点是将产生比较多的小对象，对学习上有难度，显然，java.io就是这个问题<br><br>我们以一个例子来实现Decorator模式，假设这样一个场景：在某个应用中需要打印票据，我们写了一个PrintTicket接口，然后提供一个实现类（DefaultPrintTicket）实现打印的功能：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.rubyeye.design_pattern.decorator;<br></span><span style="color: #008000;">//</span><span style="color: #008000;">抽象component接口</span><span style="color: #008000;"><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">interface</span><span style="color: #000000;">&nbsp;PrintTicket&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;print();<br>}<br><br><br></span><span style="color: #008000;">//</span><span style="color: #008000;">默认实现类，打印票据</span><span style="color: #008000;"><br></span><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.rubyeye.design_pattern.decorator;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;DefaultPrintTicket&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;PrintTicket&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;print()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">ticket&nbsp;body</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></span></div>
<br>OK,我们的功能已经实现，我们还体现了针对接口编程的原则，替换一个新的打印方式很灵活，但是客户开始提需求了——人生无法避免的三件事：交税、死亡和需求变更。客户要求打印页眉，你首先想到的是继承：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.rubyeye.design_pattern.decorator;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;AnotherPrintTicket&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;PrintTicket&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;print()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">ticket&nbsp;header</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">ticket&nbsp;body</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></span></div>
<br>请注意，我们这里只是简单的示例，在实际项目中也许意味着添加一大段代码，并且需要修改打印票据本体的功能。需求接踵而至，客户要求添加打印页码，要求增加打印花纹，要求可以联打......你的类越来越庞大，直到你看见这个类都想吐的地步！-_-。让我们看看另一个方案，使用Decorator模式来动态地给打印增加一些功能，首先是实现一个Decorator，它需要保持一个到PrintTicket接口的引用：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.rubyeye.design_pattern.decorator;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;PrintTicketDecorator&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;PrintTicket&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;PrintTicket&nbsp;printTicket;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;PrintTicketDecorator(PrintTicket&nbsp;printTicket)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">this</span><span style="color: #000000;">.printTicket&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;printTicket;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">默认调用PrintTicket的print</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;print()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printTicket.print();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></span></div>
<br>然后，我们实现两个具体的装饰类——打印页眉和页脚：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.rubyeye.design_pattern.decorator;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;HeaderPrintTicket&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;PrintTicketDecorator&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;HeaderPrintTicket(PrintTicket&nbsp;printTicket){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">(printTicket);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;print()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">ticket&nbsp;header</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">.print();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></span></div>
<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.rubyeye.design_pattern.decorator;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;FooterPrintTicket&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;PrintTicketDecorator&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;FooterPrintTicket(PrintTicket&nbsp;printTicket)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">(printTicket);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;print()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">.print();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">ticket&nbsp;footer</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></span></div>
<br>&nbsp;&nbsp;&nbsp; 使用起来也很容易：<br>&nbsp;&nbsp;&nbsp;
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;com.rubyeye.design_pattern.decorator;<br><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;DecoratorTest&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@param</span><span style="color: #008000;">&nbsp;args<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;main(String[]&nbsp;args)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PrintTicket&nbsp;print</span><span style="color: #000000;">=</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;HeaderPrintTicket(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;FooterPrintTicket(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;DefaultPrintTicket()));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print.print();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></span></div>
<br>输出：<br>ticket header<br>ticket body<br>ticket footer<br><br>&nbsp;&nbsp;&nbsp; 了解了Decorator模式，我们联系了下JUnit里面的应用。作为一个测试框架，应该方便地支持二次开发，也许用户开发自己的TestCase，添加自定义的功能，比如执行重复测试、多线程测试等等。动态添加职责，而又不想使用静态继承，这正是Decorator使用的地方。在junit.extensions包中有一个TestDecorator，正是所有装饰类的父类，也是作为二次开发的基础，它实现了Test接口，而Test接口就是我们定义的抽象接口：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;TestDecorator&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;&nbsp;Test&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">保有一个指向Test的引用</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;Test&nbsp;fTest;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;TestDecorator(Test&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTest</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;test;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;basicRun(TestResult&nbsp;result) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; fTest.run(result);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;run(TestResult&nbsp;result)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; basicRun(result);<br>&nbsp;&nbsp; &nbsp; &nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"><br>}</span></div>
<br>Junit已经提供了两个装饰类：junit.extensions.ActiveTest用于处理多线程，junit.extensions.RepeatedTest用于执行重复测试，看看RepeatedTest是怎么实现的：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;RepeatedTest&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;&nbsp;TestDecorator&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">重复次数</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;fTimesRepeat;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;RepeatedTest(Test&nbsp;test,&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;repeat)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">(test);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTimesRepeat</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;repeat;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;run(TestResult&nbsp;result)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">重复执行</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;fTimesRepeat;&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(result.shouldStop())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">break</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">.run(result);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://www.blogjava.net/Images/dot.gif"><img src="http://www.blogjava.net/Images/dot.gif"><br>}</span></div>
<br>&nbsp;&nbsp;&nbsp; <span style="color: #000000;">RepeatedTest继承</span><span style="color: #000000;">TestDecorator ，覆写</span><span style="color: #000000;">run（TestReult result）方法，重复执行，super.run(result)将调用传入的TestCase的run(TestResult result)方法，这已经在</span><span style="color: #000000;"></span><span style="color: #000000;">TestDecorator默认实现。看看使用方式，使用装饰模式的好处不言而喻。<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">TestSuite&nbsp;suite&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TestSuite();<br>suite.addTest(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TestSetup(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;RepeatedTest(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Testmath(</span><span style="color: #000000;">"</span><span style="color: #000000;">testAdd</span><span style="color: #000000;">"</span><span style="color: #000000;">),</span><span style="color: #000000;">12</span><span style="color: #000000;">)));<br><br></span></div>
</span><span style="color: #0000ff;"></span> <img src ="http://www.blogjava.net/killme2008/aggbug/108894.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-04-06 12:39 <a href="http://www.blogjava.net/killme2008/archive/2007/04/06/108894.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JUnit源码分析 （三）——Template Method模式</title><link>http://www.blogjava.net/killme2008/archive/2007/04/06/108860.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Fri, 06 Apr 2007 01:54:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/04/06/108860.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/108860.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/04/06/108860.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/108860.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/108860.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 在JUnit执行测试时，我们经常需要初始化一些环境供测试代码使用，比如数据库连接、mock对象等等，这些初始化代码应当在每一个测试之前执行并在测试方法运行后清理。在JUnit里面就是相应的setUp和tearDown方法。如果没有这两个方法，那么我们要在每个测试方法的代码内写上一大堆重复的初始化和清理代码，这是多么愚蠢的做法。那么JUnit是怎么让setUp和tearDown在测试执行前后被调用的呢？<br>&nbsp;&nbsp;&nbsp; 如果你查看下TestCase方法，你会发现TestCase和TestSuite的run()方法都是将执行测试的任务委托给了TestResult，由TestResult去执行测试代码并收集测试过程中的信息（这里用到了Collecting Parameter模式）。<br>&nbsp;&nbsp;&nbsp;
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">&nbsp;&nbsp;&nbsp; public</span><span style="color: #000000;">&nbsp;TestResult&nbsp;run()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TestResult&nbsp;result</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;createResult();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;run(result);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;result;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Runs&nbsp;the&nbsp;test&nbsp;case&nbsp;and&nbsp;collects&nbsp;the&nbsp;results&nbsp;in&nbsp;TestResult.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;This&nbsp;is&nbsp;the&nbsp;template&nbsp;method&nbsp;that&nbsp;defines&nbsp;the&nbsp;control&nbsp;flow<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;for&nbsp;running&nbsp;a&nbsp;test&nbsp;case.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;run(TestResult&nbsp;result)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;result.run(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 我们直接找到TestResult，看看它的run方法：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Runs&nbsp;a&nbsp;TestCase.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;run(</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;TestCase&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;startTest(test);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Protectable&nbsp;p&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Protectable()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;protect()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Throwable&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test.runBare();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;runProtected(test,&nbsp;p);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;endTest(test);<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br>&nbsp;&nbsp;&nbsp; 这里实例化了一个内部类，内部类实现了<span style="color: #000000;">Protectable接口的 </span><span style="color: #000000;">protect()方法，并执行传入的TestCase的runBare()方法，显然，真正的测试代码在TestCase的runBare()方法中，让我们来看下：<br></span><span style="color: #000000;"><br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">将被子类实现</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setUp()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Throwable&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">同上，将被具体的TestCase实现</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;tearDown()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Throwable&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></span><span style="color: #008000;">&nbsp;&nbsp;&nbsp;&nbsp; /**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;模板方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Runs&nbsp;the&nbsp;bare&nbsp;test&nbsp;sequence.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;</span><span style="color: #808080;">@exception</span><span style="color: #008000;">&nbsp;Throwable&nbsp;if&nbsp;any&nbsp;exception&nbsp;is&nbsp;thrown<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;runBare()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Throwable&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setUp();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">try</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;runTest();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tearDown();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br></span></div>
</span><span style="color: #000000;">真相水落石出，对于每一个测试方法，都遵循这样的模板：setUp-&gt;执行测试 runTest()-&gt;tearDown。这正是模板方式模式的一个应用例子。什么是template method模式呢？<br><br>Template Method模式<br><br>类行为模式的一种<br>1.意图：定义一个操作中的算法的骨架，而将一些延迟步骤到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些步骤。<br>2.适用场景：<br>1）一次性实现算法的不变部分（基本骨架），将可变的行为留给子类来完成<br>2）子类中的公共部分（比如JUnit中的初始化和清理）被抽取到一个公共父类中以避免代码重复。<br>3）控制了子类的扩展，这里其实也有类似回调函数的性质，具体步骤先在骨架中注册，在具体执行时被回调。<br><br>3.UML图和结构<br>&nbsp;&nbsp;&nbsp; <img  src="http://www.blogjava.net/images/blogjava_net/killme2008/template.jpg" border="0"><br>&nbsp; 抽象父类定义了算法的基本骨架（模板方法），而不同的子类实现具体的算法步骤，客户端由此可以与算法的更改隔离。<br><br>4.效果：<br>1）模板方法是代码复用的基本技术，在类库中经常使用，可以减少大量的代码重复<br>2）通过隔离算法的不变和可变部分，增加了系统的灵活性，扩展算法的某些步骤将变的很容易。<br><br>&nbsp;&nbsp;&nbsp; 了解了Template Method模式之后，让我们回到JUnit的源码，看看runTest()方法，这里主要应用的是java的反射技术，对于学习反射技术的有参考价值：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;runTest()&nbsp;</span><span style="color: #0000ff;">throws</span><span style="color: #000000;">&nbsp;Throwable&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Method&nbsp;runMethod</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">try</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;runMethod</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;getClass().getDeclaredMethod(fName,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Class[</span><span style="color: #000000;">0</span><span style="color: #000000;">]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(NoSuchMethodException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fail(</span><span style="color: #000000;">"</span><span style="color: #000000;">Method&nbsp;\</span><span style="color: #000000;">""</span><span style="color: #000000;">+fName+</span><span style="color: #000000;">"</span><span style="color: #000000;">\</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;not&nbsp;found</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(runMethod&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">&amp;&amp;</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">!</span><span style="color: #000000;">Modifier.isPublic(runMethod.getModifiers()))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fail(</span><span style="color: #000000;">"</span><span style="color: #000000;">Method&nbsp;\</span><span style="color: #000000;">""</span><span style="color: #000000;">+fName+</span><span style="color: #000000;">"</span><span style="color: #000000;">\</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;should&nbsp;be&nbsp;public</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">try</span><span style="color: #000000;">&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;runMethod.invoke(</span><span style="color: #0000ff;">this</span><span style="color: #000000;">,&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Class[</span><span style="color: #000000;">0</span><span style="color: #000000;">]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(InvocationTargetException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.fillInStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;e.getTargetException();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">&nbsp;(IllegalAccessException&nbsp;e)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.fillInStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">throw</span><span style="color: #000000;">&nbsp;e;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br></span>&nbsp;&nbsp;&nbsp; <br><img src ="http://www.blogjava.net/killme2008/aggbug/108860.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-04-06 09:54 <a href="http://www.blogjava.net/killme2008/archive/2007/04/06/108860.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JUnit源码分析（二）——观察者模式</title><link>http://www.blogjava.net/killme2008/archive/2007/04/05/108743.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Thu, 05 Apr 2007 09:19:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/04/05/108743.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/108743.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/04/05/108743.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/108743.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/108743.html</trackback:ping><description><![CDATA[<br>&nbsp;&nbsp;&nbsp; 我们知道JUnit支持不同的使用方式：swt、swing的UI方式，甚至控制台方式，那么对于这些不同的UI我们如何提供统一的接口供它们获取测试过程的信息（比如出现的异常信息，测试成功，测试失败的代码行数等等）？我们试想一下这个场景，当一个error或者exception产生的时候，测试能够马上通知这些UI客户端：发生错误了，发生了什么错误，错误是什么等等。显而易见，这是一个订阅-发布机制应用的场景，应当使用观察者模式。那么什么是观察者模式呢？<br><br><span style="font-weight: bold;">观察者模式(Observer)<br><br></span>Observer是对象行为型模式之一<br><br>1.意图：定义对象间的一种一对多的依赖关系，当一个对象的状态发现改变时，所有依赖于它的对象都得到通知并被自动更新<br><br>2.适用场景：<br>1）当一个抽象模型有两个方面，其中一个方面依赖于另一个方面，通过观察者模式将这两者封装在不同的独立对象当中，以使它们可以独立的变化和复用<br>2）当一个对象改变时，需要同时改变其他对象，并且不知道其他对象的具体数目<br>3）当一个对象需要引用其他对象，但是你又不想让这个对象与其他对象产生紧耦合的时候<br><br>3.UML图：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img src="http://www.blogjava.net/images/blogjava_net/killme2008/observer.jpg" border="0"><br>&nbsp;&nbsp; Subject及其子类维护一个观察者列表，当需要通知所有的Observer对象时调用Nitify方法遍历Observer集合，并调用它们的update方法更新。而具体的观察者实现Observer接口（或者抽象类），提供具体的更新行为。其实看这张图，与Bridge有几分相似，当然两者的意图和适用场景不同。<br><br>4.效果：<br>1）目标和观察者的抽象耦合,目标仅仅与抽象层次的简单接口Observer松耦合，而没有与具体的观察者紧耦合<br>2）支持广播通信<br>3）缺点是可能导致意外的更新，因为一个观察者并不知道其他观察者，它的更新行为也许将导致一连串不可预测的更新的行为<br><br>5.对于观察者实现需要注意的几个问题：<br>1）谁来触发更新？最好是由Subject通知观察者更新，而不是客户，因为客户可能忘记调用Notify<br>2)可以通过显式传参来指定感兴趣的更新<br>3）在发出通知前，确保Subject对象状态的一致性，也就是Notify操作应该在最后被调用<br>4）当Subject和Observer的依赖关系比较复杂的时候，可以通过一个更新管理器来管理它们之间的关系，这是与中介者模式的结合应用。<br><br><br>&nbsp;&nbsp;&nbsp; 讨论完观察者模式，那我们来看JUnit是怎么实现这个模式的。在junit.framework包中我们看到了一个Observer接口——TestListener，看看它的代码：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;junit.framework;<br><br></span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;*&nbsp;A&nbsp;Listener&nbsp;for&nbsp;test&nbsp;progress<br>&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">interface</span><span style="color: #000000;">&nbsp;TestListener&nbsp;{<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;An&nbsp;error&nbsp;occurred.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addError(Test&nbsp;test,&nbsp;Throwable&nbsp;t);<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;A&nbsp;failure&nbsp;occurred.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addFailure(Test&nbsp;test,&nbsp;Throwable&nbsp;t);&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;A&nbsp;test&nbsp;ended.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;endTest(Test&nbsp;test);&nbsp;<br>&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;A&nbsp;test&nbsp;started.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;startTest(Test&nbsp;test);<br>}</span></div>
<br>&nbsp;&nbsp;&nbsp; 接口清晰易懂，就是一系列将测试过程的信息传递给观察者的操作。具体的子类将接受这些信息，并按照它们的方式显示给用户。<br>&nbsp;&nbsp;&nbsp;&nbsp; 比如，我们看看swing的UI中的TestRunner，它将这些信息显示在一个swing写的UI界面上：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;startTest(Test&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;showInfo(</span><span style="color: #000000;">"</span><span style="color: #000000;">Running:&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">+</span><span style="color: #000000;">test);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br></span><span style="color: #0000ff;">&nbsp;&nbsp;&nbsp; public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addError(Test&nbsp;test,&nbsp;Throwable&nbsp;t)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fNumberOfErrors.setText(Integer.toString(fTestResult.errorCount()));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;appendFailure(</span><span style="color: #000000;">"</span><span style="color: #000000;">Error</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;test,&nbsp;t);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addFailure(Test&nbsp;test,&nbsp;Throwable&nbsp;t)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fNumberOfFailures.setText(Integer.toString(fTestResult.failureCount()));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;appendFailure(</span><span style="color: #000000;">"</span><span style="color: #000000;">Failure</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;test,&nbsp;t);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></span><span style="color: #0000ff;">&nbsp;&nbsp;&nbsp; public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;endTest(Test&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;setLabelValue(fNumberOfRuns,&nbsp;fTestResult.runCount());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fProgressIndicator.step(fTestResult.wasSuccessful());<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></span></div>
<br>可以看到，它将错误信息，异常信息保存在List或者Vector集合内，然后显示在界面上：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;showErrorTrace()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;index</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;fFailureList.getSelectedIndex();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(index&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">-</span><span style="color: #000000;">1</span><span style="color: #000000;">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Throwable&nbsp;t</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(Throwable)&nbsp;fExceptions.elementAt(index);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(fTraceFrame&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTraceFrame</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TraceFrame();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTraceFrame.setLocation(</span><span style="color: #000000;">100</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">100</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTraceFrame.showTrace(t);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTraceFrame.setVisible(</span><span style="color: #0000ff;">true</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;showInfo(String&nbsp;message)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fStatusLine.setFont(PLAIN_FONT);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fStatusLine.setForeground(Color.black);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fStatusLine.setText(message);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;showStatus(String&nbsp;status)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fStatusLine.setFont(BOLD_FONT);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fStatusLine.setForeground(Color.red);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fStatusLine.setText(status);<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br>而Junit中的目标对象(Subject）就是TestResult对象，它有添加观察者的方法：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Registers&nbsp;a&nbsp;TestListener<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addListener(TestListener&nbsp;listener)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fListeners.addElement(listener);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></span></div>
<br>而通知观察者又是怎么做的呢？请看这几个方法，都是循环遍历观察者列表，并调用相应的更新方法：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Adds&nbsp;an&nbsp;error&nbsp;to&nbsp;the&nbsp;list&nbsp;of&nbsp;errors.&nbsp;The&nbsp;passed&nbsp;in&nbsp;exception&nbsp;caused&nbsp;the<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;error.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addError(Test&nbsp;test,&nbsp;Throwable&nbsp;t)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fErrors.addElement(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TestFailure(test,&nbsp;t));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(Enumeration&nbsp;e&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;fListeners.elements();&nbsp;e.hasMoreElements();)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((TestListener)&nbsp;e.nextElement()).addError(test,&nbsp;t);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Adds&nbsp;a&nbsp;failure&nbsp;to&nbsp;the&nbsp;list&nbsp;of&nbsp;failures.&nbsp;The&nbsp;passed&nbsp;in&nbsp;exception&nbsp;caused<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;the&nbsp;failure.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addFailure(Test&nbsp;test,&nbsp;AssertionFailedError&nbsp;t)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fFailures.addElement(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TestFailure(test,&nbsp;t));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(Enumeration&nbsp;e&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;fListeners.elements();&nbsp;e.hasMoreElements();)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((TestListener)&nbsp;e.nextElement()).addFailure(test,&nbsp;t);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Registers&nbsp;a&nbsp;TestListener<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addListener(TestListener&nbsp;listener)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fListeners.addElement(listener);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Informs&nbsp;the&nbsp;result&nbsp;that&nbsp;a&nbsp;test&nbsp;was&nbsp;completed.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">synchronized</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;endTest(Test&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(Enumeration&nbsp;e&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;fListeners.elements();&nbsp;e.hasMoreElements();)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((TestListener)&nbsp;e.nextElement()).endTest(test);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br><br>使用这个模式后带来的好处：<br>1)上面提到的Subject与Observer的抽象耦合，使JUnit可以支持不同的使用方式<br>2）支持了广播通信，目标对象不关心有多少对象对自己注册，它只是通知注册的观察者<br><br>最后，我实现了一个简单的ConsoleRunner，在控制台执行JUnit,比如我们写了一个简单测试：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;junit.samples;<br><br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;junit.framework.</span><span style="color: #000000;">*</span><span style="color: #000000;">;<br><br></span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;*&nbsp;Some&nbsp;simple&nbsp;tests.<br>&nbsp;*&nbsp;<br>&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;SimpleTest&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;TestCase&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;fValue1;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">protected</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;fValue2;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;SimpleTest(String&nbsp;name)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">super</span><span style="color: #000000;">(name);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;setUp()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fValue1&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">2</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fValue2&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">3</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;testAdd()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">double</span><span style="color: #000000;">&nbsp;result&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;fValue1&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;fValue2;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">assert</span><span style="color: #000000;">(result&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">5</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;testEquals()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span style="color: #000000;">12</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">12</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span style="color: #000000;">12L</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">12L</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Long(</span><span style="color: #000000;">12</span><span style="color: #000000;">),&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Long(</span><span style="color: #000000;">12</span><span style="color: #000000;">));<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span style="color: #000000;">"</span><span style="color: #000000;">Size</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">12</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">12</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assertEquals(</span><span style="color: #000000;">"</span><span style="color: #000000;">Capacity</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">12.0</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">11.99</span><span style="color: #000000;">,&nbsp;</span><span style="color: #000000;">0.01</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</span></div>
<br>使用ConsoleRunner调用这个测试，代码很简单，不多做解释了：<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;net.rubyeye.junit.framework;<br><br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.lang.reflect.Method;<br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.util.ArrayList;<br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.util.List;<br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;java.util.Vector;<br><br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;junit.framework.Test;<br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;junit.framework.TestListener;<br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;junit.framework.TestResult;<br></span><span style="color: #0000ff;">import</span><span style="color: #000000;">&nbsp;junit.samples.SimpleTest;<br></span><span style="color: #008000;">//</span><span style="color: #008000;">实现观察者接口</span><span style="color: #008000;"><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;ConsoleRunner&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;TestListener&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;TestResult&nbsp;fTestResult;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;Vector&nbsp;fExceptions;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;Vector&nbsp;fFailedTests;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;List&nbsp;fFailureList;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;ConsoleRunner()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fExceptions&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Vector();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fFailedTests&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Vector();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fFailureList&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ArrayList();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;endTest(Test&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">测试结束：</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;message&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;test.toString();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(fTestResult.wasSuccessful())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(message&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;测试成功!</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(fTestResult.errorCount()&nbsp;</span><span style="color: #000000;">==</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">1</span><span style="color: #000000;">)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(message&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;had&nbsp;an&nbsp;error</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">else</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(message&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;had&nbsp;a&nbsp;failure</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;fFailureList.size();&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(fFailureList.get(i));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;fFailedTests.size();&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(fFailureList.get(i));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;fExceptions.size();&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(fFailureList.get(i));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">------------------------</span><span style="color: #000000;">"</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;startTest(Test&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span style="color: #000000;">"</span><span style="color: #000000;">开始测试:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;test);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;TestResult&nbsp;createTestResult()&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;TestResult();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;String&nbsp;truncateString(String&nbsp;s,&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;length)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(s.length()&nbsp;</span><span style="color: #000000;">&gt;</span><span style="color: #000000;">&nbsp;length)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;s.substring(</span><span style="color: #000000;">0</span><span style="color: #000000;">,&nbsp;length)&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;"><img src="http://www.blogjava.net/Images/dot.gif"></span><span style="color: #000000;">"</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">return</span><span style="color: #000000;">&nbsp;s;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addError(Test&nbsp;test,&nbsp;Throwable&nbsp;t)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(fTestResult.errorCount());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;appendFailure(</span><span style="color: #000000;">"</span><span style="color: #000000;">Error</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;test,&nbsp;t);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addFailure(Test&nbsp;test,&nbsp;Throwable&nbsp;t)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(fTestResult.failureCount());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;appendFailure(</span><span style="color: #000000;">"</span><span style="color: #000000;">Failure</span><span style="color: #000000;">"</span><span style="color: #000000;">,&nbsp;test,&nbsp;t);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;appendFailure(String&nbsp;kind,&nbsp;Test&nbsp;test,&nbsp;Throwable&nbsp;t)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;kind&nbsp;</span><span style="color: #000000;">+=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">:&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;test;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String&nbsp;msg&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;t.getMessage();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(msg&nbsp;</span><span style="color: #000000;">!=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">null</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;kind&nbsp;</span><span style="color: #000000;">+=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">"</span><span style="color: #000000;">:</span><span style="color: #000000;">"</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">+</span><span style="color: #000000;">&nbsp;truncateString(msg,&nbsp;</span><span style="color: #000000;">100</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fFailureList.add(kind);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fExceptions.addElement(t);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fFailedTests.addElement(test);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;go(String&nbsp;args[])&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Method[]&nbsp;methods&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;SimpleTest.</span><span style="color: #0000ff;">class</span><span style="color: #000000;">.getDeclaredMethods();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;i&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #000000;">0</span><span style="color: #000000;">;&nbsp;i&nbsp;</span><span style="color: #000000;">&lt;</span><span style="color: #000000;">&nbsp;methods.length;&nbsp;i</span><span style="color: #000000;">++</span><span style="color: #000000;">)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">取所有以test开头的方法</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(methods[i].getName().startsWith(</span><span style="color: #000000;">"</span><span style="color: #000000;">test</span><span style="color: #000000;">"</span><span style="color: #000000;">))&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Test&nbsp;test&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;SimpleTest(methods[i].getName());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTestResult&nbsp;</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;createTestResult();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTestResult.addListener(ConsoleRunner.</span><span style="color: #0000ff;">this</span><span style="color: #000000;">);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">//</span><span style="color: #008000;">执行测试</span><span style="color: #008000;"><br></span><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test.run(fTestResult);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">static</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;main(String&nbsp;args[])&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;ConsoleRunner().go(args);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>}<br></span></div>
<br>  <img src ="http://www.blogjava.net/killme2008/aggbug/108743.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-04-05 17:19 <a href="http://www.blogjava.net/killme2008/archive/2007/04/05/108743.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JUnit源码分析（一）——Command模式和Composite模式</title><link>http://www.blogjava.net/killme2008/archive/2007/04/05/108702.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Thu, 05 Apr 2007 07:10:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/04/05/108702.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/108702.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/04/05/108702.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/108702.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/108702.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; JUnit的源码相比于spring和hibernate来说比较简单，但麻雀虽小，五脏俱全，其中用到了比较多的设计模式。很多人已经在网上分享了他们对JUnit源码解读心得，我这篇小文谈不出什么新意，本来不打算写，可最近工作上暂时无事可做，那就写写吧，结合《设计模式》来看看。<br>&nbsp;&nbsp;&nbsp; 我读的是JUnit3.0的源码，目前JUnit已经发布到4.0版本了，尽管有比较大的改进，但基本的骨架不变，读3.0是为了抓住重点，省去对旁支末节的关注。我们来看看JUnit的核心代码，也就是Junit.framework包，除了4个辅助类（Assert,AssertFailedError,Protectable,TestFailure），剩下的就是我们需要重点关注的了。我先展示一张UML类图：<br><br><img alt="" src="http://www.blogjava.net/images/blogjava_net/killme2008/junit_core.jpg">&nbsp;&nbsp;&nbsp; 我们先不去关注TestDecorator类（此处是Decorator模式，下篇文章再讲），看看Test接口，以及它的两个实现类TestCase和TestSuite。很明显，此处用到了Command模式，为什么要使用这个模式呢？让我们先来看看什么是Command模式。<br><br><span style="font-weight: bold;">Command模式<br><br></span>Command模式是行为型模式之一<br><br>1.意图：将一个请求封装为一个对象，从而使你可用不同的请求对客户进行参数化；对请求排队或者记录请求日志，以及支持可撤销的操作。<br>2.适用场景：<br>1）抽象出待执行的动作以参数化对象，Command模式是回调函数的面向对象版本。回调函数，我想大家都明白，函数在某处注册，然后在稍后的某个时候被调用。<br>2）可以在不同的时刻指定、排列和执行请求。<br>3）支持修改日志，当系统崩溃时，这些修改可以被重做一遍。<br>4）通过Command模式，你可以通过一个公共接口调用所有的事务，并且也易于添加新的事务。<br><br><br>3。UML图：<br>&nbsp;&nbsp;&nbsp; <img alt="" src="http://www.blogjava.net/images/blogjava_net/killme2008/command.jpg" height="275" width="408"><br><br>4.效果：<br>1）命令模式将调用操作的对象与如何实现该操作的对象解耦。<br>2）将命令当成一个头等对象，它们可以像一般对象那样进行操纵和扩展<br>3）可以将多个命令复合成一个命令，与Composite模式结合使用<br>4）增加新的命令很容易，隔离对现有类的影响<br>5）可以与备忘录模式配合，实现撤销功能。<br><br>&nbsp;&nbsp;&nbsp; 在了解了Command模式之后，那我们来看JUnit的源码，Test接口就是命令的抽象接口，而TestCase和TestSuite是具体的命令<br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #008000;">//</span><span style="color: #008000;">抽象命令接口</span><span style="color: #008000;"><br></span><span style="color: #0000ff;">package</span><span style="color: #000000;">&nbsp;junit.framework;<br><br></span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;*&nbsp;A&nbsp;&lt;em&gt;Test&lt;/em&gt;&nbsp;can&nbsp;be&nbsp;run&nbsp;and&nbsp;collect&nbsp;its&nbsp;results.<br>&nbsp;*<br>&nbsp;*&nbsp;</span><span style="color: #808080;">@see</span><span style="color: #008000;">&nbsp;TestResult<br>&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">interface</span><span style="color: #000000;">&nbsp;Test&nbsp;{<br><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Counts&nbsp;the&nbsp;number&nbsp;of&nbsp;test&nbsp;cases&nbsp;that&nbsp;will&nbsp;be&nbsp;run&nbsp;by&nbsp;this&nbsp;test.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">abstract</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">int</span><span style="color: #000000;">&nbsp;countTestCases();<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Runs&nbsp;a&nbsp;test&nbsp;and&nbsp;collects&nbsp;its&nbsp;result&nbsp;in&nbsp;a&nbsp;TestResult&nbsp;instance.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">abstract</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;run(TestResult&nbsp;result);<br>}<br><br></span><span style="color: #008000;">//</span><span style="color: #008000;">具体命令一</span><span style="color: #008000;"><br></span><span style="color: #000000;"><br></span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">abstract</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">class</span><span style="color: #000000;">&nbsp;TestCase&nbsp;</span><span style="color: #0000ff;">extends</span><span style="color: #000000;">&nbsp;Assert&nbsp;</span><span style="color: #0000ff;">implements</span><span style="color: #000000;">&nbsp;Test&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;the&nbsp;name&nbsp;of&nbsp;the&nbsp;test&nbsp;case<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">final</span><span style="color: #000000;">&nbsp;String&nbsp;fName;<br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp; <img src="http://www.blogjava.net/Images/dot.gif"><img src="http://www.blogjava.net/Images/dot.gif"><br><br>//具体命令二<br><br>public&nbsp;class&nbsp;TestSuite&nbsp;implements&nbsp;Test&nbsp;{<br>&nbsp;&nbsp;&nbsp; &nbsp;<img src="http://www.blogjava.net/Images/dot.gif"></span></div>
<br>由此带来的好处：<br>1.客户无需使用任何条件语句去判断测试的类型，可以用统一的方式调用测试和测试套件，解除了客户与具体测试子类的耦合<br>2.如果要增加新的TestCase也很容易，实现Test接口即可，不会影响到其他类。<br>3.很明显，TestSuite是通过组合多个TestCase的复合命令，这里使用到了Composite模式（组合）<br>4.尽管未实现redo和undo操作，但将来也很容易加入并实现。<br><br>&nbsp;&nbsp;&nbsp; 我们上面说到TestSuite组合了多个TestCase，应用到了Composite模式，那什么是Composite模式呢？具体来了解下。<br><br><span style="font-weight: bold;">Composite模式<br><br></span>composite模式是对象结构型模式之一。<span style="font-weight: bold;"><br></span>1.意图：将对象组合成树形结构以表示&#8220;部分——整体&#8221;的层次结构。使得用户对单个对象和组合结构的使用具有一致性。<br><br>2.适用场景：<br>1）想表示对象的部分-整体层次<br>2）希望用户能够统一地使用组合结构和单个对象。具体到JUnit源码，我们是希望用户能够统一地方式使用TestCase和TestSuite<br><br>3.UML图：<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <img alt=""  src="http://www.blogjava.net/images/blogjava_net/killme2008/composite.gif"><br><br>图中单个对象就是树叶（Leaf），而组合结构就是Compoiste，它维护了一个Leaf的集合。而Component是一个抽象角色，给出了共有接口和默认行为，也就是JUnit源码中的Test接口。<br><br>4.效果：<br>1）定义了基本对象和组合对象的类层次结构，通过递归可以产生更复杂的组合对象<br>2）简化了客户代码，客户可以使用一致的方式对待单个对象和组合结构<br>3）添加新的组件变的很容易。但这个会带来一个问题，你无法限制组件中的组件，只能靠运行时的检查来施加必要的约束条件<br><br>&nbsp;&nbsp;&nbsp; 具体到JUnit源码，单个对象就是TestCase，而复合结构就是TestSuite，Test是抽象角色只有一个run方法。TestSuite维护了一个TestCase对象的集合fTests：<br><br>
<div style="border: 1px solid #cccccc; padding: 4px 5px 4px 4px; background-color: #eeeeee; font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: #000000;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">private</span><span style="color: #000000;">&nbsp;Vector&nbsp;fTests</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">new</span><span style="color: #000000;">&nbsp;Vector(</span><span style="color: #000000;">10</span><span style="color: #000000;">);&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">/**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Adds&nbsp;a&nbsp;test&nbsp;to&nbsp;the&nbsp;suite.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;addTest(Test&nbsp;test)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fTests.addElement(test);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></span><span style="color: #008000;">&nbsp;&nbsp;&nbsp; /**</span><span style="color: #008000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;*&nbsp;Runs&nbsp;the&nbsp;tests&nbsp;and&nbsp;collects&nbsp;their&nbsp;result&nbsp;in&nbsp;a&nbsp;TestResult.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #008000;">*/</span><span style="color: #000000;"><br>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">public</span><span style="color: #000000;">&nbsp;</span><span style="color: #0000ff;">void</span><span style="color: #000000;">&nbsp;run(TestResult&nbsp;result)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">for</span><span style="color: #000000;">&nbsp;(Enumeration&nbsp;e</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;tests();&nbsp;e.hasMoreElements();&nbsp;)&nbsp;{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">if</span><span style="color: #000000;">&nbsp;(result.shouldStop()&nbsp;)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="color: #0000ff;">break</span><span style="color: #000000;">;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Test&nbsp;test</span><span style="color: #000000;">=</span><span style="color: #000000;">&nbsp;(Test)e.nextElement();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;test.run(result);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}</span></div>
<br>当执行run方法时遍历这个集合，调用里面每个TestCase对象的run()方法，从而执行测试。我们使用的时候仅仅需要把TestCase添加到集合内，然后用一致的方式（run方法）调用他们进行测试。<br><br>考虑使用Composite模式之后带来的好处：<br>1）JUnit可以统一地处理组合结构TestSuite和单个对象TestCase，避免了条件判断，并且可以递归产生更复杂的测试对象<br>2）很容易增加新的TestCase。<br><br><br>参考资料：《设计模式——可复用面向对象软件的基础》<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 《JUnit设计模式分析》 刘兵<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JUnit源码和文档<br><br><br><br><span style="font-weight: bold;"></span><br><br><br><br><br><span style="font-weight: bold;"></span><br>&nbsp;&nbsp;&nbsp;&nbsp; <br><img src ="http://www.blogjava.net/killme2008/aggbug/108702.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-04-05 15:10 <a href="http://www.blogjava.net/killme2008/archive/2007/04/05/108702.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>org.springframework.core.io包的类图</title><link>http://www.blogjava.net/killme2008/archive/2007/04/05/108598.html</link><dc:creator>dennis</dc:creator><author>dennis</author><pubDate>Thu, 05 Apr 2007 01:23:00 GMT</pubDate><guid>http://www.blogjava.net/killme2008/archive/2007/04/05/108598.html</guid><wfw:comment>http://www.blogjava.net/killme2008/comments/108598.html</wfw:comment><comments>http://www.blogjava.net/killme2008/archive/2007/04/05/108598.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/killme2008/comments/commentRss/108598.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/killme2008/services/trackbacks/108598.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; org.springframework.core.io包中主要是各样的Resource类，Spring的 <code class="interfacename">Resource</code> 接口是为了提供更强的访问底层资源能力的抽象。具体的用法参考spring reference。这个包的类图非常简单，已经有人画的很清晰了，我直接拿过来，来自：<a href="http://gocom.primeton.com/blog/index.php?op=ViewArticle&amp;articleId=93&amp;blogId=23">Spring代码赏析：Resource类层次</a> <br><br><img src="http://www.blogjava.net/images/blogjava_net/killme2008/spring00.gif" border="0"><br><br>相当巧妙的地方在于，为何AbstractResource的子类有的override了getFile()而有的没有？这是因为在AbstractResource的getFile()方法设计为抛出异常，如果子类没有重写此方法，说明子类不支持通过绝对路径查找资源的方式，而override的子类则提供自己的实现。这里通过类的层次设计，充分利用继承带来的优点，避免了大量的条件语句。<br> <img src ="http://www.blogjava.net/killme2008/aggbug/108598.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/killme2008/" target="_blank">dennis</a> 2007-04-05 09:23 <a href="http://www.blogjava.net/killme2008/archive/2007/04/05/108598.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>