JDK自带VM分析工具
文章来自网络,作为资料收集。
————————————————
jps : jps 的作用是显示当前系统的java进程情况,及其id号。可以通过它来查看到底启动了几个java进程(因为每一个java程序都会独 占一个java虚拟机实例),和他们的进程号(为下面几个程序做准备),并可通过opt来查看这些进程的详细启动参数。
使用方法:在当前命令行下打 jps(需要JAVA_HOME,没有的话,到该程序的目录下打) 。
jstat :对VM内存使用量进行监控。
jstat工具特别强大,有众多的可选项,详细查看堆内各个部分的使用量,以及加载类的数量。使用时,需加上查看进程的进程id,和所选参数。以下详细介 绍各个参数的意义。
jstat -class pid:显示加载class的数量,及所占空间等信息。
jstat -compiler pid:显示VM实时编译的数量等信息。
jstat -gc pid:可以显示gc的信息,查看gc的次数,及时间。其中最后五项,分别是young gc的次数,young gc的时间,full gc的次数,full gc的时间,gc的总时间。
jstat -gccapacity:可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,如:PGCMN显示的是最小perm的内存使 用量,PGCMX显示的是perm的内存最大使用量,PGC是当前新生成的perm内存占用量,PC是但前perm内存占用量。其他的可以根据这个类推, OC是old内纯的占用量。
jstat -gcnew pid:new对象的信息。
jstat -gcnewcapacity pid:new对象的信息及其占用量。
jstat -gcold pid:old对象的信息。
jstat -gcoldcapacity pid:old对象的信息及其占用量。
jstat -gcpermcapacity pid: perm对象的信息及其占用量。
jstat -gcutil pid:统计gc信息统计。
jstat -printcompilation pid:当前VM执行的信息。
除了以上一个参数外,还可以同时加上 两个数字,如:jstat -printcompilation 3024 250 6是每250毫秒打印一次,一共打印6次,还可以加上-h3每三行显示一下标题。
jmap 是一个可以输出所有 内存中对象的工具,甚至可以将VM 中的heap,以二进制输出成文本。使用方法 jmap -histo pid。如果连用 SHELL jmap -histo pid > a.log可以将其保存到文本中去(windows下也可以使用),在一段时间后,使用文本对比工具,可以对比出GC回收了哪些对象。jmap -dump:format=b,file=f1 3024可以将3024进程的内存heap输出出来到f1文件里。
jconsole 是一个用java写的GUI程序,用来监控VM,并可监控远程的VM,非常易用,而且功能非常强。由于是GUI程序,这里就不详细介绍了,不会的地方可以 参考SUN的官方文档。
使用方法:命令行里打 jconsole,选则进程就可以了。
艺无止境
看了别人用SWING实现的一个桌面应用程序,有些吃惊,效果非常的棒,抛开实现这个效果的成本,SWING也很惊艳。
不过惊艳的SWING也只能有时间的时候去研究了,现在更看重的是快速的开发,没有时间去细细的雕琢和研究,这种研究和雕琢付出并不会带来与之相应的价值。
感叹一下,技术无止境。
细说Java GUI:AWT,SWT,Swing
来自网络的老文,很有意思,收藏之。
======================================================
Overview概述
Java GUI 工具包一直是一个倍受争议的话题。同样的争论也发生在其他编程语言如Smalltalk。实际上每个平台无关的语言都存在着这样的争论。Java作为当前最受广泛使用的编程语言而尤为突出。 这场争论在支持模拟组件(如widgets和control,在下文中也称之为仿造组件)和支持本地组件(在下文中也称之为原生组件)的人们之间展开,于是Java开发者形成了两个不同的阵营,提倡使用模拟组件的Swing,和提倡使用原生组件的SWT。历史
Internet上有许多围绕这一争论的故事。你可能已经听说过它们中的大多数了,其中之一有助于让你理清头绪,让我们就从这里开始,Amy Fowler是Swing阵营的一个倡导者。
回 到上个世纪90年代,曾几何时有3家庞大的Smalltalk公司——IBM、Parc-Place和 Digitalk。在90年代初期3家公司的市场份额大致相等,生活是美好的。Parc-Place采用仿窗口部件(emulated widgets)的设计(即Swing的设计),IBM和Digitalk则采用原生窗口部件(native widgets)。后来IBM压倒了另外两家,因此他们打算合并成一家,假设叫做Parc-Place Digitalk。随后当他们试图将他们的产品融合到一个叫做Jigsaw的计划中时爆发了一场大战,计划由于政治原因失败了(开发人员实际上已经能让它 运转起来),就因为原生和仿造两派的死战。
Amy赢得了精神上的胜利,不过IBM赢得了他们所有的生意,因为这两家公司在一整年里除了吵架什么都没做。当尘埃落定之后PPD(Parc-Place Digitalk当时已改名为Objectshare,跟Windscale改名为Sellafield的原因相同——让人们淡忘之前发生的灾难)的股票 价格从60美元掉到了低于1美元1股。他们因为伪报收入被NASDAQ摘牌,从此消失。
当 时,AWT已经出现了。SUN当时已经建立了一套基本的可移植控件类,这些类映射到不同操作系统上的原生窗口组件(native widget),当时的AWT还满是漏洞,远不能称为可靠,还需要SUN的coder们去修补。然后Amy被雇佣了,她承诺通过轻量级方案解决所有窗口组 件的问题,以此说服SUN管理层让她当了GUI开发部门的头头。随后Amy雇佣了所有她过去在Parc-Place的旧朋友,让他们来开发Swing。
在IBM,VisualAge for Java最初是用Smalltalk(用的是原生窗口组件)写的,当将这些工具向Java代码库迁移时,他们需要一套窗口组件。IBM这边的开发人员都是 原来搞Smalltalk的那一批人,他们对管理层要求用Swing来构建WebSphere Studio工具都非常不情愿。“Swing是个可怕的充满缺陷的怪兽“。因此开始了一个新的项目,把他们的Smalltalk原生窗口组件移植到 Java上去。这个工具集后来被成为SWT,S开始是Simple的缩写,不过后来变成了Standard的缩写。这个项目获得了成功,被运用在发布的 VisualAge Micro Edition产品中。他们当时发现在Swing读事件队列的时候用了一种可能留下内存漏洞的方式,而不得不采用他们自己的查询 Windows事件队列的循环,以纠正这个错误。这促成了他们关于SWT和 AWT/Swing不能共存的决定。他们把这个工具包放到了Eclipse中,这是一个来自于早期Visual Age的工具平台。

