﻿<?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-coolfiry-随笔分类-Java</title><link>http://www.blogjava.net/coolfiry/category/14260.html</link><description>认认真真做人,兢兢业业做事!</description><language>zh-cn</language><lastBuildDate>Fri, 12 Oct 2007 12:32:43 GMT</lastBuildDate><pubDate>Fri, 12 Oct 2007 12:32:43 GMT</pubDate><ttl>60</ttl><item><title>垃圾收集机制(Garbage Collection)批判</title><link>http://www.blogjava.net/coolfiry/archive/2007/10/12/152260.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Fri, 12 Oct 2007 02:43:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2007/10/12/152260.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/152260.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2007/10/12/152260.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/152260.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/152260.html</trackback:ping><description><![CDATA[<p><span>在Java版发表这篇文章，似乎有点把矛头指向Java了。其实不是，GC是所有新一代语言共有的特征，<br />
Python, Eiffel，C#，Roby等无一例外地都使用了GC机制。但既然Java中的GC最为著名，所以天塌<br />
下来自然应该抗着。</span></p>
<p><span>这篇短文源于comp.lang.</span><a href="http://www.chinaitpower.com/Dev/Programme/Java/index.html" target="_blank"><span>java</span></a><span>.programmer跟comp.lang.c++上发生的一场大辩论，支持C++和Java<br />
的两派不同势力展开了新世纪第一场冲突，跟贴发言超过350，两派都有名角压阵。C++阵营的擂主是<br />
Pete Becker，ACM会员，Dinkumware Ltd. 的技术副总监。此君精通C++和Java，开发过两种语言的<br />
核心类库，但是却对C++狂热之极，而对于Java颇不以为然。平时谈到Java的时候还好，一旦有人胆<br />
敢用Java来批判C++，立刻忍不住火爆脾气跳将出来，以坚韧不拔的毅力和大无畏精神与对手周旋，<br />
舌战群儒，哪怕只剩下一个人也要血战到底。这等奇人当真少见！我真奇怪他整天泡在usenet上，<br />
不用工作么？他的老板P.J. Plauger如此宽宏大量？Java阵营主角是一个网名Razzi的兄弟，另外有<br />
Sun公司大名鼎鼎的Peter van der Linden助阵，妙语连珠，寸土必争，加上人多势众，一度占据优势。<br />
C++阵营里大拿虽然很多，但是大多数没有Pete那么多闲工夫，例如Greg Comeau，Comeau公司老板，<br />
每次来个只言片语，实在帮不了Pete多大忙。但是自从C++阵营中冒出一个无名小子，网名Courage(勇气)，<br />
发动对Java GC机制的批判，形势为之一变。C++阵营眼下处于全攻之势，Java阵营疲于防守，只能<br />
招架说：&#8220;你们没有证据，没有统计资料&#8221;，形势很被动。</span></p>
<p><span>垃圾收集(GC)不是一直被Java fans用来炫耀，引以为傲的优点么？怎么成了弱点了？我大惑不解，定睛<br />
一看，才觉得此中颇有道理。</span></p>
<p><span>首先，Java Swing库存在大量资源泄漏问题，这一点SUN非常清楚，称之为bugs，正在极力修正。但是看来<br />
这里的问题恐怕不仅是库编写者的疏忽，可能根源在于深层的机制，未必能够轻易解决，搞不好要伤筋动骨。<br />
不过这个问题不是那么根本，C++阵营觉得如果抓住对方的弱点攻击，就算是占了上风也没什么说服力。谁<br />
没有缺点呢？于是反其道而行之，猛烈攻击Java阵营觉得最得意的东西，Java的GC机制本身。</span></p>
<p><span>首先来想一想，memory leak到底意味着什么。在C++中，new出来的对象没有delete，这就导致了memory<br />
leak。但是C++早就有了克服这一问题的办法——smart pointer。通过使用标准库里设计精致的auto_ptr<br />
以及各种STL容器，还有例如boost库(差不多是个准标准库了)中的四个smart pointers，C++</span><a href="http://www.chinaitpower.com/Dev/index.html" target="_blank"><span>程序</span></a><span>员只要<br />
花上一个星期的时间学习最新的资料，就可以拍着胸脯说：&#8220;我写的</span><a href="http://www.chinaitpower.com/Dev/index.html" target="_blank"><span>程序</span></a><span>没有memory leak!&#8221;。</span></p>
<p><span>相比之下，Java似乎更优秀，因为从一开始你就不用考虑什么特殊的机制，大胆地往前new，自有GC替你<br />
收拾残局。Java的GC实际上是</span><a name="baidusnap0"></a><span>JVM中的一个独立线程，采用不同的算法</span><a name="baidusnap4"></a><span>策略来收集heap中那些不再有<br />
reference指向的垃圾对象所占用的内存。但是，通常情况下，GC线程的优先级比较低，只有在当前</span><a href="http://www.chinaitpower.com/Dev/index.html" target="_blank"><span>程序</span></a><br />
<span>空闲的时候才会被调度，收集垃圾。当然，如果JVM感到内存紧张了，JVM会主动</span><a name="baidusnap1"></a><span>调用GC来收集垃圾，获取<br />
更多的内存。请注意，Java的GC工作的时机是：1. 当前</span><a href="http://www.chinaitpower.com/Dev/index.html" target="_blank"><span>程序</span></a><span>不忙，有空闲时间。2. 空闲内存不足。<br />
现在我们考虑一种常见的情况，</span><a href="http://www.chinaitpower.com/Dev/index.html" target="_blank"><span>程序</span></a><span>在紧张运行之中，没哟空闲时间给GC来运行，同时机器内存很大，<br />
JVM也没有感到内存不足，结果是什么？对了，GC形同虚设，得不到调用。于是，内存被不断吞噬，而那些<br />
早已经用不着的垃圾对象仍在在宝贵的内存里睡大觉。例如：</span></p>
<p><span>class BadGc {</span></p>
<p><span>&nbsp;&nbsp;&nbsp; public void job1() {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String garbage = "I am a garbage, and just sleeping in your precious memory, " +<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "how do you think you can deal with me? Daydreaming! HAHA!!!";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ....<br />
&nbsp;&nbsp;&nbsp; }</span></p>
<p><span>&nbsp;&nbsp;&nbsp; public void job2() {...}</span></p>
<p><span>&nbsp;&nbsp;&nbsp; ...<br />
&nbsp;&nbsp;&nbsp; ...</span></p>
<p><span>&nbsp;&nbsp;&nbsp; public void job1000() {...}</span></p>
<p><span>&nbsp;&nbsp;&nbsp; public static void main(String[] args) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bgc = new BadGc();<br />
&nbsp;bgc.job1();<br />
&nbsp;bgc.job2();<br />
&nbsp;...<br />
&nbsp;bgc.job1000();<br />
&nbsp;&nbsp;&nbsp; }<br />
}</span></p>
<p><span>运行中，虽然garbage对象在离开job1()之后，就再也没有用了。但是因为</span><a href="http://www.chinaitpower.com/Dev/index.html" target="_blank"><span>程序</span></a><span>忙，内存还够用，所以GC得<br />
不到调度，garbage始终不会被回收，直到</span><a href="http://www.chinaitpower.com/Dev/index.html" target="_blank"><span>程序</span></a><span>运行到bgc.job1000()时还躺在内存里嘲笑你。没辙吧！</span></p>
<p><span>好了，我承认这段</span><a href="http://www.chinaitpower.com/Dev/index.html" target="_blank"><span>程序</span></a><span>很傻。但是你不要以为这只是理论上的假设，恰恰相反，大多数实用中的Java</span><a href="http://www.chinaitpower.com/Dev/index.html" target="_blank"><span>程序</span></a><span>都有<br />
类似的效应。这就是为什么Java</span><a href="http://www.chinaitpower.com/Dev/index.html" target="_blank"><span>程序</span></a><span>狂耗内存，而且好像给它多少内存吃都不够。你花上大笔的银子把内存<br />
从128升到256，再升到512，结果是，一旦执行复杂任务，内存还是被轻易填满，而且多出来的这些内存只是<br />
用来装垃圾，GC还是不给面子地千呼万唤不出来。等到你的内存终于心力交瘁，GC才姗姗来迟，收拾残局。而<br />
且GC工作的方式也很不好评价，一种方法是一旦有机会回收内存，就把所有的垃圾都回收。你可以想象，这要<br />
花很长时间(几百M的垃圾啊！)，如果你这时侯正在压下开炮的按钮，GC却叫了暂定，好了，你等死吧！另一<br />
种方法，得到机会之后，回收一些内存，让JVM感到内存不那么紧张时就收手。结果呢，内存里始终有大批垃<br />
圾，</span><a href="http://www.chinaitpower.com/Dev/index.html" target="_blank"><span>程序</span></a><span>始终在半死不活的荡着。最后，GC可以每隔一段时间就运行一次，每次只回收一部分垃圾，这是现在<br />
大部分JVM的方式，结果是内存也浪费了，还动不动暂停几百毫秒。难啊！</span></p>
<p><span>反过来看看C++利用smart pointer达成的效果，一旦某对象不再被引用，</span><a href="http://www.chinaitpower.com/System/index.html" target="_blank"><span>系统</span></a><span>刻不容缓，立刻回收内存。这<br />
通常发生在关键任务完成后的清理(cleanup)时期，不会影响关键任务的实时性，同时，内存里所有的对象<br />
都是有用的，绝对没有垃圾空占内存。怎么样？传统、朴素的C++是不是更胜一筹？</span></p>
<p><span>据统计，目前的Java</span><a href="http://www.chinaitpower.com/Dev/index.html" target="_blank"><span>程序</span></a><span>运行期间占用的内存通常为对应C++</span><a href="http://www.chinaitpower.com/Dev/index.html" target="_blank"><span>程序</span></a><span>的4-20倍。除了其它的原因，上面所说的是一个<br />
非常主要的因素。我们对memory leak如此愤恨，不就是因为它导致大量的内存垃圾得不到清除吗？如果有了<br />
GC之后，垃圾比以前还来势汹汹，那么GC又有什么好处呢？</span></p>
<p><span>当然，C++的smart pointer现在会使用的人不多，所以现在的C++</span><a href="http://www.chinaitpower.com/Dev/index.html" target="_blank"><span>程序</span></a><span>普遍存在更严重的memory leak问题。<br />
但是，如果我奶奶跟舒马赫比赛车输掉了，你能够埋怨那辆车子么？<br />
http://www.594k.com/java/html/y2007m1/12051/</span></p>
<img src ="http://www.blogjava.net/coolfiry/aggbug/152260.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2007-10-12 10:43 <a href="http://www.blogjava.net/coolfiry/archive/2007/10/12/152260.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>从LiveJournal后台发展看大规模网站性能优化方法</title><link>http://www.blogjava.net/coolfiry/archive/2007/09/29/149681.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Sat, 29 Sep 2007 13:26:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2007/09/29/149681.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/149681.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2007/09/29/149681.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/149681.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/149681.html</trackback:ping><description><![CDATA[<h3>从LiveJournal后台发展看大规模网站性能优化方法</h3>
<h2>一、LiveJournal发展历程</h2>
<p><a href="http://www.livejournal.com/">LiveJournal</a>是99年始于校园中的项目，几个人出于爱好做了这样一个应用，以实现以下功能： </p>
<ul>
    <li>博客，论坛
    <li>社会性网络，找到朋友
    <li>聚合，把朋友的文章聚合在一起 </li>
</ul>
<p>LiveJournal采用了大量的开源软件，甚至它本身也是一个开源软件。 </p>
<p>在上线后，LiveJournal实现了非常快速的增长：</p>
<ul>
    <li>2004年4月份：280万注册用户。
    <li>2005年4月份：680万注册用户。
    <li>2005年8月份：790万注册用户。
    <li>达到了每秒钟上千次的页面请求及处理。
    <li>使用了大量MySQL服务器。
    <li>使用了大量通用组件。 </li>
</ul>
<h2>二、LiveJournal架构现状概况</h2>
<p><img height="445" alt="livejournal_backend.png" src="http://www.example.net.cn/archives/livejournal_backend.png" width="600" /><br />
<br />
</p>
<h2>三、从LiveJournal发展中学习</h2>
<p>&nbsp;</p>
<p>LiveJournal从1台服务器发展到100台服务器，这其中经历了无数的伤痛，但同时也摸索出了解决这些问题的方法，通过对LiveJournal的学习，可以让我们避免LJ曾经犯过的错误，并且从一开始就对系统进行良好的设计，以避免后期的痛苦。</p>
<p>下面我们一步一步看LJ发展的脚步。</p>
<div id="a000073more">
<div id="more">
<h2>1、一台服务器</h2>
<p>一 台别人捐助的服务器，LJ最初就跑在上面，就像Google开始时候用的破服务器一样，值得我们尊敬。这个阶段，LJ的人以惊人的速度熟悉的Unix的操 作管理，服务器性能出现过问题，不过还好，可以通过一些小修小改应付过去。在这个阶段里LJ把CGI升级到了FastCGI。</p>
<p>最终问题出现了，网站越来越慢，已经无法通过优过化来解决的地步，需要更多的服务器，这时LJ开始提供付费服务，可能是想通过这些钱来购买新的服务器，以解决当时的困境。<br />
毫无疑问，当时LJ存在巨大的单点问题，所有的东西都在那台服务器的铁皮盒子里装着。</p>
<p><img height="187" alt="LJ-backend-7.png" src="http://www.example.net.cn/archives/LJ-backend-7.png" width="500" /></p>
<h2>2、两台服务器</h2>
<p>用付费服务赚来的钱LJ买了两台服务器：一台叫做Kenny的Dell 6U机器用于提供Web服务，一台叫做Cartman的Dell 6U服务器用于提供数据库服务。</p>
<p><img height="279" alt="LJ-backend-8.png" src="http://www.example.net.cn/archives/LJ-backend-8.png" width="218" /></p>
<p>LJ有了更大的磁盘，更多的计算资源。但同时网络结构还是非常简单，每台机器两块网卡，Cartman通过内网为Kenny提供MySQL数据库服务。<br />
<br />
暂时解决了负载的问题，新的问题又出现了：</p>
<ul>
    <li>原来的一个单点变成了两个单点。
    <li>没有冷备份或热备份。
    <li>网站速度慢的问题又开始出现了，没办法，增长太快了。
    <li>Web服务器上CPU达到上限，需要更多的Web服务器。 </li>
</ul>
<h2>3、四台服务器</h2>
<p>又买了两台，Kyle和Stan，这次都是1U的，都用于提供Web服务。目前LJ一共有3台Web服务器和一台数据库服务器。这时需要在3台Web服务器上进行负载均横。</p>
<p><img height="241" alt="LJ-backend-9.png" src="http://www.example.net.cn/archives/LJ-backend-9.png" width="499" /></p>
<p>LJ把Kenny用于外部的网关，使用mod_backhand进行负载均横。</p>
<p>然后问题又出现了：</p>
<ul>
    <li>单点故障。数据库和用于做网关的Web服务器都是单点，一旦任何一台机器出现问题将导致所有服务不可用。虽然用于做网关的Web服务器可以通过保持心跳同步迅速切换，但还是无法解决数据库的单点，LJ当时也没做这个。
    <li>网站又变慢了，这次是因为IO和数据库的问题，问题是怎么往应用里面添加数据库呢？ </li>
</ul>
<h2>4、五台服务器</h2>
<p>又买了一台数据库服务器。在两台数据库服务器上使用了数据库同步(Mysql支持的Master-Slave模式)，写操作全部针对主数据库（通过Binlog，主服务器上的写操作可以迅速同步到从服务器上），读操作在两个数据库上同时进行(也算是负载均横的一种吧)。</p>
<p><img height="265" alt="LJ-backend-10.png" src="http://www.example.net.cn/archives/LJ-backend-10.png" width="500" /></p>
<p>实现同步时要注意几个事项：</p>
<ul>
    <li>读操作数据库选择算法处理，要选一个当前负载轻一点的数据库。
    <li>在从数据库服务器上只能进行读操作
    <li>准备好应对同步过程中的延迟，处理不好可能会导致数据库同步的中断。只需要对写操作进行判断即可，读操作不存在同步问题。 </li>
</ul>
<h2>5、更多服务器</h2>
<p>有钱了，当然要多买些服务器。部署后快了没多久，又开始慢了。这次有更多的Web服务器，更多的数据库服务器，存在 IO与CPU争用。于是采用了BIG-IP作为负载均衡解决方案。</p>
<p><img height="483" alt="LJ-backend-11.png" src="http://www.example.net.cn/archives/LJ-backend-11.png" width="453" /></p>
<h2>6、现在我们在哪里：</h2>
<p><img height="357" alt="LJ-backend-1.png" src="http://www.example.net.cn/archives/LJ-backend-1.png" width="600" /></p>
<p>现在服务器基本上够了，但性能还是有问题，原因出在架构上。</p>
<p>数据库的架构是最大的问题。由于增加的数据库都是以Slave模式添加到应用内，这样唯一的好处就是将读操作分布到了多台机器，但这样带来的后果就是写操作被大量分发，每台机器都要执行，服务器越多，浪费就越大，随着写操作的增加，用于服务读操作的资源越来越少。</p>
<p><img height="195" alt="LJ-backend-2.png" src="http://www.example.net.cn/archives/LJ-backend-2.png" width="500" /></p>
<p>由一台分布到两台</p>
<p><img height="273" alt="LJ-backend-3.png" src="http://www.example.net.cn/archives/LJ-backend-3.png" width="500" /></p>
<p>最终效果</p>
<p>现在我们发现，我们并不需要把这些数据在如此多的服务器上都保留一份。服务器上已经做了RAID，数据库也进行了备份，这么多的备份完全是对资源的浪费，属于冗余极端过度。那为什么不把数据分布存储呢？</p>
<p>问题发现了，开始考虑如何解决。现在要做的就是把不同用户的数据分布到不同的服务器上进行存储，以实现数据的分布式存储，让每台机器只为相对固定的用户服务，以实现平行的架构和良好的可扩展性。</p>
<p>为 了实现用户分组，我们需要为每一个用户分配一个组标记，用于标记此用户的数据存放在哪一组数据库服务器中。每组数据库由一个master及几个slave 组成，并且slave的数量在2-3台，以实现系统资源的最合理分配，既保证数据读操作分布，又避免数据过度冗余以及同步操作对系统资源的过度消耗。</p>
<p><img height="324" alt="LJ-backend-4.png" src="http://www.example.net.cn/archives/LJ-backend-4.png" width="500" /></p>
<p>由一台（一组）中心服务器提供用户分组控制。所有用户的分组信息都存储在这台机器上，所有针对用户的操作需要先查询这台机器得到用户的组号，然后再到相应的数据库组中获取数据。</p>
<p>这样的用户架构与目前LJ的架构已经很相像了。</p>
<p>在具体的实现时需要注意几个问题：</p>
<ul>
    <li>在数据库组内不要使用自增ID，以便于以后在数据库组之间迁移用户，以实现更合理的I/O，磁盘空间及负载分布。
    <li>将userid，postid存储在全局服务器上，可以使用自增，数据库组中的相应值必须以全局服务器上的值为准。全局服务器上使用事务型数据库InnoDB。
    <li>在数据库组之间迁移用户时要万分小心，当迁移时用户不能有写操作。 </li>