你应该已经从上述的故事中对三者的历史有了大概的了解,尤其是SWT。现在你也许会觉得,IBM创建SWT的理由是合理的而Swing应该沿用SWT采用的方式。这样的观点是片面的,当你深入了解到Java的本质之后,你会发现其实并不像你想象的那么简单。
先决条件
什么才是Java本质的,影响到工具集设计的特征呢?或者说,什么才是Java GUI工具集设计的先决条件呢?
答 案来自于Sun对Java的承诺之一:write once, run anywhere(一次编写,随处运行)。这是Java不同于其他语言的优势所在。在Java被创建之前,软件的跨平台性能是开发者,特别是那些希望对多 平台提供支持的开发者的梦魇。在当今的生活中Internet的使用已经相当的普遍了,在世界不同角落的人们在不同的平台上工作着。软件提供商为不同的操 作系统提供支持是再平凡不过的事情。Java的write-once-run-anywhere(WORA)承诺显然减轻了开发者的负担,极大地提高了软 件开发的生产力。
然 而编写跨平台的应用程序,你必须使用支持平台无关性的标准库。这些标准库包括语言支持,公共用途,网络,I/O和GUI工具集等。所以当Sun开始设计 GUI工具集的时候,首要任务就是考虑一个设计良好的平台无关的API。AWT和Swing都被小心地设计以保证平台兼容性。SWT则相反,它在设计之初 并不以扩展性为原则,它为一个专有的IDE Visual Age for Java而设计,Windows作为这个IDE的首选运行环境拥有很高的优先级考量。SWT API类似于WIndows,通常它并不如Swing的扩展性好,尽管Steve Northover,SWT之父,辩称SWT是平台无关的,你可以很容易地发现许多Windows API的痕迹。
区别
GUI 应用程序是软件的一种主要类型,所以Java的GUI库应该是标准化并被集成到JRE平台中的。然而不同的操作系统有不同的GUi风格和组件集。有一些组 件在所以平台上有相似的观感。这些共有组件如按钮,标签,文本域,单选框等被称为标准组件。不同的GUI工具集提供了不同的组件集。GUI工具集总是遵循 不同的原则来选择组件类型和特征以实现。考察一个工具集,有两个不同的要素:组件类型和组件特征。
Terms
首先让我图解两个数学概念:最大公约数和最小公倍数。如下图,三个集合代表不同的操作系统。相交的部分是最大公约数,合并的部分是最小公倍数。

现在让我们来考察Java GUI工具集AWT,SWT和Swing的组件类型和特征
AWT
AWT 组件集遵循最大公约数原则,即AWT只拥有所有平台上都存在的组件的公有集合。所以你在AWT中无法获取如表或树等高级组件,因为它们在某些平台上不支 持。AWT的组件特征同样遵循这一原则。它只提高平台上公有的特征。例如AWT按钮不能附着图片,因为在Motif平台上,按钮是不支持图片的。
由于它低劣的组件集和特征,AWT无法吸引开发者。它是Sun不推荐使用的,只是为了确保向下兼容和支持Swing。
SWT
SWT 最初的目标之一是为了提供比AWT更为丰富的组件集。它遵循最小公倍数原则以提供一个各个平台上包含的组件的并集。思路是如果一个组件在某个平台上包含, 那么SWT就会包装它并用java代码和JNI来调用它。如果一个组件在某一平台上不存在,它就会用继承并绘制Composite的方式来模拟组件。一个 SWT Composite类似于AWT的Canvas。以这种方式,SWT提供了较AWT更为丰富的组件集。值得指出的是SWT的JNI封装不同于AWT,它的 模拟也不同于Swing。
在 组件特征方面,SWT类似于AWT。它遵循最小公倍数原则。在早期的SWT版本中,SWT按钮因为和AWT同样的原因不支持附着图片。在之后的版本中,许 多缺失的特征采用模拟的方式补全。但仍有许多特征无法采用纯粹的模拟实现。SWT将组件的控制交给本地操作系统。它难以扩展。只有例如图形装饰等特征可以 借助模拟绘制来自定义实现。所以严格意义上将,SWT组件的组件集和特征因其难于扩展而不如Swing来得丰富。
Swing
Swing 是三者中最强大和灵活的。在组件类型上,它遵循最大公约数原则。由于Swing可以控制自身GUI系统的全部并有很好的可扩展和灵活性,它几乎可以创建所 有你想象得到的组件。唯一的限制是它的AWT容器。在Swing中你还不能跨平台地实现真正的透明化和不规则矩形窗口,因为Swing依赖于AWT顶层容 器例如Applet, Window, Frame and Dialog等。除此之外,Swing几乎实现了所有平台上的标准组件。
在组件特征上,Swing遵循最小公倍数原则。它拥有所有平台上可提供的组件特征。不仅如此,你还可以继承已有的Swing组件并添加新的特性。
上 面比较主要是在API级别上的。让我们将比较的焦点转移到实现细节上。Swing和SWT/AWT的区别是Swing是纯Java实现,而SWT和AWT 是Java和JNI的混合。当然,它们的目标都是相同的,提供一个跨平台的APIs。然而为了达到这一点,SWT和AWT不得不牺牲一些组件和特性以提供 一个通用的APIs。
AWT
一 个AWT组件通常是一个包含了对等体接口类型引用的组件类。这个引用指向本地对等体实现。举java.awt.Label为例,它的对等体接口是 LabelPeer。LabelPeer是平台无关的。在不同平台上,AWT提供不同的对等体类来实现LabelPeer。在Windows上,对等体类 是WlabelPeer,它调用JNI来实现label的功能。这些JNI方法用C或C++编写。它们关联一个本地的label,真正的行为都在这里发 生。作为整体,AWT组件由AWT组件类和AWT对等体提供了一个全局公用的API给应用程序使用。一个组件类和它的对等体接口是平台无关的。底层的对等 体类和JNI代码是平台相关的。

SWT
SWT也使用JNI的方法论来实现。但细节不同于AWT。SWT的拥护者听到人们拿SWT和AWT相提并论可是会很生气的,Steve Northover,SWT之父,就曾为此抱怨过。
没 错,它们是不同的。让我们深究SWT的代码。在SWT中,各个平台上唯一相同的部分是组件的接口,是类和方法的定义签名。所有的底层代码都是平台差异的。 SWT为每个平台提供了OS类。这个类用JNI封装了许多本地APIs。SWT组件类通过把这些JNI方法黏合在一起提供一个有意义的功能。
例如,在Windows上,文本域的选择是由一个系统调用处理的。这个系统调用在Windows的OS类中作为一个本地方法实现。所以在Windows平台的Text的setSelection方法中只用到了一个JNI调用。
然而,在motif上,文本域的选择包含两个本地调用。SWT就在motif的OS类中实现了两个调用。所以在motif上组件类需要作两次调用来实现文本的选择。