</ul>
<h2>7、现在我们在哪里</h2>
<p><img height="365" alt="LJ-backend-5.png" src="http://www.example.net.cn/archives/LJ-backend-5.png" width="500" /></p>
<p>问题：</p>
<ul>
    <li>一个全局主服务器，挂掉的话所有用户注册及写操作就挂掉。
    <li>每个数据库组一个主服务器，挂掉的话这组用户的写操作就挂掉。
    <li>数据库组从服务器挂掉的话会导致其它服务器负载过大。 </li>
</ul>
<p>对于Master-Slave模式的单点问题，LJ采取了Master-Master模式来解决。所谓Master-Master实际上是人工实现的，并不是由MySQL直接提供的，实际上也就是两台机器同时是Master，也同时是Slave，互相同步。</p>
<p>Master-Master实现时需要注意：</p>
<ul>
    <li>一个Master出错后恢复同步，最好由服务器自动完成。
    <li>数字分配，由于同时在两台机器上写，有些ID可能会冲突。 </li>
</ul>
<p>解决方案：<br />
</p>
<ul>
    <li>奇偶数分配ID，一台机器上写奇数，一台机器上写偶数
    <li>通过全局服务器进行分配(LJ采用的做法)。 </li>
</ul>
<p>&nbsp;</p>
<p>Master-Master模式还有一种用法，这种方法与前一种相比，仍然保持两台机器的同步，但只有一台机器提供服务（读和写），在每天晚上的时候进行轮换，或者出现问题的时候进行切换。</p>
<h2>8、现在我们在哪里</h2>
<p><img height="349" alt="LJ-backend-6.png" src="http://www.example.net.cn/archives/LJ-backend-6.png" width="500" /></p>
<p>现在插播一条广告，MyISAM VS InnoDB。</p>
<p>使用InnoDB：</p>
<ul>
    <li>支持事务
    <li>需要做更多的配置，不过值得，可以更安全的存储数据，以及得到更快的速度。 </li>
</ul>
<p>使用MyISAM：</p>
<ul>
    <li>记录日志（LJ用它来记网络访问日志）
    <li>存储只读静态数据，足够快。
    <li>并发性很差，无法同时读写数据（添加数据可以）
    <li>MySQL非正常关闭或死机时会导致索引错误，需要使用myisamchk修复，而且当访问量大时出现非常频繁。 </li>
</ul>
<h2>9、缓存</h2>
<p>去年我写过<a href="http://www.example.net.cn/archives/2006/01/eoamemcachedoea.html">一篇文章介绍memcached</a>，它就是由LJ的团队开发的一款缓存工具，以key-value的方式将数据存储到分布的内存中。LJ缓存的数据：</p>
<ul>
    <li>12台独立服务器（不是捐赠的）
    <li>28个实例
    <li>30GB总容量
    <li>90-93%的命中率（用过squid的人可能知道，squid内存加磁盘的命中率大概在70-80%） </li>
</ul>
<p>如何建立缓存策略？</p>
<p>想缓存所有的东西？那是不可能的，我们只需要缓存已经或者可能导致系统瓶颈的地方，最大程度的提交系统运行效率。通过对MySQL的日志的分析我们可以找到缓存的对象。</p>
<p>缓存的缺点？</p>
<ul>
    <li>没有完美的事物，缓存也有缺点：
    <li>增大开发量，需要针对缓存处理编写特殊的代码。
    <li>管理难度增加，需要更多人参与系统维护。
    <li>当然大内存也需要钱。 </li>
</ul>
<h2>10、Web访问负载均衡</h2>
<p>在数据包级别使用BIG-IP，但BIG-IP并不知道我们内部的处理机制，无法判断由哪台服务器对这些请求进行处理。反向代理并不能很好的起到作用，不是已经够快了，就是达不到我们想要的效果。</p>
<p>所以，LJ又开发了<a href="http://www.danga.com/perlbal/">Perlbal</a>。特点：</p>
<ul>
    <li>快，小，可管理的http web 服务器/代理
    <li>可以在内部进行转发
    <li>使用Perl开发
    <li>单线程，异步，基于事件，使用epoll , kqueue
    <li>支持Console管理与http远程管理，支持动态配置加载
    <li>多种模式：web服务器，反向代理，插件
    <li>支持插件：GIF/PNG互换？ </li>
</ul>
<h2>11、MogileFS</h2>
<p>LJ使用开源的<a href="http://www.danga.com/mogilefs/">MogileFS</a>作为分布式文件存储系统。MogileFS使用非常简单，它的主要设计思想是：</p>
<ul>
    <li>文件属于类（类是最小的复制单位）
    <li>跟踪文件存储位置
    <li>在不同主机上存储
    <li>使用MySQL集群统一存储分布信息
    <li>大容易廉价磁盘 </li>