现在你应该能看出SWT和AWT的最大不同了,它们使用了不同的对等体编程方式来消除平台差异。SWT用java代码或有JNI实现的java对等体来黏合系统调用。而AWT把代码包含在对等体中,使情况复杂化了,我个人觉得SWT的方法更加明智。
SWING
到 了Swing这里,一切就变得清晰和直接了。除了顶层容器,Swing的实现不依赖于具体平台。它掌管了所有的控制和资源。Swing所需要的是事件输入 来驱动系统,以及承接自顶层AWT容器的图形处理,字体和颜色。普通的Swing组件可以看作是AWT容器的一块逻辑区域。它们并没有注册对等体。所有添 加到同一顶层容器的Swing组件共享它的AWT对等体以获取系统资源,如字体,图形处理等。Swing将组件自己的数据结构存储在JVM的空间中。它完 全由自己管理画图处理,事件分发和组件布局。

由 于AWT和SWT都持有对本地组件的引用,它们必须以正确的方式释放这些引用以避免内存泄露和JVM崩溃。AWT将绝大多数资源管理任务交给系统,将开发 者从单调乏味的资源管理中解救出来。然而这使得AWT的实现复杂化了。一旦它实现了,开发者很少有机会犯错误并使他们的程序崩溃。
SWT 用的是另一种方法。大体上,SWT让开发者自己来管理资源。它的一条著名的规则是:谁创建,谁释放。因此开发者必须谨慎地显式调用dispose方法释放 每一个由他创建的组件和资源。这简化了SWT的实现模型,但把开发者摆在了因错误编码而易于造成程序崩溃这一风险之上。
模拟方式的区别
Swing 和SWT在它们的实现上都使用了模拟。SWT只模拟平台上缺失的组件。区别是SWT的模拟更像是AWT的Canvas实现的模拟。SWT的 Composite类有它自己在操作系统中相应的对等体。它从自己的对等体中获得所有它所需要的资源如图形处理的对象,字体和颜色等。它直接从操作系统获 取所有的事件并进行处理。然而,Swing组件在操作系统中没有相应的对等体。它只是一块顶层容器中的逻辑区域,实际上它从顶层容器的对等体中借用资源。 Swing的事件并不是底层系统产生的事件。它们实际是由顶层容器处理AWT事件所产生的伪事件。我们会在稍后的事件部分中详细介绍它。
图形层结构
另 一个不同之处是Swing组件的z-order系统是来自于AWT组件的。如上所述,Swing组件与顶层AWT容器共享一个对等体。因此,Swing组 件也和顶层容器有相同的z-order。SWT和AWT组件都有不同于顶层容器的z-order,通常是高于顶层容器。故而如果AWT组件和Swing组 件混合在一起的话,Swing组件将可能被AWT组件遮住。当操作系统开始更新UI的时候,顶层容器和Swing组件总是先于AWT组件绘制。当它们完成 绘制,AWT组件会覆盖Swing可能绘制过的地方。因此不提倡Swing和AWT组件的混用。如果有一个浮动的Swing组件如菜单,AWT组件很可能 遮盖菜单。

布局管理器
并 不是三者中的所有部分都是不同的。布局管理器是一个例外。开发GUI应用程序,当容器改变大小的时候,组件需要重定位或改变大小。在传统的编程语言中,这 依靠监听大小改变的事件来实现。相应的片段散落在源代码的各个角落降低了程序的可读性。Java引入了将布局代码封装的思路,称之为布局管理器。当布局管 理器对象被设置到一个容器中,它自动处理大小改变的事件。当大小改变时,管理器的布局方法被调用以重定位子组件或调整它们的形状。
AWT,SWT和Swing都以这样的方式来组织,而都有它们各种独特的布局管理器。由于AWT和Swing拥有一个共同的超类java.awt.Component,它们的布局管理器可以交替地使用。
Look And Feel观感
包 括SWT和AWT在内的本地工具集并不支持Look And Feel机制。它们将组件捆绑在操作系统上,有其优势和劣势。其中的一个劣势是它们不支持可插拔的Look And Feel。将绘制处理交由操作系统完成剥夺了它们实现自定义组件Look And Feel的能力,也就使得它们无法提供这种机制。Look And Feel机制越来越成为GUI工具集中不可缺少的一部分。
Swing拥有 很好的Look And Feel支持。你甚至可以动态地改变Swing应用程序的Look And Feel,鉴于AWT和SWT将组件控制完全交给操作系统处理,这是它们所无法超越的任务。我曾经听很多人抱怨过Sun在Swing上的设计。他们觉得 Swing为什么不像SWT那样沿用AWT的思路呢?事实上,Look And Feel机制正是Swing走到这个方向上的原因之一。如果Swing遵循的是包装已有的组件并模拟不存在的组件的路线,那它就无法提供Look And Feel机制。因为提供Look And Feel机制是本地策略所无法完成的任务。
Graphics and Fonts图形和字体
Swing作为一个仿生系统,它的图形工具集较之AWT和SWT强大许多。Swing基于其自身系统中的两个基础组件群:Java 2D和AWT。Java 2D在Java中是强大的类库,它为高级图像处理,颜色管理,图形绘制和填充,坐标系变换和字体生成提供丰富的特性。相较之下,AWT和AWT仅对这些特 性提供有限访问,它们是相对原始和低级的。
JavaBeans Specification Conformity JavaBeans规范一致性
Swing 和AWT在设计之初就秉承了JavaBeans规范,它们的组件类与JavaBeans规范一致。然而SWT并没有很好的遵循这一规范。例如,在SWT的 组件类中没有无参的构造器。每个组件都必须至少拥有一个单参数的构造器。这个参数就是父组件的引用。这意味着无论何时组件被创建,它都直接被添加到一棵组 件树中。一个组件无法脱离于已注册的本地对等体而存在。这样,SWT就能让由编程者创建的组件在display的dispose方法被调用的时候自动被释 放。
More on Resource Management更多在资源管理方面的内容
SWT 的组件构造器策略可以排除某些内存泄露的可能性。AWT在资源管理方面也有类似的问题。但它采用了不同的方式解决。当AWT组件被创建的时候,相应的对等 体并不会立即被创建。即便它被添加到一棵组件树,而如果这棵树还不可见,那么对等体仍不会被创建。只有当顶层容器被设为可见,这些对等体才会被创建。创建 对等体的方法通常在addNotify中,它们通常递归地调用父组件的addNotify直到整棵组件树上的对等体都被创建了。当顶层容器由 dispose方法销毁的时候,一个对应的方法removeNotify将会被递归地调用以释放这些对等体。这样,AWT在不由开发者介入的情况下管理了 它的资源。
Event System事件系统
一个事件要求特定的动作被执行,它被作为消息由外界或系统自身发送给GUI系统。这些事件包括来自计算机设备如鼠标键盘和网络端口的I/O中断,以及GUI系统的逻辑事件触发,比如一个按钮的ActionEvent事件。
Single-Threaded vs Multiple-Threaded 单线程 vs 多线程
事件分发遵循两种不同的模型。单线程分发模型和多线程分发模型。