</ul>
<p>到目前为止就这么多了，更多文档可以在<a href="http://www.danga.com/words/">http://www.danga.com/words/</a>找到。<a href="http://www.danga.com/">Danga.com</a>和<a href="http://www.livejournal.com/">LiveJournal.com</a>的 同学们拿这个文档参加了两次MySQL Con，两次OS Con，以及众多的其它会议，无私的把他们的经验分享出来，值得我们学习。在web2.0时代快速开发得到大家越来越多的重视，但良好的设计仍是每一个应 用的基础，希望web2.0们在成长为Top500网站的路上，不要因为架构阻碍了网站的发展。</p>
&nbsp;http://blog.csdn.net/xmr_gxcfe/archive/2007/09/14/1785292.aspx</div>
</div>
<p>&nbsp;<br />
</p>
<img src ="http://www.blogjava.net/coolfiry/aggbug/149681.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2007-09-29 21:26 <a href="http://www.blogjava.net/coolfiry/archive/2007/09/29/149681.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转(PO BO VO DTO POJO DAO概念及其作用)</title><link>http://www.blogjava.net/coolfiry/archive/2007/05/17/117981.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Thu, 17 May 2007 01:44:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2007/05/17/117981.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/117981.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2007/05/17/117981.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/117981.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/117981.html</trackback:ping><description><![CDATA[<h2><a id=viewpost1_TitleUrl href="http://www.blogjava.net/vip01/archive/2007/01/08/92430.html"><u><font color=#0000ff>PO BO VO DTO POJO DAO概念及其作用（附转换图）</font></u></a> </h2>
<div class=postbody>
<p>&nbsp;&nbsp;&nbsp;J2EE开发中大量的专业缩略语很是让人迷惑，尤其是跟一些高手讨论问题的时候，三分钟就被人家满口的专业术语喷晕了，PO VO BO DTO POJO DAO，一大堆的就来了（听过老罗对这种现象的批判的朋友会会心一笑）。</p>
<p>&nbsp;&nbsp;&nbsp; 首先声明偶也不是什么高手，以下总结都是自己的体会。不对之处请您多指教。</p>
<p>PO：<br>persistant object持久对象<br><br>最形象的理解就是一个PO就是数据库中的一条记录。<br>好处是可以把一条记录作为一个对象处理，可以方便的转为其它对象。<br></p>
<p>&nbsp;</p>
<hr>
<p><br>BO：<br><font size=4>business object</font><font size=4>业务对象<br><br>主要作用是把业务逻辑封装为一个对象。这个对象可以包括一个或多个其它的对象。<br>比如一个简历，有教育经历、工作经历、社会关系等等。<br>我们可以把教育经历对应一个PO，工作经历对应一个PO，社会关系对应一个PO。<br>建立一个对应简历的BO对象处理简历，每个BO包含这些PO。<br>这样处理业务逻辑时，我们就可以针对BO去处理。<br></font></p>
<p>&nbsp;</p>
<hr>
<p><br>VO ：<br>value object值对象<br><font size=3>ViewObject表现层对象<br><br></font>主要对应界面显示的数据对象。对于一个WEB页面，或者SWT、SWING的一个界面，用一个VO对象对应整个界面的值。<br></p>
<p>&nbsp;</p>
<hr>
<p><br>DTO ：<br>Data Transfer Object数据传输对象<br>主要用于远程调用等需要大量传输对象的地方。<br>比如我们一张表有100个字段，那么对应的PO就有100个属性。<br>但是我们界面上只要显示10个字段，<br>客户端用WEB service来获取数据，没有必要把整个PO对象传递到客户端，<br>这时我们就可以用只有这10个属性的DTO来传递结果到客户端，这样也不会暴露服务端表结构.到达客户端以后，如果用这个对象来对应界面显示，那此时它的身份就转为VO<br></p>
<p>&nbsp;</p>
<hr>
<p><br>POJO ：<br><font color=#333333 size=3>plain&nbsp;ordinary&nbsp;java&nbsp;object&nbsp;简单java对象<br></font>个人感觉POJO是最常见最多变的对象，是一个中间对象，也是我们最常打交道的对象。<br><br>一个POJO持久化以后就是PO<br>直接用它传递、传递过程中就是DTO<br>直接用来对应表示层就是VO<br><br></p>
<p>&nbsp;</p>
<hr>
<p>DAO：<br>data access object数据访问对象<br>这个大家最熟悉，和上面几个O区别最大，基本没有互相转化的可能性和必要.<br>主要用来封装对数据库的访问。通过它可以把POJO持久化为PO，用PO组装出来VO、DTO<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;总结下我认为一个对象究竟是什么O要看具体环境，在不同的层、不同的应用场合，对象的身份也不一样，而且对象身份的转化也是很自然的。就像你对老婆来说就是老公，对父母来说就是子女。设计这些概念的初衷不是为了唬人而是为了更好的理解和处理各种逻辑，让大家能更好的去用<font color=#ff0000>面向对象</font>的方式处理问题.<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;大家千万不要陷入过度设计，大可不必为了设计而设计一定要在代码中区分各个对象。一句话技术是为应用服务的。<br><br>欢迎指正。<br><br></p>
<hr>
<br>画了个图，感觉没有完全表达出自己的意思。。。。。谁帮忙完善下，最好能体现各个O在MVC中的位置<br><img height=480 alt=snap20070108.jpg src="http://www.blogjava.net/images/blogjava_net/vip01/snap20070108.jpg" width=557 border=0>&nbsp;<br><br><br>转自：<a href="http://www.blogjava.net/vip01/archive/2007/01/08/92430.html">http://www.blogjava.net/vip01/archive/2007/01/08/92430.html</a></div>
<img src ="http://www.blogjava.net/coolfiry/aggbug/117981.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2007-05-17 09:44 <a href="http://www.blogjava.net/coolfiry/archive/2007/05/17/117981.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转载(【服务配置】apache+tomcat配置负载均衡的网站)</title><link>http://www.blogjava.net/coolfiry/archive/2006/11/06/79441.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Mon, 06 Nov 2006 09:20:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2006/11/06/79441.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/79441.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2006/11/06/79441.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/79441.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/79441.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1.   				目标										使用				apache				和				tomcat				配置一个可以应用的				web				网站，要达到以下要求：														1、  								Apache				做为				HttpServer				，后面连接多个				tomcat...&nbsp;&nbsp;<a href='http://www.blogjava.net/coolfiry/archive/2006/11/06/79441.html'>阅读全文</a><img src ="http://www.blogjava.net/coolfiry/aggbug/79441.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2006-11-06 17:20 <a href="http://www.blogjava.net/coolfiry/archive/2006/11/06/79441.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WINIZX的JDBC学习笔记</title><link>http://www.blogjava.net/coolfiry/archive/2006/11/03/78860.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Fri, 03 Nov 2006 02:14:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2006/11/03/78860.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/78860.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2006/11/03/78860.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/78860.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/78860.html</trackback:ping><description><![CDATA[JDBC学习笔记<br />2004-9-13     星期一     小雨<br /><br />l. 连接到数据库的方法<br />答：1) ODBC(Open Database Connectivity)<br />       一个以C语言为基础访问SQL为基础数据库引擎的接口，它提供了一致的接口用于和数据库沟通以及访问数据。<br />    2) JDBC<br />       Java版本的ODBC<br /><br />2. JDBC应用编程接口<br />答：JDBC应用编程接口是：<br />    1) 标准的数据访问接口，可以连到不同的数据库;<br />    2) JAVA编程语言的一组类和接口。<br />    JDBC应用编程接口能够：<br />    1) 连接到数据库;<br />    2) 发SQL查询字符串到数据库;<br />    3) 处理结果。<br />    JDBC应用编程接口有二个主要的部分：<br />    1) JAVA应用程序开发接口面向JAVA应用程序开发者;<br />    2) JDBC驱动程序开发接口<br />    <br />3. JDBC Driver<br />答：1) 一大堆实现了JDBC类和接口的类;<br />    2) 提供了一个实现java.sql.Driver接口的类。<br /><br />4. JDBC Driver的四种类型<br />答：1) JDBC-ODBC桥<br />    由ODBC驱动提供JDBC访问<br />    2) 本地API<br />    部分Java driver把JDBC调用转化成本地的客户端API<br />    3) JDBC-net<br />    纯的Java driver，将JDBC调用转入DBMS，与网络协议无关。然后通过服务器将调用转为DBMS协议。<br />    4) 本地协议<br />    纯的java driver，将JDBC调用直接转为DBMS使用的网络协议<br /><br />5. JDBC开发者接口<br />答：1) java.sql--java 2平台下JDBC的主要功能，标准版(J2SE)<br />    2) javax.sql--java 2平台下JDBC增强功能，企业版(J2EE)<br /><br />6. 使用URL确认数据库<br />答：我们使用URL来确定一个数据库(正确的Driver,正确的主机,正确的协议，正确的协议，正确的用户名和密码);<br />    语法：protocol:subprotocol:subname<br />    范例：jdbc:db2:MyTest<br />          jdbc:db2://localhost:6789/MyTest<br /><br />7. javax.sql包JDBC2.0的增强功能<br />答：1) 数据源接口;<br />    2) 连接池;<br />    3) 分布式交易;<br />    4) 行集;<br /><br />8. 创建一个基本的JDBC应用<br />答：1) 步骤一：注册一个driver;<br />    2) 步骤二：建立一个到数据库的连接;<br />    3) 步骤三：创建一个statement;<br />    4) 步骤四：执行SQL语句;<br />    5) 步骤五：处理结果;<br />    6) 步骤六：关闭JDBC对象<br /><br />9. 注册一个Driver(步骤一)<br />答：1) driver被用于连接到数据库;<br />    2) JDBC应用编程接口使用第一个能成功连接到给定URL的driver;<br />    3) 在同一时间可以装载多个driver<br /><br />10.注册一个driver的方法：<br />答：1) 使用类loader(装载;实例化;注册入DriverManager)<br />       a. Class.forName("Com.ibm.db2.jdbc.app.DB2Driver");<br />       b. Class.forName("Com.ibm.db2.jdbc.net.DB2Driver");<br />       c. Class.forName("Com.microsoft.jdbc.sqlServer.SQLServerDriver);<br />       d. Class.forName("oracl.jdbc.driver.OracleDriver");<br />       e. Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");<br />    2) 实例化一个Driver<br />       a. Driver drv = new COM.cloudscape.core.RmiJdbcDriver();<br /><br />2004-9-14     星期二     阴<br /><br />1. 建立一个到数据库的连接(步骤二)<br />答：DriverManager调用getConnection(urlString)方法，实际上调用的是driver的connect(urlString)方法;<br />    1) 当一个driver肯定地对应到一个数据库URL，DriverManager建立一个连接;<br />    2) 当没有driver匹配，返回null然后下一个driver被检验;<br />    3) 假如没有建立连接，抛出一个SQLExcepiton异常<br /><br />2. 经常使用的一些JDBC URL<br />答：1) JDBC-ODBC: jdbc:odbc:&lt;DB&gt;<br />    2) Oracle: jdbc:oracle:oci:@&lt;sid&gt; or jdbc:oracle:thin:@&lt;SID&gt;<br />    3) Weblogic MS-SQL: jdbc:weblogic:mssqlserver4:&lt;DB&gt;@&lt;HOST&gt;:&lt;PORT&gt;<br />    4) DB2: jdbc:db2:MyTest or jdbc.db2://localhost:6789/MyTest(需要用户名和密码)<br /><br />3. Driver连接方法<br />答：1) 创建一个到指定Driver实例的直接调用;<br />    2) 避免一般访问的问题<br />       Driver drv = new COM.ibm.db2.jdbc.app.DB2Driver();<br />       Connection con = null;<br />       try {con = drv.connect("jdbc:db2:MyTest",new Properties())}<br />       catch(SQLException e){}<br /><br />4. 创建一个Statement(步骤三)<br />答：1) Statement的三个接口:<br />       a. Statement;<br />       b. PreparedStatement(继承自Statement);<br />       c. CallableStatement(继承自PreparedStatement);<br />    2) 使用方法Connection.createStatement()得到一个Statement对象<br /><br />5. PreparedStatement对象<br />答：1) 调用ProparedStatement比statement更为高效;<br />    2) 继承自Statement;<br />    3) 语法：PreparedStatement pstm = connection.prepareStatement(sqlString);<br /><br />6. CallableStatement对象<br />答：1) 通过CallableStatement调用数据库中的存储过程;<br />    2) 继承自PreparedStatement;<br />    3) CallableStatement cstm = connection.prepareCall("{call return_student[?,?]}");<br />       cstm.setString(1,"8623034");<br />       cstm.registerOutparameter(2, Types.REAL);<br />       cstm.execute();<br />       float gpa = cstm.getFloat(2);<br /><br />7. Statement接口的比较<br />答：             | Statement           | PreparedStatement         |  CallableStatement<br />    ------------------------------------------------------------------------------<br />    写代码位置   |   客户端            | 客户端                    |  服务器端<br />    ------------------------------------------------------------------------------<br />    写代码位置   |   客户端            | 服务器端                  |  服务器端<br />    ------------------------------------------------------------------------------<br />    编写代码技术 |Java，SQL操作        |Java，SQL操作              |  数据库的程序语言，如PL/SQL<br />    ------------------------------------------------------------------------------<br />    可配置性     |   高                |第一次高，以后低           |  低<br />    ------------------------------------------------------------------------------<br />    可移植性     |   高                |假设支持PreparedStatement的话高    <br />    ------------------------------------------------------------------------------<br />    传输效率     |   低                |第一次低，以后高           |  高<br /><br />8. 执行SQL Statement(步骤四)<br />答：通过接口方法将SQL语句传输至?认的数据库连接，返回结果可能是一个数据表，可以通过java.sql.ResultSet访问。<br />    1) Statement的接口方法：<br />    a. executeQuery(sqlString): 执行给定的SQL声明，返回一个结果集(ResultSet)对象;<br />    b. executeUpdate(sqlString): 执行给定的SQL声明，可以是INSERT、UPDATE或DELETE声明，也可以是SQL DDL声明;<br />    c. execute(sqlString): 执行给定的SQL声明。<br /><br />9. 处理结果(步骤五)<br />答：1) 使用结果集(ResultSet)对象的访问方法获取数据;<br />       a. next():下一个记录<br />       b. first():第一个记录<br />       c. last():最后一个记录<br />       d. previous():上一个记录<br />    2) 通过字段名或索引取得数据<br />    3) 结果集保持了一个指向了当前行的指针，初始化位置为第一个记录前。<br /><br />10. 关闭JDBC对象(步骤六)<br />答：1) 首先关闭记录集;<br />    2) 其次关闭声明;<br />    3) 最后关闭连接对象。<br /><br />11. 数据表和类对应的三种关系：<br />答：1) 一个表对应一个类;<br />    2) 一个表对应相关类;<br />    3) 一个表对应整个类关系层<br /><br />12. 类间关系的几种表设计：<br />答：1) 多对一，<br />    2) 一对一: <br />    3) 一对多：<br />    4) 多对多：<br /><br />13. SQL数据类型及其相应的Java数据类型<br />答：SQL数据类型                     Java数据类型              说明<br />    ------------------------------------------------------------------<br />    INTEGER或者INT                  int                     通常是个32位整数<br />    SMALLINT                        short                   通常是个16位整数<br />    NUMBER(m,n) DECIMAL(m,n)        Java.sql.Numeric        合计位数是m的定点十进制数，小数后面有n位数<br />    DEC(m,n)                        Java.sql.Numeric        合计位数是m的定点十进制数，小数后面有n位数<br />    FLOAT(n)                        double                  运算精度为n位二进制数的浮点数<br />    REAL                            float                   通常是32位浮点数<br />    DOUBLE                          double                  通常是64位浮点数<br />    CHARACTER(n)或CHAR(n)           String                  长度为n的固定长度字符串<br />    VARCHAR(n)                      String                  最大长度为n的可变长度字符串<br />    BOOLEAN                         boolean                 布尔值<br />    DATE                            Java.sql.Date           根据具体设备而实现的日历日期<br />    TIME                            Java.sql.Time           根据具体设备而实现的时戳<br />    TIMESTAMP                       Java.sql.Timestamp      根据具体设备而实现的当日日期和时间<br />    BLOB                            Java.sql.Blob           二进制大型对象<br />    CLOB                            Java.sql.Clob           字符大型对象<br />    ARRAY                           Java.sql.Array<br />    <br /><br />2004-9-15     星期三      阴<br /><br />1. 元数据<br />答：关于数据的信息，例如类型或者容量。通过JDBC API可以访问：<br />    1) 数据库元数据;<br />       a. 使用connection.getMetadata方法返回DataMetaData引用<br />       b. 能够使用isReadOnly此类方法获取信息<br />    2) 结果集元数据;<br />       a. 使用ResultSet.getMetadata方法返回ResultSetMetaData引用<br />       b. 能够使用getColumnCount此类方法获取信息<br /><br />2. 事务处理<br />答：1) 一系列的动作作为一个不可分的操作;<br />    2) JDBC API中使用事务处理步骤：<br />       a. 用false作为参数调用setAutoCommit方法;<br />       b. 执行一或多个关于数据库的操作;<br />       c. 调用commit方法完成改变;<br />       d. 恢复上次提交后的改变，调用rollback方法.<br /><br />       try<br />       {<br />          con.setAutoCommit(false);<br />          Statement stm = con.createStatement();<br />          stm.executeUpdate("insert into student(name, age, gpa) values('gzhu', 30, 4.8)");<br />          stm.commit();<br />       }<br />       catch(SQLException e)<br />       {<br />          try<br />          {<br />             con.rollback();<br />          }<br />          catch(Exception e)<br />          {<br />          }<br />       }<br /><br />3. 并发控制<br />答：1) 设置隔离级别方法：setTransactionIsolation<br />    2) 隔离级别静态变量<br />       a. TRANSACTION_NONE：只读的数据字典;<br />       b. TRANSACTION_READ_UNCOMMITTED：只读未提交数据;<br />       c. TRANSACTION_READ_COMMITTED：只读未提交数据;<br />       d. TRANSACTION_REPEATABLE_READ：重复读取数据;<br />       e. TRANSACTION_SERIALIZABLE：无论做什么操作都不许别人动。<br />    3) 示例：con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);<br /><br />4. JDBC 2.0 应用程序编程接口增强功能<br />答：1) ResultSet增强：<br />       a. 可以回卷;<br />       b. 可以修改;<br />       设置示例：Statement stm = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);<br />    2) Statement增强了批量修改能力(batch updates);<br />    3) 更高级的数据类型(例：Struct)。<br /><br />5. JDBC 2.0标准扩展<br />答：1) JNDI(Java Naming and Directory Interface): 解决离散状态下Object的查找;<br />    2) 连接池：在内存中保存了一个数据库连接，不需要注册驱动器，提高性能的重要方法。<img src ="http://www.blogjava.net/coolfiry/aggbug/78860.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2006-11-03 10:14 <a href="http://www.blogjava.net/coolfiry/archive/2006/11/03/78860.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java打包详解</title><link>http://www.blogjava.net/coolfiry/archive/2006/10/12/74844.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Thu, 12 Oct 2006 10:40:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2006/10/12/74844.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/74844.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2006/10/12/74844.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/74844.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/74844.html</trackback:ping><description><![CDATA[ 和 'f'标志指定的相同顺序。<br /><br />示例1：将两个class文件存档到一个名为 'classes.jar' 的存档文件中：<br /> &amp; nbsp;     jar cvf&amp; nbsp;classes.jar Foo.class Bar.class<br />示例2：用一个存在的清单（manifest）文件 'mymanifest' 将 foo/&amp; nbsp;目录下的所有<br />            文件存档到一个名为  'classes.jar' 的存档文件中：<br />        jar cvfm  classes.jar mymanifest -C foo/ .<br /><br />来个小例子试试看：<br />我们只有一个HelloWorld，如下：<br />public&amp; nbsp; class  HelloWorld{<br /> public&amp; nbsp;static void main(String[] args){<br />  System.out.println(“Hi, Hello World!”);<br />} <br />}<br />我将这个java文件存到C盘跟目录下，ok，接下来，<br />在先前打开的命令提示符下(跳转到C盘提示符下)，我们输入javac HelloWorld.java，然后继续输入：jar &amp; nbsp;cvf  hello.jar  HelloWorld.class，回车后去你的C盘看看，多了什么，没错 hello.jar 。<br />基本的步骤我们现在都知道了，你可以自己去尝试一下随着jar后面的参数的不同，结果有什么变化。<br />紧接着我们看看如何运行我们的jar包。<br />在进入正题之前，你要先打开我们刚刚做好的jar包看看，多了什么呢，META-INF目录？再看看里面是什么，还有一个MANIFEST.MF文件是不是？用文本编辑器(我这里是UltraEdit)打开它看看：<br />Manifest-Version:  1.0<br />Created-By: 1.4.2 (Sun Microsystems&amp; nbsp;Inc.)<br />就是这样。这里我们对它进行修改，加一句：Main-Class:  HelloWorld (在第三行)。这个就是我们之前写的那个类，也就是我们的入口类。也即，<br />Manifest -Version: 1.0<br />Created-By: 1.4.2 (Sun&amp; nbsp;Microsystems Inc.)<br />Main-Class: HelloWorld&lt; BR&gt;接下来，我们在命令提示符里执行：<br />jar umf  MANIFEST.MF app.jar<br />这样我们使用了我们自己的MANIFEST.MF文件对原来默认的进行了更新。你不妨可以再进去看看是不是添上了Main-Class: HelloWorld这一句。<br />Ok，这个最后的一步了，来验证我们做的一切，在命令提示符中输入：<br />java -jar hello.jar (执行)<br />出现了什么，――Hi, Hello World!<br />我们再来看看 jar文件在tomcat中发布，注意：在tomcat中我们就不能再用jar这种格式，而改war格式，它是专门用于web应用的，其实整个过程下来基本上和jar是类似的：<br />先准备我们要打包的资源。<br />找到存放tomcat的webapps目录，进到其中，新建一个文件夹，这里命名为hello，再进去新建WEB-INF文件夹，再进去新建classes文件夹，此时我们也将我们唯一的servlet， HelloWorld.java放到这里，在与classes目录同级下建立一文件web.xml。Ok，目前我们初步建立了一个简单的web应用。 <br />这是HelloWorld.java：<br />import java.io.*;&lt; BR&gt;import javax.servlet.*;<br />import javax.servlet.http.*; <br />public class HelloWorld extends&amp; nbsp;HttpServlet {<br /> public void  doGet(HttpServletRequest req, HttpServletResponse  res)<br />              &amp; nbsp;               &amp; nbsp; throws ServletException, IOException&amp; nbsp;{<br />  res.setContentType("text/html");<br />   PrintWriter out = res.getWriter ();<br />  out.println("&lt;HTML&gt;");&lt; BR&gt;  out.println("&lt;HEAD&gt;&lt;TITLE&amp; gt;Hello, World!&lt;/TITLE&gt;&lt;/HEAD&gt;");&lt; BR&gt;  out.println("&lt;BODY&gt;");<br />&amp; nbsp; out.println("Hello, World!");<br />   out.println("&lt;/BODY&gt;&lt;/HTML&gt;");<br />  }<br />}//end here!<br />对它编译。下面是web.xml：&lt; BR&gt;&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;<br />&lt;!DOCTYPE web-app PUBLIC<br />  '-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN'<br />  '<img src="http://www.matrix.org.cn/images/small/url.gif" align="absmiddle" /><a href="http://java.sun.com/j2ee/dtds/web-app_2_3.dtd&amp;" target="_blank"><font color="#00659c">http://java.sun.com/j2ee/dtds/web-app_2_3.dtd&amp;&lt; /FONT&gt;</font></a><font color="#00659c">#39;&gt;<br />&lt;web-app&gt;<br />   &lt;servlet&gt;<br />     &lt;servlet-name&gt;hello&lt;/servlet-name&amp; gt;<br />    &lt;servlet-class&amp; gt;HelloWorld&lt;/servlet-class&gt;<br />   &lt;/servlet&gt;<br />  &lt;servlet-mapping&amp; gt;<br /> &lt;servlet-name&gt;hello&lt;/servlet- name&gt;<br /> &lt;url-pattern&gt;/HelloWorld&amp; lt;/url-pattern&gt;<br />  &lt;/servlet-mapping&amp; gt;<br />&lt;/web-app&gt;<br />开始压缩，形成war档：<br />在命令提示符下进到先前创制的hello目录下，执行 jar  cvf   hello.war  * ，我们便得到hello.war。将它拷贝至 webapps目录下，ok，来看最后一步，打开tomcat的目录conf中的server.xml，加入：<br />   &lt;Context path="/hello" docBase="hello.war"&amp; nbsp;debug="0"<br />    reloadable ="true"/&gt;<br />大功告成！运行它，启动tomcat，后在浏览器中输入<img src="http://www.matrix.org.cn/images/small/url.gif" align="absmiddle" /><a href="http://localhost:8080/hello/HelloWorld" target="_blank"><font color="#00659c">http://localhost:8080/hello/HelloWorld</font></a>，有了吗？<br />最后，如果你想用ant来完成以上的打包活动，下面就告诉你：<br />对于jar来说。在build.xml 中，<br /> &lt;target name="jar"&gt;&lt; BR&gt;  &lt;jar destfile="${app_home}/hello.jar"&amp; gt;<br />   &lt;fileset dir=" ${dest}" includes="**"/&gt;<br />  &amp; nbsp;   &lt;!--fileset dir="${dest} " includes="**/action.properties"/--&gt;<br />     &lt;/jar&gt;<br /> &amp; lt;/target&gt;<br /><br />对于war，<br />&lt;war&amp; nbsp;warfile="hello.war" webxml="./WEB-INF/web.xml"&gt;<br />     &lt;fileset dir="html"/&amp; gt;<br />    &lt;lib  dir="lib/"&gt;<br />    &amp; nbsp;   &lt;exclude name="oracle*.jar"/&amp; gt;<br />    &lt;/lib&gt; <br />    &lt;classes  dir="build/servlets"&gt;<br />   &amp; nbsp;     &lt;include&amp; nbsp;name="**/*.class"/&gt;<br />  &lt;/classes&amp; gt;<br />&lt;/war&gt; <br />好了，就这么多，希望对你有点帮助。：） <br />我上传了上面打过的两个包，hello.jar和hello.war。<img src="http://www.matrix.org.cn/images/small/download.gif" align="absmiddle" border="0" height="16" width="16" /><a href="http://www.matrix.org.cn/upload/forum/200441295728.rar" target="_blank"><font color="#00659c">『 点击下载』</font></a><br /><font color="#00659c"><img src="http://www.matrix.org.cn/images/small/download.gif" align="absmiddle" border="0" height="16" width="16" /></font><a href="http://www.matrix.org.cn/upload/forum/200441295831.rar" target="_blank"><font color="#00659c">『 点击下载』</font></a><br />第一rar文件对应的是hello.jar,下载后将其名改为 hello.jar<br />第二rar文件对应hello.war，下载后改为hello.war。<br />这是由于上传不了 jar格式和war格式的文件，你只好照我上面说的去做了 ：）<br /><br />补充：<br /> ############<br />jar基本操作：<br />############<br />1.&amp; nbsp;创建jar文件<br />  jar cf jar- file input-file(s)<br />c---want to Create&amp; nbsp;a JAR file.<br />f---want the  output to go to a file  rather than to stdout.<br />eg: 1) jar cf myjar.jar query_maintain_insert.htm&lt; BR&gt;    2)jar cvf  myjar.jar query_maintain_insert.htm<br />       v---Produces verbose(详细的) output.<br />    3)jar&amp; nbsp;cvf myjar.jar query_maintain_insert.htm mydirectory&lt; BR&gt;    4)jar cv0f  myjar.jar query_maintain_insert.htm mydirectory<br />       0---don't&amp; nbsp;want the JAR file to be&amp; nbsp;compressed.<br />    5)jar&amp; nbsp;cmf MANIFEST.MF myjar.jar yahh.txt<br />       m---Used&amp; nbsp;to include manifest information  from an existing manifest file.<br />     6)jar cMf MANIFEST.MF&amp; nbsp;myjar.jar yahh.txt<br />   &amp; nbsp;  M---the default manifest  file should not be produced.<br />     7)jar cvf myjar.jar&amp; nbsp;*<br />       *---create all contents in current&amp; nbsp;directory.<br />2. 察看jar文件<br /> &amp; nbsp;jar tf jar-file<br />t---want to&amp; nbsp;view the Table of contents  of the JAR file.<br />eg: 1)jar&amp; nbsp;vft yahh.jar<br />       v---Produces verbose(详细的)  output.<br />3. 提取jar文件<br />   jar xf jar-file [archived-file(s)]<br />x- --want to extract files from  the JAR archive.<br />eg: 1)jar xf&amp; nbsp;yahh.jar yahh.txt(仅提取文件yahh.txt)<br /> &amp; nbsp;  2)jar xf yahh.jar alex/yahhalex.txt (仅提取目录alex下的文件yahhalex.txt)<br />   &amp; nbsp;3)jar xf yahh.jar(提取该jar包中的所有文件或目录)<br /> 4. 修改Manifest文件<br />  jar  cmf manifest-addition jar-file input-file(s)&lt; BR&gt;m---Used to include manifest information&amp; nbsp;from an existing manifest file.&lt; BR&gt;5. 更新jar文件<br />  jar  uf jar-file input-file(s)<br />u---want to update a</font><img src ="http://www.blogjava.net/coolfiry/aggbug/74844.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2006-10-12 18:40 <a href="http://www.blogjava.net/coolfiry/archive/2006/10/12/74844.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>简化Java应用程序的打包和发布</title><link>http://www.blogjava.net/coolfiry/archive/2006/10/12/74843.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Thu, 12 Oct 2006 10:38:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2006/10/12/74843.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/74843.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2006/10/12/74843.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/74843.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/74843.html</trackback:ping><description><![CDATA[发布Java应用程序时你会感到困难？好在Java提供了一系列打包和发布工具，可以显著的简化发布过程<br />　　<br />　　该文章提供了打包Java code的几种方法，我们将会探讨Java manifest 文件，给出用于管理JAR文件所依赖文件、估计跨平台发布所需的CLasspath的合适方法.我也会解释如何使用manifest包版本特性来确认包的兼容性...<br />　　<br />　　<b>什么是JAR文件？</b><br />　　<br />　　在开发过程中，我们可以直接使用Java class文件来运行程序，但这并不是一个好方式，好在Java 提供了 JAR(Java Archive)文件来提供发布和运行。<br />　　<br />　　jar 文件实际上是class 文件的ZIP压缩存档，这种格式被广泛使用，因此易与使用，有很多中工具可以操作这种格式的文件。也正是因为这个原因，jar文件本身并不能表达所包含应用程序的标签信息。<br />　　<br />　　<b>Manifest 因此得以出现</b><br />　　<br />　
　为了要提供存档的标签信息，jar 文件指定了一个特定目录来存放标签信息：META-INF
目录，其中我们来关注该目录中的MANIFEST.MF文件，他就是JAR的manifest文件，他包含了JAR文件的内容描述，并在运行时向JVM提
供应用程序的信息，大多数JAR文件含有一个默认生成的manifest 文件,执行JAR命令或使用zip工具，都可以产生它<br />　　<br />　　如果是由jar命令产生的 manifest 文件，形如:<br />　　Manifest-Version: 1.0<br />　　Created-By:1.4.0-beta<br />　　(Sun Microsystems Inc.)<br />　　<br />　
　这些信息没甚么用,仅仅告诉我们使用的是1.0的manifest文件,第一行定义manifest的格式，第二行说明使用 SUN
的JDK1.4的jar工具生成该文件，如果manifest文件是由其他 （如ant） 创建的，那将会出现 “Created-By: Ant
1.2” 之类的内容，如果你是自己创建manifest文件，你可以加入自己的一些相关信息.<br />　　<br />　　<b>基础格式</b><br />　　<br />　　manifest 文件的格式 是很简单的，每一行都是 名－值 对应的:属性名开头，接着是 ":" ，然后是属性值，每行最多72个字符，如果需要增加，你可以在下一行续行，续行以空格开头，以空格开头的行都会被视为前一行的续行。<br />　　<br />　　所有在开头的属性都是全局的，你也可以定义特定class 或package的属性，稍后将介绍这种<br />　　<br />　　<b>把manifest文件插入JAR文件</b><br />　　<br />　　使用 m 选项，把指定文件名的manifest文件 传入，例如<br />　　jar cvfm myapplication.jar myapplication.mf -C classdir<br />　　<br />　　如果你使用ant来创建时，在ant 的build.xml 加入如下条目<br />　　&lt;target name="jar"&gt;<br />　　&lt;jar jarfile ="myapplication.jar"<br />　　manifest="myapplication.mf"&gt;<br />　　&lt;fileset dir="classdir"<br />　　includes="**/*.class"/&gt;<br />　　&lt;/jar&gt;<br />　　&lt;/target&gt;<br />　　<br />　　<b>运行Java程序</b><br />　　<br />　　现在我们来体验一下manifest文件的作用，如果现在我们有一个Java 应用程序打包在myapplication.jar中， main class为 com.example.myapp.MyAppMain ，那么我们可以用以下的命令来运行<br />　　java -classpath myapplication.jar com.example.myapp.MyAppMain<br />　　<br />　　这显然太麻烦了，现在我们来创建自己的manifest文件，如下：<br />　　Manifest-Version: 1.0<br />　　Created-By: JDJ example<br />　　Main-Class: com.example.myapp.MyAppMain<br />　　<br />　　这样我们就可以使用如下的命令来运行程序了：（明显简单多了，也不会造成无谓的拼写错误）<br />　　java -jar myapplication.jar<br />　　<br />　　<b>管理JAR的依赖资源</b><br />　　<br />　
　很少Java应用会仅仅只有一个jar文件，一般还需要 其他类库。比如我的应用程序用到了Sun 的 Javamail classes
，在classpath中我需要包含activation.jar 和 mail.jar,这样在运行程序时,相比上面的例子,我们要增加一些:<br />　　java -classpath mail.jar:activation.jar -jar myapplication.jar<br />　　<br />　　在不同的操作系统中,jar包间的分隔符也不一样，在UNIX用“：”，在window中使用 “；”，这样也不方便<br />　　<br />　　同样，我们改写我们的manifest文件，如下<br />　　Manifest-Version: 1.0<br />　　Created-By: JDJ example<br />　　Main-Class: com.example.myapp.MyAppMain<br />　　Class-Path: mail.jar activation.jar<br />　　<br />　　（加入了Class-Path: mail.jar activation.jar，用空格分隔两个jar包）<br />　　<br />　　这样我们仍然可以使用和上例中相同的命令来执行该程序：<br />　　java -jar myapplication.jar<br />　　<br />　
　Class-Path属性中包含了用空格分隔的jar文件，在这些jar文件名中要对特定的字符使用逃逸符，比如空格，要表示成"%20"，在路径的表
示中，都采用“/”来分隔目录，无论是在什么操作系统中，(即使在window中)，而且这里用的是相对路径（相对于本身的JAR文件）：<br />　　Manifest-Version: 1.0<br />　　Created-By: JDJ example<br />　　Main-Class: com.example.myapp.MyAppMain<br />　　Class-Path: ext/mail.jar ext/activation.jar<br />　　Multiple Main Classes（多主类）<br />　　<br />　
　还有一种Multiple Main Classes情况，如果你的应用程序可能有命令行版本
和GUI版本，或者一些不同的应用却共享很多相同的代码，这时你可能有多个Main
Class，我们建议你采取这样的策略：把共享的类打成lib包，然后把不同的应用打成不同的包，分别标志主类：如下<br />　　Manifest for myapplicationlib.jar:<br />　　Manifest-Version: 1.0<br />　　Created-By: JDJ example<br />　　Class-Path: mail.jar activation.jar<br />　　Manifest for myappconsole.jar:<br />　　Manifest-Version: 1.0<br />　　Created-By: JDJ example<br />　　Class-Path: myapplicationlib.jar<br />　　Main-Class: com.example.myapp.MyAppMain<br />　　Manifest for myappadmin.jar:<br />　　Manifest-Version: 1.0<br />　　Created-By: JDJ example<br />　　Class-Path: myapplicationlib.jar<br />　　Main-Class: com.example.myapp.MyAdminTool<br />　　在myappconsole.jar 和 myappadmin.jar的manifest文件中分别注明各自的 Main Class<br />　　Package Versioning<br />　　<br />　　完成发布后，如果使用者想了解 ，哪些代码是谁的？目前是什么版本？使用什么版本的类库？解决的方法很多 ，manifest提供了一个较好的方法，你可以在manifest文件中描述每一个包的信息。<br />　　<br />　
　Java 秉承了实现说明与描述分离的原则，package 的描述 定义了package 是什么，实现说明
定义了谁提供了描述的实现，描述和实现包含 名、版本号和提供者。要得到这些信息，可以查看JVM的系统属性（使用
java.lang.System.getProperty() ）<br />　　<br />　　在manifest文件中，我可以为每个package定义描述和实现版本，声明名字，并加入描述属性和实现属性，这些属性是<br />　　<br />　　Specification-Title<br />　　Specification-Version<br />　　Specification-Vendor<br />　　Implementation-Title<br />　　Implementation-Version<br />　　Implementation-Vendor<br />　　<br />　　当要提供一个类库或编程接口时，描述信息显得是很重要，见以下范例：<br />　　<br />　　Manifest-Version: 1.0<br />　　Created-By: JDJ example<br />　　Class-Path: mail.jar activation.jar<br />　　Name: com/example/myapp/<br />　　Specification-Title: MyApp<br />　　Specification-Version: 2.4<br />　　Specification-Vendor: example.com<br />　　Implementation-Title: com.example.myapp<br />　　Implementation-Version: 2002-03-05-A<br />　　Implementation-Vendor: example.com<br />　　<br />　　<b>Package Version 查询</b><br />　　<br />　　在manifest文件中加入package描述后，就可以使用Java提供的java.lang.Package class进行Package 的信息查询，这里列举3个最基本的获取package object的方法<br />　　<br />　　1.Package.getPackages():返回系统中所有定义的package列表<br />　　<br />　　2.Package.getPackage(String packagename):按名返回package<br />　　<br />　　3.Class.getPackage():返回给定class所在的package<br />　　<br />　　使用者这方法就可以动态的获取package信息.<br />　　<br />　　需要注意的是如果给定的package中没有class被加载,则也无法获得package 对象<br />　　<br />　　<b>Manifest 技巧</b><br />　　<br />　　总是以Manifest-Version属性开头<br />　　<br />　　每行最长72个字符，如果超过的化，采用续行<br />　　<br />　　确认每行都以回车结束，否则改行将会被忽略<br />　　<br />　　如果Class-Path 中的存在路径，使用"/"分隔目录，与平台无关<br />　　<br />　　使用空行分隔主属性和package属性<br />　　<br />　　使用"/"而不是"."来分隔package 和class ,比如 com/example/myapp/<br />　　<br />　　class 要以.class结尾，package 要以 / 结尾<img src ="http://www.blogjava.net/coolfiry/aggbug/74843.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2006-10-12 18:38 <a href="http://www.blogjava.net/coolfiry/archive/2006/10/12/74843.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>插件式设计的架构模型与实例</title><link>http://www.blogjava.net/coolfiry/archive/2006/09/30/73100.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Sat, 30 Sep 2006 14:57:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2006/09/30/73100.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/73100.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2006/09/30/73100.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/73100.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/73100.html</trackback:ping><description><![CDATA[
		<div class="guanggao">
				<span id="ad3">
				</span>
		</div>
		<div class="guanggao">
				<span id="contentAdv">
				</span>
		</div>　　插件式设计近年来非常流行，其中<a href="http://dev.yesky.com/javaide/javaeclipse/" target="_blank"><font color="#4822dd">eclipse</font></a>起了推波助澜的作用，提到插件式就会不由自主的想到<a href="http://dev.yesky.com/javaide/javaeclipse/" target="_blank"><font color="#4822dd">eclipse</font></a>。其实插件式设计并不是什么新事物，早在几十年前就有了。像X Server就是基于插件式设计的，除了核心功能外，它所有的扩展功能和设备驱动都是以插件方式加入进来的。 <br /><br />　　基于插件的设计好处很多：把扩展功能从框架中剥离出来，降低了框架的复杂度，让框架更容易实现。扩展功能与框架以一种很松的方式耦合，两者在保持接口不变的情况下，可以独立变化和发布。公开插件接口，让第三方有机会扩展应用程序的功能，有财大家一起发。另外，还可以让开源与闭源共存于一套软件，你的插件是开源还是闭源，完全由你自己决定。 <br /><br />　　基于插件设计并不神秘，相反它比起一团泥的设计更简单，更容易理解。各种基于插件设计的架构都有自己的特色，但从总体架构上看，其模型都大同小异。这里我们介绍一个简单的模型，并给出几个实例，希望对新手有所启发。<br /><br />　　1. 基本架构<br /><br /><table width="90%" align="center"><tbody><tr><td><div align="center"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"><img alt="plugin.jpg" src="http://dev.yesky.com/imagelist/06/28/z0qt19na01c1.jpg" /></span></div></td></tr></tbody></table><br />　　插件式设计的应用程序，基本上可以用上图来表示。当然，此图是一种较高层次的表示，实际的设计会更复杂一些。我们在这里为了阐述方便，不用故意搞得那么复杂。<br /><br />　　应用程序由应用程序框架、插件接口、插件和公共函数库四部分组成。 <br /><br />　　应用程序框架负责应用程序的整体运作，它清楚程序整个流程，但并不知道每个过程具体要做什么。它在适当的时候调用一些插件，来完成真正的功能。<br /><br />　　插件接口是一个协议，可能用IDL描述，可能是头文件，也可能一段文字说明。插件按照这个协议实现出来，就可以加入到应用程序中来。当然，对于复杂的系统，插件接口可能有多个，各自具有独立的功能。<br /><br />　　插件是完成实际功能的实体，实现了要求的插件接口。尽管实现什么以及怎么实现，完全是插件自己的自由。在实际情况来，一般还是有些限制，因为插件接口本身可能就是一个限制。如，实现编译功能的插件，自然不能实现成一个聊天功能的插件。 <br /><br />　　公共函数库是一组函数或者类，应用程序框架和插件都可以调用。它通常是一个独立的动态库（DLL）。应用程序框架本身是公用的，是代码复用的一种方式。但并不是所有可复用代码都可以放在框架中，特别是插件会用到的公共代码，那会造成插件对框架的依赖。把这些公共代码提取到一个独立的库中，是一种好的方法。<br /><br />　　另外，值得补充说明一下的是插件接口。插件接口通常有两种：<br /><br />　　通用插件接口：这一类插件接口是通用的，你无法从接口函数看出这个插件的功能。它的接口函数通常有这些函数：<br /><br />　　init : 用于初始化插件，通常在插件被加载时调用。<br /><br />　　deinit：用于反初始化插件，通常在插件被卸载时调用。<br /><br />　　run：让插件起动。<br /><br />　　stop：让插件停止。<br /><br />　　至于插件要完成什么功能，要插到哪里，在init函数里决定，它调用公共函数库里的函数把自己注册到框架中某个位置。<br /><br />　　专用插件接口：这一类插件接口是专用的，看到它的接口函数说明，你就可以大致了解它的功能了。<br /><br />　　加入插件的方式通常采用配置信息来实现，配置信息可以是注册表，也可以配置文件。也可以动态注册进来，或者把插件放到指定的位置。<br /><br />　　下面我们来看几个实例：<br /><br />　　2. 桌面设计<br /><br />　　最近一段时间完成了桌面模块的设计和实现。按照以往的经验，桌面模块通常是变化最多的一个模块，SPEC总是在不断的调整的效果，不同客户要求实现具有个性化的桌面，直到产品快发布了，桌面的SPEC还在不停的修改。另外，在智能手机中，桌面占有特殊的地位，很多东西都可能往桌面里塞，桌面不但是各种功能的大杂烩，还是一些系统消息的中转站。<br /><br />　　这个任务比较棘手，所以在设计时就分外小心。首先想到的就是采用插件式设计，把外围功能独立出来，尽量简化框架的实现。<br /><br />　　插件：每一个最小功能单元都是一个插件，它可以是可见的，也可以是不可的，也可以是动态变化的。比如时间、电池电量、网络连接、信号强弱、新事件(如SMS、MMS、EMAL、ALARM和未接电话等)、应用程序快捷方式、左右操作按钮和其它处理系统事件的功能单元。每个插件都用一个.desktop来描述，这是遵循freedesktop.org的标准的。<br /><br />　　桌面框架包括：状态栏、开始菜单、操作栏、桌面区、事件管理器和主题管理器。而状态栏、开始菜单、操作栏、桌面区和事件管理器都是容器，容纳各种插件。对于可见的插件，可以有自己的表现方式，也可以采用通用的表现方式。<br /><br />　　公共函数库：一些抽象的类、实现插件的辅助类以及其它一些可能被公用的类。<br /><br />　　插件接口：对于不可见的插件要求实现事件处理功能，可见的插件还要求实现绘制功能。<br /><br />　　3. 模拟器设计<br /><br />　　一个同事负责设计另外一个平台的PC模拟环境设计。在我的建议下，他对架构作了调整。调整后的架构非常简单，也可以认为是插件式的设计，它由下面几部分组成：<br /><br />　　应用程序框架：负责模拟器基本功能，如模拟键盘和显示设备、换肤功能等。<br /><br />　　插件：就是被模拟的平台，如microwindow及相应的手机应用程序。尽管运行时通常只有一个插件运行，这样做仍然有意义，如果要换成minigui或者其它平台时，模拟器不需要作任何修改。<br /><br />　　公共函数库：它由应用程序框架初始化一些信息和回调函数，然后供插件(即microwindow)调用，插件利用它来实现显示和输入等驱动程序。<br /><br />　　插件接口：如起动和停止模拟平台等。<br /><br />　　4. GIMP<br /><br />　　GIMP是一个功能强大的图形图像编辑器，典型的基于插件式的设计，在《unix编程艺术》中，作为插件式设计示例介绍过。<br /><br />　　应用程序框架：GUI<br /><br />　　插件：完成图像的各种转换和处理功能，如模糊、去斑和色彩调整等。<br /><br />　　公共函数库：放在libgimp.so里。<br /><br />　　插件接口：对GIMP感兴趣的朋友，可以到官方网站上去阅读更多的文档。 <img src ="http://www.blogjava.net/coolfiry/aggbug/73100.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2006-09-30 22:57 <a href="http://www.blogjava.net/coolfiry/archive/2006/09/30/73100.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>插件开发框架</title><link>http://www.blogjava.net/coolfiry/archive/2006/09/30/73097.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Sat, 30 Sep 2006 14:53:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2006/09/30/73097.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/73097.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2006/09/30/73097.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/73097.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/73097.html</trackback:ping><description><![CDATA[
		<!-- end good artical -->
		<!-- content -->
		<table class="table" cellspacing="0" cellpadding="0" width="100%" border="0">
				<tbody>
						<tr>
								<td valign="top">
										<div class="wrap_m_c_up" id="div_diggs0">
												<h1> </h1>
										</div>
								</td>
								<td>
										<div class="wrap_m_c_a_c">
												<h3>原文地址：<a id="LinkUrl" href="/BlueDavy/archive/2006/05/28/48593.html" target="_blank">http://www.blogjava.net/BlueDavy/archive/2006/05/28/48593.html</a></h3>
												<p>
														<span id="lblsummary">摘要:插件开发框架其实和目前开源界流行的MVC框架之类的相同，都决定了基于这个框架的开发方式，如基于MVC框架，就会按照MVC思想来进行开发，而插件开发框架呢，也是同样如此，就要求基于插件的方式来进行开发，不过插件开发框架和MVC框架又有不同，插件开发框架是一个可以成为系统基础架构的框架，而MVC框架通常来讲不足以成为，如在目前的MVC框架Webwork、Struts上我们通常都需要加上Spring、Hibernate来构成系统完整的基础架构，这个时候由于MVC框架的实现是没有标准可参照的，就造成了在各种系统中形成了不同的但很类似的基础架构，但却造成了无法复用的现象；插件开发框架则是作为统一系统基础架构的一种开发方式，它使得系统的复用成为了可能，而同时由于插件开发框架对于动态性的支持，使得系统更加的灵活和可扩展。来看看一个插件开发框架，应该提供些什么东西，作为改变系统架构思想的框架，插件框架需要考虑很多方面，如开发、测试、部署等，总结下来一个插件框架应提供插件的开发规范；插件开发、调试的IDE；</span>
												</p>
										</div>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/coolfiry/aggbug/73097.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2006-09-30 22:53 <a href="http://www.blogjava.net/coolfiry/archive/2006/09/30/73097.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Sun 收购 JRuby 项目, 两创始人加入SUN</title><link>http://www.blogjava.net/coolfiry/archive/2006/09/11/68947.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Mon, 11 Sep 2006 05:58:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2006/09/11/68947.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/68947.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2006/09/11/68947.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/68947.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/68947.html</trackback:ping><description><![CDATA[
		<div class="center">
				<h4>摘要:</h4>近日, Sun 宣布收购 JRuby 项目.Sun终于开始表露出让不仅仅是Java运行在JVM上的兴趣了. </div>
		<div class="right">
				<div class="help">
						<u>
								<font color="#0000ff">
								</font>
						</u>
						<br /> </div>
		</div>
		<!-- end of summary line -->
		<div class="overflow" id="text">近日, Sun 宣布收购 JRuby 项目.<br /><br />Sun终于开始表露出让不仅仅是Java运行在JVM上的兴趣了.<br /><br />近日,SUN迈出了重要的一步.<br /><br />Charles Nutter, JRuby 的核心开发者之一, 宣布: <span style="COLOR: red">Sun 已经整体收购了JRuby 项目. 他与JRuby的另外一个核心开发者<br />Thomas Enobo 都将会加入Sun.</span><br /><br />从这次动作看的出来Ruby在Sun战略中的地位,  看来Ruby很有可能成为JVM支持的第一个非Java语言.<br /><br />动态语言, 已经不遥远了.<br /><br />官方站点:<br /><a href="http://jruby.sourceforge.net/" target="_new">http://jruby.sourceforge.net/</a><br />(<a href="http://www.matrix.org.cn/resource/news/JRuby+Sun_954.html">http://www.matrix.org.cn/resource/news/JRuby+Sun_954.html</a>)</div>
<img src ="http://www.blogjava.net/coolfiry/aggbug/68947.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2006-09-11 13:58 <a href="http://www.blogjava.net/coolfiry/archive/2006/09/11/68947.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用JNI调用C或C++动态联接库入门</title><link>http://www.blogjava.net/coolfiry/archive/2006/09/09/68716.html</link><dc:creator>Coolfiry</dc:creator><author>Coolfiry</author><pubDate>Sat, 09 Sep 2006 08:30:00 GMT</pubDate><guid>http://www.blogjava.net/coolfiry/archive/2006/09/09/68716.html</guid><wfw:comment>http://www.blogjava.net/coolfiry/comments/68716.html</wfw:comment><comments>http://www.blogjava.net/coolfiry/archive/2006/09/09/68716.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/coolfiry/comments/commentRss/68716.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/coolfiry/services/trackbacks/68716.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 用																						JNI																				调用																						C																				或																						C++																				动态联接库原来如此简单		...&nbsp;&nbsp;<a href='http://www.blogjava.net/coolfiry/archive/2006/09/09/68716.html'>阅读全文</a><img src ="http://www.blogjava.net/coolfiry/aggbug/68716.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/coolfiry/" target="_blank">Coolfiry</a> 2006-09-09 16:30 <a href="http://www.blogjava.net/coolfiry/archive/2006/09/09/68716.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>