在 单线程分发模型中,一个事件从队列中抽出并在同一个线程中被立即处理。事件处理后,紧跟着的下一个事件再被抽出并继续下一轮的循环。在多线程分发模型中, 从队列中获取事件的线程启动另一个被称作任务线程的线程,并把事件交给它处理。而获取事件的线程并不等待处理线程的结束。它简单的获取下一个线程并分发 它。
事件处理通常涉及应用程序的数据变化。而且这些数据经常是组件需要显示的。多线程分发很容易产生同步问题,它产生多个可能互相干扰的事件处理线 程。在一个稳定的GUI系统中,组件应该能够保持视图与模型间的同步。由于同步问题的出现,多线程模型要求开发者拥有更多并发编程的经验。而对于普通编程 人员,造成同步错误是很容易的。因此许多GUI系统并不使用这一模型。
单线程模型通过强制事件序列化地被处理提供了实际上的同步。AWT,SWT和Swing 都采用了这一模型来分发事件。但单线程模型也会有它自己的问题。其中之一就是线程专注。既然所有的事件都在一个线程中被分发,如果其中的一个事件的处理费 时过久,将会拖延下一个事件的抽取和执行。如果有一个PAINT事件被延后,那么在屏幕上就会呈现为无法响应。这经常使用户感觉到软件很慢。许多这样的低 效程序是由于开发者的经验不足造成的。他们的做法是将耗时任务填充到监听器方法中。由于这种错误的编程方式在Swing中大量被使用而尤为突出,这也是它 慢而丑陋的坏名声的由来之一。实际上,如果你懂得使用线程,Swing应用程序可以表现出很高的响应度。
Thread Safety线程安全
上 述问题的解决方案是启动一个单独的工作者线程来完成耗时处理,这样就能把事件分发线程释放处理以继续运作。如多线程模型中所做的那样,然而这同样会引入在 多线程模型中出现的同步问题。许多GUI工具集都有自己先天性的机制来解决这一问题,例如,在组件树上的组合锁。开发者不需要为如何安全编程而操心。这样 的工具集被成为线程安全的。AWT就是其中之一。
然而,由于不必要的同步使得建立这样的GUI系统过于负责并造成了额外的开销。因此,Swing和SWT被设计为非线程安全的,这意味着开发者必 须谨慎地实现他们的多线程任务。SWT和Swing在运行时线程安全行为上有一个小小的区别。SWT总是检查改变组件的操作是否在事件分发线程上执行。这 样,开发者就能够发现同步问题。而Swing不这样,这是Swing的一个不知之初,这其实并不难实现。
Event Dispatching Thread事件分发线程
AWT读取操作系统中的基本事件并在java代码中处理它们。AWT事件分发线程被命名为?AWT-{OS}-Thread。这个线程由AWT系 统隐式启动。当AWT应用程序启动时,这个线程在背后启动,在操作系统上抽取和移除事件。当诸如按钮动作这样的逻辑事件被触发时,它传递给注册在按钮上的 操作监听器。开发者也能为AWT组件编写鼠标,键盘和绘制事件的事件监听器。这通常通过扩展AWT Canvas组件来完成。这一组件支持了所有已提供的事件,而且你可以通过重写事件处理方法,实现一个自定义的组件。
然而,在Swing中,这是一个不同的线程。Swing把AWT事件作为自身事件系统的一个输入。它获取AWT事件并做一些初始化处理,产生一个 对应的Swing事件并把它放到自己的事件队列上。Swing也有自己的事件分发线程,通常命名为EventQueue-0。这个线程从事件队列中抽取 Swing事件,就如同AWT从操作系统中抽取那样。然后它把事件分发给目标组件。通常事件首先被分发给组件的顶层容器,然后由顶层容器的 dispatch方法处理,它可能被再分发或重定向到一个Swing组件,在那里继续由自己的监听器进行处理。
例如,当一个鼠标移过一个JButton或JFrame时,一个指向JFrame的AWT事件在AWT线程上触发。AWT线程检查它是否需要作更 多的处理,然后把它包装成一个Swing的MouseEvent对象并把它添加到EventQueue队列中。当获得MouseEvent事件 后,EventQueue-0抽取这个事件并判断出目标Swing组件。这里,这个组件是JButton。然后它产生了一个包含相对坐标位置和事件源的新 的MouseEvent重定向到这个JButton上,然后调用这个JButton的dispatch以继续分发。JButton的dispatch过滤 事件给特定的方法最终实现由鼠标监听器在该点上的分发的点击。
SWT更类似于AWT。唯一的区别是它要求开发者显式地书写事件循环代码。然而底层的实现细节是不同于AWT的。看看SWT的读取和分发事件代码,它会让你想起MFC的代码风格。
Event Listener事件监听器
AWT,SWT 和Swing都有相似的事件监听器模型。它们都使用观察者模式,组件和监听器的连接方式是把监听器添加到组件上,这组成了一个对象网络的模型。当事件被触 发并分发给组件,组件调用它的监听器以处理事件。一个监听器也可以继续分发事件给后续的处理,或产生一个新的事件并把它广播到网络中的其他节点上。基本上 有两种不同的广播事件方式。一种是同步调用监听器。另一种是异步地将事件发送回队列,它将在新一轮的事件分发中被分发出去。
除 了直接发送给队列的方式,Swing还有一些其他的分发异步事件的方法。其中之一是调用SwingUtilities 或EventQueue 的invokeLater,这一方法包装一个Runnable对象到事件中并把它发送给事件队列。这确保了Runnable的run方法能在事件分发线程 上执行,保证了线程安全。实际上,Swing的Timer好SwingWorker基于这一机制实现。SWT也有相应的发送异步事件的方式。它的 invokeLater的对应方法是display.asyncExec,它以一个Runnable对象作为参数。
我从一个技术层面给出了他们优劣势上的一个清单,以结束本文。
AWT
AWT是Sun不推荐使用的工具集。然而它在许多非桌面环境如移动或嵌入式设备中有着自己的优势。更少的内存。它对运行在有限环境中的GUI程序的开发,是合适的。
1.更少的启动事件。由于AWT组件是本地由操作系统实现的。绝大多数的二进制代码已经在如系统启动的时候被预装载了,这降低了它的启动事件。
2.更好的响应。由于本地组件由操作系统渲染。
3.从java 1.x时代就为JRE支持的标准GUI工具集,你不用单独安装它,你不用担心平台差异的问题。
4.成熟稳定的。它能够正常工作并很少使你的程序崩溃。
然而,事物都有它们不好的一面。让我们来例数它吧。
1. 更少的组件类型。表和树这些重要的组件缺失了。它们是桌面应用程序中普遍使用的。
2.缺乏丰富的组件特征。按钮不支持图片附着。这很明显是它遵循的设计原则造成的。
3.不支持Look And Feel。AWT被设计为使用本地组件。因此,它依赖系统来提供Look And Feel支持。如果目标系统并不支持这一特性,那么AWT将无法改变它的Look And Feel。
4.无扩展性。AWT的组件是本地组件。JVM中的AWT类实例实际只是包含本地组件的引用。唯一的扩展点是AWT的Canvas组件,你可以从零开始创建自定义组件。然而无法继承和重用一个已有的AWT组件。
SWT
SWT有如下优势:
1.丰富的组件类型。SWT提供了种类繁多的组件,从基础组件如按钮和标签到高级的表格和树。
2.相对的丰富组件特性。尽管SWT也遵循最大公倍数原则,它采用模拟的方式重新设计了对更多组件特性的支持。所以同AWT相比,它有着相对丰富的组件特性。
3.更快的响应时间。基于和AWT同样的原因,SWT组件包装了本地组件,由操作系统实现渲染。操作系统通常对渲染处理做了优化,保存GUI二进制代码为标准库,减少了内存的使用,提高了响应性能。
4.更少的内存消耗。既然操作系统为本地组件提供了优化,这一点就容易理解了。
不足之处:
1.不在JRE的标准库中。因此你必须将它和你的程序捆绑在一起,并为你所要支持的每个操作系统创建单独的安装程序。
2. 不够成熟和稳定。SWT因其设计上的一些缺陷,如资源管理,Windows友好等,被认为是不稳定的。它可以在Windows上表现得很好,但在其他操作 系统上,它经常是不稳定且容易崩溃的。这很大程度上是因为它把资源管理交给开发者来处理,而并不是所有的开发人员能够正确地处理这些。
3.在非Windows平台下的性能不高。如同第2点提到的,SWT被设计为与Windows API相协调的,这导致了在非Windows平台上的性能问题,糟糕的UI感官,不稳定甚至内存泄露。
4.无Look And Feel 支持。和AWT同样的原因。
5.不可扩展,和AWT同样的原因。在SWT中你可以通过有限的方式扩展一个组件,例如,监听PAINT事件并添加自定义绘图到组件上,但鉴于你只能控制绘制处理的一部分,这是十分有限的。你也只能在操作系统绘制完组件后补充,这并不能很好支持自定义。许多应用程序在自定义行为上有很高的要求。
SWING
Swing是三者中最强大的GUI工具集。它和另外两者相比同样有自己的优劣势。
1、丰富的组件类型。Swing提供了非常广泛的标准组件。这些组件和SWT一样丰富。基于它良好的可扩展性,除了标准组件,Swing还提供了大量的第三方组件。许多商业或开源的Swing组件库在开发多年后都已经可以方便地获取了。
2、丰富的组件特性。Swing不仅包含了所有平台上的特性,它还支持根据程序所运行的平台来添加额外特性。Swing组件特性遵循特定原则,易于扩展,因此能够提供较SWT和AWT更多的功能。
3、好 的组件API模型支持。Swing遵循MVC模式,这是一种非常成功的设计模式。它的API成熟并设计良好。经过多年的演化,Swing组件APIs变得 越来越强大,灵活和可扩展。它的API设计被认为是最成功的GUI API之一。较之SWT和AWT更面向对象,也更灵活而可扩展。
4、出 色的Look And Feel支持。MVC设计模型允许Swing分离组件视图和它的数据模型。它有高级的UI委托来将UI渲染委托给UI类。这些类被注册到一个展现特定的 Look And Feel的对象上。已经有上百个Look And Feel 可以提高各种各样的GUI风格。你甚至可以基于其他人的成果编写组件的Look And Feel 。
5、标准的GUI库。Swing和AWT一样是JRE中的标准库。因此,你不用单独地将它们随你的应用程序一起分发。它们是平台无关的,所以你不用担心平台兼容性。
6、成熟稳定。Swing已经开发出来7年之久了。在Java5之后它变得越来越成熟稳定。由于它是纯Java实现的,不会有SWT的兼容性问题。Swing在每个平台上都有相同的性能,不会有明显的性能差异。
7、可扩展和灵活性。Swing完全在Java空间中实现。它可以控制它所需要的一起。Swing基于MVC的结构使得它可以发挥Java作为一门面向对象语言的优势。它提供了许多扩展组件的方法。让我们来列举一下:
A.继承已有组件;
B.靠复合组件的方式扩展。
C.从零开始使用JComponent编写自定义组件;
D.使用渲染器和编辑器机制扩展复制的Swing组件,如JList,JComboBox,JTable,JTree等;
E.基于已有Look And Feel 或从零开始创建新的Look And Feel;
F.使用LayeredPane,GlassPane或拖放机制开发高级的组件,例如浮动的固定组件,自定义弹出窗口,自定义菜单等。
8、总体上良好的性能。Swing的速度是其为人诟病的一点。然而随着JRE的开发,Swing的性能如今已经有了很大的提高。特别是Java5之后,Swing的总体速度能够接近本地小控件系统。
一个GUI的速度总是从两个方面被衡量:响应时间和数据反馈时间。
响 应事件指从事件任务出现到组件更新UI的这段时间。例如按下一个按钮,拖动一个组件,改变一个多标签面板等。在这个方面本地组件总能比模拟组件有更好的响 应。AWT和SWT通常比Swing表现出更好的响应时间。然而事实并非总是如此。这取决于操作系统。如果本地组件没有被良好的实现,那结果就是相反的。 例如Windows开发了不错的GUI库,而Linux平台通常差得较远。SWT可能在Windows上表现得比Swing好,而在Linux上表现得比 Swing差。也就是说,AWT/SWT的性能决定于底层平台。随着JRE的开发,Swing的响应性能能够随着JVM的优化,更好的实现方式,以及图形 硬件加速而得到长足的改进。在Windows上,Java6的Swing可以媲美SWT的性能。在非Windows环境中,Swing可以表现出更好的响 应时间。
数据输送时间是指用于将应用程序数据传递给UI组件所需要的时间。例如,一个学生管理系统要求从数据库中装载学生信息并在一个表格中显示出来。 花费在从内存到表格组件的数据传递时间被称为数据输送时间.在这个方面,Swing通常比其他二者的性能更好。或许当数据量不大的情况下并不明显。但当海 量的数据被输送给表格的时候,这一点就显而易见了。为了理解这一点,提醒你注意JVM和本地操作系统是两个分离的运行时环境。由于JNI的调用在两个环境 中跨越式地发生,通常比一个普通的Java调用花费更长的时间。通常这包含两个处理。一个是Java数据结构转换为本地数据结构,另一个是方法返回时的本 地数据结构转换为Java对象。其他的性能开销暂时忽略不计。当一个大范围数组的数据从本地组件中输送过来,大量反复的JNI调用将极大地拖垮性能。
Swing的另一个优势是它有许多的组件模型以提高输送的性能。例如TableModel被映射为两个维度上的数组。这样,在Swing组件中甚至不需要进行数据方式的转换。Swing直接将应用程序数据显示在屏幕上,节省了在数据转换上所花费的事件。
Swing也有不足之处:
比AWT和SWT更多的内存消耗。Swing自己实现了所有组件。因此,它在运行时装载了大量的类。一些其他的问题来源于小的可变对象的创建, 如Rectangle,Point,这些对象基于同步的考虑通常不可重用。Java在堆上创建所以对象。小的对象通常导致了额外的堆空间消耗。许多小的对 象较之大对象更难以有效地被垃圾回收。因此,Swing应用程序通常无法及时回收大而小的对象。这种情况的普遍就会导致性能下降。
更多的启动时间。现在JVM已经快得多了。许多人甚至扬言它可以媲美C++的实现。但多数的Java应用程序还是看上去很慢。实际上,Java性 能的很多问题来源于类装载机制。这是一个I/O操作,故而能够明显地降低Java应用程序的速度。也许这是每个动态链接系统中都要面对的问题吧。 Swing通常包含了上千个Swing类的使用,在Swing应用程序可以显示它的主窗口之前,它比AWT或SWT装载了多得多的类。这严重降低了 Swing的启动时间。这种问题也许会相对好一点如果Swing的类是以共享系统库的方式预加载的。
上述的比较总的来说是技术上的总结。相对其他方面的因素也会影响你对一个工具集的选择。例如,文档,支持,学习曲线和社区等,但既然我们关注的是技术层面,就不在这里讲的太多了。
Java桌面开发的一些体会
给客户交付了单机版的第一个完整版本,客户很满意,还有些不完善的地方,不过第一次做Java桌面开发能做到这个程度,可以小小的骄傲一下了。
Java的桌面开发是个比较繁琐的事情,尤其是如果不熟悉API的话,很多时候不知道该如何入手,熟悉API后会好些,但依然麻烦。主要是没有好的可视化开发工具(也可能是我孤陋寡闻),NETBEANS在GUI开发方面比Eclipse要好,但NETBEANS实在太难用了,开发效率很低。
XUI是Java桌面开发不错的入门途径,它在SWING和AWT上做了更高一层的封装,并且提供了基于XML的配置方式,在一定程度上可以降低开发难度提高开发效率,但是对于复杂的GUI开发我觉得效果不明显。
Java的GUI的最大优势还是跨平台,如果没有跨平台的需求,VB、DELPHI之类的会是更好的选择。个人感觉Java GUI如果要达到类似VB、DELPHI GUI的效果,估计得付出双倍的精力。
如果Java的GUI能提供一个框架,这个框架提供GUI解释引擎,在这个框架之上采用脚本语言(象Groovy)来开发,类似于在浏览器中采用Javascript的方式进行开发,同时能支持样式配置和XML配置的话,Java的GUI开发就该是一件轻松和惬意的事情了。
Roller全接触之基本概念和表结构
Roller的基本概念在网上可以找到很多,不再赘述,下面两个链接有着较高的参考价值:
3:Roller的逻辑结构图(参考)
4:Roller数据库表结构图
Roller全接触之源码构建
构建Roller源码之前须保证JDK5.0和Ant已经安装并配置完毕。
出于测试的需要,Tomcat和MySql也应该安装完毕,并且MySQL Driver以及Roller的数据库和用户名都已设置完毕。
另外还要有个IDE工具,我的是Eclipse,源码构建离不了IDE。
1:从Apache.org下载Roller源码,可以从SVN Checkout 也可以直接Download源码,目前的版本为4.0。
2: 将下载的Roller解压缩,将mail.jar和activation.jar拷贝到tools/lib下。
3:进入apps/weblogger,编辑properties.xmlf,添加如下代码(粗体),将mail.jar和activation.jar放入classpath。
<fileset id=”base.jars” dir=”${ro.tools}/lib”>
<include name=”commons-id-0.1-SNAPSHOT.jar”/>
<include name=”commons-collections-3.2.jar” />
<include name=”commons-codec-1.3.jar” />
<include name=”commons-digester-1.6.jar” />
<include name=”commons-httpclient-2.0.2.jar” />
<include name=”commons-logging-1.0.4.jar” />
<include name=”commons-lang-2.1.jar” />
<include name=”concurrent-1.3.2.jar”/>
<include name=”jaxen-full.jar” />
<include name=”saxpath.jar” />
<include name=”jdom.jar”/>
<include name=”lucene-1.4.3.jar”/>
<include name=”log4j-1.2.11.jar”/>
<include name=”rome-0.9.jar”/>
<include name=”rome-fetcher-0.9.jar”/>
<include name=”velocity-1.5.jar”/>
<include name=”guice-1.0.jar”/>
<include name=”activation.jar”/>
<include name=”javamail.jar”/>
</fileset>
4:编辑build.xml,将SVN(粗体)部分注释掉。
<!– Get the SVN last changed rev value w/o using the overly flakey
svnant task. This will fail if svn is not in your PATH but the only
negative impact will be that the rev number that appears in your
log will be “${svn.LastChangedRev}” instead of the correct value.
<delete file=”${ro.build}/svn.properties” />
<exec executable=”svn” output=”${ro.build}/svn.properties” failifexecutionfails=”false”>
<arg value=”info” />
<arg value=”${root}” />
</exec>
<replace dir=”${ro.build}”>
<include name=”svn.properties” />
<replacetoken> </replacetoken>
<replacevalue></replacevalue>
</replace>
<property prefix=”svn” file=”${ro.build}/svn.properties”/>
<echo file=”${build.compile.business}/roller-version.properties”>
ro.version=${ro.version}
ro.revision=${svn.LastChangedRev}
ro.buildTime=${ro.buildTime}
ro.buildUser=${user.name}
</echo>–>
5:在web/WEB-INF/classes/下新建一个roller-custom.properties文件,文件内容如下,这个文件用来告诉Roller数据库以及mail server的连接参数。
installation.type=auto
database.configurationType=jdbc
database.jdbc.driverClass=com.mysql.jdbc.Driver
database.jdbc.connectionURL=jdbc:mysql://localhost:3306/roller_db
database.jdbc.username=your name
database.jdbc.password=your password
mail.configurationType=properties
mail.hostname=your SMTP server
mail.username=your mail user name
mail.password=your mail password
6:ant dist,没有意外,应该可以在dist/webapp下生成一个war包, 可以将此war包发布到tomcat的webapp下进行部署。
7:启动Tomcat,Listener error,部署失败,看log是发生了NullPointerException,没关系, 接下来我们将在Eclipse中对代码进行调试。到这里为止,可以基于源码Build一个Roller的部署文件。
8:在Eclipse中新建一个Java Project,类型为Java project from existing Ant buildfile,选择刚刚编辑过的build.xml文件,项目建立后,配置一下build path,将缺的包补上。
9:编辑org.apache.roller.weblogger.business.WebloggerImpl,修改下面这段代码,添加一个捕捉NullPointerException的块。
Properties props = new Properties();
try {
props.load(getClass().getResourceAsStream(”/roller-version.properties”));
} catch (IOException e) {
log.error(”roller-version.properties not found”, e);
}catch(NullPointerException e2){// new code
log.error(”roller-version.properties not found”, e2);
}
10:编辑org.apache.roller.weblogger.business.startup.DataBaseInstaller文件,修改如下代码:
try {
props.load(getClass().getResourceAsStream(”/roller-version.properties”));
} catch (IOException e) {
log.error(”roller-version.properties not found”, e);
}catch(NullPointerException e2){//new code
log.error(”roller-version.properties not found”, e2);
}
11:回到Roller解压目录/apps/webblogger下,ant clean,删除已构建的文件,ant dist,重新构建,重新部署,再启动Tomcat,一切OK。
12:至此,可以在Eclipse中对Roller进行二次开发并进行构建和部署,不足的是不够直观,每次构建很耗时,在去掉build.xml中的打包指令后,速度提高了一些,曾想把源文件按照Eclipse web project的目录进行组织,不过始终担心在这个过程当中遗失什么东西而无法发觉,最终决定笨些没关系,稳妥是第一位的。
Roller全接触之开篇
要基于Apache Roller搭建一个独立的多用户博客站点,这将是我第一次仔细的研读著名开源项目的源代码,以前也想读但是水平不够,现在终于可以一偿夙愿了。
希望通过这次阅读和二次开发,不但能做出一个好的多用户博客站,还能进一步提高自己的Java水准,捎带把英语水平也提高一下。
这会是一个系列的日志,今天开篇。
XUI初体验
对于Java的桌面应用我一直都比较排斥,一方面觉得将来都是B/S的天下,C/S没什么意义,另一方面习惯了WEB的开发思维,对于桌面的开发方式始终觉得格格不入。不过在接触过XUI之后,感觉桌面开发也是件蛮有乐趣的事情。
XUI是纯Java+XML的桌面开发方式,并且基于MVC模型和XML配置方式, 很多地方都有WEB开发的影子,感觉很舒服,我很喜欢。
两天的假期全部扔到了XUI上边,收获颇丰,现在我可以介入到桌面开发领域了。
遗憾的是XUI目前没有针对Eclipse的可视化插件,只能在NetBeans下开发,而我用NetBeans非常的不顺手,比较影响开发效率。期待Eclipse版的XUI可视化插件。
CruiseControl+ANT+SVN实现持续集成。
CruiseControl是一个开源的持续集成框架,最初由ThoughtWorks开发。基于CruiseControl/ANT/SVN可以轻松的实现持续集成,并可以通过GUI轻松进行管理。
1:下载CruiseControl,我的版本服务器为Win,所以下载的2.8的exe版。然后安装。
2:将需要进行持续集成的项目check out 一个copy出来。
3:修改CruiseControl安装目录下的config,dashboard-config.xml文件。以及CruiseControl安装目录下的webapps\cruisecontrol\WEB-INF\web.xml文件。下面是三个文件的sample,需要修改的地方用粗体标出。
config.xml
<cruisecontrol>
<property name=”project_home” value=”x:\dailybuild\projects” />
<property name=”ant_home” value=”x:\Program Files\Apache Software Foundation\apache-ant-1.7.1″ />
<property name=”log_home” value=”x:\dailybuild\logs”/>
<project name=”myproject”>
<listeners>
<currentbuildstatuslistener file=”logs/${project.name}/status.txt” />
</listeners>
<bootstrappers>
<!–cvsbootstrappers?vssbootstrappers?svnbootstrapper –>
<svnbootstrapper localWorkingCopy=”${project_home}/${project.name}” />
</bootstrappers>
<modificationset quietperiod=”600″>
<svn localWorkingCopy=”${project_home}/${project.name}” />
</modificationset>
<schedule interval=”60″>
<ant anthome=”${ant_home}” buildfile=”${project_home}/${project.name}/etc/build.xml” />
</schedule>
<log dir=”${log_home}/${project.name}”/>
<publishers>
<htmlemail charset=”UTF-8″ mailhost=”localhost” defaultsuffix=”@xxx.com” username=xxx@xx.com password=”abc1234″ returnname=”CruiseControl” returnaddress=xxx@xx.com subjectprefix=”Daily Build” xsldir=”webapps/cruisecontrol/xsl” css=”webapps/cruisecontrol/css/cruisecontrol.css” logdir=”${log_home}/${project.name}”>
<always address=xxx@xx.com />
<failure address=xxx@xx.com />
</htmlemail>
</publishers>
</project>
</cruisecontrol>
dashboard-config.xml
<dashboard>
<buildloop
logsdir=”D:\dailybuild\logs”
artifactsdir=”D:\dailybuild\artifacts” />
<features allowforcebuild=”true”/>
<trackingtool projectname=”" baseurl=”" keywords=”"/>
<subtabs>
<subtab class=”net.sourceforge.cruisecontrol.dashboard.widgets.ErrorsAndWarningsMessagesWidget” />
</subtabs>
</dashboard>
web.xml
…….
<context-param>
<!– You can set this value via the user.log.dir property in Ant, when building the WAR file. –>
<param-name>logDir</param-name>
<param-value>D:\dailybuild\logs</param-value>
<description> This should be the full path to your CruiseControl log directory. If you
are in single project mode, this will contain only the logs for your project. If you
are in multi-project mode, it is expected that you will have multiple sub-directories
inside this log directory, one for each project.
</description>
</context-param>
………..
<servlet>
<servlet-name>ArtifactServlet</servlet-name>
<servlet-class>net.sourceforge.cruisecontrol.servlet.FileServlet</servlet-class>
<init-param>
<param-name>rootDir</param-name>
<param-value>D:\dailybuild\artifacts</param-value>
</init-param>
</servlet>
…………
4:CruiseControl自带有Jetty的服务器,默认为8080端口,通过修改cruisecontrol.bat的webPort参数可以修改这个端口。
5:确保安装有ANT和SVN,并且二者进行了集成,ANT和SVN集成比较简单,下载SVNANT,然后将lib下的包全部拷贝到ANT的lib下。在build.xml中引用SVN的示例如下:
……
<taskdef name=”svn” classname=”org.tigris.subversion.svnant.SvnTask” />
<target name=”checkout” depends=”prepare”>
<svn javasvn=”true” username=”myname” password=”mypassword” >
<checkout url=”${remote.url}” destPath=”${local.location}” />
</svn>
</target>
…….
6:运行cruisecontril.bat,然后通过http://localhost:pord/dashboard 和 http://localhost:port/cruisecontrol 进行图形化管理。
7:更多详细的设置可以访问cruisecontrol的官方网站。
图解 Tomcat 体系结构
来自网络的文章,图解 Tomcat 体系结构 ,很不错,自己始终没有耐心/时间去写这么长的文章,汗一个,比较佩服这些作者。
一段简单的ant build.xml文件
在研究ant,准备用它来实现项目的编译/测试/集成,总体感觉使用ant就是一个熟能生巧的事儿。
<project name="console" default="war" basedir=".">
<property name="src" value="src"></property>
<property name="dest" value="${basedir}/WebRoot/WEB-INF/classes"></property>
<property name="tomcatlib" value="C:Program FilesApache Software FoundationTomcat 5.5commonlib">
<property name="reflib" value="${basedir}/WebRoot/WEB-INF/lib"></property>
<property name="warname" value="console.war"></property>
<path id="lib">
<fileset dir="${reflib}"> <include name="*/*.jar"></include>
</fileset>
<fileset dir="${tomcatlib}">
<include name="*/*.jar"></include>
</fileset>
</path>
<target name="init">
<mkdir dir="${dest}">
</mkdir>
<target name="compile" depends="init">
<javac debuglevel="${debuglevel}" srcdir="${src}" destdir="${dest}">
<classpath refid="lib">
</classpath>
</javac>
<!--
<target name="run" depends="compile">
<java classname="org.apache.ant.HelloWorld" classpath="${dest}"/>
</target>
-->
<target name="war" depends="compile">
<jar jarfile="${warname}">
<fileset dir="${basedir}/WebRoot">
<include name="**/*"></include>
</fileset>
</jar>
</target>
</property>
XP的十二种方法以及四个核心价值
- 有计划的开发(The Planning Game):通过结合使用优先级”故事”和技术估算,确定下一版本的功能
- 小版本(Small Release):以小的增量版本经常向客户发布软件
- 隐喻(System Metaphor) :隐喻是一个简单、共享的”故事”或描述,说明系统如何工作
- 简单设计(Simple Design):通过保持代码简单从而保证设计简单。不断的在代码中寻找复杂点并且立刻进行移除
- 测试驱动开发(TDD):用户编写测试内容以对”故事”进行测试。程序员编写测试内容来发现代码中的任何问题。在编写代码前先编写测试内容
- 重构(Refractoring):这是一项简化技术,用来移除代码中的重复内容和复杂之处
- 结对编程(Pair programming):团队中的两个成员使用同一台计算机开发所有的代码。一个人编写代码或者驱动,另一个人同时审查代码的正确性和可理解性
- 集体代码所有权(Collective Code Ownership):任何人都拥有所有的代码。这就意味这每个人都可以在任何时候变更任何代码
- 持续集成(Continuous Integration):每天多次创建和集成系统,只要任何实现任务完成就要进行
- 每周 40 个小时(40-hour Week):程序员在疲劳时无法保证最高效率。连续两周加班是绝对不允许的
- 现场客户(On-site Customer):一名真实的客户全时工作于开发环境中,帮助定义系统、编写测试内容并回答问题
- 编码标准(Code Standards):程序员采用一致的编码标准证
极限编程中有四个核心价值
沟通(Communication)、简单(Simplicity)、反馈(Feedback)和勇气(Courage)。
EasyMock初探
EasyMock是用于JUnit中的虚拟测试辅助包,它提供对interface类的模拟,能够通过录制、回放、检查三步来完成大体的测试过程,可以验证方法的调用种类、次数、顺序,可以令mock对象返回指定的值或抛出指定异常。
EasyMock的主页:http://www.easymock.org。
一段简单的代码:
接口:
public interface Calculator {
public int add(int number1,int number2);
public int sub(int number1,int number2);
}
调用类:
public class Bank {
private Calculator cc;
public void setCc(Calculator cc)
{
this.cc=cc;
}
public int add(int number1,int number2)
{
return cc.add(number1, number2);
}
public int sub(int number1,int number2)
{
return cc.sub(number1, number2);
}
}
测试类:
import junit.Calculator;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import static org.easymock.EasyMock.aryEq;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import java.util.*;
public class TestCalculator {
private Calculator mock;
private Bank bank;
@Before
public void setUp() throws Exception {
mock=createMock(Calculator.class);
bank=new Bank();
bank.setCc(mock);
}
@Test
public void testAdd() {
expect(mock.add(1,2)).andReturn(4);
replay(mock);
assertEquals(bank.add(1, 2),3);
verify(mock);
}
}
Tomcat性能调优
Tomcat性能调优主要涉及到五个参数:maxThreads、 minSpareThreads、 maxSpareThreads、Xms、Xmx,前三项用来调整Tomcat的线程池,后两项用来调整JVM的内存。
maxThreads:Tomcat可以创建的最大线程数。
minSpareThreads:最小空余线程,此值决定了当前线程用尽时Tomcat新创建的线程数,也可以看作Tomcat初始创建的线程数。
maxSpareThreads:最大空余线程,当空余的线程数大于此值时Tomcat会将多余的空余线程杀掉。
Xms:JVM可用的最小内存。
Xmx:JVM可用的最大内存。
其中, maxThreads、Xms、Xmx最为重要,足够高的线程数可以有效提高Tomcat的响应能力,但同时也要为JVM提供足够的内存。
还有几个参数也对Tomcat性能调整有帮助,但是不如上面五项来的明显,这几个参数分别是:enableLookups、 acceptCount、 connectionTimeout。
Tomcat性能调优是个反复尝试的过程,与操作系统、WEB应用、JVM都有关系, 需要根据实际情况来进行。
