﻿<?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-Sung in Blog</title><link>http://www.blogjava.net/qq13367612/</link><description>&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;font class="subhead" size=3&gt;&lt;b&gt;一些技术文章 &amp; 一些生活杂碎&lt;/b&gt;&lt;/font&gt;</description><language>zh-cn</language><lastBuildDate>Fri, 01 May 2026 11:41:13 GMT</lastBuildDate><pubDate>Fri, 01 May 2026 11:41:13 GMT</pubDate><ttl>60</ttl><item><title>逝者如斯夫</title><link>http://www.blogjava.net/qq13367612/archive/2006/02/19/31433.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Sat, 18 Feb 2006 17:17:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2006/02/19/31433.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/31433.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2006/02/19/31433.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/31433.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/31433.html</trackback:ping><description><![CDATA[<P>一年多的Java学习，半年多的java兼职，再到如今实习彻彻底底脱离java，想到现在的路由器交换机，再想到外包公司的web项目，有的时候发现，工作不比兴趣，逝者如斯夫，再回blogjava，踩个脚印。</P><img src ="http://www.blogjava.net/qq13367612/aggbug/31433.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2006-02-19 01:17 <a href="http://www.blogjava.net/qq13367612/archive/2006/02/19/31433.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>值得回顾和体味：Java语言的9个关键特性 </title><link>http://www.blogjava.net/qq13367612/archive/2005/11/04/18135.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 04 Nov 2005 06:16:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/11/04/18135.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/18135.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/11/04/18135.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/18135.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/18135.html</trackback:ping><description><![CDATA[<STRONG>Java的白皮书为我们提出了Java语言的9个关键特性<BR><BR></STRONG>(1)Easy:Java的语法比C++的相对简单，另一个方面就是Java能使软件在很小的机器上运行，基础解释其和类库的支持的大小约为40kb，增加基本的标准库和线程支持的内存需要增加125kb。<BR><BR>(2)分布式:Java带有很强大的TCP/IP协议族的例程库，Java应用程序能够通过URL来穿过网络来访问远程对象，由于servlet机制的出现，使Java编程非常的高效，现在许多的大的web server都支持servlet。<BR><BR>(3)OO:面向对象设计是把重点放在对象及对象的接口上的一个编程技术.其面向对象和C++有很多不同，在与多重继承的处理及Java的原类模型。<BR><BR>(4)健壮特性:Java采取了一个安全指针模型，能减小重写内存和数据崩溃的可能型。<BR><BR>(5)安全:Java用来设计网路和分布系统，这带来了新的安全问题，Java可以用来构建防病毒和防攻击的System.事实证明Java在防毒这一方面做的比较好。<BR><BR>(6)中立体系结构:Java编译其生成体系结构中立的目标文件格式可以在很多处理器上执行，编译器产生的指令字节码(Javabytecode)实现此特性，此字节码可以在任何机器上解释执行。<BR><BR>(7)可移植性:Java中对基本数据结构类型的大小和算法都有严格的规定所以可移植性很好。<BR><BR>(8)多线程:Java处理多线程的过程很简单，Java把多线程实现交给底下操作系统或线程程序完成.所以多线程是Java作为服务器端开发语言的流行原因之一。<BR><BR>(9)Applet和servlet:能够在网页上执行的程序叫Applet，需要支持Java的浏览器很多，而applet支持动态的网页，这是很多其他语言所不能做到的。<BR><BR><STRONG>值得回顾和体味的概念</STRONG><BR><BR>1.OOP中唯一关系的是对象的接口是什么，就像计算机的销售商她不管电源内部结构是怎样的，他只关系能否给你提供电就行了，也就是只要知道can or not而不是how and why.所有的程序是由一定的属性和行为对象组成的，不同的对象的访问通过函数调用来完成，对象间所有的交流都是通过方法调用，通过对封装对象数据，很大限度上提高复用率。<BR><BR>2.OOP中最重要的思想是类，类是模板是蓝图，从类中构造一个对象，即创建了这个类的一个实例(instance)。<BR><BR>3.封装:就是把数据和行为结合起在一个包中)并对对象使用者隐藏数据的实现过程，一个对象中的数据叫他的实例字段(instance field)。<BR><BR>4.通过扩展一个类来获得一个新类叫继承(inheritance)，而所有的类都是由Object根超类扩展而得，根超类下文会做介绍。<BR><BR>5.对象的3个主要特性<BR><BR>behavior---说明这个对象能做什么。<BR><BR>state---当对象施加方法时对象的反映。<BR><BR>dentity---与其他相似行为对象的区分标志。<BR><BR>每个对象有唯一的indentity 而这3者之间相互影响。<BR><BR>6.类之间的关系: <BR><BR>use-a :依赖关系<BR><BR>has-a :聚合关系<BR><BR>is-a:继承关系--例:A类继承了B类，此时A类不仅有了B类的方法，还有其自己的方法.(个性存在于共性中)<BR><BR>7.构造对象使用构造器:构造器的提出，构造器是一种特殊的方法，构造对象并对其初始化。<BR><BR>例:Data类的构造器叫Data<BR><BR>new Data()---构造一个新对象，且初始化当前时间。<BR><BR>Data happyday=new Data()---把一个对象赋值给一个变量happyday，从而使该对象能够多次使用，此处要声明的使变量与对象变量二者是不同的。new返回的值是一个引用。<BR><BR>构造器特点:构造器可以有0个，一个或多个参数，构造器和类有相同的名字。一个类可以有多个构造器，构造器没有返回值，构造器总是和new运算符一起使用。<BR><BR>8.重载:当多个方法具有相同的名字而含有不同的参数时，便发生重载.编译器必须挑选出调用哪个方法。<BR><BR>9.包(package)Java允许把一个或多个类收集在一起成为一组，称作包，以便于组织任务，标准Java库分为许多包.java.lang java.util java，net等，包是分层次的所有的java包都在java和javax包层次内。<BR><BR>10.继承思想:允许在已经存在的类的基础上构建新的类，当你继承一个已经存在的类时，那么你就复用了这个类的方法和字段，同时你可以在新类中添加新的方法和字段。<BR><BR>11.扩展类:扩展类充分体现了is-a的继承关系. 形式为:class (子类) extends (基类)。<BR><BR>12.多态:在java中，对象变量是多态的.而java中不支持多重继承。<BR><BR>13.动态绑定:调用对象方法的机制。<BR><BR>(1)编译器检查对象声明的类型和方法名。<BR><BR>(2)编译器检查方法调用的参数类型。<BR><BR>(3)静态绑定:若方法类型为priavte static final 编译器会准确知道该调用哪个方法。 (4)当程序运行并且使用动态绑定来调用一个方法时，那么虚拟机必须调用x所指向的对象的实际类型相匹配的方法版本。<BR><BR>(5)动态绑定:是很重要的特性，它能使程序变得可扩展而不需要重编译已存代码。<BR><BR>14.final类:为防止他人从你的类上派生新类，此类是不可扩展的。<BR><BR>15.动态调用比静态调用花费的时间要长。<BR><BR>16.抽象类:规定一个或多个抽象方法的类本身必须定义为abstract。<BR><BR>例: 
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>public abstract string getDescripition</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>17.Java中的每一个类都是从Object类扩展而来的。 <BR><BR>18.object类中的equal和toString方法。 <BR><BR>equal用于测试一个对象是否同另一个对象相等。 <BR><BR>toString返回一个代表该对象的字符串，几乎每一个类都会重载该方法，以便返回当前状态的正确表示。(toString 方法是一个很重要的方法) <BR><BR>19.通用编程:任何类类型的所有值都可以同object类性的变量来代替。 <BR><BR>20.数组列表:ArrayList动态数组列表，是一个类库，定义在java.uitl包中，可自动调节数组的大小。 <BR><BR>21.class类object类中的getclass方法返回ckass类型的一个实例，程序启动时包含在main方法的类会被加载，虚拟机要加载他需要的所有类，每一个加载的类都要加载它需要的类。<BR><BR>22.class类为编写可动态操纵java代码的程序提供了强大的功能反射，这项功能为JavaBeans特别有用，使用反射Java能支持VB程序员习惯使用的工具。<BR><BR>能够分析类能力的程序叫反射器，Java中提供此功能的包叫Java.lang.reflect反射机制十分强大。<BR><BR>1).在运行时分析类的能力。<BR><BR>2).在运行时探察类的对象。<BR><BR>3).实现通用数组操纵代码。<BR><BR>4).提供方法对象。<BR><BR>而此机制主要针对是工具者而不是应用及程序。 反射机制中的最重要的部分是允许你检查类的结构.用到的API有:<BR><BR>
<CENTER><CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE>　　java.lang.reflect.Field //返回字段.  

　　java.reflect.Method //返回方法.  

　　java.lang.reflect.Constructor //返回参数.</CCID_CODE></PRE></TD></TR></TBODY></TABLE></CCID_NOBR></CENTER><BR><BR>方法指针:java没有方法指针，把一个方法的地址传给另一个方法，可以在后面调用它，而接口是更好的解决方案。 <BR>23.接口(Interface)说明类该做什么而不指定如何去做，一个类可以实现一个或多个interface。 <BR><BR>24.接口不是一个类，而是对符合接口要求的类的一套规范。<BR><BR>若实现一个接口需要2个步骤:<BR><BR>1.声明类需要实现的指定接口。<BR><BR>2.提供接口中的所有方法的定义。<BR><BR>声明一个类实现一个接口需要使用implements 关键字 class actionB implements Comparable 其actionb需要提供CompareTo方法，接口不是类，不能用new实例化一个接口。<BR><BR>25.一个类只有一个超类，但一个类能实现多个接口。Java中的一个重要接口：Cloneable<BR><BR>26.接口和回调.编程一个常用的模式是回调模式，在这种模式中你可以指定当一个特定时间发生时回调对象上的方法。<BR><BR>例:ActionListener 接口监听. <BR><BR>类似的API有:<BR><BR>java.swing.JOptionPane<BR><BR>java.swing.Timer<BR><BR>java.awt.Tookit<BR><BR>27.对象clone:clone方法是object一个保护方法，这意味着你的代码不能简单的调用它。<BR><BR>28.内部类:一个内部类的定义是定义在另一个内部的类。<BR><BR>原因是: <BR><BR>1.一个内部类的对象能够访问创建它的对象的实现，包括私有数据。<BR><BR>2.对于同一个包中的其他类来说，内部类能够隐藏起来。<BR><BR>3.匿名内部类可以很方便的定义回调。<BR><BR>4.使用内部类可以非常方便的编写事件驱动程序。<BR><BR>29.代理类(proxy): <BR><BR>1.指定接口要求所有代码<BR><BR>2.object类定义的所有的方法(toString equals)<BR><BR>30.数据类型:Java是强调类型的语言，每个变量都必须先申明它都类型，java中总共有8个基本类型。4种是整型，2种是浮点型，一种是字符型，被用于Unicode编码中的字符，布尔型。<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/18135.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-11-04 14:16 <a href="http://www.blogjava.net/qq13367612/archive/2005/11/04/18135.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java的Web框架对比 </title><link>http://www.blogjava.net/qq13367612/archive/2005/11/04/18086.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 04 Nov 2005 01:54:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/11/04/18086.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/18086.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/11/04/18086.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/18086.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/18086.html</trackback:ping><description><![CDATA[Java的Web框架对比<BR>第一项是优点，第二项是缺点。<BR><BR>以下是各种web框架比较: (by Matt Raible &amp; 个人观点)<BR><BR>Struts： <BR>轻量级表现层框架，在适当的时间适当的出现；<BR>网上拥有很多信息和示例；学习曲线较缓，容易上手；<BR>HTML标签库是一个非常好的东西。<BR>&nbsp;<BR>静态Forms难以让人接受；不能够做单元测试；<BR>大量的邮件列表令人无法忍受<BR>&nbsp;<BR>Spring MVC <BR>生命周期由重写绑定，效验等确定；能够无缝隙地与各种表现层程序结合在一起，如JSP，XSL等；IC使得很容易测试。 <BR><BR>实际使用较少；有脱离轻量级框架向包容万象发展的趋势；需要在JSP中写入大量代码；过于灵活，反而不能得到通用的控制。<BR>&nbsp;<BR>WebWork <BR>结构简单，容易被扩展；标签库容易被自定义，并获得Velocity支持；拦截机制成熟可靠。 <BR>文档资源不够充分，示例很少；客户端效验很不成熟。<BR>&nbsp;<BR>Tapestry <BR>效率较高；HTML模板很合适美工；有良好的社区支持。 <BR>文档过于理论，而缺少实践；学习曲线陡峭，示例非常少； <BR><BR><BR>JSF <BR>J2EE规范；能够快速和容易地开发；丰富的导航框架。已有不少公司开始尝试该方面技术的应用； <BR>标签依赖JSP；技术不够成熟；实现资源不单纯。 <BR><BR><BR>大家可以发表一下意见<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/18086.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-11-04 09:54 <a href="http://www.blogjava.net/qq13367612/archive/2005/11/04/18086.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[强烈推荐]IBM公司面试题（附答案）——病狗问题</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/31/17544.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 31 Oct 2005 06:12:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/31/17544.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17544.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/31/17544.html#Feedback</comments><slash:comments>19</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17544.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17544.html</trackback:ping><description><![CDATA[<FONT size=2><FONT style="BACKGROUND-COLOR: #ffffff" color=#000000 size=3><STRONG>村子中有50个人，每人有一条狗。在这50条狗中有病狗(这种病不会传染)。于是人们就要找出病狗。每个人可以观察其他的49条狗，以判断它们是否生病，只有自己的狗不能看。观察后得到的结果不得交流，也不能通知病狗的主人。主人一旦推算出自己家的是病狗就要枪毙自己的狗，而且每个人只有权利枪毙自己的狗，没有权利打死其他人的狗。第一天，第二天都没有枪响。到了第三天传来一阵枪声，问有几条病狗，如何推算得出？</STRONG></FONT> <BR></FONT><BR>推论 见本文结束（字体为白色）！<BR><BR>另附：IBM社会招聘笔试题<BR><BR>1．一个粗细均匀的长直管子，两端开口，里面有4个白球和4个黑球，球的直径、两端开口的直径等于管子的内径，现在白球和黑球的排列是wwwwbbbb，要求不取出任何一个球，使得排列变为bbwwwwbb。 <BR><BR>2．一只蜗牛从井底爬到井口，每天白天蜗牛要睡觉，晚上才出来活动，一个晚上蜗牛可以向上爬3尺，但是白天睡觉的时候会往下滑2尺，井深10尺，问蜗牛几天可以爬出来？ <BR><BR>3．在一个平面上画1999条直线最多能将这一平面划分成多少个部分？ <BR><BR>4．在太平洋的一个小岛上生活着土人，他们不愿意被外人打扰，一天，一个探险家到了岛上，被土人抓住，土人的祭司告诉他，你临死前还可以有一个机会留下一句话，如果这句话是真的，你将被烧死，是假的，你将被五马分尸，可怜的探险家如何才能活下来？ <BR><BR>5．怎样种四棵树使得任意两棵树的距离相等。 <BR><BR>6．27个小运动员在参加完比赛后，口渴难耐，去小店买饮料，饮料店搞促销，凭三个空瓶可以再换一瓶，他们最少买多少瓶饮料才能保证一人一瓶？ <BR><BR>7．有一座山，山上有座庙，只有一条路可以从山上的庙到山脚，每周一早上8点，有一个聪明的小和尚去山下化缘，周二早上8点从山脚回山上的庙里，小和尚的上下山的速度是任意的，在每个往返中，他总是能在周一和周二的同一钟点到达山路上的同一点。例如，有一次他发现星期一的8点30和星期二的8点30他都到了山路靠山脚的3/4的地方，问这是为什么？ <BR><BR>8．有两根不均匀分布的香，每根香烧完的时间是一个小时，你能用什么方法来确定一段15分钟的时间？ <BR><BR>IBM面试题目 <BR><BR>1. Describe your greatest achievement in the past 4-5 years? <BR><BR>2. What are your short &amp; long term career objectives? What do you think is the most ideal job for you? <BR><BR>3. Why do you want to join IBM? What do you think you can contribute to IBM? <BR><BR><BR><FONT color=#ffffff size=2>推论： <BR><BR>　　A、假设有1条病狗，病狗的主人会看到其他狗都没有病，那么就知道自己的狗有病，所以第一天晚上就会有枪响。因为没有枪响，说明病狗数大于1。 <BR><BR>　　B、假设有2条病狗，病狗的主人会看到有1条病狗，因为第一天没有听到枪响，是病狗数大于1，所以病狗的主人会知道自己的狗是病狗，因而第二天会有枪响。既然第二天也每有枪响，说明病狗数大于2。 <BR><BR>　　由此推理，如果第三天枪响，则有3条病狗。 </FONT><BR><img src ="http://www.blogjava.net/qq13367612/aggbug/17544.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-31 14:12 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/31/17544.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用微软试题膨胀你的思维</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/31/17543.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 31 Oct 2005 06:07:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/31/17543.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17543.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/31/17543.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17543.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17543.html</trackback:ping><description><![CDATA[<SPAN style="FONT-SIZE: 10pt">“微软”招聘考试，除了专业知识，往往更看重一个人的聪明程度。据参加招聘考试的学生说，“微软”笔试题“古怪刁钻”，有些简直“异想天开”，令人匪夷所思。不过，“微软”似乎不为所动，年年出一些“怪题目”。几年下来，这些题目不再被认为怪，更逐渐成为许多大企业拿来考大学生的“经典”。 <BR><BR>　　快速估算题：测试你的快速反应能力 <BR><BR>　　这类题目有不同的版本。比如说，问你如何在不使用台秤的情况下，称出一架飞机的重量？估算一下长江里的水的质量？ 这是大的、宏观方面的问题；小的方面试题，还会问你这一类题目：“估算一下一个行进在小雨中的人5分钟内身上淋到的雨的质量。” <BR><BR>　　再看另一道快速估算题。去年3月，“微软”在复旦大学举行了一场校园招聘，第一轮笔试题目中就有一道令应试者困惑的“东方明珠”题：“请估算一下东方明珠电视塔的重量”。当时真是难倒了众多学子。据上海微软软件有限公司负责招聘考试的软件研发部经理蔡先生说：“其实，有同学认为这个题目刁钻古怪也在情理之中，毕竟这种类型的试题在国内还不太多，所以大家都有些摸不着头脑。”他表示：“就东方明珠这道题来说，它和一般的谜语或智力题还是有区别的。这类题为快速估算题，主要考的是快速估算的能力，这是开发软件必备的能力之一。重要的是对考生得出这个结果的过程也就是方法的考察。” <BR><BR>　　蔡经理说了一种比较合理的答法。他首先在纸上画出东方明珠的草图，然后快速估算支架和各个支柱的高度，以及球的半径，算出各部分体积，然后进行各部分密度运算，最后相加得出一个结果。蔡经理认为：“像这样的题目，包括一些推理题，考的都是人解决问题的能力，不是哪道题你记住了答案就可以了的。” <BR><BR>　　开放性思维题：考验你的逻辑推理能力 <BR><BR>　　去年应聘“微软”的大学生，考试时遇到了一道“古怪”的智力题。题目是：一楼到十楼的每层电梯门口都放着一颗钻石，钻石大小不一。你乘坐电梯从一楼到十楼，每层楼电梯门都会打开一次，只能拿一次钻石，问：怎样才能拿到最大的一颗﹖应试者不知该怎么办。考试后主考官并没有明确公布答案，但他对其中一位女士的做法表示赞赏。那位女士的回答是：选择前五层楼都不拿，观察各层钻石的大小，做到心中有数。后面五个楼层再选择，选择大小接近前五层楼出现过最大钻石大小的钻石。这位女士后来在互联网上谈体会时说： “我至今也不知道这道题的准确答案，也许本来就没有准确答案，就是考一下你的思路。” <BR><BR>　　这种说法得到微软中国有限公司人力资源部招聘经理尹冬梅的肯定。尹冬梅表示，以上这些都是属于笔试的题目。“微软”希望招到更多开放型思维的人，因此很多题目其实都没有一个标准答案。比如说，你认为北京有多少公共汽车站﹖你可以随便给出答案，5家或者5000家，但你得有理由。 <BR><BR>　　基础数学题：考核你的数学基础是否扎实 <BR><BR>　　一般来说，“微软”招聘只收理工科的学生，要求有扎实的数学基础，因此基础数学题量是最大的。有些题目初看很复杂，思路一打开，其实相当简单，所以有的大学生戏称是“小学三年级的题目”。比如如下这道题：有8颗弹子球，其中1颗是“缺陷球”，也就是它比其他的球都重。你怎样使用天平只通过两次称量就能够找到这个球﹖ <BR><BR>　　主考官在解释这道题的答案时指出：要想解决这个问题，必须充分利用天平可以量出两边弹子球重量是否相等这一事实，即无论什么时候只要两边重量相等，就表明“缺陷球”不在这些弹子球中。第一次称重，在天平的两边各任意放3颗球。这时候会有两种可能的结果。如果天平两边的重量是平衡的，就可以确定所称量的6个球当中没有“缺陷球”。因此第二次称重时只要称量剩下的2颗球，较重的 1颗就是“缺陷球”。如果天平的一边比另一边重。那么可以确定 “缺陷球”肯定位于天平较重一边的3颗球当中。第二次称量时只要从这3个球当中任意拿出2个进行称量。如果两边平衡，则3颗球中剩下的没有参加称量的1颗球就是“缺陷球”，如果两边不平衡，则较重的一边就是“缺陷球”。 <BR><BR>　　从上面这道题和其他大量的数学题内容来看，“微软”招聘考试重视数学基础是由它工作性质决定的。“微软”招聘主管戴维·普里查得先生认为，软件开发需要极严格的数学模式。对数学没有兴趣的人难以胜任最起码的程序员工作。他表示，对于笔试题目，如果有大学生对他说：“这真是一个愚蠢的问题”，这并不是错误的回答。 <BR><BR>　　智力测试题：看你能否创造性地思考 <BR><BR>　　“请用一笔画出四根直线，将图上9个点全部联结。” 答案：画一根与水平成45度角的斜线到某一点，然后以此点作为直角三角形两个直角边的交点，向任何一边作直角三角形，就可以把9个点联结起来。 <BR><BR>　　这类智力题考的是在懂得数学原理基础上的创造性思考。比如下面这个问题就需要动动脑子了。题目是：一个正三角形的每个角上各有一只蚂蚁。每只蚂蚁开始朝另一只蚂蚁做直线运动，目标角是随机选择。蚂蚁互不相撞的概率是多少﹖ <BR><BR>　　答案应当是：只有两种方法可以让蚂蚁避免相撞：或者它们全部顺时针运动，或者它们全部逆时针运动。否则，肯定会撞到一起。选择一只蚂蚁，一旦它确定了自己是逆时针或者是顺时针运动，其他的蚂蚁就必须做相同方向的运动才能避免相撞。由于蚂蚁运动的方向是随机选择的，那么第二只蚂蚁有1／2的概率选择与第一只蚂蚁相同的运动方向。第三只蚂蚁也有1／2的概率选择与第一只相同的方向。因此，蚂蚁避免撞到一起的概率是1／4。 <BR><BR>　　还有一些问题虽然不需要数学知识，但要懂得基本的科学常识。比如问你：太阳总是从东边升起吗﹖答案应该是否定的。因为在北极点，根本就没有“东方”这个方向。每一个方向都是南。在6个月的“极昼”时间，太阳从南边升起从南边落下。另外在南极也一样，每一个方向都是北方。 <BR><BR>　　诸如此类的问题我们还可以举出一些，其中包括拿“微软”领袖比尔·盖茨“开涮”的题目。此题是“比尔·盖茨的办公桌下有五只带锁的抽屉，分别贴着财富、兴趣、幸福、荣誉和成功五个标签；盖茨总是只带一把钥匙，请问是哪一把？” 答案：兴趣。 <BR><BR>　　所有这些都出于这样的考虑： “微软”想找“聪明的、有开放思维的人。”负责“微软”招聘工作的戴维·普里查得先生强调：其实我们并不是想得到“正确”的答案，我们是想看看应聘者是否能找到最好的解题方案，看他们是否能够创造性地思考问题。 “我们的目的是选人，而不是难倒学生。”</SPAN><img src ="http://www.blogjava.net/qq13367612/aggbug/17543.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-31 14:07 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/31/17543.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一些腾讯笔试题目 </title><link>http://www.blogjava.net/qq13367612/archive/2005/10/31/17524.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 31 Oct 2005 03:50:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/31/17524.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17524.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/31/17524.html#Feedback</comments><slash:comments>17</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17524.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17524.html</trackback:ping><description><![CDATA[1、请定义一个宏，比较两个数a、b的大小，不能使用大于、小于、if语句<BR>2、如何输出源文件的标题和目前执行行的行数<BR>3、两个数相乘，小数点后位数没有限制，请写一个高精度算法<BR>4、写一个病毒<BR>5、有A、B、C、D四个人，要在夜里过一座桥。他们通过这座桥分别需要耗时1、2、5、10分钟，只有一支手电，并且同时最多只能两个人一起过桥。请问，如何安排，能够在17分钟内这四个人都过桥？<BR><BR>2005年腾讯招聘<BR>选择题(60) <BR>  c/c++ os linux 方面的基础知识 c的Sizeof函数有好几个! <BR>程序填空(40) <BR>1.(20) 4空x5 <BR>  不使用额外空间,将 A,B两链表的元素交叉归并 <BR>2.(20) 4空x5 <BR>MFC  将树序列化 转存在数组或 链表中!<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/17524.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-31 11:50 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/31/17524.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VO、TO、PO、POJO、BO</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/28/17149.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 28 Oct 2005 03:39:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/28/17149.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17149.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/28/17149.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17149.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17149.html</trackback:ping><description><![CDATA[VO：实际上很模糊，通常指ValueObject和ViewObject <BR><BR>ViewObject：UI需要的对象，如Struts的FormBean <BR><BR>Value Object：ValueObject和Transfer Object的总称。 <BR><BR>TO： Transfer Object，数据传输物件，在应用程序不同tie之间传输的对象 <BR><BR>PO：Persistent Object（持久化物件），基本上就是Entity了 <BR><BR>POJO：plain ordinary java object ，POJO是这样一个对象，它是一个普通的Java对象，它不同于EJB这样的带有繁重的容器控制功能的对象，它也不是JDO那种被Enhanced过的对象，也不是类似Hibernate那样被动态的byte code generation过<BR><BR>BO：Business Object。可以参照<A HREF="/qq13367612/archive/2005/10/21/16250.aspx"><STRONG>这里</STRONG></A>的讨论。<img src ="http://www.blogjava.net/qq13367612/aggbug/17149.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-28 11:39 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/28/17149.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于VO、PO的理解</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/28/17147.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 28 Oct 2005 03:30:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/28/17147.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/17147.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/28/17147.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/17147.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/17147.html</trackback:ping><description><![CDATA[O/R Mapping 是 Object Relational Mapping（对象关系映射）的缩写。通俗点讲，就是将对象与关系数据库绑定，用对象来表示关系数据。在O/R Mapping的世界里，有两个基本的也是重要的东东需要了解，即VO，PO。<BR>　　VO，值对象(Value Object)，PO，持久对象(Persisent Object)，它们是由一组属性和属性的get和set方法组成。从结构上看，它们并没有什么不同的地方。但从其意义和本质上来看是完全不同的。<BR><BR>１．VO是用new关键字创建，由GC回收的。 <BR>　　PO则是向数据库中添加新数据时创建，删除数据库中数据时削除的。并且它只能存活在一个数据库连接中，断开连接即被销毁。 <BR><BR>２．VO是值对象，精确点讲它是业务对象，是存活在业务层的，是业务逻辑使用的，它存活的目的就是为数据提供一个生存的地方。 <BR>　　PO则是有状态的，每个属性代表其当前的状态。它是物理数据的对象表示。使用它，可以使我们的程序与物理数据解耦，并且可以简化对象数据与物理数据之间的转换。<BR><BR>３．VO的属性是根据当前业务的不同而不同的，也就是说，它的每一个属性都一一对应当前业务逻辑所需要的数据的名称。 <BR>　　PO的属性是跟数据库表的字段一一对应的。<BR><BR>PO对象需要实现序列化接口。<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/17147.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-28 11:30 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/28/17147.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>热点招聘信息汇总!!</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/26/16899.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Wed, 26 Oct 2005 09:32:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/26/16899.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16899.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/26/16899.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16899.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16899.html</trackback:ping><description><![CDATA[一,投行,咨询,四大: <BR><BR>Morgan Stanley <BR>http://www.morganstanley.com/careers/recruiting/apply_now.html <BR><BR>UBS <BR>http://graduates.ubs.com/career/cms/index.html <BR><BR>瑞士信贷第一波士顿 <BR>http://www.csfb.com/standout <BR>Deadline: 24th.Oct <BR><BR>高盛 <BR>www.gs.com/careers/apply_online <BR><BR>美林 <BR>http://www.ml.com/index.asp?id=7695_8199_8205_14187_14215_16358 <BR><BR>中国国际金融有限公司公司(CICC) <BR>http://www.cicc.com.cn/CICCHRWEB/Resume_CPage.asp?op=Jobs_1&amp;typ=2 <BR><BR>麦肯锡 <BR>http://www.mckinsey.com/locations/ChinaSimplified/index.asp <BR>或http://campus.chinahr.com/pages/mckinsey/ <BR>截止日期：11.4下午5点 <BR><BR>BCG <BR>邮寄地址: <BR>Anna Zhang <BR>Boston Consulting (Shanghai) Co., Ltd Beijing Branch <BR>朝阳区建国路乙118号京汇大厦902室，邮编：100022 <BR><BR>贝恩 <BR>www.joinbainchina.com <BR>由于网络问题,请同学们于10月21号到11月9号期间提交简历 <BR><BR>Monitor <BR>Mailing Address: 北京市朝阳区霄云路38号现代汽车大厦808室 100027 <BR>Deadline for candidates in Beijing: November 3, 2005 <BR>Deadline for candidates in Shanghai: November 10, 2005 <BR><BR><BR>科尔尼 <BR>Application Methods (Choose ONE ONLY) <BR>Submitting your application on the campus presentation day; <BR>or Sending in your application electronically to BA-recruiting.bj@atkearney.com;or Mailing in your application to A.T. Kearney Beijing Office at the belowaddress: <BR>科尔尼企业咨询有限公司北京办事处 <BR>人力资源部 <BR>北京市建国门外大街1号 <BR>中国国际贸易中心西办公楼504室 <BR>邮政编码 100004 <BR>Application Deadline:November 9, 2005 <BR><BR>Booz Allen Hamilton <BR>具体申请方式：Download the application form from : www.bah.com.cn , <BR>"people and career" and go to "application" and then download the <BR>application form and send applicaton form, Chinese and English CV as well as cover letter to china_hr@bah.com <BR><BR>PWC <BR>http://www.pwccn.com/home/eng/graduate_opportunities.html <BR>Deadline: 10.28 <BR><BR>KPMG <BR>http:// www.kpmg.com.hk <BR><BR>德勤 <BR>http://career.deloitte.com.cn/RMS0102_Requisition.aspx?Loc=BJ# <BR>Deadline: 11.6 <BR><BR>安永 <BR>www.eygraduate.com <BR>Deadline: November 6, 2005 <BR>答疑邮箱: graduate.bj@cn.ey.com <BR><BR>二,IT,通信,电子电器类: <BR><BR>微软(MS China, MSN China, MSRA, MSATC) <BR>http://campus.chinahr.com/pages/microsoft/ <BR><BR>IBM <BR>http://campus.chinahr.com/pages/ibm/ <BR>截至日期:10.31 <BR><BR>英特尔(Intel) <BR>http://campus.chinahr.com/pages/intel/jobs.asp <BR>(宣讲会前一周截至) <BR><BR>百度 <BR>http://www.baidu.com/hr/index.html <BR><BR>英飞凌 <BR>http://campus.chinahr.com/pages/Infineon/Programs.asp <BR><BR>锐捷网络 <BR>http://campus.chinahr.com/pages/ruijie/index.asp <BR><BR>卓越网 <BR>http://joyo.com/campus <BR><BR>华为集团 <BR>http://career.huawei.com/career/zh/campus.do <BR>截至日期:11月5日 <BR><BR>中兴 <BR>http://job.zte.com.cn/zte.graduate.web/index.aspx <BR><BR>掌中万维科技有限公司 <BR>http://www1.zhaopin.com/Publish/Company/Newpalm/index.htm <BR><BR>威盛电子（VIA）电子股份有限公司 <BR>http://campus.chinahr.com/viatech/ <BR>截止日期：10.24 <BR><BR>中电赛龙 <BR>http://campus.chinahr.com/pages/cecw/ <BR><BR>Google中国 <BR>http://www.google.com/intl/zh-CN/jobs/ <BR><BR>网易互动娱乐2006高校巡回招聘会 <BR>http://hr2006.163.com/ 十月中旬上线 <BR><BR>松下电器（中国）有限公司 <BR>http://recruit.panasonic.cn/campusjob.asp <BR><BR>创维集团 <BR>http://skyworth.com/job/default.asp?action=1&amp;title <BR><BR>ericsson <BR>http://www.pincn.com/clientpages/2005/ericsson/ <BR>截至日期:11月28日 <BR><BR>联想集团 <BR>http://campus.chinahr.com/pages/lenovo <BR><BR>腾讯 <BR>http://hr.tencent.com/ <BR><BR>ABB电气校园招聘 <BR>http://companyads.51job.com/companyads/shanghai/bj/abb050824/index.htm <BR><BR>台积电 <BR>http://tsmcsh.51job.com/ <BR><BR>海信Hisense <BR>http://campus.chinahr.com/pages/hisense/ <BR><BR>TCL校园招聘 <BR>http://www.tcl.com/job/hr/school/ <BR><BR>中奥集团OTIS(奥的斯电梯)2006年校园招聘网申： <BR>http://otis.51job.com/page/6-4.htm <BR><BR>施奈德电气 <BR>http://career.schneider-electric.com.cn/hr0001.nsf/pwelcome?open <BR><BR>2006 GE China Campus <BR>http://hr.ge-china.com <BR><BR>美的集团 <BR>http://campus.chinahr.com/pages/midea/ <BR><BR>西门子 <BR>http://www.careers.siemens.com.cn/graduates <BR><BR>三,其他类: <BR><BR>玛氏中国Mars <BR>http://campus.chinahr.com/pages/mars <BR>截至日期:11.1 <BR><BR>强生(中国)有限公司 <BR>http://campus.chinahr.com/pages/jnj/ <BR>截至日期:11月15日 <BR><BR>高露洁 <BR>http://campus.chinahr.com/pages/colgate <BR><BR>BP <BR>http://www1.zhaopin.com/Publish/Market/bp/index.htm <BR><BR>联合利华 <BR>http://www.unilever.com.cn/ourcompany/careers/trainee/ <BR>截止日期10.31 <BR><BR>博世（Bosch） <BR>http://campus.chinahr.com/pages/bosch/jobs.asp <BR><BR>壳牌（Shell） <BR>http://student.zhaopin.com/extend/shell/grad_apply.htm <BR><BR>艾默生网络能源有限公司2006年校园招聘 <BR>登陆网站http://www.emersonnetwork.com.cn <BR><BR>金地（集团）股份有限公司2006年校园招聘 <BR>公司网址：http://www.gemdale.com <BR>简历投递地址：2006gemdale@gemdale.com <BR><BR>马士基2006校园招聘 <BR>http://campus.chinahr.com/pages/maersk/ <BR><BR>广州本田 <BR>http://campus.chinahr.com/pages/gzhonda/ <BR><BR>中国广东核电集团有限公司 <BR>http://campus.chinahr.com/pages/cgnpc/ <BR><BR>欧莱雅 <BR>http://campus.chinahr.com/pages/loreal/ <BR><BR>龙湖房地产 <BR>http://pages.chinahr.com/2005/cd/longhu_1009/ <BR><BR>世联房地产 <BR>http://companyadc.51job.com/companyads/shanghai/shzh/shilian051011/index.htm <BR><BR>ICI <BR>http://campus.chinahr.com/pages/ici/ <BR><BR>炬力 <BR>http://www.actions.com.cn/campus.aspx <BR><BR>招商证券 <BR>http://news.newone.com.cn/adv/zhaopin/zhaopin.html <BR>泰鼎多媒体技术（上海）有限公司 <BR>http://www.trident.com.cn/careers.asp <BR><BR>优识 <BR>http://www.ucamp.com.cn/About_us/usys_job.jsp <BR><BR>农夫山泉 <BR>http://www.51job.com/hot/show_list.php?id=4670965 <BR><BR>青岛啤酒 <BR>http://campus.chinahr.com/pages/tsingtao/index.asp <BR><BR>环球 <BR>http://career.globalmarket.com/jobs.aspx <BR>截至日期:10月26日 <BR><BR>=================================================<BR><BR>有借鉴价值，值得保留<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/16899.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-26 17:32 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/26/16899.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>2005各大公司薪水民间版本</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/26/16848.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Wed, 26 Oct 2005 02:57:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/26/16848.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16848.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/26/16848.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16848.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16848.html</trackback:ping><description><![CDATA[<FONT face=Georgia>我们在学校都混得差不多，可是因为去了不同的城市、进了不同的公司，薪水有了差异，似乎也有了“贵贱”之分。<BR><BR>　　下面是我的老同学们从各大公司传来的薪水民间版本。<BR><BR>　　日本SONY 索尼 &nbsp;1万／月，仅要研究生。<BR><BR>　　韩国三星电子中国总部&nbsp;25万／年<BR><BR>　　法国索姆软件&nbsp;20万／年，赴欧工作。<BR><BR>　　美国Cisco 思科 &nbsp;1.5万／月，仅要研究生。<BR><BR>　　美国INTEL 英特尔 &nbsp;1.3万／月<BR><BR>　　美国IBM&nbsp;5000左右／月<BR><BR>　　德国西门子&nbsp;8000／月<BR><BR>　　西藏联通&nbsp;8年100万，不过高原反应太艰苦。<BR><BR>　　青海联通&nbsp;5000-7000／月，可以只签一年。<BR><BR>　　深圳联通&nbsp;3000-4000／月&nbsp;<BR><BR>　　深圳记忆科技&nbsp;本科10-15万／年，硕士15-20万／年。<BR><BR>　　上海网道信息技术 除房食补外 7000-8000／月<BR><BR>　　中国电信&nbsp;7-10万／年，另有房交补。<BR><BR>　　AVANT 6-8万／年&nbsp;<BR><BR>　　东方通信&nbsp;6-8万／年，仅要研究生。<BR><BR>　　北京联想&nbsp;6-7.5万／年&nbsp;<BR><BR>　　深大通信&nbsp;6万／年<BR><BR>　　福州康顺光通讯&nbsp;5-10万／年<BR><BR>　　美国FIBERON公司&nbsp;本科6万／年，硕士10万／年。<BR><BR>　　深圳华为&nbsp;5480／月<BR><BR>　　青岛海尔&nbsp;5-6万／年<BR><BR>　　ut斯达康&nbsp;本科5000／月，研究生6000-8000／月。<BR><BR>　　MOTOROLA(苏州)&nbsp;5000／月<BR><BR>　　深圳中兴&nbsp;5000／月<BR><BR>　　北京华虹NEC&nbsp;4000-5000／月<BR><BR>　　深圳黎明网络&nbsp;本4000／月，硕6500／月，博8500／月。&nbsp;<BR><BR>　　广东北电&nbsp;4000-5000／月<BR><BR>　　上海贝尔&nbsp;4万／年&nbsp;＋&nbsp;1.5万(年终奖)<BR><BR>　　成都迈普&nbsp;5万／年(研发)<BR><BR>　　中国建设银行新疆分行&nbsp;4.5万／年<BR><BR>　　北大方正奥德&nbsp;3500-4000／月<BR><BR>　　青岛海信&nbsp;4万／年，两年后6-7万／年。<BR><BR>　　中华通信&nbsp;本3000元／月，硕4000元／月，博5000元／月&nbsp;。<BR><BR>　　烟台东方电子&nbsp;4000／月，年终奖1.5万，婚后分房。<BR><BR>　　广东金鹏&nbsp;3000-4000／月<BR><BR>　　深圳康佳&nbsp;3000-4000／月&nbsp;<BR><BR>　　TCL&nbsp;3800／月 深圳 ，2000-3000／月 惠州 。<BR><BR>　　深圳奥林巴斯 日 &nbsp;3000／月，另1200／月房补。<BR><BR>　　武汉邮科院&nbsp;本科 3000／月，研究生5000／月。<BR><BR>　　福建实达&nbsp;3万／年<BR><BR>　　上海计算机研究所&nbsp;2500-4000／月<BR><BR>　　深圳飞通&nbsp;本科3000／月，研究生5000／月。<BR><BR>　　中讯&nbsp;3000／月<BR><BR>　　北京网通&nbsp;3000／月<BR><BR>　　宁波雅戈尔&nbsp;3000／月<BR><BR>　　广州华南资讯&nbsp;本科2500／月，硕士6万／年。<BR><BR>　　宁波波导&nbsp;2.5-5万／年<BR><BR>　　青岛朗讯 合资 &nbsp;2.5-3万／年&nbsp;（研究生 大連朗訊 1万/月）<BR><BR>　　上海电真空研究所&nbsp;2000／月，转正3000／月。<BR><BR>　　和记黄埔&nbsp;本2000-4000／月，硕4000-6000／月。<BR><BR>　　中国电信广州研究中心&nbsp;2-10万／年<BR><BR>　　西安电子工程研究所&nbsp;2000-3000／月<BR><BR>　　南京自动化研究所&nbsp;2000／月<BR><BR>　　AMD&nbsp;本科2000／月<BR><BR>　　厦门厦新&nbsp;1800-1900／月<BR><BR>　　广东美的&nbsp;1800-2400／月<BR><BR>　　苏州明基电脑 台资 1600-2000／月<BR><BR>　　四川长虹&nbsp;1500-2000／月<BR><BR>　　深圳天马微电子&nbsp;1500／月，转正3-4万／年。<BR><BR>　　合肥英图微电子 合资 &nbsp;1500／月，2000／月(转正)。<BR><BR>　　长城电脑&nbsp;1500／月<BR><BR>　　成都国腾通信&nbsp;1200／月，研发人员2000-4000／月。<BR><BR>　　成都TOP&nbsp;1500／月<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;三菱，三星研发中心 5000/月<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 西门子制造部 3200/月<BR></FONT><img src ="http://www.blogjava.net/qq13367612/aggbug/16848.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-26 10:57 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/26/16848.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正确优雅地解决用户退出问题——JSP及Struts解决方案</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/25/16710.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Tue, 25 Oct 2005 02:00:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/25/16710.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16710.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/25/16710.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16710.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16710.html</trackback:ping><description><![CDATA[大部分Web应用不会包含象银行账户或信用卡资料那样机密的信息，但一旦涉及到敏感数据，我们就需要提供一类密码保护机制。举例来说，一个工厂中工人通过Web访问他们的时间安排、进入他们的训练课程以及查看他们的薪金等等。此时应用SSL(Secure Socket Layer)有点杀鸡用牛刀的感觉，但不可否认，我们又必须为这些应用提供密码保护，否则，工人（也就是Web应用的使用者）可以窥探到工厂中其他雇员的私人机密信息。<BR>&nbsp;&nbsp;&nbsp;&nbsp;与上述情形相似的还有位处图书馆、医院等公共场所的计算机。在这些地方，许多用户共同使用几台计算机，此时保护用户的个人数据就显得至关重要。设计良好编写优秀的应用对用户专业知识的要求少之又少。<BR>&nbsp;&nbsp;&nbsp;&nbsp;我们来看一下现实世界中一个完美的Web应用是如何表现的：一个用户通过浏览器访问一个页面。Web应用展现一个登陆页面要求用户输入有效的验证信息。用户输入了用户名和密码。此时我们假设用户提供的身份验证信息是正确的，经过了验证过程，Web应用允许用户浏览他有权访问的区域。用户想退出时，点击退出按钮，Web应用要求用户确认他是否则真的需要退出，如果用户确定退出，Session结束，Web应用重新定位到登陆页面。用户可以放心的离开而不用担心他的信息会泄露。另一个用户坐到了同一台电脑前，他点击后退按钮，Web应用不应该出现上一个用户访问过的任何一个页面。事实上，Web应用在第二个用户提供正确的验证信息之前应当一直停留在登陆页面上。<BR>&nbsp;&nbsp;&nbsp;&nbsp;通过示例程序，文章向您阐述了如何在一个Web应用中实现这一功能。<BR><BR><BR><BR><B>JSP示例</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了更为有效地阐述实现方案，本文将从展示一个示例应用logoutSampleJSP1中碰到的问题开始。这个示例代表了许多没有正确解决退出过程的Web应用。logoutSampleJSP1包含了下述jsp页面：login.jsp, home.jsp, secure1.jsp, secure2.jsp, logout.jsp, loginAction.jsp, and logoutAction.jsp。其中页面home.jsp, secure1.jsp, secure2.jsp, 和logout.jsp是不允许未经认证的用户访问的，也就是说，这些页面包含了重要信息，在用户登陆之前或者退出之后都不应该出现在浏览器中。login.jsp包含了用于用户输入用户名和密码的form。logout.jsp页包含了要求用户确认是否退出的form。loginAction.jsp和logoutAction.jsp作为控制器分别包含了登陆和退出代码。<BR>&nbsp;&nbsp;&nbsp;&nbsp;第二个示例应用logoutSampleJSP2展示了如何解决示例logoutSampleJSP1中的问题。然而，第二个应用自身也是有疑问的。在特定的情况下，退出问题还是会出现。<BR>&nbsp;&nbsp;&nbsp;&nbsp;第三个示例应用logoutSampleJSP3在第二个示例上进行了改进，比较完善地解决了退出问题。<BR>&nbsp;&nbsp;&nbsp;&nbsp;最后一个示例logoutSampleStruts展示了Struts如何优美地解决登陆问题。<BR>&nbsp;&nbsp;&nbsp;&nbsp;注意：本文所附示例在最新版本的Microsoft Internet Explorer (IE), Netscape Navigator, Mozilla, FireFox和Avant浏览器上测试通过。<BR><BR><BR><B>Login action</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Brian Pontarelli的经典文章<A href="http://www.javaworld.com/javaworld/jw-07-2004/jw-0726-security.html" target=_new>《J2EE Security: Container Versus Custom》</A>讨论了不同的J2EE认证途径。文章同时指出，HTTP协议和基于form的认证并未提供处理用户退出的机制。因此，解决途径便是引入自定义的安全实现机制。<BR>&nbsp;&nbsp;&nbsp;&nbsp;自定义的安全认证机制普遍采用的方法是从form中获得用户输入的认证信息，然后到诸如LDAP (lightweight directory access protocol)或关系数据库的安全域中进行认证。如果用户提供的认证信息是有效的，登陆动作往HttpSession对象中注入某个对象。HttpSession存在着注入的对象则表示用户已经登陆。为了方便读者理解，本文所附的示例只往HttpSession中写入一个用户名以表明用户已经登陆。清单1是从loginAction.jsp页面中节选的一段代码以此阐述登陆动作：<BR><BR><PRE class=overflow title="pre code"><BR>Listing 1 <BR>//...<BR>//initialize RequestDispatcher object; set forward to home page by default<BR>RequestDispatcher rd = request.getRequestDispatcher("home.jsp");<BR><BR>//Prepare connection and statement<BR>rs = stmt.executeQuery("select password from USER where userName = &amp;#39;" + userName + "&amp;#39;");<BR>if (rs.next()) { //Query only returns 1 record in the result set; only 1 <BR>&nbsp;&nbsp;password per userName which is also the primary key<BR>&nbsp;&nbsp; if (rs.getString("password").equals(password)) { //If valid password<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;session.setAttribute("User", userName); //Saves username string in the session object<BR>&nbsp;&nbsp; }<BR>&nbsp;&nbsp; else { //Password does not match, i.e., invalid user password<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request.setAttribute("Error", "Invalid password."); <BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rd = request.getRequestDispatcher("login.jsp");<BR>&nbsp;&nbsp; }<BR>} //No record in the result set, i.e., invalid username<BR>&nbsp;&nbsp; else {<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request.setAttribute("Error", "Invalid user name.");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rd = request.getRequestDispatcher("login.jsp");<BR>&nbsp;&nbsp; }<BR>}<BR><BR>//As a controller, loginAction.jsp finally either forwards to "login.jsp" or "home.jsp"<BR>rd.forward(request, response);<BR>//...<BR><BR></PRE><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;本文所附示例均以关系型数据库作为安全域，但本文所阐述的观点对任何类型的安全域都是适用的。<BR><BR><B>Logout action </B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;退出动作就包含了简单的删除用户名以及对用户的HttpSession对象调用invalidate()方法。清单2是从loginoutAction.jsp页面中节选的一段代码以此阐述退出动作：<BR><BR><PRE class=overflow title="pre code"><BR>Listing 2 <BR>//...<BR>session.removeAttribute("User");<BR>session.invalidate();<BR>//...<BR><BR></PRE><BR><BR><B>阻止未经认证访问受保护的JSP页面</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从form中获取用户提交的认证信息并经过验证后，登陆动作简单地往 HttpSession对象中写入一个用户名，退出动作则做相反的工作，它从用户的HttpSession对象中删除用户名并调用invalidate()方法销毁HttpSession。为了使登陆和退出动作真正发挥作用，所有受保护的JSP页面都应该首先验证HttpSession中是否包含了用户名以确认当前用户是否已经登陆。如果HttpSession中包含了用户名，也就是说用户已经登陆，Web应用则将剩余的JSP页发送给浏览器，否则，JSP页将跳转到登陆页login.jsp。页面home.jsp, secure1.jsp, secure2.jsp和logout.jsp均包含清单3中的代码段：<BR><BR><PRE class=overflow title="pre code"><BR>Listing 3 <BR>//...<BR>String userName = (String) session.getAttribute("User");<BR>if (null == userName) {<BR>&nbsp;&nbsp; request.setAttribute("Error", "Session has ended.&nbsp;&nbsp;Please login.");<BR>&nbsp;&nbsp; RequestDispatcher rd = request.getRequestDispatcher("login.jsp");<BR>&nbsp;&nbsp; rd.forward(request, response);<BR>}<BR>//...<BR>//Allow the rest of the dynamic content in this JSP to be served to the browser<BR>//...<BR><BR></PRE><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在这个代码段中，程序从HttpSession中减缩username字符串。如果字符串为空，Web应用则自动中止执行当前页面并跳转到登陆页，同时给出Session has ended. Please log in.的提示；如果不为空，Web应用则继续执行，也就是把剩余的页面提供给用户。<BR><BR><B>运行logoutSampleJSP1</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;运行logoutSampleJSP1将会出现如下几种情形：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&amp;#8226;&nbsp;&nbsp;如果用户没有登陆，Web应用将会正确中止受保护页面home.jsp, secure1.jsp, secure2.jsp和logout.jsp的执行，也就是说，假如用户在浏览器地址栏中直接敲入受保护JSP页的地址试图访问，Web应用将自动跳转到登陆页并提示Session has ended.Please log in.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&amp;#8226;&nbsp;&nbsp;同样的，当一个用户已经退出，Web应用也会正确中止受保护页面home.jsp, secure1.jsp, secure2.jsp和logout.jsp的执行<BR>&nbsp;&nbsp;&nbsp;&nbsp;&amp;#8226;&nbsp;&nbsp;用户退出后，如果点击浏览器上的后退按钮，Web应用将不能正确保护受保护的页面——在Session销毁后（用户退出）受保护的JSP页重新在浏览器中显示出来。然而，如果用户点击返回页面上的任何链接，Web应用将会跳转到登陆页面并提示Session has ended.Please log in.<BR><BR><B>阻止浏览器缓存</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上述问题的根源在于大部分浏览器都有一个后退按钮。当点击后退按钮时，默认情况下浏览器不是从Web服务器上重新获取页面，而是从浏览器缓存中载入页面。基于Java的Web应用并未限制这一功能，在基于PHP、ASP和.NET的Web应用中也同样存在这一问题。<BR>&nbsp;&nbsp;&nbsp;&nbsp;在用户点击后退按钮后，浏览器到服务器再从服务器到浏览器这样通常意思上的HTTP回路并没有建立，仅仅只是用户，浏览器和缓存进行了交互。所以，即使包含了清单3上的代码来保护JSP页面，当点击后退按钮时，这些代码是不会执行的。<BR>&nbsp;&nbsp;&nbsp;&nbsp;缓存的好坏，真是仁者见仁智者见智。缓存的确提供了一些便利，但通常只在使用静态的HTML页面或基于图形或影响的页面你才能感受到。而另一方面，Web应用通常是基于数据的，数据通常是频繁更改的。与从缓存中读取并显示过期的数据相比，提供最新的数据才是更重要的！<BR>&nbsp;&nbsp;&nbsp;&nbsp;幸运的是，HTTP头信息“Expires”和“Cache-Control”为应用程序服务器提供了一个控制浏览器和代理服务器上缓存的机制。HTTP头信息Expires告诉代理服务器它的缓存页面何时将过期。HTTP1.1规范中新定义的头信息Cache-Control可以通知浏览器不缓存任何页面。当点击后退按钮时，浏览器重新访问服务器已获取页面。如下是使用Cache-Control的基本方法：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&amp;#8226;&nbsp;&nbsp;no-cache:强制缓存从服务器上获取新的页面<BR>&nbsp;&nbsp;&nbsp;&nbsp;&amp;#8226;&nbsp;&nbsp;no-store: 在任何环境下缓存不保存任何页面<BR>&nbsp;&nbsp;&nbsp;&nbsp;HTTP1.0规范中的Pragma:no-cache等同于HTTP1.1规范中的Cache-Control:no-cache，同样可以包含在头信息中。 <BR>&nbsp;&nbsp;&nbsp;&nbsp;通过使用HTTP头信息的cache控制，第二个示例应用logoutSampleJSP2解决了logoutSampleJSP1的问题。logoutSampleJSP2与logoutSampleJSP1不同表现在如下代码段中，这一代码段加入进所有受保护的页面中:<BR><BR><PRE class=overflow title="pre code"><BR>//...<BR>response.setHeader("Cache-Control","no-cache"); //Forces caches to obtain a new copy of the page from the origin server<BR>response.setHeader("Cache-Control","no-store"); //Directs caches not to store the page under any circumstance<BR>response.setDateHeader("Expires", 0); //Causes the proxy cache to see the page as "stale"<BR>response.setHeader("Pragma","no-cache"); //HTTP 1.0 backward compatibility<BR>String userName = (String) session.getAttribute("User");<BR>if (null == userName) {<BR>&nbsp;&nbsp; request.setAttribute("Error", "Session has ended.&nbsp;&nbsp;Please login.");<BR>&nbsp;&nbsp; RequestDispatcher rd = request.getRequestDispatcher("login.jsp");<BR>&nbsp;&nbsp; rd.forward(request, response);<BR>}<BR>//...<BR><BR></PRE><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;通过设置头信息和检查HttpSession中的用户名确保了浏览器不缓存页面，同时，如果用户未登陆，受保护的JSP页面将不会发送到浏览器，取而代之的将是登陆页面login.jsp。<BR><BR><B>运行logoutSampleJSP2</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;运行logoutSampleJSP2后将回看到如下结果：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&amp;#8226;&nbsp;&nbsp;当用户退出后试图点击后退按钮，浏览器并不会显示受保护的页面，它只会现实登陆页login.jsp同时给出提示信息Session has ended. Please log in.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&amp;#8226;&nbsp;&nbsp;然而，当按了后退按钮返回的页是处理用户提交数据的页面时，IE和Avant浏览器将弹出如下信息提示：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 警告：页面已过期……（你肯定见过）<BR>&nbsp;&nbsp;&nbsp;&nbsp;选择刷新后前一个JSP页面将重新显示在浏览器中。很显然，这不是我们所想看到的因为它违背了logout动作的目的。发生这一现象时，很可能是一个恶意用户在尝试获取其他用户的数据。然而，这个问题仅仅出现在后退按钮对应的是一个处理POST请求的页面。<BR><BR><B>记录最后登陆时间 </B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上述问题之所以出现是因为浏览器将其缓存中的数据重新提交了。这本文的例子中，数据包含了用户名和密码。无论是否给出安全警告信息，浏览器此时起到了负面作用。<BR>&nbsp;&nbsp;&nbsp;&nbsp;为了解决logoutSampleJSP2中出现的问题，logoutSampleJSP3的login.jsp在包含username和password的基础上还包含了一个称作lastLogon的隐藏表单域，此表单域动态的用一个long型值初始化。这个long型值是调用System.currentTimeMillis()获取到的自1970年1月1日以来的毫秒数。当login.jsp中的form提交时，loginAction.jsp首先将隐藏域中的值与用户数据库中的值进行比较。只有当lastLogon表单域中的值大于数据库中的值时Web应用才认为这是个有效的登陆。<BR>&nbsp;&nbsp;&nbsp;&nbsp;为了验证登陆，数据库中lastLogon字段必须以表单中的lastLogon值进行更新。上例中，当浏览器重复提交数据时，表单中的lastLogon值不比数据库中的lastLogon值大，因此，loginAction转到login.jsp页面，并提示Session has ended.Please log in.清单5是loginAction中节选的代码段：<BR><BR><PRE class=overflow title="pre code"><BR>清单5<BR>//...<BR>RequestDispatcher rd = request.getRequestDispatcher("home.jsp"); //Forward to homepage by default<BR>//...<BR>if (rs.getString("password").equals(password)) { //If valid password<BR>&nbsp;&nbsp; long lastLogonDB = rs.getLong("lastLogon");<BR>&nbsp;&nbsp; if (lastLogonForm &gt; lastLogonDB) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;session.setAttribute("User", userName); //Saves username string in the session object<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stmt.executeUpdate("update USER set lastLogon= " + lastLogonForm + " where userName = &amp;#39;" + userName + "&amp;#39;");<BR>&nbsp;&nbsp; }<BR>&nbsp;&nbsp; else {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request.setAttribute("Error", "Session has ended.&nbsp;&nbsp;Please login.");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rd = request.getRequestDispatcher("login.jsp");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR>}<BR>else&nbsp;&nbsp; { //Password does not match, i.e., invalid user password<BR>&nbsp;&nbsp; request.setAttribute("Error", "Invalid password.");<BR>&nbsp;&nbsp; rd = request.getRequestDispatcher("login.jsp");&nbsp;&nbsp; <BR>}<BR>//...<BR>rd.forward(request, response);<BR>//...<BR><BR></PRE><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了实现上述方法，你必须记录每个用户的最后登陆时间。对于采用关系型数据库安全域来说，这点可以可以通过在某个表中加上lastLogin字段轻松实现。LDAP以及其他的安全域需要稍微动下脑筋，但很显然是可以实现的。<BR>&nbsp;&nbsp;&nbsp;&nbsp;表示最后登陆时间的方法有很多。示例logoutSampleJSP3利用了自1970年1月1日以来的毫秒数。这个方法在许多人在不同浏览器中用一个用户账号登陆时也是可行的。<BR><BR><B>运行logoutSampleJSP3</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;运行示例logoutSampleJSP3将展示如何正确处理退出问题。一旦用户退出，点击浏览器上的后退按钮在任何情况下都不会是受保护的页面在浏览器上显示出来。这个示例展示了如何正确处理退出问题而不需要额外的培训。<BR>&nbsp;&nbsp;&nbsp;&nbsp;为了使代码更简练有效，一些冗余的代码可以剔除掉。一种途径就是把清单4中的代码写到一个单独的JSP页中，通过标签&lt;jsp:include&gt;其他页面也可以引用。<BR><BR><B>Struts框架下的退出实现</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;与直接使用JSP或JSP/servlets相比，另一个可选的方案是使用Struts。为一个基于Struts的Web应用添加一个处理退出问题的框架可以优雅地不费气力的实现。这部分归功于Struts是采用MVC设计模式的因此将模型和视图清晰的分开。另外，Java是一个面向对象的语言，其支持继承，可以比JSP中的脚本更为容易地实现代码重用。在Struts中，清单4中的代码可以从JSP页面中移植到Action类的execute()方法中。<BR>&nbsp;&nbsp;&nbsp;&nbsp;此外，我们还可以定义一个继承Struts Action类的基本类，其execute()方法中包含了清单4中的代码。通过使用类继承机制，其他类可以继承基本类中的通用逻辑来设置HTTP头信息以及检索HttpSession对象中的username字符串。这个基本类是一个抽象类并定义了一个抽象方法executeAction()。所有继承自基类的子类都应实现exectuteAction()方法而不是覆盖它。清单6是基类的部分代码：<BR><BR><PRE class=overflow title="pre code"><BR>清单6<BR>&nbsp;&nbsp;public abstract class BaseAction extends Action {<BR>&nbsp;&nbsp; public ActionForward execute(ActionMapping mapping, ActionForm form,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpServletRequest request, HttpServletResponse response) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws IOException, ServletException {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setHeader("Cache-Control","no-cache"); //Forces caches to obtain a new copy of the page from the origin server<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setHeader("Cache-Control","no-store"); //Directs caches not to store the page under any circumstance<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setDateHeader("Expires", 0); //Causes the proxy cache to see the page as "stale"<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setHeader("Pragma","no-cache"); //HTTP 1.0 backward compatibility <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (!this.userIsLoggedIn(request)) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ActionErrors errors = new ActionErrors();<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; errors.add("error", new ActionError("logon.sessionEnded"));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.saveErrors(request, errors);<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return mapping.findForward("sessionEnded");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return executeAction(mapping, form, request, response);<BR>&nbsp;&nbsp; }<BR><BR>&nbsp;&nbsp; protected abstract ActionForward executeAction(ActionMapping mapping,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ActionForm form, HttpServletRequest request, HttpServletResponse response) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws IOException, ServletException;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR><BR>&nbsp;&nbsp; private boolean userIsLoggedIn(HttpServletRequest request) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (request.getSession().getAttribute("User") == null) {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return true;<BR>&nbsp;&nbsp; }<BR>}<BR><BR></PRE><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;清单6中的代码与清单4中的很相像，仅仅只是用ActionMapping findForward替代了RequestDispatcher forward。清单6中，如果在HttpSession中未找到username字符串，ActionMapping对象将找到名为sessionEnded的forward元素并跳转到对应的path。如果找到了，子类将执行其实现了executeAction()方法的业务逻辑。因此，在配置文件struts-web.xml中为所有子类声明个一名为sessionEnded的forward元素是必须的。清单7以secure1 action阐明了这样一个声明：<BR><BR><PRE class=overflow title="pre code">清单7<BR>&lt;action path="/secure1" <BR>&nbsp;&nbsp; type="com.kevinhle.logoutSampleStruts.Secure1Action"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp; scope="request"&gt;<BR>&nbsp;&nbsp; &lt;forward name="success" path="/WEB-INF/jsps/secure1.jsp"/&gt;<BR>&nbsp;&nbsp; &lt;forward name="sessionEnded" path="/login.jsp"/&gt;<BR>&lt;/action&gt;<BR></PRE><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;继承自BaseAction类的子类Secure1Action实现了executeAction()方法而不是覆盖它。Secure1Action类不执行任何退出代码，如清单8：<BR><BR><PRE class=overflow title="pre code"><BR>public class Secure1Action extends BaseAction {<BR>&nbsp;&nbsp; public ActionForward executeAction(ActionMapping mapping, ActionForm form,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpServletRequest request, HttpServletResponse response)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws IOException, ServletException {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HttpSession session = request.getSession();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return (mapping.findForward("success"));<BR>&nbsp;&nbsp; }<BR>}<BR><BR></PRE><BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;只需要定义一个基类而不需要额外的代码工作，上述解决方案是优雅而有效的。不管怎样，将通用的行为方法写成一个继承StrutsAction的基类是许多Struts项目的共同经验，值得推荐。<BR><BR><B>局限性</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;上述解决方案对JSP或基于Struts的Web应用都是非常简单而实用的，但它还是有某些局限。在我看来，这些局限并不是至关紧要的。<I>（局限性未翻译，请参见原文）</I><BR><BR><B>结论</B><BR>&nbsp;&nbsp;&nbsp;&nbsp;本文阐述了解决退出问题的方案，尽管方案简单的令人惊讶，但却在所有情况下都能有效地工作。无论是对JSP还是Struts，所要做的不过是写一段不超过50行的代码以及一个记录用户最后登陆时间的方法。在Web应用中混合使用这些方案能够使拥护的私人数据不致泄露，同时，也能增加用户的经验。<BR><img src ="http://www.blogjava.net/qq13367612/aggbug/16710.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-25 10:00 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/25/16710.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 性能优化技巧集锦 </title><link>http://www.blogjava.net/qq13367612/archive/2005/10/24/16659.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 24 Oct 2005 14:16:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/24/16659.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16659.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/24/16659.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16659.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16659.html</trackback:ping><description><![CDATA[<P>1.用new关键词创建类的实例时，构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口，我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。 <BR><BR>在使用设计模式（Design Pattern）的场合，如果用Factory模式创建对象，则改用clone()方法创建新的对象实例非常简单。例如，下面是Factory模式的一个典型实现： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public static Credit getNewCredit() {
return new Credit();
} 
 
改进后的代码使用clone()方法，如下所示：

private static Credit BaseCredit = new Credit();
public static Credit getNewCredit() {
return (Credit) BaseCredit.clone();
}</PRE></TD></TR></TBODY></TABLE></CENTER><BR><BR>面的思路对于数组处理同样很有用。 <BR><BR>2. 使用非阻塞I/O <BR><BR>版本较低的JDK不支持非阻塞I/O API。为避免I/O阻塞，一些应用采用了创建大量线程的办法（在较好的情况下，会使用一个缓冲池）。这种技术可以在许多必须支持并发I/O流的应用中见到，如Web服务器、报价和拍卖应用等。然而，创建Java线程需要相当可观的开销。 <BR><BR>3. 慎用异常 <BR><BR>异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地（Native）方法，fillInStackTrace()方法检查堆栈，收集调用跟踪信息。只要有异常被抛出，VM就必须调整调用堆栈，因为在处理过程中创建了一个新的对象。异常只能用于错误处理，不应该用来控制程序流程。 <BR><BR>4. 不要重复初始化变量 <BR><BR>默认情况下，调用类的构造函数时， Java会把变量初始化成确定的值：所有的对象被设置成null，整数变量（byte、short、int、long）设置成0，float和double变量设置成0.0，逻辑值设置成false。当一个类从另一个类派生时，这一点尤其应该注意，因为用new关键词创建一个对象时，构造函数链中的所有构造函数都会被自动调用。 <BR><BR>5. 尽量指定类的final修饰符 <BR><BR>带有final修饰符的类是不可派生的。在Java核心API中，有许多应用final的例子，例如java.lang.String。为String类指定final防止了人们覆盖length()方法。另外，如果指定一个类为final，则该类所有的方法都是final。Java编译器会寻找机会内联（inline）所有的final方法（这和具体的编译器实现有关）。此举能够使性能平均提高50%。 <BR><BR>6. 尽量使用局部变量 <BR><BR>调用方法时传递的参数以及在调用中创建的临时变量都保存在栈（Stack）中，速度较快。其他变量，如静态变量、实例变量等，都在堆（Heap）中创建，速度较慢。另外，依赖于具体的编译器/JVM，局部变量还可能得到进一步优化。 <BR><BR>7. 乘法和除法 <BR><BR>考虑下面的代码： <BR><BR>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>for (val = 0; val &lt; 100000; val +=5) { alterX = val * 8; myResult = val * 2; } 
 
用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码：

for (val = 0; val &lt; 100000; val += 5) { alterX = val &lt;&lt; 3; myResult = val &lt;&lt; 1; }</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>修改后的代码不再做乘以8的操作，而是改用等价的左移3位操作，每左移1位相当于乘以2。相应地，右移1位操作相当于除以2。值得一提的是，虽然移位操作速度快，但可能使代码比较难于理解，所以最好加上一些注释。 </P>
<P></P>
<P>前面介绍的改善性能技巧适合于大多数Java应用，接下来要讨论的问题适合于使用JSP、EJB或JDBC的应用。 <BR><BR>1. 使用缓冲标记 <BR><BR>一些应用服务器加入了面向JSP的缓冲标记功能。例如，BEA的WebLogic Server从6.0版本开始支持这个功能，Open Symphony工程也同样支持这个功能。JSP缓冲标记既能够缓冲页面片断，也能够缓冲整个页面。当JSP页面执行时，如果目标片断已经在缓冲之中，则生成该片断的代码就不用再执行。页面级缓冲捕获对指定URL的请求，并缓冲整个结果页面。对于购物篮、目录以及门户网站的主页来说，这个功能极其有用。对于这类应用，页面级缓冲能够保存页面执行的结果，供后继请求使用。 <BR><BR>对于代码逻辑复杂的页面，利用缓冲标记提高性能的效果比较明显；反之，效果可能略逊一筹。 <BR><BR>2. 始终通过会话Bean访问实体Bean <BR><BR>直接访问实体Bean不利于性能。当客户程序远程访问实体Bean时，每一个get方法都是一个远程调用。访问实体Bean的会话Bean是本地的，能够把所有数据组织成一个结构，然后返回它的值。 <BR><BR>用会话Bean封装对实体Bean的访问能够改进事务管理，因为会话Bean只有在到达事务边界时才会提交。每一个对get方法的直接调用产生一个事务，容器将在每一个实体Bean的事务之后执行一个“装入-读取”操作。一些时候，使用实体Bean会导致程序性能不佳。如果实体Bean的唯一用途就是提取和更新数据，改成在会话Bean之内利用JDBC访问数据库可以得到更好的性能。 <BR><BR>3. 选择合适的引用机制 <BR><BR>在典型的JSP应用系统中，页头、页脚部分往往被抽取出来，然后根据需要引入页头、页脚。当前，在JSP页面中引入外部资源的方法主要有两种：include指令，以及include动作。 <BR><BR>include指令：例如 <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>&lt;%@ include file="copyright.html" %&gt;</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>该指令在编译时引入指定的资源。在编译之前，带有include指令的页面和指定的资源被合并成一个文件。被引用的外部资源在编译时就确定，比运行时才确定资源更高效。 <BR><BR>include动作：例如 <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>&lt;jsp:include page="copyright.jsp" /&gt;</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>该动作引入指定页面执行后生成的结果。由于它在运行时完成，因此对输出结果的控制更加灵活。但时，只有当被引用的内容频繁地改变时，或者在对主页面的请求没有出现之前，被引用的页面无法确定时，使用include动作才合算。 <BR><BR>4. 在部署描述器中设置只读属性 <BR><BR>实体Bean的部署描述器允许把所有get方法设置成“只读”。当某个事务单元的工作只包含执行读取操作的方法时，设置只读属性有利于提高性能，因为容器不必再执行存储操作。 <BR><BR><BR><BR>5. 缓冲对EJB Home的访问 <BR><BR>EJB Home接口通过JNDI名称查找获得。这个操作需要相当可观的开销。JNDI查找最好放入Servlet的init()方法里面。如果应用中多处频繁地出现EJB访问，最好创建一个EJBHomeCache类。EJBHomeCache类一般应该作为singleton实现。 <BR><BR>6. 为EJB实现本地接口 <BR><BR>本地接口是EJB 2.0规范新增的内容，它使得Bean能够避免远程调用的开销。请考虑下面的代码。 <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>PayBeanHome home = (PayBeanHome)
javax.rmi.PortableRemoteObject.narrow
(ctx.lookup ("PayBeanHome"), PayBeanHome.class); 
PayBean bean = (PayBean)
javax.rmi.PortableRemoteObject.narrow 
(home.create(), PayBean.class);</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>第一个语句表示我们要寻找Bean的Home接口。这个查找通过JNDI进行，它是一个RMI调用。然后，我们定位远程对象，返回代理引用，这也是一个RMI调用。第二个语句示范了如何创建一个实例，涉及了创建IIOP请求并在网络上传输请求的stub程序，它也是一个RMI调用。要实现本地接口，我们必须作如下修改： <BR><BR>方法不能再抛出java.rmi.RemoteException异常，包括从RemoteException派生的异常，比如TransactionRequiredException、TransactionRolledBackException和NoSuchObjectException。EJB提供了等价的本地异常，如TransactionRequiredLocalException、TransactionRolledBackLocalException和NoSuchObjectLocalException。 <BR><BR>所有数据和返回值都通过引用的方式传递，而不是传递值。本地接口必须在EJB部署的机器上使用。简而言之，客户程序和提供服务的组件必须在同一个JVM上运行。如果Bean实现了本地接口，则其引用不可串行化。 </P>
<P></P>
<P></P>
<P>7. 生成主键 <BR><BR>在EJB之内生成主键有许多途径，下面分析了几种常见的办法以及它们的特点。利用数据库内建的标识机制（SQL Server的IDENTITY或Oracle的SEQUENCE）。这种方法的缺点是EJB可移植性差。由实体Bean自己计算主键值（比如做增量操作）。它的缺点是要求事务可串行化，而且速度也较慢。 <BR><BR>利用NTP之类的时钟服务。这要求有面向特定平台的本地代码，从而把Bean固定到了特定的OS之上。另外，它还导致了这样一种可能，即在多CPU的服务器上，同一个毫秒之内生成了两个主键。借鉴Microsoft的思路，在Bean中创建一个GUID。然而，如果不求助于JNI，Java不能确定网卡的MAC地址；如果使用JNI，则程序就要依赖于特定的OS。 <BR><BR>还有其他几种办法，但这些办法同样都有各自的局限。似乎只有一个答案比较理想：结合运用RMI和JNDI。先通过RMI注册把RMI远程对象绑定到JNDI树。客户程序通过JNDI进行查找。下面是一个例子： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public class keyGenerator
extends UnicastRemoteObject implements
Remote { private static long KeyValue = System.currentTimeMillis();
public static synchronized long getKey()
throws RemoteException { return KeyValue++; }</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>8. 及时清除不再需要的会话 <BR><BR>为了清除不再活动的会话，许多应用服务器都有默认的会话超时时间，一般为30分钟。当应用服务器需要保存更多会话时，如果内存容量不足，操作系统会把部分内存数据转移到磁盘，应用服务器也可能根据“最近最频繁使用”（Most Recently Used）算法把部分不活跃的会话转储到磁盘，甚至可能抛出“内存不足”异常。在大规模系统中，串行化会话的代价是很昂贵的。当会话不再需要时，应当及时调用HttpSession.invalidate()方法清除会话。HttpSession.invalidate()方法通常可以在应用的退出页面调用。 <BR><BR>9. 在JSP页面中关闭无用的会话 <BR><BR>对于那些无需跟踪会话状态的页面，关闭自动创建的会话可以节省一些资源。使用如下page指令： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>&lt;%@ page session="false"%&gt;</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>10. Servlet与内存使用 <BR><BR>许多开发者随意地把大量信息保存到用户会话之中。一些时候，保存在会话中的对象没有及时地被垃圾回收机制回收。从性能上看，典型的症状是用户感到系统周期性地变慢，却又不能把原因归于任何一个具体的组件。如果监视JVM的堆空间，它的表现是内存占用不正常地大起大落。解决这类内存问题主要有二种办法。第一种办法是，在所有作用范围为会话的Bean中实现HttpSessionBindingListener接口。这样，只要实现valueUnbound()方法，就可以显式地释放Bean使用的资源。 <BR><BR>另外一种办法就是尽快地把会话作废。大多数应用服务器都有设置会话作废间隔时间的选项。另外，也可以用编程的方式调用会话的setMaxInactiveInterval()方法，该方法用来设定在作废会话之前，Servlet容器允许的客户请求的最大间隔时间，以秒计算。 <BR><BR>11. HTTP Keep-Alive <BR><BR>Keep-Alive功能使客户端到服务器端的连接持续有效，当出现对服务器的后继请求时，Keep-Alive功能避免了建立或者重新建立连接。市场上的大部分Web服务器，包括iPlanet、IIS和Apache，都支持HTTP Keep-Alive。对于提供静态内容的网站来说，这个功能通常很有用。但是，对于负担较重的网站来说，这里存在另外一个问题：虽然为客户保留打开的连接有一定的好处，但它同样影响了性能，因为在处理暂停期间，本来可以释放的资源仍旧被占用。当Web服务器和应用服务器在同一台机器上运行时，Keep-Alive功能对资源利用的影响尤其突出。 <BR><BR>12. JDBC与Unicode <BR><BR>想必你已经了解一些使用JDBC时提高性能的措施，比如利用连接池、正确地选择存储过程和直接执行的SQL、从结果集删除多余的列、预先编译SQL语句，等等。除了这些显而易见的选择之外，另一个提高性能的好选择可能就是把所有的字符数据都保存为Unicode（代码页13488）。Java以Unicode形式处理所有数据，因此，数据库驱动程序不必再执行转换过程。但应该记住：如果采用这种方式，数据库会变得更大，因为每个Unicode字符需要2个字节存储空间。另外，如果有其他非Unicode的程序访问数据库，性能问题仍旧会出现，因为这时数据库驱动程序仍旧必须执行转换过程。 <BR></P>
<P>13. JDBC与I/O <BR><BR>如果应用程序需要访问一个规模很大的数据集，则应当考虑使用块提取方式。默认情况下，JDBC每次提取32行数据。举例来说，假设我们要遍历一个5000行的记录集，JDBC必须调用数据库157次才能提取到全部数据。如果把块大小改成512，则调用数据库的次数将减少到10次。在一些情形下这种技术无效。例如，如果使用可滚动的记录集，或者在查询中指定了FOR UPDATE，则块操作方式不再有效。 <BR><BR>14. 内存数据库 <BR><BR>许多应用需要以用户为单位在会话对象中保存相当数量的数据，典型的应用如购物篮和目录等。由于这类数据可以按照行/列的形式组织，因此，许多应用创建了庞大的Vector或HashMap。在会话中保存这类数据极大地限制了应用的可伸缩性，因为服务器拥有的内存至少必须达到每个会话占用的内存数量乘以并发用户最大数量，它不仅使服务器价格昂贵，而且垃圾收集的时间间隔也可能延长到难以忍受的程度。 <BR><BR>一些人把购物篮/目录功能转移到数据库层，在一定程度上提高了可伸缩性。然而，把这部分功能放到数据库层也存在问题，且问题的根源与大多数关系数据库系统的体系结构有关。对于关系数据库来说，运行时的重要原则之一是确保所有的写入操作稳定、可靠，因而，所有的性能问题都与物理上把数据写入磁盘的能力有关。关系数据库力图减少I/O操作，特别是对于读操作，但实现该目标的主要途径只是执行一套实现缓冲机制的复杂算法，而这正是数据库层第一号性能瓶颈通常总是CPU的主要原因。 <BR><BR>一种替代传统关系数据库的方案是，使用在内存中运行的数据库（In-memory Database），例如TimesTen。内存数据库的出发点是允许数据临时地写入，但这些数据不必永久地保存到磁盘上，所有的操作都在内存中进行。这样，内存数据库不需要复杂的算法来减少I/O操作，而且可以采用比较简单的加锁机制，因而速度很快。 <BR></P>
<P></P>
<P>这一篇中介绍的内容适合于图形用户界面的应用（Applet和普通应用），要用到AWT或Swing。 1. 用JAR压缩类文件 <BR><BR>Java档案文件（JAR文件）是根据JavaBean标准压缩的文件，是发布JavaBean组件的主要方式和推荐方式。JAR档案有助于减少文件体积，缩短下载时间。例如，它有助于Applet提高启动速度。一个JAR文件可以包含一个或者多个相关的Bean以及支持文件，比如图形、声音、HTML和其他资源。要在HTML/JSP文件中指定JAR文件，只需在Applet标记中加入ARCHIVE = "name.jar"声明。 <BR><BR>2. 提示Applet装入进程 <BR><BR>你是否看到过使用Applet的网站，注意到在应该运行Applet的地方出现了一个占位符？当Applet的下载时间较长时，会发生什么事情？最大的可能就是用户掉头离去。在这种情况下，显示一个Applet正在下载的信息无疑有助于鼓励用户继续等待。下面我们来看看一种具体的实现方法。首先创建一个很小的Applet，该Applet负责在后台下载正式的Applet： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>import java.applet.Applet;
import java.applet.AppletStub;
import java.awt.Label;
import java.awt.Graphics;
import java.awt.GridLayout;
public class PreLoader extends Applet implements Runnable, AppletStub {
String largeAppletName;
Label label;
public void init() {
// 要求装载的正式Applet
largeAppletName = getParameter("applet");// “请稍等”提示信息
label = new Label("请稍等..." + largeAppletName);
add(label);
}
public void run(){
try 
{
// 获得待装载Applet的类
Class largeAppletClass = Class.forName(largeAppletName);
// 创建待装载Applet的实例
Applet largeApplet = (Applet)largeAppletClass.newInstance();
// 设置该Applet的Stub程序
largeApplet.setStub(this);
// 取消“请稍等”信息
remove(label);
// 设置布局
setLayout(new GridLayout(1, 0));
add(largeApplet);
// 显示正式的Applet
largeApplet.init();
largeApplet.start();
}
catch (Exception ex)
{
// 显示错误信息
label.setText("不能装入指定的Applet");
}
// 刷新屏幕
validate();
}
public void appletResize(int width, int height)
{
// 把appletResize调用从stub程序传递到Applet
resize(width, height);
}
}</PRE></TD></TR></TBODY></TABLE><BR><BR></CENTER>
<P><BR><BR>编译后的代码小于2K，下载速度很快。代码中有几个地方值得注意。首先，PreLoader实现了AppletStub接口。一般地，Applet从调用者判断自己的codebase。在本例中，我们必须调用setStub()告诉Applet到哪里提取这个信息。另一个值得注意的地方是，AppletStub接口包含许多和Applet类一样的方法，但appletResize()方法除外。这里我们把对appletResize()方法的调用传递给了resize()方法。 <BR><BR>3. 在画出图形之前预先装入它 <BR><BR>ImageObserver接口可用来接收图形装入的提示信息。ImageObserver接口只有一个方法imageUpdate()，能够用一次repaint()操作在屏幕上画出图形。下面提供了一个例子。 <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public boolean imageUpdate(Image img, int flags, int x, int y, int w, int h) {
if ((flags &amp; ALLBITS) !=0 {
repaint();
}
else if (flags &amp; (ERROR |ABORT )) != 0) {
error = true;
// 文件没有找到，考虑显示一个占位符
repaint();
}
return (flags &amp; (ALLBITS | ERROR| ABORT)) == 0;
}</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>当图形信息可用时，imageUpdate()方法被调用。如果需要进一步更新，该方法返回true；如果所需信息已经得到，该方法返回false。 <BR><BR>4. 覆盖update方法 <BR><BR>update()方法的默认动作是清除屏幕，然后调用paint()方法。如果使用默认的update()方法，频繁使用图形的应用可能出现显示闪烁现象。要避免在paint()调用之前的屏幕清除操作，只需按照如下方式覆盖update()方法： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public void update(Graphics g) {
paint(g);
}</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>更理想的方案是：覆盖update()，只重画屏幕上发生变化的区域，如下所示: <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public void update(Graphics g) {
g.clipRect(x, y, w, h);
paint(g);
}</PRE></TD></TR></TBODY></TABLE><BR></CENTER>
<P><BR></P>
<P></P>
<P>5. 延迟重画操作 <BR><BR>对于图形用户界面的应用来说，性能低下的主要原因往往可以归结为重画屏幕的效率低下。当用户改变窗口大小或者滚动一个窗口时，这一点通常可以很明显地观察到。改变窗口大小或者滚动屏幕之类的操作导致重画屏幕事件大量地、快速地生成，甚至超过了相关代码的执行速度。对付这个问题最好的办法是忽略所有“迟到”的事件。 <BR><BR>建议在这里引入一个数毫秒的时差，即如果我们立即接收到了另一个重画事件，可以停止处理当前事件转而处理最后一个收到的重画事件；否则，我们继续进行当前的重画过程。 <BR><BR>如果事件要启动一项耗时的工作，分离出一个工作线程是一种较好的处理方式；否则，一些部件可能被“冻结”，因为每次只能处理一个事件。下面提供了一个事件处理的简单例子，但经过扩展后它可以用来控制工作线程。 <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public static void runOnce(String id, final long milliseconds) {
synchronized(e_queue) {
// e_queue: 所有事件的集合
if (!e_queue.containsKey(id)) {
e_queue.put(token, new LastOne());
}
}
final LastOne lastOne = (LastOne) e_queue.get(token);
final long time = System.currentTimeMillis(); // 获得当前时间
lastOne.time = time;
(new Thread() {public void run() {
if (milliseconds &gt; 0) {
try {Thread.sleep(milliseconds);} // 暂停线程
catch (Exception ex) {}
}
synchronized(lastOne.running) { // 等待上一事件结束
if (lastOne.time != time) // 只处理最后一个事件
return;
}
}}).start();
}
private static Hashtable e_queue = new Hashtable(); private static class LastOne {
public long time=0;
public Object running = new Object();
}</PRE></TD></TR></TBODY></TABLE><BR><BR></CENTER>
<P><BR><BR>6. 使用双缓冲区 <BR><BR>在屏幕之外的缓冲区绘图，完成后立即把整个图形显示出来。由于有两个缓冲区，所以程序可以来回切换。这样，我们可以用一个低优先级的线程负责画图，使得程序能够利用空闲的CPU时间执行其他任务。下面的伪代码片断示范了这种技术。 <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>Graphics myGraphics;
Image myOffscreenImage = createImage(size().width, size().height);
Graphics offscreenGraphics = myOffscreenImage.getGraphics();
offscreenGraphics.drawImage(img, 50, 50, this);
myGraphics.drawImage(myOffscreenImage, 0, 0, this);</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>7. 使用BufferedImage <BR><BR>Java JDK 1.2使用了一个软显示设备，使得文本在不同的平台上看起来相似。为实现这个功能，Java必须直接处理构成文字的像素。由于这种技术要在内存中大量地进行位复制操作，早期的JDK在使用这种技术时性能不佳。为解决这个问题而提出的Java标准实现了一种新的图形类型，即BufferedImage。BufferedImage子类描述的图形带有一个可访问的图形数据缓冲区。一个BufferedImage包含一个ColorModel和一组光栅图形数据。这个类一般使用RGB（红、绿、蓝）颜色模型，但也可以处理灰度级图形。它的构造函数很简单，如下所示： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public BufferedImage (int width, int height, int imageType)</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>ImageType允许我们指定要缓冲的是什么类型的图形，比如5-位RGB、8-位RGB、灰度级等。 <BR><BR><BR><BR>8. 使用VolatileImage <BR><BR>许多硬件平台和它们的操作系统都提供基本的硬件加速支持。例如，硬件加速一般提供矩形填充功能，和利用CPU完成同一任务相比，硬件加速的效率更高。由于硬件加速分离了一部分工作，允许多个工作流并发进行，从而缓解了对CPU和系统总线的压力，使得应用能够运行得更快。利用VolatileImage可以创建硬件加速的图形以及管理图形的内容。由于它直接利用低层平台的能力，性能的改善程度主要取决于系统使用的图形适配器。VolatileImage的内容随时可能丢失，也即它是“不稳定的（volatile）”。因此，在使用图形之前，最好检查一下它的内容是否丢失。VolatileImage有两个能够检查内容是否丢失的方法： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>public abstract int validate(GraphicsConfiguration gc);public abstract Boolean contentsLost();</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>每次从VolatileImage对象复制内容或者写入VolatileImage时，应该调用validate()方法。contentsLost()方法告诉我们，自从最后一次validate()调用之后，图形的内容是否丢失。虽然VolatileImage是一个抽象类，但不要从它这里派生子类。VolatileImage应该通过Component.createVolatileImage()或者GraphicsConfiguration.createCompatibleVolatileImage()方法创建。 <BR><BR>9. 使用Window Blitting <BR><BR>进行滚动操作时，所有可见的内容一般都要重画，从而导致大量不必要的重画工作。许多操作系统的图形子系统，包括WIN32 GDI、MacOS和X/Windows，都支持Window Blitting技术。Window Blitting技术直接在屏幕缓冲区中把图形移到新的位置，只重画新出现的区域。要在Swing应用中使用Window Blitting技术，设置方法如下： <BR><BR></P>
<CENTER><CCID_NOBR></CCID_NOBR>
<TABLE cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=#000000 border=1>
<TBODY>
<TR>
<TD class=code style="FONT-SIZE: 9pt" bgColor=#e6e6e6><PRE><CCID_CODE></CCID_CODE>setScrollMode(int mode);</PRE></TD></TR></TBODY></TABLE></CENTER>
<P><BR><BR>在大多数应用中，使用这种技术能够提高滚动速度。只有在一种情形下，Window Blitting会导致性能降低，即应用在后台进行滚动操作。如果是用户在滚动一个应用，那么它总是在前台，无需担心任何负面影响</P><img src ="http://www.blogjava.net/qq13367612/aggbug/16659.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-24 22:16 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/24/16659.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Eclipse使用秘技 </title><link>http://www.blogjava.net/qq13367612/archive/2005/10/24/16645.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 24 Oct 2005 14:05:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/24/16645.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16645.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/24/16645.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16645.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16645.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 前言： 本来我都是使用JBuilderX当主力IDE、但使用了Eclipse后 发现...Eclipse原来也这么好用...渐渐的就爱上了它...... Eclipse优点：免费、程序代码排版功能、有中文化包、可增 设许多功能强大的外挂、支持多种操作系统(Windows、 Linux、Solaris、Mac OSX)..等等。 开此篇讨论串的目的，是希望能将Eclipse的一些使用技巧集 合起来....&nbsp;&nbsp;<a href='http://www.blogjava.net/qq13367612/archive/2005/10/24/16645.html'>阅读全文</a><img src ="http://www.blogjava.net/qq13367612/aggbug/16645.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-24 22:05 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/24/16645.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Web Framework综述</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/24/16565.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 24 Oct 2005 06:04:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/24/16565.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16565.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/24/16565.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16565.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16565.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 0.简介本文介绍Java Web Framework的基本工作原理，和一些常用的开源Web MVC Framework(Struts, Web Work, Tapestry, Echo, JSF, Maverick, Spring MVC, Turbine, Cocoon, Barracuda)。Web开发的最重要的基本功是HTTP；Java Web开发的最重要的基本功是Servlet S...&nbsp;&nbsp;<a href='http://www.blogjava.net/qq13367612/archive/2005/10/24/16565.html'>阅读全文</a><img src ="http://www.blogjava.net/qq13367612/aggbug/16565.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-24 14:04 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/24/16565.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>推荐一篇好文章——Java Web开发构想</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/24/16549.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 24 Oct 2005 03:24:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/24/16549.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16549.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/24/16549.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16549.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16549.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 width="100%" border=0>
<TBODY>
<TR>
<TD><SPAN>Java Web开发构想</SPAN></TD></TR>
<TR>
<TD></TD></TR>
<TR>
<TD>
<P>1．背景、形势 <BR>能够进行Web开发的编程语言和技术很多 <BR>(1) 动态解释语言 <BR>PHP; Perl; Python (Zope, Plone); Ruby (Ruby on Rails); <BR>(2) 编译语言 <BR>Java; .net <BR><BR>Java Web开发远非一枝独秀： <BR>除了受到来自.net 这个重量级对手的最大挑战之外，更受到Zope, Ruby on Rail 等新式轻骑兵的冲击（当然，也继续受到老式轻步兵PHP, Perl的冲击）。 <BR><BR>官方Java走的是复杂路线，Servlet -&gt; JSP -&gt; Taglib。.net走的也是复杂路线，依靠成熟友好的集成化开发环境取胜。Java阵营好容易应对过来，从纷纭复杂的各种开发框架基础上，发展出了重量级Web开发框架JSF，以及相应的集成化开发环境；渴望以此应对.net的攻势。胜负未分，前途未卜。这时，另一个方向又杀来了新式轻骑Zope, Ruby on Rail。 <BR>Python, Ruby等动态解释语言，面向对象特性更好，先天支持 动态绑定、AOP、函数式编程、“编程即配置”等时髦概念。开发速度更快，代码量更小，达到killer级别。 <BR><BR>传统的HTML Web开发领域里面，Java已经是腹背受敌。领域外也展开了征战，Rich Client Architecture的兴起：AJAX(XMLHttp), Flash RIA, XUL, XAML, Smart Client（以及从前的ActiveX, Applet, Web Start）。 <BR><BR>Web的发展趋势是 语义Web，最终目的是让整个Web成为一个巨大的数据库。 <BR>这意味着，未来的Web应用将更加的面向文本内容数据，更加搜索引擎友好 – Search Engine Friendly. <BR>二进制的客户端插件，如Flash RIA, ActiveX, Applet, Web Start等，虽然交互性能最好，但不是以文本内容数据为中心，搜索引擎不友好。所以，我只是保持适当关注。我更关注基于文本的UI表现，如HTML, XUL, XAML等。XUL, XAML还没有广泛流行，只是保持一种有兴趣的关注。 <BR>当下关注的重点，还是 XHTML + CSS + Javascript少量的 AJAX(XMLHttp)增加更好的交互性。 <BR><BR>我一直认为：轻量、简洁、高效 才是硬道理。后面阐述我对Java Web开发的理解和构想。 </P>
<P>2. Web开发框架层次概述 <BR>从上到下，Web开发框架的层次如下： <BR>(1) HTML, JavaScript, CSS等页面资源。 <BR>(2) 页面模板层。 <BR>如JSP, Freemarker, Velocity, XSL，fastm等。用来生成HTML, JavaScript, CSS等页面资源。 <BR>(3) Web框架。把HTTP Request调度分派到对应的Service Entry。 <BR>(4) Business Logic. <BR>(5) O/R Mapping. <BR>(6) JDBC <BR>(7) DB <BR><BR>根据我的经验，一个典型的Web应用中的代码比例如下： <BR>页面逻辑约占 50%，商业逻辑约占30%, O/R 约占20%。 <BR><BR>但事实上，页面却是最不受重视的部分，从来都被认为是脏活，累活，杂活。典型的开发过程通常是这样： <BR>页面设计人员迅速的用Dreamweaver等生成一堆文本杂乱无章的页面，然后交给JSP程序员加入更加杂乱无章的Java代码和Taglib。 <BR>当页面布局风格需要改变的时候，页面设计人员用Dreamweaver等生成一堆新的页面。JSP程序员再重新加入更加杂乱无章的Java代码Taglib。 <BR>至于页面中的脚本逻辑调试，更是一门精深的工夫了。 <BR><BR>根据社会规则，通常来说，工作内容越轻松，收入越高；工作内容越脏月累，收入越低；Web开发也是如此：做着最脏最累的活的页面程序员，工资一般比不上后台业务逻辑程序员。 <BR><BR>开发框架通常会带来这样的结果：让简单的东西，变得更简单；让复杂的东西，变得更复杂。 <BR>这其中的原因在于： <BR>一般来说，一个应用中简单重复的东西占80%，复杂特殊的东西占20%。 <BR>简单重复的东西很容易摸清规律，进行包装，通用化。但是，在包装的同时，经常就阻挡住了底层的一些灵活强大的控制能力。在复杂特殊的需求中，确实又需要这些底层控制能力，那么为了绕开框架的限制，付出的努力要比不用框架 大得多。 <BR>打个比方，一个比较极端的例子。编译语言比汇编语言的开发效率高很多，但是却无法直接操作寄存器。当需要在编译语言中操作寄存器的时候，就非常的痛苦。比如Java，也许需要JNI，写C代码，还要在C代码里面嵌入汇编。编译、连接都很麻烦。 <BR>所以，一个框架的开发效率，就在于这个80%简单 与 20%复杂之间的平衡。 <BR>假如，不用框架来开发，简单的80%要消耗 80个资源数，复杂的20%要消耗20个资源数，总资源数是100；使用了某个框架，简单的80%只要消耗10个资源数，复杂的20%要消耗40个资源数，总资源数是50。那么，我们说，这个开发框架是有效率的。 <BR><BR>我的思路是，同时应对复杂和简单。当然，为了应对复杂，简单的东西可能就应对得不那么好。比如，做这样一个开发框架，简单的80%要消耗20个资源数，复杂的20%要消耗10个资源数，总资源数是30。 <BR>这种开发框架是有可能实现的。而且是很有意义的。尤其是在复杂部分的比例提高的时候。越复杂的系统，这种开发框架就越有意义。 <BR><BR>后面的关于Web各层开发的论述，主要就按照这个“应对复杂、让复杂更简单”的思路展开。</P>
<P>3．页面资源 <BR>也许有人会说，页面资源，不就是HTML吗？太简单，太低极了，没劲。Dreamweaver、Frontpage多简单阿。随便找个人来用就可以了。文本内容乱糟糟不要紧，浏览器里面显示出来的效果好看就行。要增加炫的、酷的动画效果，那就写JavaScript呗。写在HTML里面，看看在IE里面能不能运行就可以了呗。 <BR>这也正是大多数公司开发页面资源的方式。因为页面的需求变化是最多、最快的，而页面的制作成本很低，人们不愿意在上面投入更多的资源。 <BR><BR>我的看法是，万丈高楼平地起。应用程序的每一个部分都应该完善管理，结构优美。越是需求变化多的地方，越是脏乱差的地方，越应该加大力度处理好。 <BR><BR>页面结构方面，Javaeye论坛的Dlee做了很多工作。 <BR><BR>(1) 在 2005 年我们如何写 JavaScript <BR><A href="http://forum.javaeye.com/viewtopic.php?t=12973" target=_blank><FONT color=#004377>http://forum.javaeye.com/viewtopic.php?t=12973</FONT></A> <BR>(2)使用 Unordered Lists 制作的下拉菜单和树 <BR><A href="http://forum.javaeye.com/viewtopic.php?t=12995" target=_blank><FONT color=#004377>http://forum.javaeye.com/viewtopic.php?t=12995</FONT></A> <BR>从上面的Dlee的论述和给出的资料。可以看出，页面资源分为三部分： <BR>(1) XHTML。结构，Structure。 <BR>XHTML里面的Tag部分只应该包括 &lt;ul&gt; &lt;table&gt; &lt;p&gt; &lt;div&gt;&lt;span&gt;等结构布局Tag，或者&lt;strong&gt;&lt;emphasis&gt;表示语义的Tag。 <BR>XHTML里面不应该包括风格信息，比如字体、颜色、大小、粗细等，也不应该包括&lt;font&gt; &lt;b&gt; &lt;i&gt; &lt;h&gt; 等字体信息。 <BR>XHTML里面不应该包括Javascript的定义和调用。 <BR><BR>(2) JavaScript。行为，behavior。 <BR>JavaScritp应该存在于一个独立于XHTML文件的独立文件中。这样可以做自动化单元测试。JavaScript应该只改变HTML DOM的结构和内容，而不应该改变它的风格。 <BR><BR>(3) CSS。Style，风格。或者说，Presentation，表现。 <BR>前面说了，XHTML里面不应该包括JavaScript的调用。那么，XHTML的元素是如何JavaScript事件绑定起来？就是在CSS里面指定的。 <BR>当然，众所周知，CSS的本职工作是处理页面风格。 <BR><BR>页面资源方面，我完全认同Dlee的观点。从技术和资源积累的长远目标看来，这方面的初期投入的回报将是非常丰厚的。 <BR>即使将来HTML消亡了，进入了XAML, XUL, RSS时代，这些结构清晰的各部分，重用的可能性都非常巨大。JavaScript + CSS + XML UI的这种经典设计思路，将留存很久。混杂成一团的HTML的命运只能是全盘被抛弃。 </P>
<P>4．页面模板层 <BR>页面模板层是指Server端运行的用来生成HTML（或JavaScript，CSS）的Server Side Template Engine。 <BR>这一层也是著名的脏乱差楼层。著名的HTML的Java代码污染事件，就发生在这个楼层。不仅JSP有这个问题，其他的template, 如freemarker, velocity, tapestry等含有逻辑的脚本，都不同程度上有HTML的Script Logic污染问题。 <BR><BR>Dlee的做法很美。直接就不要页面模板层，不用Server Side Template Engine。直接用JavaScript更改HTML DOM的结构、内容、数据。同时，会用到少量的浏览器端XSL。 <BR>这样带来的结果，Template就是很干净纯粹的HTML，不含有任何Server Side Script。这个效果，和Servier Side Template 的 Jivan，XMLC达到的一样。只是一个是在浏览器端执行，一个是在Server端执行。 <BR><BR>我研究比较了几乎所有的Server Side Template Engine，力图采众家之长，避众家之短，写了一个Server Side Template Engine -- fastm, 能够最优雅方便的实现页面模板层。关于fastm，我的Blog上有不少文章论述。 <BR>我的Blog，里面专门有个fastm 分类。 <BR><A href="http://blog.csdn.net/buaawhl" target=_blank><FONT color=#004377>http://blog.csdn.net/buaawhl</FONT></A> <BR><A href="http://buaawhl.blogdriver.com/" target=_blank><FONT color=#004377>http://buaawhl.blogdriver.com</FONT></A> <BR><BR>Fastm发布在java.net上。 <BR><A href="https://fastm.dev.java.net/" target=_blank><FONT color=#004377>https://fastm.dev.java.net</FONT></A> <BR><BR><BR>我仍然对Server Side Template Engine持肯定态度。基于如下原因： <BR>(1) JavaScript代码量大、文件多的时候，不容易管理，不容易进行语法检查，不容易跟踪调试。 <BR>这里有人会争辩，Server Side Template Engine也用到了很多脚本阿，比如Freemarker, Velocity, 而且嵌在HTML中，怎么管理，怎么调试？即使是JSP，也是Java Code嵌在HTML里面，怎么管理，怎么调试？ <BR>这里我要说，Jivan, XMLC, fastm，Wicket等Template Engine的逻辑都是在Java Code里面。 <BR><BR>(2) 用JavaScript生成文本内容，搜索引擎不友好。 <BR>一般的网络蜘蛛程序，只根据URL获取HTML文本，搜索里面的文本内容，而不会执行里面的JavaScript脚本。 <BR><BR>(3) JavaScript代码重用还是有些局限 <BR>比如，有两个HTML文件，一个是Table布局，一个是List布局。 <BR>我有同样的一批数据，要在这两种布局中显示。 <BR>这时候，就要给这两个HTML分别写两套JavaScript。这里面的DOM层次，元素，属性都不同，再怎么定义ID，Class，也无法用完全相同的一套JavaScript处理。 <BR>这里有人会争辩，Server Side Template Engine也无法做到。别说JSP, Velocity, Freemarker等要在两套HTML里面嵌入相同的代码，就是Jivan, XMLC, Wicket也要分别写不同的两套Java Code，因为它们的XML DOM Node / Model View (Table, List) 都是不同的。 <BR>这里我要说。fastm可以做到只用一套代码逻辑。而且只有fastm可以。fastm的代码重用率是最高的。 <BR><BR>关于Ajax(XMLHttp)，我的意见是必要时才用，而且最好采用粗粒度的用法 -- JavaScript发出一个URL请求，返回一整段HTML，直接替换到页面的某一块，而不是用JavaScript来做这样的把数据填充到HTML DOM中。如果你直接在浏览器里面输入那个URL，也可以获取那整段的HTML内容。 <BR>典型的应用场合是Portal。Portal页面的每个Portlet都含有这样的 Ajax(XMLHttp) javascript代码 -- 发出一个Portlet URL请求，返回一整段Portlet的内容，直接替换当前的Portlet块。 <BR>这样做的好处是： <BR>(1) 减少JavaScript代码的量和复杂度。 <BR>(2) 搜索引擎友好。网络蜘蛛程序可以辨别JavaScript中的URL，并根据这个URL，获取整段处理好的HTML文本，进行内容搜索。 <BR>有人可能会争辩：如果URL请求返回的是XML数据，不是整段处理好的HTML，搜索引擎也可以进行内容搜索。 <BR>这点我同意。前提是XML数据的内容是足够连贯的，而不是散落的。比如，你返回的XML数据是“中国”。这个“中国”要放在HTML中的一个{country}位置，{country}足球。这个时候，结果HTML的内容含有“中国足球”。而XML数据中只含有“中国”。如果用户用“中国足球”作为关键字来搜索，就找不到这个URL。 <BR><BR>从前面给出的fastm资料的连接中，可以得知。如同Jivan, XMLC, Wicket一样，fastm的template里面不含有逻辑，所有的逻辑都写在Java里面。 <BR>有人会争辩说：页面逻辑写在Java里面，我改变了页面逻辑，还需要重新编译。这也太不方便了。Velocity, Freemarker, JSP就不用重新编译。 <BR>这里我的看法是：业务逻辑代码改变了，不也需要重新编译吗？页面逻辑就不是逻辑了吗？HTML里面的脚本怎么语法检查、跟踪调试？业务逻辑需要语法检查、跟踪调试，页面逻辑就不需要语法检查、跟踪调试了吗？ <BR>对方可能会说：在我的应用中，页面逻辑的改动需求非常频繁，而且这些页面逻辑非常简单，不需要语法检查、跟踪调试。 <BR>这里我的意见是： <BR>(1) 那就使用JSP, Velocity, Freemarker等脚本。 <BR>(2) fastm, Jivan, XMLC, Wicket的Java代码部分也可以写在脚本里面，比如，Server Side JavaScript, Jython(Python), Groovy, Bean Shell 等脚本语言都可以很方便的和Java相互调用。 <BR><BR>fastm的生命周期将很长。 <BR>HTML, XUL, XAML都是，或将是可以在浏览器或可视化编辑工具里面显示的XML UI定义语言。Microsoft Office的Word, Excel, Powerpoint等格式都提供了相应的XML格式。这些XML文件都可以在Office里面显示，并编辑。 <BR>Adobe公司也提供了PDF的XML格式 -- XDP。可以在Adobe Designer里面显示并编辑。 <BR>由于fastm是Designer Friendly的XML UI所见即所得的模板技术。这方面具有很大的潜力。 <BR>根本不需要第三方花大力气专门做个IDE，来显示自定义的Tag。目标文件格式提供商自己的阅读编辑工具就可以直接用了，而且效果就是运行后产生的结果文件的效果。 <BR><BR>即使没有可视化要求的场合。比如，Web Service需要的XML数据。fastm同样有用武之地。比如， <BR>&lt;!-- BEGIN DYNAMIC: users --&gt; <BR>&lt;user&gt; <BR>&lt;name&gt;{name}&lt;/name&gt; <BR>&lt;address&gt;{name}&lt;/address&gt; <BR>&lt;/user&gt; <BR>&lt;!-- END DYNAMIC: users --&gt; <BR><BR>可以很容易的把一个Java Object List转化为XML数据。 <BR><BR>另外，我不得不承认。浏览器端的JavaScript的页面逻辑，可移植性要高于Server Side Template Engine。因为Server Side Template Engine通常是特定语言相关的。 <BR>目前fastm是用Java实现的。由于实现很简单，移植到其它的语言，也很简单。如果是移植到Python, Ruby等动态解释语言，那就更简单了。我是有这个考虑，因为Zope, Ruby on Rails 的模板还是Logic 和 HTML混杂的，fastm这个思路有很大的用武之地。 <BR><BR>前面讲了这么多。清理了两层有名的脏乱差的老大难的楼层 -- 页面资源层和页面模板层。让这两层变得和下面的楼层同样的优雅、清洁。 <BR><BR>下面该讲到Web框架层了。在向下讲之前，由于前面提到了脚本，我想先插入一段关于“可配置”、“可编程”、“可热部署”、“脚本逻辑 vs XML Tag逻辑”的话题。把这个人们比较关心、讨论比较多的话题，先讲清楚。 </P>
<P>5．可配置、可编程、可热部署、脚本逻辑 vs XML Tag逻辑 <BR>由于Java是编译语言，人们通常把变化的参数部分抽取出来，放到配置文件中。 <BR>这些配置文件通常是XML文件。这很好，没什么问题。XML很适合用来表达数据结构。 <BR>但是，对于某一种技术的狂热，通常引起对这种技术的过度使用，或者误用。 <BR>人们开始觉得，XML能够表达一切东西，包括for, if, else等逻辑。这方面的典型例子有 Workflow XML Definition，Logic TagLib, XSL Logic Tag等。 <BR>这点我不敢苟同。我的看法是，XML不适合表达逻辑，XML表达逻辑非常蹩脚。XML表达逻辑相当于自定义一门XML格式的脚本语言。 <BR><BR>比如，Logic Tablib，很难自然的支持 if else, switch。只能蹩脚地支持一堆 &lt;logic:if&gt; &lt;logic:ifNot&gt; &lt;logic:exists&gt; &lt;logic:notExists&gt; &lt;logic:ifNull&gt; &lt;logic:notNull&gt;。 <BR>（注，好久没有接触过Taglib了。这些Tag Name都是凭以前的使用印象写的，也许名字不对，但表达这些意思的TagLib都还是有的） <BR>如果要表达if () else if() else 就更蹩脚了。要进行非常麻烦的嵌套。 <BR><BR>再比如，XSL 支持if, else 也非常蹩脚。非要多出来一个层次才行。 <BR>&lt;xsl:choose&gt; <BR>&lt;xsl:when test="…"&gt; <BR>…. If …. <BR>&lt;/xsl:when&gt; <BR>&lt;xsl:otherwise&gt; <BR>… else … <BR>&lt;/xsl:otherwise&gt; <BR>&lt;/xsl:choose&gt; <BR><BR>同样，如果要表达if () else if() else 就更蹩脚了。 <BR>&lt;xsl:choose&gt; <BR>&lt;xsl:when test="…"&gt; <BR>…. If …. <BR>&lt;/xsl:when&gt; <BR>&lt;xsl:otherwise&gt; <BR>&lt;xsl:choose&gt; <BR>&lt;xsl:when test="…"&gt; <BR>…. If …. <BR>&lt;/xsl:when&gt; <BR>&lt;xsl:otherwise&gt; <BR>… else … <BR>&lt;/xsl:otherwise&gt; <BR>&lt;/xsl:choose&gt; <BR>&lt;/xsl:otherwise&gt; <BR>&lt;/xsl:choose&gt; <BR><BR>可以看到，XML Tag 表达逻辑，非常麻烦，可读性很差，完全是一种误用，没有半点优势。当然，逻辑简单的情况下，还是可以接受的。 <BR>有人会说：XML表达逻辑，可以免编译阿。 <BR>那么我说：语法检查呢，跟踪调试呢？ <BR>对方说：只是一些简单的逻辑，不需要语法检查、跟踪调试。 <BR>我说：如果只是为了免编译，前面列出的那么多的解释执行的脚本语言更适合。XML表达的逻辑，比Java等编译语言还要麻烦很多，而脚本语言比Java等编译语言简洁多了，可读性非常好，而且脚本语言和Java语言有很好的交互性，可以相互调用。重用、结构方面都具有优势。 <BR><BR>有人会举出<B>Spring</B> IoC为例子，说：你看，<B>Spring</B> IoC的配置文件不都是XML格式吗？ <BR>我说： <BR>(1) <B>Spring</B> IoC的配置文件基本都是属性设置，Bean ID声明。没有逻辑。 <BR>(2) 我也不是很赞同<B>Spring</B> IoC在XML配置文件里面引用Java类的做法。这方面，其它的容器如 Pico, Nano都支持多种配置方式，其中包括了不少脚本方式。我觉得，在脚本里面定义生成Java Object，比在XML中要好。当然，Web.xml里面也引用了Java Class名字。但那是非常简单的情况。没有嵌套引用、属性赋值、构造参数等复杂的定义方式。XML适合描述一些通用的资源、数据、结构。比如，HTML, XUL, XAML，RSS就是XML用的恰当的例子。 <BR><BR>所以，我的基本观点是这样。 <BR>(1) 纯数据，不用说，应该定义在XML中。 <BR>(2) 如果是系统中一些Java Object要用到的基本属性。比如，连接池大小等。定义在properties, XML, Script中都可以。如果定义中没有出现具体的Java Class名，倾向于定义在properties, XML文件中。如果出现了具体的Java Class名，倾向于定义在Script中。这个界限不那么明显，两者皆可。 <BR>(3) 复杂结构的Java Bean的构造生成，那是肯定会出现具体的Java Class名，应该定义在Script中。 <BR><BR>关于“可配置 vs 可编程”，有一点要明确：只要是可编程的，一定是可配置的。但如果是可配置的，却不一定是可编程的。 <BR>这里的可编程，是指框架给程序员提供了API；可配置，是指框架给程序员提供了配置文件的格式写法。 <BR>“可编程”一定是“可配置”的。 <BR>(1) 用户至少可以自己定义配置文件，读取参数，调用API。 <BR>(2) 有那么多的解释脚本可以直接和Java互操作，完全可以直接用来当作配置文件，定义参数。 <BR>“可配置” 却不一定“可编程”的。 <BR>如果框架只给你提供了配置方式，而没有API，那意味着，你只能进行参数的静态配置。很难在动态期间改变这些参数了。你总不能尝试着用代码去改变配置文件的内容吧？即使你改动了，如果框架不进行文件的时间戳检查，就是一开始装载进来，就不再检查更改了，你不就一点办法都没有了吗？ <BR>比如，Struts Tiles的XML定义，你只能静态配置，你想在运行期间改变布局，没有办法。Site Mesh也是如此。而我们可以在运行期间任意操作XML DOM Node，别说布局了，任何东西都可以改变。 <BR>所以，一个框架首要注重的是提供API，而不是提供配置方式。这是一个重要的原则。 <BR><BR>讨论完了“可编程”、“可配置”问题，我们来看“热部署”问题。 <BR>XML配置文件、脚本文件支持“热部署”当然要比编译语言程序的热部署容易得多。只要解释执行前，检查一下时间戳就可以了。要注意的问题，只是做好测试，因为没有编译期的语法检查。 <BR>不过，Java程序也是可以“热部署”的。只是稍微麻烦一点。典型的例子是JSP, EJB Jar等。JSP修改之后，会自动编译执行；EJB Jar丢到EJB Container里面，会被检测到并装载到JNDI命名空间。 <BR>编译语言Java程序的热部署的一个可能的技术难点是，Class或者Jar已经存在，如何监测到Class或者Jar的更改，并装载这个新版本，替换旧版本。 <BR>这个问题我具体没有研究过。从道理上讲，应该在Class Loader上下功夫。如果需要，可以参阅开源EJB Container的相关实现部分。Java还有一种“Hot Swap”技术，专门解决这个问题，可以搜索查阅一下。 <BR><BR>这段小插曲，就到这里。下面讨论Web框架。 </P>
<P>6．Web框架 <BR>Web框架层是一个清洁的楼层。很多优秀的程序员在这一层大展身手，做出了很多好作品。我感觉不错的有<B>Spring</B> MVC, Web Work。 <BR>对于Web应用来说，Web框架层是最重要的一层。SOA、Semantic Web等效果都要在这一层实现。 <BR>首先，我们来讨论，框架的编程结构。 <BR>我的Blog中有一篇《Java Web框架综述》的文章。讲解了一些流行的Web框架的编程结构，很多重复的内容不再赘述。 <BR><A href="http://blog.csdn.net/buaawhl" target=_blank><FONT color=#004377>http://blog.csdn.net/buaawhl</FONT></A> <BR><BR>Java Web框架综述 <BR><A href="http://blog.csdn.net/buaawhl/archive/2004/12/21/224069.aspx" target=_blank><FONT color=#004377>http://blog.csdn.net/buaawhl/archive/2004/12/21/224069.aspx</FONT></A> <BR><BR><B>Spring</B> MVC的编程接口是最清晰的。大多数简单情况下，Web Work的用法是最简单有效的，编程结构比较特殊，可以说具有一定的变革意义。 <BR><B>Spring</B> MVC的Controller接口相当于Struts Action，也具有Request, Response两个参数，虽然编程接口非常清晰优雅，但是本质上没有什么变化。 <BR><B>WebWork</B>的Action则失去了Controller的身份，只相当于FormBean的身份，或者说相当于ActionBean的身份。<B>WebWork</B> Action不具有Request, Response两个参数，它只具有属性，并通过属性Setter获取HTTP Request的参数，通过属性getter把结果数据输出到HTTP Response。 <BR>可以说，<B>WebWork</B>的这个把握是相当到位的。95%以上的情况下，程序员是不需要Request, Response参数的。当需要这些参数的时候，<B>WebWork</B>并没有挡住路，可以通过实现RequestAware，ResponseAware等接口来获取，或者通过一个Thread Local获取。这种情况下，编程结构的约定，就不那么清晰了。 <BR><BR>我从Canonical的帖子和Blog受到了很多启发。 <BR><A href="http://canonical.blogdriver.com/" target=_blank><FONT color=#004377>http://canonical.blogdriver.com</FONT></A> <BR><BR>jsplet:对Model 2模式的批判 <BR><A href="http://canonical.blogdriver.com/canonical/591479.html" target=_blank><FONT color=#004377>http://canonical.blogdriver.com/canonical/591479.html</FONT></A> <BR><BR>jsplet与<B>webwork</B>的概念对比 <BR><A href="http://canonical.blogdriver.com/canonical/594671.html" target=_blank><FONT color=#004377>http://canonical.blogdriver.com/canonical/594671.html</FONT></A> <BR><BR>从级列理论看MVC架构 <BR><A href="http://canonical.blogdriver.com/canonical/579747.html" target=_blank><FONT color=#004377>http://canonical.blogdriver.com/canonical/579747.html</FONT></A> <BR><BR>从Canonical的文章可以看出。JSPLet用JSP文件作为Dispatcher，然后在JSP里面注册并调用对应的Object。这个寻访Object的过程，完全是根据丰富的URL定义来做的。URL里面包括Object Scope, Object Name, Method Name, Method Parameters，天生就对事件机制有良好的支持。 <BR><BR>Zope的一些做法也有异曲同工之妙。 <BR>Zope Object Publishing <BR><A href="http://www.zope.org/Documentation/Books/ZDG/current/ObjectPublishing.stx" target=_blank><FONT color=#004377>http://www.zope.org/Documentation/Books/ZDG/current/ObjectPublishing.stx</FONT></A> <BR><A href="http://www.plope.com/Books/2_7Edition/ZopeArchitecture.stx#2-3" target=_blank><FONT color=#004377>http://www.plope.com/Books/2_7Edition/ZopeArchitecture.stx#2-3</FONT></A> <BR><BR>这种通过URL获取Published Object的服务的思路，是一种实现SOA效果的有效思路。 <BR><BR>我们首先来看Web Service的现状。目前Web Service主要分为两大阵营。SOAP和REST。关于REST，请参阅 <BR><A href="http://www.xfront.com/REST-Web-Services.html" target=_blank><FONT color=#004377>http://www.xfront.com/REST-Web-Services.html</FONT></A> <BR>关于SOAP和REST的比较、互操作，网上有很多文章。如果需要请搜索查阅。 <BR><BR>我个人比较倾向于REST风格的Web Service。 <BR>因为SOAP是一门固定的协议，如果用SOAP来编写Web Service程序，需要一个SOAP协议的解析库 ，也许还需要一些专门的“SOAP 数据 -- 编程语言”映射库，如同CORBA IDL的多语言映射一样。如果你要让自己的Web应用支持SOAP，你需要把发布的服务对象、方法都包装为SOAP协议，这需要一些编程语言相关的数据结构的映射工作。 <BR>REST则只是一种风格，而不是一个协议。中心思想是简单的通过丰富的URI定义 (如XLink + XPointer等) 获取资源。如果你要让自己的Web应用支持REST，那么很简单，只要在URI上下功夫就可以了，比如，多增加一个参数format=REST，在程序中多增加一种XML输出格式就可以了。（从道理上来说，SOAP也可以这么实现，但SOAP的输入和输出都要遵守SOAP协议，SOAP的输入参数一般都包装在SOAP信封里面） <BR><BR>关于HTTP Get和Post，我表述一下自己的看法。 <BR>我认为，Web的精髓在于Get，而不是Post，在于获取服务器的输出，而不是输入到服务器。即，Web的精髓在于以小搏大，四两拨千斤。最经典的用法就是用一个URL，获取一个长篇的文本内容，这个内容里面充满了其他更多的资源连接。这也是超文本连接HTML发明的初衷。 <BR>至于HTTP Post，则是这上面的一个扩展。B/S结构如此流行，很多应用都要转移到Web上面，怎么办，应用总是交互的，总要让用户输入数据吧，就增加了HTTP Post协议。 <BR>HTTP Get经典、简单、有效。可以用丰富的URI定义把这个优势发挥到极致。这个实现也比较简单、优雅。就不多说了。主要的难点在于HTTP Post。下面的讨论主要应对“HTTP Post”这个复杂现象。 <BR>HTTP Post从来就不让人们满意。当输入逻辑复杂到一定程度，表单数据的繁杂、凌乱、散落，到了服务器端很难组织起来。输入方面B/S结构确实和C/S结构难以匹敌。于是，出现了XMLHttp，能够把参数在浏览器里面组织成为一个统一的XML数据结构（或其他格式），发送到服务器端，一次解析出来。SOAP做这个方面，更是拿手好戏。所以，很多XMLHttp程序直接采用SOAP作为通信协议。而REST风格的HTTP Post则和HTML Form Post没有太大的本质区别。 <BR>REST在HTTP Get方面更胜一筹，SOAP在HTTP Post方面更胜一筹。可以根据Web应用的特点，根据HTTP Get / HTTP Post 页面的比例，选择适合的技术。 <BR>我们再进一步分析HTTP Post的数据内容。HTTP Post的数据，可能包含三种类型： <BR>(1) 需要存档在服务器的数据 <BR>比如，用户注册时候，输入的基本信息，用户名、密码、电子邮件等。这些信息要存放到服务器的数据库。 <BR>对于这种基本信息，HTTP Post，XMLHttp，SOAP处理起来，难度都不大，没有很大区别。 <BR>B2B的数据交换，也属于这个类别。用何种技术区别不大。一般采用SOAP，因为SOAP是一种流行的标准协议。 <BR>(2) 服务调用参数 <BR>比如，用户进行复合条件查询的时候，输入的查询条件。这个时候，HTTP Post处理起来就非常蹩脚。而XMLHttp，SOAP则具有很大的优势。可以把复杂的查询条件很好组织成XML数据，发送到服务器端统一处理。SOAP里面甚至可以定义对象名、方法名等详细的调用信息。 <BR>(3) 指令 <BR>这种情况比较少见。上面的参数类别中提到的“对象名、方法名等详细的调用信息”，和这个指令类别有些交叉。 <BR>假如一个SOAP调用方法里面的参数也是一个自定义的对象，这个自定义对象的属性数据在SOAP信息中进行了定义。到了服务器端之后，服务端程序首先调用这个自定义参数的构造函数，生成这个参数对象，然后调用对应的服务对象，把这个参数传给服务。这个过程可以看作是一个顺序指令：[1]构造参数[2]调用服务。 <BR>这只是最简单的情况。而目前的Web Service一般也就支持到这个程度。 <BR>我的看法是，一不做，而不休。既然都把调用信息定义到这个程度了，不如做的更彻底一些，全面完善的支持指令。这个指令则意味着逻辑。前面讲过了，我不赞成用XML Tag表示逻辑，而赞成脚本。这里比较适合的脚本是JavaScript，因为JavaScript比较通用，客户端、服务器端都可以解释执行。注意，这里和一般的做法正好相反：一般的Web应用总是把JavaScript从服务器传到浏览器里面执行，而这里是把JavaScript在浏览器里组织好，发给服务器端处理；这个JavaScript将会在服务器端执行，调用服务器端的对象。举个SOAP含有JavaScript指令的例子 (只是示意，非标准格式) ： <BR>&lt;soap envelope&gt; <BR>&lt;XML Data&gt; <BR>&lt;a&gt; <BR>&lt;b&gt;12&lt;/b&gt; <BR>&lt;/a&gt; <BR>&lt;c&gt; <BR>&lt;d&gt;21&lt;/d&gt; <BR>&lt;/c&gt; <BR>&lt;e&gt; <BR>&lt;e&gt;16&lt;/e&gt; <BR>&lt;/e&gt; <BR>&lt;/XML Data&gt; <BR><BR>&lt;script&gt; <BR>final_result = default; <BR>result1 = service1.service(a.b); <BR>if(result1.ok){ <BR>result2 = service2.service(c.d); <BR>if(result2.ok) <BR>final_result = service3.service(e.f); <BR>} <BR>&lt;/script&gt; <BR>&lt; /soap envelope &gt; <BR><BR>这个好处是： <BR>[1] 发布了更多的基本Service。给客户提供了更大的灵活度。 <BR>比如，这里就发布了3个Service。由用户自己组织逻辑。 <BR>按照传统的做法，上述流程将整个包装在服务器端执行。发布给用户的Service只有最外面的一个Service，而且高度耦合（if, else, if, else流程hard code在服务器端），不灵活，不通用。 <BR>这里的方法，就可以让客户端随意组织service1, service2, service3的调用顺序和方式。 <BR>[2] 减少了通信次数。 <BR>假如这段Script在客户端执行，那么和服务器要进行3次通信。 <BR><BR>传统Web的权限控制一般在URL级别，这种script -&gt; server方式的权限控制则要在对象级别、方法级别、Code片断级别了，复杂很多，也许要大量应用Java的Code权限认证机制。 <BR><BR>以上展开讨论了 Web Service, HTTP Get/Post。下面我们回到Web框架层。 <BR>前面说了，JSPLet给了我很大的启发。很多思路可以借鉴。 <BR>当然，我并不赞成用JSP作Dispatcher, Controller。(1) 因为JSP要编译成Servlet，而Servlet是Web Server管理的比较昂贵的资源。一个Web系统中JSP达到几千个，就会遇到性能瓶颈。(2) JSP中的代码重用很成问题。一般只能通过include file的方式。 <BR>可以借鉴的思路。(1) JSPLet 的入口是JSP文件，这一步的URL到处理程序的映射是Servlet/JSP Container自然支持的。这是免配置的。(2) 丰富的URL参数定义，良好的对象方法寻址能力。 <BR><BR>我开发的开源Web框架lightweb，将具备如下特性： <BR>(1) 支持两个层次的编程接口。 <BR>interface Action { void service(request, response, servletContext); } <BR>这个Action比Struts Action, <B>Spring</B> MVC Controller高一个级别。相当于Dispatcher, 相当于JSPLet的JSP控制文件。这个用来做最外层的入口控制。 <BR>同时，也支持简单的JavaBean.method的直接调用。相当于<B>WebWork</B> Action，JSPLet Registered Object。这个用来做具体的事情。 <BR><BR>(2) 支持丰富的对象寻址URI，比如http://my.com/myProject/myModule/myEntry.action?object=calculator&amp;method=add&amp;p1=1&amp;p2=3 <BR>这表示要通过 myEntry.acion这个入口，调用caculator.add(1, 2)方法。 <BR>如果用URL Rewriter可以美化为 <BR><A href="http://my.com/myProject/myModule/myEntry/calculator/add/1/3" target=_blank><FONT color=#004377>http://my.com/myProject/myModule/myEntry/calculator/add/1/3</FONT></A> <BR>看起来就很象XLink + XPointer了。 <BR><BR>(3) 免配置。或者说极少的配置。 <BR>框架根据一定的匹配准则，把myModule/myEntry.action映射到 <BR>com.mycompany.mymodule.MyEntryAction 这个类的service方法。 <BR>这个service方法负责根据object, method的名字，寻找到对应的bean，并根据参数进行属性设置验证，并执行对应的bean.method。然后，把这个bean作为Model和template结合，输出结果。 <BR>同样，template的获取也是根据一定的匹配准则，根据myModule/myEntry找到 <BR>Mymodule/myentry.html 或者Mymodule/myentry/calculator.html。 <BR><BR>这样的lightweb就能够同时对应简单和复杂。复杂控制的需求交给Action接口来做，简单的一般具体任务交给普通Java Bean去做。 <BR>Web框架层可以做的非常复杂，可以做的非常简单。Lightweb的目标，就是分成多个简单的部分；各部分合起来就能够完成从非常简单到非常复杂的需求。 <BR>接下来，我们来看O/R。 </P>
<P>7．O/R <BR>Hibernate, EJB Entity Bean产品，JDO产品，iBatis是比较流行的几种O/R Mapping Framework。 <BR>我做的一些工作中，经常涉及到复杂的优化过的native SQL，并且涉及到大量的批量复杂逻辑处理，现有的O/R框架都不能满足功能和性能要求。 <BR><BR>我做出这样一个lightor框架，思路借鉴了Martin Fowler的《企业架构模式》里面讲述的一些O/R的Row Mapper, Column Mapper等概念。 <BR><BR>最经典的用法是： <BR>ResultSet rs = ps.executeQuery( a long complex native sql); <BR>//will return a lot of records <BR>A a = new A(); <BR>B b = new B(); <BR>IMapper aMapper = MapperService.getMapper(A.class); <BR>IMapper bMapper = MapperService.getMapper(B.class); <BR><BR>While(rs.next()){ <BR>aMapper.populate(a, rs); <BR>bMapper.populate(b, rs); <BR><BR>businessLogic(a, b); <BR>} <BR><BR>可以看到，Lightor不需要一下子把所有纪录都放到一个Object List里面。完全可以随取随用。整个过程中，a, b只有一份，极大的节省了空间、时间，也极大的提高了开发效率，减少了重复代码。 <BR>没有任何一个其它O/R能够支持这种用法。这里面，lightor的mapper的populate方法需要ResultSet参数。一般的O/R不屑于这么做的，别说ResultSet，连Connection都想包装起来不给你看。 <BR><BR>Lightor的设计思路也是同时应对简单和复杂。Lightor的Mapper实体部分是自动生成代码。类似于JDO的静态Enhance。不同的是，JDO静态Enhance直接修改bean class。而Lightor则不动原有的bean，只是多生成了对应的Mapper Source/Class。这种方式是最利于跟踪调试的。至于发布部署，和JDO的情况差不多，不如Hibernate的动态代码增强。 <BR>这里我很羡慕Python, Ruby等动态解释语言的 <BR><BR>这一层我主要关注的是性能，缓存策略等等，而不是简便。我觉得，一个应用系统的瓶颈主要存在于O/R, DB层。不应该单纯为了追求OO结构的优雅，或者编程的方便，而牺牲了一些可能优化的地方。 <BR><BR>关于Lightor的缓存策略, 我的Blog上有几篇文章。 <BR><A href="http://blog.csdn.net/buaawhl" target=_blank><FONT color=#004377>http://blog.csdn.net/buaawhl</FONT></A> <BR><BR>数据库对象的缓存策略 <BR><A href="http://blog.csdn.net/buaawhl/archive/2004/12/21/224184.aspx" target=_blank><FONT color=#004377>http://blog.csdn.net/buaawhl/archive/2004/12/21/224184.aspx</FONT></A> <BR><BR>分页 &amp; QueryKey &amp; 定长预取 <BR><A href="http://blog.csdn.net/buaawhl/archive/2005/01/08/245005.aspx" target=_blank><FONT color=#004377>http://blog.csdn.net/buaawhl/archive/2005/01/08/245005.aspx</FONT></A> </P>
<P>8．总结 <BR>我理想中的Web开发架构是这样的： <BR>开发速度快，运行速度快，结构清晰优雅。 <BR>具体到每一层。 <BR>Web框架层主要追求 开发速度快。 <BR>O/R层主要追求 运行速度快。 <BR>页面资源层和页面模板层主要追求 结构清晰优雅。 </P></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/qq13367612/aggbug/16549.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-24 11:24 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/24/16549.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts常见异常信息和解决方法 </title><link>http://www.blogjava.net/qq13367612/archive/2005/10/24/16541.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Mon, 24 Oct 2005 02:21:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/24/16541.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16541.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/24/16541.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16541.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16541.html</trackback:ping><description><![CDATA[<P>以下所说的struts-config.xml和ApplicationResources.properties等文件名是缺省时使用的，如果你使用了多模块，或指定了不同的资源文件名称，这些名字要做相应的修改。</P>
<P><STRONG>1、“No bean found under attribute key XXX”<BR></STRONG>在struts-config.xml里定义了一个ActionForm，但type属性指定的类不存在，type属性的值应该是Form类的全名。或者是，在Action的定义中，name或attribute属性指定的ActionForm不存在。<BR></P>
<P><STRONG>2、“Cannot find bean&nbsp;XXX in any scope”</STRONG><BR>在Action里一般会request.setAttribute()一些对象，然后在转向的jsp文件里（用tag或request.getAttribute()方法）得到这些对象并显示出来。这个异常是说jsp要得到一个对象，但前面的Action里并没有将对象设置到request（也可以是session、servletContext）里。<BR>可能是名字错了，请检查jsp里的tag的一般是name属性，或getAttribute()方法的参数值；或者是Action逻辑有问题没有执行setAttribute()方法就先转向了。<BR>还有另外一个可能，纯粹是jsp文件的问题，例如&lt;logic:iterate&gt;会指定一个id值，然后在循环里&lt;bean:write&gt;使用这个值作为name的值，如果这两个值不同，也会出现此异常。（都是一个道理，request里没有对应的对象。）<BR></P>
<P><STRONG>3、“Missing message for key "XXX"”</STRONG><BR>缺少所需的资源，检查ApplicationResources.properties文件里是否有jsp文件里需要的资源，例如：</P>
<P></P>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">bean:message&nbsp;key</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">msg.name.prompt</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">/&gt;</SPAN></DIV></DIV>
<P></P>
<P>这行代码会找msg.name.prompt资源，如果AppliationResources.properties里没有这个资源就会出现本异常。在使用多模块时，要注意在模块的struts-config-xxx.xml里指定要使用的资源文件名称，否则当然什么资源也找不到，这也是一个很容易犯的错误。</P>
<P><STRONG>4、“No getter method for property&nbsp;XXX of bean teacher”</STRONG><BR>这条异常信息说得很明白，jsp里要取一个bean的属性出来，但这个bean并没有这个属性。你应该检查jsp中某个标签的property属性的值。例如下面代码中的cade应该改为code才对：</P>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">&lt;</SPAN><SPAN style="COLOR: #000000">bean:write&nbsp;name</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">teacher</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;property</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">cade</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">&nbsp;filter</SPAN><SPAN style="COLOR: #000000">=</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">true</SPAN><SPAN style="COLOR: #000000">"</SPAN><SPAN style="COLOR: #000000">/&gt;</SPAN></DIV></DIV><BR>
<P>
<P><STRONG>5、“Cannot find ActionMappings or ActionFormBeans collection”<BR></STRONG>待解决。</P>
<P><STRONG>6、“Cannot retrieve mapping for action XXX”<BR></STRONG>在.jsp的&lt;form&gt;标签里指定action='/XXX'，但这个Action并未在struts-config.xml里设置过。</P>
<P><STRONG>7、HTTP Status 404 - /xxx/xxx.jsp<BR></STRONG>Forward的path属性指向的jsp页面不存在，请检查路径和模块，对于同一模块中的Action转向，path中不应包含模块名；模块间转向，记住使用contextRelative="true"。</P>
<P><STRONG>8、没有任何异常信息，显示空白页面</STRONG><BR>可能是Action里使用的forward与struts-config.xml里定义的forward名称不匹配。<BR></P>
<P><STRONG>9、“The element type "XXX" must be terminated by the matching end-tag "XXX".”<BR></STRONG>这个是struts-config.xml文件的格式错误，仔细检查它是否是良构的xml文件，关于xml文件的格式这里就不赘述了。</P>
<P><STRONG>10、“Servlet.init() for servlet action threw exception”</STRONG><BR>一般出现这种异常在后面会显示一个关于ActionServlet的异常堆栈信息，其中指出了异常具体出现在代码的哪一行。我曾经遇到的一次提示如下：</P>
<DIV style="BORDER-RIGHT: windowtext 0.5pt solid; PADDING-RIGHT: 5.4pt; BORDER-TOP: windowtext 0.5pt solid; PADDING-LEFT: 5.4pt; BACKGROUND: #e6e6e6; PADDING-BOTTOM: 4px; BORDER-LEFT: windowtext 0.5pt solid; WIDTH: 98%; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">
<DIV><IMG src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><SPAN style="COLOR: #000000">java.lang.NullPointerException<BR><IMG src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;org.apache.struts.action.ActionServlet.parseModuleConfigFile(ActionServlet.java:</SPAN><SPAN style="COLOR: #000000">1003</SPAN><SPAN style="COLOR: #000000">)<BR><IMG src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;at&nbsp;org.apache.struts.action.ActionServlet.initModuleConfig(ActionServlet.java:</SPAN><SPAN style="COLOR: #000000">955</SPAN><SPAN style="COLOR: #000000">)<BR><IMG src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align=top><IMG src="http://www.cnblogs.com/Images/dot.gif"></SPAN></DIV></DIV>
<P><BR>为解决问题，先下载struts的源码包，然后在ActionServlet.java的第1003行插入断点，并对各变量进行监视。很丢人，我竟然把struts-config.xml文件弄丢了，因此出现了上面的异常，应该是和CVS同步时不小心删除的。</P>
<P><STRONG>11、“Resources not defined for Validator”</STRONG><BR>这个是利用Validator插件做验证时可能出现的异常，这时你要检查validation.xml文件，看里面使用的资源是否确实有定义，form的名称是否正确，等等。</P>
<P>上面这些是我在用Struts做项目时遇到过的问题，其中一些曾困绕我不少时间，其实大部分都是自己不细心造成的。希望这篇文章能对你的开发有所帮助，并欢迎继续补充。</P><img src ="http://www.blogjava.net/qq13367612/aggbug/16541.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-24 10:21 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/24/16541.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用 Java 实现回调例程</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/23/16495.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Sun, 23 Oct 2005 13:07:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/23/16495.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16495.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/23/16495.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16495.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16495.html</trackback:ping><description><![CDATA[<SPAN id=ArticleContent1_ArticleContent1_lblContent><FONT color=#000000>熟悉 MS-Windows 和 X Window System 事件驱动编程模型的开发人员，习惯于传递在某种事件发生时调用（即“回调”）的函数指针。Java 的面向对象模型目前并不支持方法指针，这样似乎就不可能使用这种很好的机制。但我们并不是一点办法都没有！ <BR>Java 的接口支持提供了一种获得回调的等价功能的机制。其技巧就是：定义一个简单接口，并在该接口中声明我们要调用的方法。 <BR><BR>例如，假定我们希望在某个事件发生时得到通知。我们可以定义一个接口： <BR><BR>public interface InterestingEvent<BR>{<BR>// 这仅是一个常规方法。因此如果需要，<BR>// 它可有返回值，也可接收参数。<BR>public void interestingEvent ();<BR>}<BR><BR><BR>这使得我们可以控制实现该接口的类的任何对象。因此，我们不必关心任何外部类型信息。与在将 C++ 代码用于 Motif 时使用窗口小部件的数据域来容纳对象指针的难以控制的 C 函数相比，这种方法要好得多。 <BR><BR>发出事件信号的类必须等待实现了 InterestingEvent 接口的对象，并在适当时候调用 interestingEvent() 方法。 <BR><BR>public class EventNotifier<BR>{<BR>private InterestingEvent ie;<BR>private boolean somethingHappened;<BR><BR>public EventNotifier (InterestingEvent event)<BR>{<BR>// 保存事件对象以备后用。<BR>ie = event;<BR><BR>// 还没有要报告的事件。<BR>somethingHappened = false;<BR>}<BR><BR>//... <BR><BR>public void doWork ()<BR>{<BR>// 检查在别处设置的谓词。<BR>if (somethingHappened)<BR>{<BR>// 通过调用接口的这个方法发出事件信号。<BR>ie.interestingEvent ();<BR>}<BR>//...<BR>}<BR><BR>// ...<BR>}<BR><BR><BR>在上例中，我使用 somethingHappened 谓词来跟踪是否应触发事件。在许多情况下，调用此方法足以保证向 interestingEvent() 发出信号。 <BR><BR>希望接收事件通知的代码必须实现 InterestingEvent 接口，并将自身引用传递给事件通知程序。 <BR><BR>public class CallMe implements InterestingEvent<BR>{<BR>private EventNotifier en;<BR><BR>public CallMe ()<BR>{<BR>// 创建事件通知程序，并将自身引用传递给它。<BR>en = new EventNotifier (this);<BR>}<BR><BR>// 为事件定义实际的处理程序。<BR>public void interestingEvent ()<BR>{<BR>// 噢！必定发生了感兴趣的事件！<BR>// 执行某些操作 ...<BR>}<BR><BR>//...<BR>}<BR></FONT></SPAN><img src ="http://www.blogjava.net/qq13367612/aggbug/16495.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-23 21:07 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/23/16495.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>获取用户需求的十大沟通技巧 </title><link>http://www.blogjava.net/qq13367612/archive/2005/10/23/16494.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Sun, 23 Oct 2005 13:03:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/23/16494.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16494.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/23/16494.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16494.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16494.html</trackback:ping><description><![CDATA[<FONT color=#000000 size=2>成功的软件产品是建立在成功的需求基础之上的，而高质量的需求来源于用户与开发人员之间有效的沟通与合作。当用户有一个问题可以用计算机系统来解决，而开发人员开始帮助用户解决这个问题，沟通就开始了。 </FONT>
<P><FONT color=#000000 size=2>　　需求获取可能是软件开发中最困难、最关键、最易出错及最需要沟通交流的活动。对需求的获取往往有错误的认识：用户知道需求是什么，我们所要做的 就是和他们交谈从他们那里得到需求，只要问用户系统的目标特征，什么是要完成的，什么样的系统能适合商业需要就可以了，但是实际上需求获取并不是想象的这 样简单，这条沟通之路布满了荆棘。首先需求获取要定义问题范围，系统的边界往往是很难明确的，用户不了解技术实现的细节，这样造成了系统目标的混淆。 </FONT></P>
<P><FONT color=#000000 size=2>　　其次是对问题的理解，用户对计算机系统的能力和限制缺乏了解，任何一个系统都会有很多的用户或者不同类型的用户，每个用户只知道自己需要的系 统，而不知道系统的整体情况，他们不知道系统作为一个整体怎么样工作效率更好，也不太清楚那些工作可以交给软件完成，他们不清楚需求是什么，或者说如何以 一种精确的方式来描述需求，他们需要开发人员的协助和指导，但是用户与开发人员之间的交流很容易出现障碍，忽略了那些被认为是"很明确的信息。最后是需求 的确认，因为需求的不稳定性往往随着时间的推移产生变动，使之难以确认。为了克服以上的问题，必须有组织的执行需求的获取活动。 </FONT></P>
<P><FONT color=#000000 size=2>　　需求获取活动建议要完成的11个任务或者说步骤分别是确定需求过程、编写项目视图和范围文档、用户群分类、选择用户代表、选择用户代表、建立核 心队伍、确定使用实例、召开联合会议、分析用户工作流程、确定质量属性、检查问题报告和需求重用。当然应该根据组织和项目的具体情况进行适当的裁减，比如 根据项目和用户情况把需求获取会议改成问卷调查或者座谈等等。 </FONT></P>
<P><FONT color=#000000 size=2>　　1、编写项目视图和范围文档 </FONT></P>
<P><FONT color=#000000 size=2>　　系统的需求包括四个不同的层次：业务需求、用户需求和功能需求、非功能性需求。业务需求说明了提供给用户新系统的最初利益，反映了组织机构或用 户对系统、产品高层次的目标要求，它们在项目视图与范围文档中予以说明。用户需求文档描述了用户使用产品必须要完成的任务，这在使用实例文档或方案脚本说 明中予以说明。功能需求定义了开发人员必须实现的软件功能，使得用户能完成他们的任务，从而满足了业务需求。 </FONT></P>
<P><FONT color=#000000 size=2>　　非功能性需求是用户对系统良好运作提出的期望，包括了易用性、反应速度、容错性、健壮性等等质量属性。需求获取就是根据系统业务需求去获得系统 用户需求，然后通过需求分析得到系统的功能需求和非功能需求。项目视图和范围文档就是从高层次上描述系统的业务需求，应该包括高层的产品业务目标，评估问 题解决方案的商业和技术可行性，所有的使用实例和功能需求都必须遵从的标准。而范围文档定义了项目产品所包括的所有工作及产生产品所用的过程。项目相关人 员对项目的目标和范围能达成共识，整个项目组都应该把注意力集中在项目目标和范围上。 </FONT></P>
<P><FONT color=#000000 size=2>　　2、用户群分类 </FONT></P>
<P><FONT color=#000000 size=2>　　系统用户在很多方面存在着差异，例如：使用系统的频度和程度、应用领域和计算机系统知识、所使用的系统特性、所进行的业务过程、访问权限、地理 上的布局以及个人的素质和喜好等等。根据这些差异，你可以把这些不同的用户分成不同的用户类。与UML中Usecase的Actor概念一样，用户类不一 定都指人，也可以包括其他应用系统、接口或者硬件，这样做使得与系统边界外的接口也成为系统需求。将用户群分类并归纳各自特点，并详细描述出它们的个性特 点及任务状况，将有助于需求的获取和系统设计。 </FONT></P>
<P><FONT color=#000000 size=2>　　3、选择用户代表 </FONT></P>
<P><FONT color=#000000 size=2>　　不可能对所有的用户都进行需求获取，这样做时间不允许效果也不一定好，所以要识别出能够确定需求和了解业务流程的用户作为每类用户的代表。每类 用户至少选择一位能真正代表他们需求的人作为代表并且能够作出决策，用户代表往往是本类用户中三类人：对项目有决定权的领导、熟悉业务流程的专家、系统最 终用户。 </FONT></P>
<P><FONT color=#000000 size=2>　　每一个用户代表者代表了一个特定的用户类，并在那个用户类和开发者之间充当主要的接口，用户代表从他们所代表的用户类中收集需求信息，同时每个用户代表又负责协调他们所代表的用户在需求表达上的不一致性和不兼容性。 </FONT></P>
<P><FONT color=#000000 size=2>　　4、建立核心队伍 </FONT></P>
<P><FONT color=#000000 size=2>　　通常用户和开发人员不自觉的都有一种"我们和他们"的想法，产生一种对立关系，把彼此放在对立面，每一方都定义自己的"边界"，只想自己的利益 而忽略对方的想法。他们通过文档、记录和对话来沟通，而不是作为一个合作的整体去识别和确定需求完成任务。实践证明这样的方法是不正确的，不会给双方带来 一点益处，良好的沟通关系没有建立导致了误解和忽略重要的信息。只有当双方参与者都明白要成功自己需要什么，同时也知道要成功对方需要什么时，才能建立起 一种合作关系。 </FONT></P>
<P><FONT color=#000000 size=2>　　为了建立合作关系通常采取一种组队的方式来获取需求，建立一个由用户代表和开发人员组成的联合小组作为需求获取的核心队伍。联合小组将负责识别 需求、分析解决方案和协商分歧，小组成员可以采用会议、电子邮件、综合办公系统等方式进行交流，但交流时应注意以下原则：小组会议应该由中立方来组织和主 持，用户和开发人员都要参加；交流预先要确定准备和参与的规则；议题要明确并覆盖所有关键点，但信息来源应该自由；交流目标要明确，并告知所有的成员。 </FONT></P>
<P><FONT color=#000000 size=2>　　5、确定使用实例 </FONT></P>
<P><FONT color=#000000 size=2>　　从用户代表处收集他们将使用系统完成所需任务的描述，讨论用户与系统间的交互方式和对话要求，这就是使用实例，一个单一的使用实例可能包括完成 某项任务的许多逻辑相关任务和交互顺序。使用实例方法给需求获取带来的好处来自于该方法是用以任务为中心和以用户为中心的观点，比起使用以功能为中心和以 开发者为中心的方法，使用实例方法可以使用户更清楚地理解和认识到新系统允许他们做什么和怎么做。描写使用实例的时候要注意使用简洁直白的表述，尽量使用 主动语态，"系统"或者"用户"作为主语，比如"用户提交用户密码，系统验证用户密码是否正确"，还有一点在描述中不要设计界面细节，比如"用户从下拉框 中选择产品类型".使用实例为以后写用例场景描述中的基本路径和扩展路径提供了素材。 </FONT></P>
<P><FONT color=#000000 size=2>　　6、召开联合会议 </FONT></P>
<P><FONT color=#000000 size=2>　　最常见的需求获取方法是召开会议或者面谈，联合会议是范围广的、简便的讨论会，也是核心队伍成员之间一种很好的沟通方法，该会议通过紧密而集中 的讨论得以将用户代表与开发人员间的合作伙伴关系付诸于实践并能由此拟出需求文档的底稿。联合会议的第一个议题就是系统的必要性和合理性，必须所有成员都 同意系统是必要的而且合理的。接下来就可以讨论使用实例清单，清单可以打印成大纸挂在墙上、写在黑板上或做成演示材料。对每个清单合并去掉重复项，加上补 充内容就可以得到一份总的清单，注意避免采用负面的"太差""不可行"去否定用户的想法，这些想法都应该保留下来作为被评议的清单项，这样保护了小组成员 开放的思维。最后对清单进行讨论，会议成员必须检查每一个使用实例，在把它们纳入需求之前决定其是否在项目所定义的范围内，形成最终的需求报告。 </FONT></P>
<P><FONT color=#000000 size=2>　　在进行讨论时，也应该避免受不成熟的细节的影响，在对系统需求取得共识之前，用户能很容易地在一个报表或对话框中列出某些精确设计，如果这些细 节都作为需求记录下来，他们会给随后的设计过程带来不必要的限制，应确保用户参与者将注意力集中在与所讨论的话题适合的抽象层上，重点就是讨论做什么而不 是怎么做。这里有一点很重要就是要让用户理解对于某些功能的讨论并不意味着即将在系统中实现它，更不要做暗示或者承诺什么时候完成需求。在讨论之后，记下 所讨论的条目，并请参与讨论的用户评论并更正，因为只有提供需求的人才能确定是否真正获取需求。当最后拿到了一份详细准确的需求报告书的时候，会议就算成 功完成了。但是要清楚需求过程本身就是一个迭代的过程，在以后的过程活动中不可避免的将要修改和完善这份报告。 </FONT></P>
<P><FONT color=#000000 size=2>　　7、分析用户工作流程 </FONT></P>
<P><FONT color=#000000 size=2>　　分析用户工作流程观察用户执行业务任务的过程，通过分析使用实例得到系统的用例图。编制用例图文档将有助于明确系统的使用实例和功能需求，统一 建模语言的使用有助于与用户进一步交流。每个用例的描述应包括：编号，为每个用例分配一个唯一的编号，为需求的追溯提供了方便；参与者，与这个用例交互的 actor；前置条件，开始用例前所必须具备的系统状态；后置条件，用例完成后系统达到的状态；基本路径，用例完成的关键路径，也是用户期望的路径；扩展 点，基本路径的分枝，表示意外情况；字段说明，路径中名称的进一步分解说明，对以后类属性的定义和数据库字段设计起作用；设计约束，实现用例的非功能约 束。写基本路径时应该使用主动语句；句子以actor或者系统作为主语；一句表示一个actor动作，一句表示系统动作，交叉表现交互；不要涉及界面细 节，比如"用户在文本框输入名称，下拉框选择类型". </FONT></P>
<P><FONT color=#000000 size=2>　　用例：用户注册，用户注册成为系统会员 </FONT></P>
<P><FONT color=#000000 size=2>　　编号 </FONT></P>
<P><FONT color=#000000 size=2>　　UC1 </FONT></P>
<P><FONT color=#000000 size=2>　　参与者 用户 </FONT></P>
<P><FONT color=#000000 size=2>　　前置条件 </FONT></P>
<P><FONT color=#000000 size=2>　　用户访问系统，系统运行正常 </FONT></P>
<P><FONT color=#000000 size=2>　　后置条件 </FONT></P>
<P><FONT color=#000000 size=2>　　系统记录用户注册信息 </FONT></P>
<P><FONT color=#000000 size=2>　　基本路径 </FONT></P>
<P><FONT color=#000000 size=2>　　1. 用户请求注册。 </FONT></P>
<P><FONT color=#000000 size=2>　　2. 系统显示注册界面。 </FONT></P>
<P><FONT color=#000000 size=2>　　3. 用户提交注册信息。 </FONT></P>
<P><FONT color=#000000 size=2>　　4. 系统验证注册信息是否正确。 </FONT></P>
<P><FONT color=#000000 size=2>　　5. 系统生成用户名和密码，保存注册信息。 </FONT></P>
<P><FONT color=#000000 size=2>　　6. 系统显示"注册成功"信息，进入会员页面。 </FONT></P>
<P><FONT color=#000000 size=2>　　扩展点 </FONT></P>
<P><FONT color=#000000 size=2>　　4a. 用户提供的信息不正确： </FONT></P>
<P><FONT color=#000000 size=2>　　4a1. 系统提示输入正确信息 </FONT></P>
<P><FONT color=#000000 size=2>　　4a2. 返回3 </FONT></P>
<P><FONT color=#000000 size=2>　　补充说明 </FONT></P>
<P><FONT color=#000000 size=2>　　注册信息包括＝用户实名＋电话＋传真＋Email＋联系地址联系地址＝省份＋城市＋街道＋邮编 </FONT></P>
<P><FONT color=#000000 size=2>　　设计约束 </FONT></P>
<P><FONT color=#000000 size=2>　　注册反应时间不能超过3秒 </FONT></P>
<P><FONT color=#000000 size=2>　　8、确定质量属性 </FONT></P>
<P><FONT color=#000000 size=2>　　在功能需求之外再考虑一下非功能的质量特点，以及确定由于特殊的商业应用环境对系统提出的功能或性能上的约束，这会使你的产品达到并超过客户的 期望。对系统如何能很好地执行某些行为或让用户采取某一措施的陈述就是质量属性，这是一种非功能需求。听取那些描述合理特性的意见：快捷、简易、直觉性、 用户友好、健壮性、可靠性、安全性和高效性。你将要和用户一起商讨精确定义他们模糊的和主观言辞的真正含义，并且要将质量属性分配到每个用例的设计约束中 去。 </FONT></P>
<P><FONT color=#000000 size=2>　　9、检查问题报告 </FONT></P>
<P><FONT color=#000000 size=2>　　通过检查当前已经运行系统的问题报告来进一步完善需求客户的问题报告及补充需求为新系统或新版本提供了大量丰富的改进及增加特性的想法，负责提供用户支持及帮助的人能为收集需求过程提供极有价值的信息。 </FONT></P>
<P><FONT color=#000000 size=2>　　10、需求重用 </FONT></P>
<P><FONT color=#000000 size=2>　　如果客户要求的功能与已有的系统很相似，则可查看需求是否有足够的灵活性以允许重用一些已有的软件组件。业务建模和领域建模式需求重用的最好方法，像分析模式和设计模式一样，需求也有自己的模式。</FONT></P><img src ="http://www.blogjava.net/qq13367612/aggbug/16494.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-23 21:03 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/23/16494.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Software Engineer -2006 Graduate Intake（HP2006校园招聘）</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/23/16488.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Sun, 23 Oct 2005 10:42:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/23/16488.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16488.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/23/16488.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16488.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16488.html</trackback:ping><description><![CDATA[<P><STRONG>JOB DISCRIPTION： </STRONG></P>
<P>hp invents. <BR>We never stand still. <BR>We look for new things to do, and new ways to do things we've done before. <BR>Excellence is the minimum standard acceptable. <BR>Job Description <BR>The software engineer designs, develops and tests. The software engineer works as a member of an engineering team developing, designing, and maintaining one or more of our products. This position reports to the appropriate project manager. The software engineer: <BR>-Responsible for the quality and completeness of assignments, including investigation tasks, initial design, development, bug fixing, quality engineering, documentation, and general product maintenance. <BR>-Recognizes and points out design problems during the project-planning process. <BR>-Resolves in-process problems independently, or with minor input from the project manager. <BR>-Responds to key product questions and customer needs, and ports products to new operating systems and compilers on a regular basis. <BR>-Reviews new product or new release proposals, and provides feedback to engineering on product features and project schedules. <BR>Your Profile <BR>-Bachelor/Master Candidate in Computer Science. <BR>-Good English both in oral and written, CET-6 is required. English presentation skill is a plus <BR>-Understand Intranet and Internet technologies: HTTP, firewall, Java, C++, HTML/DHTML, XML, middleware, etc. <BR>-Experience with C++ and Java programming & testing. <BR>-Experience with Data Warehouse, Business Intelligence, SAP implementation, SAP support, SAP Business Warehouse, Content Management, BOSS is highly preferred <BR>-Process a very strong understanding of the full Software Development Life Cycle (SDLC) and methodologies. <BR>-Excellent team player with strong verbal and written communication skills. <BR>-Integrity, attention to detail, timeline sensitive, goal oriented, motivator <BR></P>
<P>密切关注 & Qualify myself for this Co.</P><img src ="http://www.blogjava.net/qq13367612/aggbug/16488.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-23 18:42 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/23/16488.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java企业系统架构选择考量</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/21/16252.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 21 Oct 2005 03:45:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/21/16252.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16252.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/21/16252.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16252.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16252.html</trackback:ping><description><![CDATA[现在Java领域各种技术百花齐放，名目繁多，如何根据自己的需求选择这些框架呢？特别对于初学者，在学习选择方向上也非常迷茫，如何有针对性的根据自己项目特点进行学习就变的更加重要。
<P>　　下面我们从一个发展角度来对J2EE/Java EE的这些框架诞生进行一番考量，可能对我们的选择有很大帮助。</P>
<P>　　首先我们需要明白一个<A href="http://www.jdon.com/idea/j2eebasic.htm" target=_blank>高质量的J2EE系统是什么样子</A>？高质量的J2EE/Java EE系统标准实际就是OO设计的标准，松耦合是OO设计的主要追求目标之一，那么无疑解耦性成为衡量J2EE/JEE质量的首要标准。实际选择中，还需要兼顾可伸缩性/性能/开发效率等方面综合考虑。</P>
<P>　　J2EE/Java EE号称多层结构，为什么多层比两层好？因为多层结构解耦性好，带来维护拓展方便灵活。</P>
<P>　　典型的J2EE/Java EE至少划分三个层次：表现层/业务逻辑组件层/持久层。</P>
<P>　　如图，表现层英文是Presentation Layer，是实现显示功能的，这部分一般使用B/S结构来完成，当然你也可以使用专门远程客户端来实现;</P>
<P>　　业务逻辑层因为是由大量组件(Components)组成的，也可称为组件层，组件从不同角度又可分为各种类型，然后又有不同的流派，目前占主要位置的是Model+Service，模型加服务，所以这一层又称为业务服务层Business Service；也有称为Model业务层；</P>
<P>　　持久层是负责对象持久化也就是数据库操作的层次,英文Persistence Layer。 </P>
<P align=center><IMG height=406 src="http://www.jdon.com/jdonframework/images/architecture.jpg" width=500></P>
<P>　　SUN伙伴们推出J2EE标准时，分别对这三个层次规定了标准实现，表现层使用Jsp/Servlet技术；业务组件层使用EJB的会话Bean；持久层使用实体Bean。同时，标准将业务层和持久层在物理上组成一个新的容器EJB容器，与表现层技术完全一样的容器，这样，J2EE技术被细化为Web和EJB，物理上有Web容器和Web应用程序；以及EJB容器和EJB应用程序。</P>
<P>　　当然，J2EE/JEE的发展不止这些，这三个层次技术分别独立发展，高歌猛进，下面分别单独陈述，当你了解某种框架技术为什么诞生时，你可能就知道你该在什么情况下选择它们了，工具总是因目的而生！</P>
<P align=center><STRONG>表现层框架</STRONG></P>
<P>　　J2EE/Java EE虽是多层结构，但它不是一种强制性多层结构，也就是说，你也可能做成传统两层结构，有的初学者直接使用Jsp嵌入Java代码调用数据库这样结构实际不是多层结构，还是以前的两层结构。</P>
<P>　　在Jsp中嵌入大量代码，一旦报空指针错误就很难找出问题，很多初学者下载JiveJdon 2.5后就经常碰到这个问题，因此大量有关空指针错误问题出现论坛里，为什么他们不能自己解决呢？ 那是因为无法定位错误在Jsp中的位置，Tomcat等服务器只告诉我们错误在index_jsp.java(Jsp的java文件)位置，搞得一些人经常会跑到Tomcat服务器内部翻找Jsp的Java文件，这一过程无比痛苦（为了减少初学者这种痛苦，本站暂停了JiveJdon2.5的下载）。</P>
<P>　　J2EE/Java EE的发展就是降低这种痛苦，首先想到的方式是：在Jsp调试上下苦功，要求Tomcat等服务器提供详细的错误定位；可惜到Tomcat 5.5我们也没看到这种功能；实际上，根本解决之道就是将Jsp的调试变成java组件的调试。</P>
<P>　　首先通过MVC模式规定Jsp只能等同于Html，不能包含Java代码，将Jsp和Java代码分离，可是这样分离之后，它们结合起来又麻烦了，所以，虽然你使用MVC模式，但是还是直接基于Jsp技术，带来的是开发效率的降低。</P>
<P>　　Struts框架解决了这个问题，通过ActionForm可以将Jsp和JavaBeans方便快速地结合起来，但是有人又抱怨Struts的ActionForm限制太死，与Jsp虽能对应，只能一个ActionForm一个表单对应，而不能任意组件JavaBeans都可以和Jsp任意字段对应，这时就出来了组件型框架JSF/Tapestry。</P>
<P><A href="http://www.jdon.com/artichect/sjt.htm" target=_blank>　　表现层框架Struts/Tapestry/JSF架构比较</A></P>
<P align=center><STRONG>业务逻辑层框架</STRONG></P>
<P><STRONG>可伸缩性</STRONG></P>
<P>　　因为EJB标准的推出，业务组件层以前基本是EJB的天下，但是EJB功能实在太强大，它考虑了世界顶级大型系统需求，因此免不了显得很复杂，当初，基本上所有的大型企业高端都是选用J2EE，选用J2EE实际是选用EJB。EJB强调的高可伸缩性为大型企业日益发展提供最大的发展空间，不再因为企业快速发展导致整个企业系统结构都要发生根本变化,这是使用EJB的现实优势。</P>
<P>　　这种企业系统的可伸缩性不是因为EJB存在才显得重要，而是我们企业架构选择需要考量的基本因素。换句话说，无论我们使用EJB与否，这个问题都需要考虑:一台服务器不够用怎么办？如果这台服务器死机会对企业运营带来什么影响？如果每个星期这台服务器停机维护升级会不会对企业带来打击？我的企业系统是不是需要可靠的、几乎不当机的7x24运行？当企业系统面对大量外部访问用户时，一台服务器是否够用？多台服务器联动的需求是否涉及软件技术更换？</P>
<P>　　在这个现实因素考量后，如果觉得不是很重要，或者说将来一段时间内这些因素无所谓，那么可以不选用EJB了。</P>
<P>　　为什么有不选用EJB的理由？因为它复杂，学习起来比较困难，EJB帮助我们考虑企业中可能碰到的大部分问题，实际上，有的我们不需要，这也就是为什么说EJB是一个重量级解决方案所在。</P>
<P>　　与重量级相比的是轻量，业务组件层轻量级解决方案有Spring/HiveMidn/<A href="http://www.jdon.com/jdonframework/" target=_blank>Jdon Framework</A>等，轻量一词曾经因为EJB的出现而变得时髦，给人造成的感觉是：EJB花了老鼻子力气完成的那些功能，使用我轻量级解决方案可以轻而易举也能解决，举重若轻啊，其实这是一种误解。</P>
<P>　　当曾经以轻量自居的Spring将事务机制纳入自己怀抱中时，它也开始低调轻量了，实际是不轻不重了；当然如果它把分布式计算和事务再次加入时，天平砝码也会沉下去的。</P>
<P>　　初学者总是愿意使用简单的解决方案，学习使用方便，因此比较喜欢轻量级框架技术，这是正常的，但是在使用轻量级别框架之前必须明白：你的系统将来真的只需要一台服务器即可？你的项目完成期真的非常紧急？</P>
<P>　　如果只需要一台巨强服务器，就不必选择EJB，EJB是因分布式多服务器而生，对于单台服务器，缺乏本地透明性，也就是说：你无法透过EJB直接和本地JVM或文件系统等打交道，透明性也是衡量一个框架的重要指标。</P>
<P>　　当然，重量和轻量并不完全对立，EJB3就是为了简化J2EE的使用，使得EJB不只是擅长处理大型企业系统，中小型系统也使用很方便，这实际上也是EJB轻量化的一种努力。<A href="http://www.jdon.com/artichect/JavaEE.htm" target=_blank>什么是Java EE 5</A>？</P>
<P>　　所以，对于架构选择来说，根本前提是需要明白你的系统现在或将来一段时间（注意需要考虑将来一段时间，不能只看眼前）是中小型系统还是大型系统？</P>
<P><STRONG>灵活性/定制性/透明性</STRONG></P>
<P>　　当然这个答案有时我们自己也可能很难给出，那么我们还需要从其他角度来考量EJB和非EJB之选，例如笔者曾经经历一个大型实时娱乐平台社区系统，从规模上说肯定是大型系统，设计目标10万人在线，从这个角度非选用EJB不可！</P>
<P>　　但是，EJB因为还有事务机制，虽然应用程序可以选择失效EJB事务，但是EJB容器设计因为考虑了事务，所以在其内核上总是会显得臃肿，是一种累赘，这也是一种重量表示，不需要的东西存在肯定会影响效率，那么难道我不能根据我的需求，对EJB整体包括EJB容器进行可配置式的切割？也就是说，我上面这个案例只需要EJB的分布式计算功能，其他的功能我都拆除，只剩余我需要的功能能运行就可以了，轻装上阵。</P>
<P>　　可惜，这种任意切割，应需而定的目标在EJB3标准还没有被重视，所幸的是，Ioc/AOP技术为这种目标实现提供了实现可能，但是，只有Ioc/AOP还是不够，特别是看Ioc的范围，如果你只把应用系统组件纳入Ioc管理时，自由解耦只属于应用系统，我上面案例的目标还是无法达到，当你把框架本身组件也纳入Ioc管理时，任意切割，应需而定的目标才真正实现。</P>
<P>　　Spring和EJB3属于“只把应用系统组件纳入Ioc管理”，这从JBoss 4.0版本可以看出；那什么框架会把框架本身组件也纳入Ioc管理呢？Apache的<A href="http://jakarta.apache.org/hivemind/" target=_blank>Hivemind</A>和笔者开发的<A href="http://www.jdon.com/jdonframework/" target=_blank>Jdon框架</A>。</P>
<P>　　什么样的组件可以被纳入Ioc管理呢？POJO组件，POJO这个概念其实当初是针对EJB缺点而推出，EJB要求应用系统的组件必须继承或依赖EJB容器，这样使得调试变的不方便，但是后来，POJO的概念已经不只最初这些概念，POJO代表那种与周围完全脱离关系、自由自在的Object了，如果应用系统的Model或者Service都是POJO，意味着你的应用系统不依赖任何其他系统，解耦性灵活性高。</P>
<P>　　POJO是因为EJB而提出的，当EJB自己倾向POJO时，大家都在宣称自己是POJO时，POJO概念就没有立足点了，这也是Java领域哭笑不得的现象，但是我个人更倾向把非EJB的JavaBeans组件通称为POJO。</P>
<P>　　<A href="http://jakarta.apache.org/hivemind/" target=_blank>Hivemind</A>的Ioc依赖注射部分功能和<A href="http://www.jdon.com/jdonframework/" target=_blank>Jdon框架</A>非常类似，当上百个POJO组件配置时，完全不必指定它们之间的依赖关系；你可以将自己应用程序的组件注册到Registry中，这样Hivemind将帮助你启动这些组件，正如你在Jdon框架中将你的组件写入mycontainer.xml中，Jdon框架也将自动启动你这些组件，并解决它们之间的互相调用，包括和框架本身组件互动。</P>
<P>　　HiveMind和<A href="http://www.jdon.com/jdonframework/" target=_blank>Jdon框架</A>定义的POJO Service有如下特点：</P>
<P>　　不象EJB那样缺乏本地透明化(location transparency)概念，这些POJO Service总是能定位到同样一个JVM；也不象基于XML的Web服务web services那样没有语言透明(language transparency)概念，这些POJO Service总是可以被一组Java接口来概括替代(通过调用Java interface来替代调用这些具体Service)；当然，更不会类似JMX或Jini，不能进行service的热装载（ hot-loading）。</P>
<P>　　注意：当Ioc/AOP提供高度灵活的同时，已经有初学者开始抱怨Spring的过分灵活，那么比Spring在组件上更灵活的Jdon框架只能算是一种追求极端，它不一定构成你进行架构选择的主要依据！</P>
<P>　　上面这些讨论只是表明在灵活性（解耦性/透明性）这条需求道路上深究下去的选择，你自己的应用系统需求会产生各种不同的要求，有些要求甚至是极致的，这种偏向程度就成为你架构选择的目标，如果你的这种极致要求在目前Java世界里还没有可选框架时，那么你就要动手自己做了，这也说明什么时候你开始自己做框架（如Jdon框架），虽然在大多数情况下我们是不必要自己发明轮子的。</P>
<P><STRONG>快速构建性</STRONG></P>
<P>　　前面是从灵活性和定制性这个角度讨论架构选择目标，但是在一般情况下，我们还是从上手难易、开发效率这个角度来进行架构选择，从这个角度来说，就是需要我们将选用的框架尽可能的多帮助我们实现一些功能，但这又是和使用难易是矛盾的，因此有个取舍问题，取舍有个准则：这个框架尽量能提供越多功能；尽量需要我们少写代码，甚至不写代码（使用XML配置），少动脑筋。</P>
<P>　　关于XML配置这里也涉及难易问题，XML配置语法不能太复杂，有太多小开关Switch也增加学习成本。</P>
<P>　　从这个角度看，EJB无论是EJB2或EJB3提供的功能是最齐全的，但是XML配置开关太多 ，Spring属于中等，组件XML配置不算简单，但是因为有不少Struts+Spring+Hibernate之类现成开源代码可供参考，因此学习起来难度也不大，Spring越来越象一个J2EE API（注意，JDK是J2SE API） ，Spring除不能提供分布式计算外，也因为过分灵活降低了一些开发效率，例如它的组件依赖关系一般需要逐步指定，auotwiring功能还没有深入骨髓成为核心功能。<A href="http://www.jdon.com/AOPdesign/iocimpls.htm" target=_blank>Ioc容器的革命性优点。</A></P>
<P>　　Spring除了提供组件层功能以外，还有表现层支持Spring MVC；也有持久层实现的JDBC模板，这样，整个J2EE/Java EE系统各个层次Spring都提供了缺省实现，在这方面无疑提高了开发效率，但是Spring提供丰富API目的好像不是为了快速开发，而是为了建立一个完整的功能齐全的API功能库。正如它网页上开头文字所述：As the leading <STRONG>full-stack</STRONG> Java/J2EE application framework，注意full-stack（完整齐全）是它突出的名词。</P>
<P>　　那么，还有另外一个空白，就是以开发效率为主要考虑，这类框架除了必须考虑足够灵活性和丰富功能以外，宗旨是为了在一般缺省情况下快速完成一个J2EE/Java EE系统，这非常类似MDA工具了，但是一个完全丧失灵活性和定制性的MDA工具也不是我们欢迎的。</P>
<P>　　<A href="http://www.jdon.com/jdonframework/" target=_blank>Jdon框架</A>的发展目标是为了填补这个空白，相信也会越来越多框架向这个目标迈进，当然不可否认，Spring也可能调转枪头走入这个领域，EJB2/EJB3正依靠JBuilder等这样商业化开发工具向这个领域靠拢，这个发展方向实际是4GL RAD Tools。</P>
<P>　　很多人在快速构建方面也很早进行了探索，涌现出各种工具：<A href="http://www.jdon.com/jive/article.jsp?forum=91&amp;thread=18992" target=_blank>如何构建一个快速业务构件平台？</A> 但是如何把快速构建和构件（组件）灵活性有机结合在一起？它是考验一个业务构件（业务组件）平台好坏的准则。有些构件平台虽然开发迅速，但是对于特殊情况，可供程序员定制透明操作部分不多，很死，典型的是两层结构以前的Delphi，开发很快速，但是无法象J2EE这样深入到系统各个层次进行定制/维护/拓展！</P>
<P><STRONG>业务组件框架对比</STRONG></P>
<TABLE cellSpacing=1 cellPadding=1 width="100%" border=0>
<TBODY>
<TR>
<TD bgColor=#cccccc>
<TABLE width="100%" border=0>
<TBODY>
<TR>
<TD width="16%">&nbsp;</TD>
<TD width="28%">EJB2/EJB3</TD>
<TD width="29%">Spring Framework </TD>
<TD width="27%"><A href="http://www.jdon.com/jdonframework/" target=_blank>Jdon Framework</A> </TD></TR>
<TR bgColor=#ffffff>
<TD align=middle bgColor=#999999>灵活性<BR>(松耦合)</TD>
<TD>EJB3比EJB2更具灵活性，EJB3支持应用系统POJO</TD>
<TD>支持应用系统POJO，框架基础功能不能替换</TD>
<TD>支持应用系统POJO，框架本身可分离配置，定制性更强</TD></TR>
<TR bgColor=#ffffff>
<TD align=middle bgColor=#999999>功能完整性</TD>
<TD>全面，支持异步JMS 分布式事务</TD>
<TD>较为全面。有自己的表现层和持久层模板,可支持异步</TD>
<TD>基本完整，表现层借助Struts实现。有自己简单的持久层模板</TD></TR>
<TR bgColor=#ffffff>
<TD align=middle bgColor=#999999>单台性能</TD>
<TD>一般，批量查询等大数据量业务处理须小心，存在本地不透明缺陷。</TD>
<TD>一般，框架本身组件无性能提升极致，应用程序可配置cache/Pool</TD>
<TD>好，框架本身组件使用缓存提升性能，应用程序可配置cache/Pool，批量查询专门优化，适合实时性并发性要求较高应用</TD></TR>
<TR bgColor=#ffffff>
<TD align=middle bgColor=#999999>可伸缩性</TD>
<TD>
<P>可支持多台服务器分布式计算。 </P></TD>
<TD>不支持，可依靠EJB实现</TD>
<TD>不支持，可依靠EJB实现</TD></TR>
<TR bgColor=#ffffff>
<TD align=middle bgColor=#999999>开发效率</TD>
<TD>学习曲线长,导致熟练掌握难。借助商业开发工具可加快熟练者的开发速度。</TD>
<TD>较为复杂，可挑选只适合自己的功能实现。当组件很多时，需要照顾这些组件之间调用关系。</TD>
<TD>简单快速，表现层编码很少。当组件个数很多时，无需照顾它们之间的调用关系</TD></TR>
<TR bgColor=#ffffff>
<TD align=middle bgColor=#999999>系统规模</TD>
<TD>EJB2适合大型系统;EJB3适合中大型系统</TD>
<TD>适合中小型系统</TD>
<TD>适合小中型系统，建立一个简单的网站系统等，和EJB无缝结合，可借助EJB支持中大型系统</TD></TR>
<TR bgColor=#ffffff>
<TD align=middle bgColor=#999999>重量级别</TD>
<TD>重量，正在减肥</TD>
<TD>轻量偏重，有可能继续增肥</TD>
<TD>最轻量，恪守简单快速原则</TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE>
<P>&nbsp;</P>
<P align=center><STRONG>持久层框架</STRONG></P>
<P>　　持久层框架目前有EJB2/EJB3的实体Bean、Hibernate和各种JDO产品，当然还有直接写SQL语句的JDBC，如iBatis等。</P>
<P>　　持久层框架质量好与坏区分就是是否是O/R Mapping，也就是对象和关系数据库映射，关系数据库需要实现定义好Schema结构；对象因为字段而变的也有一个自己的结构，如何将对象数据自动持久化到数据库中，首先我们得定义两者结构的对应，这实际是数据的元数据定义。</P>
<P>　　因为Hiberante/Toplink/JDO这样O/R Mapping工具帮助你实现对象和数据库转换，克服了对象和数据库阻抗现象，<A href="http://www.jdon.com/jive/article.jsp?forum=16&amp;thread=22244" target=_blank>O/R Mapping总结</A> ，所以才使得我们更多的可以对象方式（从模型Model对象）来考虑Java EE/J2EE系统，可以完全放弃以前那种以数据库为中心的思维方式：<A href="http://www.jdon.com/artichect/dbover.htm" target=_blank>数据库时代的终结。</A></P>
<P>　　所以，是否选用好的持久层框架，取决于你整个团队思维是否彻底OO了，是否需要真正OO，当然，对于一些小型项目，有时我们觉得直接使用JDBC模板反而更加轻松快捷一点，这也是Spring的JDBC模板/iBatis/Jdon的Jdbc模板存在的理由了。</P>
<P>例如新增一个数据表，在Jdon框架只需要下面几行代码即可：</P>
<TABLE cellSpacing=0 cellPadding=1 width="100%" bgColor=#cccccc border=0>
<TBODY>
<TR>
<TD>String sql = "INSERT INTO testuser (userId , name) VALUES (?, ?)";<BR>List queryParams = new ArrayList();<BR>queryParams.add(userTest.getUserId());<BR>queryParams.add(userTest.getName());<BR>jdbcTemp.operate(queryParams,sql);</TD></TR></TBODY></TABLE>
<P>　　这种方式和O/R Mapping兴师动众的多个XML配置和关系映射思考相比，对于习惯SQL语句的程序员反而更加迅速。</P>
<P>　　从以上可以看出，灵活性/快速性/简单性/可伸缩性是我们进行架构选择的主要几个依据，架构选择实际就是在这几个策略之间做一个平衡。当然，还有一个非常重要的因素，因为它不属于某个层次的技术，性能/缓存是必须和上面因素综合考虑的因素。</P>
<P>　　因为性能最初是我们使用计算机的基本原因，别忘记这个根本。</P><img src ="http://www.blogjava.net/qq13367612/aggbug/16252.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-21 11:45 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/21/16252.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于BO的问题[转自http://www.jdon.com],可以来谈谈自己的看法</title><link>http://www.blogjava.net/qq13367612/archive/2005/10/21/16250.html</link><dc:creator>Sung</dc:creator><author>Sung</author><pubDate>Fri, 21 Oct 2005 03:40:00 GMT</pubDate><guid>http://www.blogjava.net/qq13367612/archive/2005/10/21/16250.html</guid><wfw:comment>http://www.blogjava.net/qq13367612/comments/16250.html</wfw:comment><comments>http://www.blogjava.net/qq13367612/archive/2005/10/21/16250.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/qq13367612/comments/commentRss/16250.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/qq13367612/services/trackbacks/16250.html</trackback:ping><description><![CDATA[<P>我们在开发系统时,一般VO（或者是PO）对应的是数据库中的表中的记录，view object是提供给客户端显示用的对象，在业务逻辑部分是BO。在很多情况下，我们把VO或者是PO作为了BO，但是在复杂的业务环境中，这种方式的脆弱性就体现出来了，如果我的业务对象比较复杂（具体来说，比如包含了多个表，多个视图中的数据），就没办法将VO、PO作为BO用了。这时候我们需要专门做一层业务层，通过拼装软干个PO、vo来构造我们需要的BO，以及按一定的业务逻辑处理这些BO，并生成相应的view object提供给客户端。我的疑问是：<BR>1、这方面有没有成熟的架构或者案例？<BR>2、关于BO的构成：<BR>2.1、BO中是否应该既包含业务对象的属性又应该包含业务方法？<BR>2.2、或者是BO只包含业务对象的属性，对BO的所有操作都由业务逻辑对象处理？<BR>2.3、或者BO中包含业务对象的属性以及Retrieve，Update，Create，Delete等操作，而业务逻辑由专门的业务逻辑对象处理？<BR> Re: 关于BO的问题  发表时间: Jul 17, 2005 7:55 AM   回复  <BR> <BR>发表人: banq    发表文章: 5226 / 来　　自: 上海 / 注册时间: 2002-08  <BR>你的疑问正好和这篇文章讨论的一致：</P>
<P>BO中是否应该既包含业务对象的属性又应该包含业务方法？<BR>在Ruby on Rails/Naked Objects精神指引下的域驱动开发框架</P>
<P>>1、这方面有没有成熟的架构或者案例？<BR>你这是SOA架构，是业务层加Domain，现在大量的J2EE都是这种架构</P>
<P>》2、关于BO的构成：<BR>》2.1、BO中是否应该既包含业务对象的属性又应该包含业务方法？<BR>现在SOA架构是不包括业务方法，被称为贫血模型;Naked Object是两者都包括，但这是一个研究方向。<BR>>2.2、或者是BO只包含业务对象的属性，对BO的所有操作都由业务逻辑对象处理？<BR>是的<BR>>2.3、或者BO中包含业务对象的属性以及Retrieve，Update，Create，Delete等操作，而业务逻辑由专门的业务逻辑对象处理？ <BR>一般BO只包括属性，CRUD也属于业务操作了，我认为属性和业务方法分离属于桥模式，分离是为了更灵活的组装，SOA也不违反OO，不过因为OO的"教皇"Martin fowler一句来自感觉的话：感觉不美，贫血模型；我相信不出，在一个分布环境中和一个互联共享环境中，如果把BO拿出来共享，而不是Service，会产生什么样后果。也许“教皇”不是凡人，比你我看得很远，可惜我是倡导“上帝死了”的人。</P>
<P><BR> <BR> </P>
<P> Re: 关于BO的问题  发表时间: Jul 18, 2005 12:46 PM   回复  <BR> <BR>发表人: redlly    发表文章: 42 / 注册时间: 2003-07  <BR>SOA好象是基于服务的架构,能说说和这个具体有什么关系吗? <BR> <BR> </P>
<P> Re: 关于BO的问题  发表时间: Jul 23, 2005 11:01 AM   回复  <BR> <BR>发表人: flyinair2000    发表文章: 10 / 注册时间: 2004-08  <BR>Martin fowler一句来自感觉的话：感觉不美，贫血模型<BR>－－－－－－－－－－－－－－－<BR>这根本不是感觉的话，这样的模型又回到了procedural programming的老路上了。<BR>procedural progrmming 告诉我们“世界就是世界，你就是你”。<BR>oo说“世界就是你，你就是世界”。<BR>现在多了一个SOA这个BUZZWORD，说的却还是“世界就是世界，你就是你”。除了在哲学理论上我可以看出它的“螺旋式上升”的进步以外，其他看不出有什么意义。（以上的“世界”和“你”　差可比拟为“method"和“property")</P>
<P>另外，什么是soa? 谁也搞不清楚。（看最近TSS.NET上的一篇，MS　（PDC?) 2005 大会上谁都讲SOA,谁都说不清。但是已经有architect宣称SOA是类似于oo　的paradigm shift了，搞笑不搞笑？）<BR>近来如果可以算作paradigm的话，个人认为恐怕要数AOP为代表的方面编程了。 <BR> <BR> </P>
<P> Re: 关于BO的问题  发表时间: Sep 9, 2005 8:30 AM   回复  <BR> <BR>发表人: Kyle_Yin    发表文章: 26 / 注册时间: 2005-09  <BR>"这根本不是感觉的话，这样的模型又回到了procedural programming的老路上了。"</P>
<P>除了OOP还是PROCEDURAL的编程风格之外, 其实这里有令一个重要因素大家没有提及: 性能. </P>
<P>很多大系统, 至少我听说过(看过DIAGRAM)的和亲眼见过(CODE)的系统(呵呵, 其实也没几个), 大多是尽可能采用无状态设计. 无状态的组件界面看上去当然象PROCEDURAL函数. 这样做的原因, 主要是为了性能. 在分布计算环境里, "状态"往往意味着"地点亲合", "地点亲合"往往意味着"性能瓶颈". </P>
<P>我发现直白英文翻译成中文马上就发酸了. </P>
<P> <BR> </P>
<P> Re: 关于BO的问题  发表时间: Jul 23, 2005 11:07 AM   回复  <BR> <BR>发表人: flyinair2000    发表文章: 10 / 注册时间: 2004-08  <BR>或者是BO只包含业务对象的属性，对BO的所有操作都由业务逻辑对象处理？<BR>------------<BR>至少我见过的所有经典j2ee书中都是讲要包含逻辑处理的。<BR>service只是一层wrapper而已，不包含太多的逻辑。 <BR> <BR> </P>
<P> Re: 关于BO的问题  发表时间: Jul 24, 2005 12:13 PM   回复  <BR> <BR>发表人: redlly    发表文章: 42 / 注册时间: 2003-07  <BR>> 至少我见过的所有经典j2ee书中都是讲要包含逻辑处理的。<BR>> service只是一层wrapper而已，不包含太多的逻辑。</P>
<P>我们知道，在特定的领域中，如果不发生革命性的变化，一般来说这个领域的核心数据基本是稳定的。也就是说我们可以抽象出特定领域的稳定数据模型；而在领域中的业务逻辑的变动是比较大的，是难以被固化的。最典型的案例莫过于移动、连通的资费吧，今天一个优惠，明天一个酬宾，业务逻辑可谓是变化五穷，但是它们的基本数据基本还是保持不变，如：资费、时长等等。<BR>这样来说如果在BO中即包含业务对象的属性又包含业务逻辑，在业务需要变动的时候将不可避免的对BO进行修改，更可怕的是，在业务逻辑比较烦多复杂的时候出现包括数十数百个方法，数千上万行代码的巨无霸BO的出现。这跟对象间解耦、分离的理念是严重背离的。</P>
<P><BR> <BR> </P>
<P> Re: 关于BO的问题  发表时间: Jul 24, 2005 12:21 PM   回复  <BR> <BR>发表人: redlly    发表文章: 42 / 注册时间: 2003-07  <BR>对于SOA（基于服务的架构），我也查过不少资料，包括M$、IBM、BEA等等。但是给我的感觉是SOA只是一种理念，好像并没有一个统一的标准，或者说没有一个可以拿来做实例的案例架构，不像OO、DAO等设计模式。大多数厂商的对SOA的解释也不同、解决方案更是像在做广告，让人越看越迷糊。谁能详细的阐述一下SOA，或者用几句话概括一下SOA。 <BR> <BR> </P>
<P> Re: 关于BO的问题  发表时间: Jul 24, 2005 12:42 PM   回复  <BR> <BR>发表人: banq    发表文章: 5226 / 来　　自: 上海 / 注册时间: 2002-08  <BR>SOA有两者含义：一种是你说的SUN等公司提出那种理念，这更多是一种商业概念；还有一种是J2EE的开发模式的概念，见老外这篇文章可能符合我们这个话题的讨论：<BR><A href="http://dotavery.com/blog/archive/2004/01/04/215.aspx">http://dotavery.com/blog/archive/2004/01/04/215.aspx</A><BR>在这个话题中，SOA就是将Model中的业务逻辑分离出来，单独形成一个服务，我们编程时就是面向这些服务编程了，当然原理的模型因为被抽取主要行为，变成了贫血模型了；但是我认为这符合GOF的桥模式。所以SOA并不是一种反设计的架构。</P>
<P> <BR> </P>
<P> Re: 关于BO的问题  发表时间: Jul 26, 2005 2:34 PM   回复  <BR> <BR>发表人: flyinair2000    发表文章: 10 / 注册时间: 2004-08  <BR>有很多时候，并不是不能用OO解决问题，而是我们没有能力应用OO而已。<BR>至少，对于你讲的资费问题，并不是没有OO的解决办法。<BR>加上一个service layer，并没有自动解决系统解耦问题，（相反增加了耦合程度。为什么？我也不想说了。－－知道的自然知道，不知道的争论半天，也不会有什么结果。国内论坛，抬杠骂架的居多，不想多说）<BR>只是给一个原始的procedural　programming 加上一个漂亮的wrapper而已。</P>
<P>总之，各人仁者见仁，智者见智吧。 <BR> <BR> </P>
<P> Re: 关于BO的问题  发表时间: Jul 29, 2005 11:08 AM   回复  <BR> <BR>发表人: redlly    发表文章: 42 / 注册时间: 2003-07  <BR>看了不少资料，关于BO（bussiness object）有不少种概念，主要有如下三种：<BR>1、只包含业务对象的属性；<BR>2、只包含业务方法；<BR>3、两者都包含。<BR>呵呵，看起来是仁者见仁了。bang的看法应该是第一种，如果是这样我还是比较偏向于bang的看法。其实怎么定义并不重要，这些东西原本就已经在各种项目、各种模式中存在，只是叫法不同。重要的是要找到一种适合自己项目的模式。 <BR> <BR> </P>
<P> Re: 关于BO的问题  发表时间: Aug 25, 2005 6:52 PM   回复  <BR> <BR>发表人: billywxy    发表文章: 14 / 注册时间: 2003-11  <BR>看了不少资料，关于BO（bussiness object）有不少种概念，主要有如下三种：<BR>1、只包含业务对象的属性；<BR>2、只包含业务方法；<BR>3、两者都包含。</P>
<P><BR>我一直使用第三种 <BR> <BR> </P>
<P> Re: 关于BO的问题  发表时间: Sep 9, 2005 8:02 AM   回复  <BR> <BR>发表人: Kyle_Yin    发表文章: 26 / 注册时间: 2005-09  <BR>"加上一个service layer，并没有自动解决系统解耦问题"-----所以会有ESB一类的东西, 呵呵. </P>
<P>关于SOA, 推荐一个网站 <A href="http://www.soacentral.com">www.soacentral.com</A>, 这是BEA, 微软发起的一个consortium. 上面有SOA蓝图和BEA, J2EE 和微软的参考实现. </P>
<P>SOA和OO不是一个层面的东西. OO的适用范围是偏向系统底层的, 比如编程, 构件设计. SOA是偏向宏观的, 比如集成, 整合, 工作流. </P>
<P>SOA的一个成型的例子是WSRP. 这里的SERVICE是一个PORTLET, 而服务客户是PORTAL. 与传统PORTAL不同的是这个PORTLET不是本地的, 而是基于WEB SERVICES的, 并且可以通过ESB解偶. 当然, WSRP有它的特殊性, 因为整合的地点是在最终用户面前, 呵呵. <BR> <BR> </P>
<P> Re: 关于BO的问题  发表时间: Aug 15, 2005 11:14 AM   回复  <BR> <BR>发表人: kylix_xp    发表文章: 1 / 注册时间: 2005-08  </P>
<P>我觉得这不是个问题,bo没有了操作方法,还叫面向对象?比如说银行帐号Account对象,存款deposit(),取款withdraw()操作不属于Account对象，难道还属于别的对象？这不是很自然吗？<BR>见 RUP统一软件过程<BR><A href="http://www-128.ibm.com/developerworks/cn/rational/rationaledge/content/mar05/5383/?ca=dwcn-newsletter-rational#N10098">http://www-128.ibm.com/developerworks/cn/rational/rationaledge/content/mar05/5383/?ca=dwcn-newsletter-rational#N10098</A></P>
<P> </P>
<P>对每个用例</P>
<P><BR>建立一个用例实现 </P>
<P><BR>补充用例描述（如果需要的话） </P>
<P><BR>从用例行为中，找出分析类 <BR>如果我们严格按照RUP过程进行，下一步应该是：</P>
<P>把行为分配给分析类 <BR>基于以下理由，这一次，我又会对RUP过程做一点小的改动：回顾一下我们的进展。我们刚刚找到了8个实体类，我们认为这些都是我们系统中的类。在我们做下一步之前，我们需要给这8个实体类增加一些内容，来确认他们是类。</P>
<P>有三种基本的方法来充实我们的分析类：</P>
<P>数据驱动的方法</P>
<P><BR>行为驱动的方法，和 </P>
<P><BR>职责驱动的方法。</P>
<P><BR>数据驱动的方法对于从事数据库工作，或者从事过程语言编程的人员来说很常见。他们就是用数据和数据之间的关系来认识、描述世界的，因此会首先去寻找类中的数据－一般没有什么标准的方法去寻找类的函数或功能。这看起来不错，[b]但是数据只是工作的一半。实际上， 类的准确定义，包括了数据和对数据进行的操作。[/b]</P>
<P>行为驱动的方法有着双重的成立理由。首先找出类执行的操作，从中决定这些操作涉及的数据中，哪些应该被这个类所拥有。这很棒，但是我们如何确认我们为类找出的操作之间能够保持一致呢？如何区分操作和类，以明确这个操作是属于这个类，但是 其它的操作要属于同一个类，还是其它的类？我们需要某种方法来区分各个操作。这就是职责驱动的方法带给我们的。</P>
<P>职责驱动的方法是自上而下的，从总体的类及其职责的视图开始。首先定义一个类在业务中的“使命”，这个“使命”描述了这个类对外提供的服务。从军事术语上来说，就是责任和策略。操作和数据是战术层面上的（为战略服务的，服从于某些目标、策略的）。 2 </P>
<P>一旦我们确定了我们的类的职责，我们就可以设计一个分析类图，来描述类间关系的整体结构。这个结构一般都是由业务领域决定的。一个UML分析类图可以帮助我们可视化的把这个关系的结构表示出来。</P>
<P>因此，我建议对标准的RUP过程做如下修改（次序的改变）：粗体）：</P>
<P>对每个分析类</P>
<P><BR>描述类的职责 </P>
<P><BR>在分析类图上，建立分析类间的联系 </P>
<P><BR>把行为指定给分析类（找出操作） </P>
<P><BR>描述每个类的属性和关系</P>
<P><BR>定义类的属性</P>
<P><BR>描述分析类间事件的相关性 <BR>u][b][/b][b][/b] <BR> <BR> </P>
<P> Re: 关于BO的问题  发表时间: Aug 16, 2005 6:40 PM   回复  <BR> <BR>发表人: 大愚弱智    发表文章: 7 / 注册时间: 2004-09  <BR>BO中包含业务对象的属性以及等操作，而业务逻辑由专门的业务逻辑对象处理－－我除了觉得这样做很合理外，甚至还建议把Retrieve，Update，Create，Delete等操作也放到专门的业务逻辑的bean来处理。</P>
<P>把BO当成一个参数传给专门的业务逻辑的bean，把操作和属性分开也见得违反了OO。<BR>专门的业务逻辑的bean一般由容器管理它的状态，这些bean可能还需要容器管理的事务，假如把属性糅合进去也给容器管理好像就不是很适合了。 <BR> <BR> </P>
<P> Re: 关于BO的问题  发表时间: Sep 9, 2005 10:24 AM   回复  <BR> <BR>发表人: Kyle_Yin    发表文章: 26 / 注册时间: 2005-09  <BR>个人意见, 大家对待J2EE模式有的时候太教条了. <BR>切莫忘记很多J2EE模式是在历史背景和特定语境下产生的. 比如: EJB1.1没有"local interface", 早期的容器又缺少对remote EJB界面的优化, 和早期EJB设计者对于客户端多样性的一些设想. </P>
<P>EJB的初衷是纯粹的构建在RMI上的"地点透明". 这个美好的初衷的前提假设是多样化的客户端类型, 比如 HTML, 独立Swing client, Applet client, Midlet client. EJB1 就建立在这个假设之上. 可是很快大家意识到: <BR>1. 这样做的代价是无区别无止境的marshalling, 从而导致性能恶化. <BR>2. 绝大部分J2EE应用是WEB应用, 而WEB 容器和EJB容器通常在一个JVM之内. 没有必要Marshalling. <BR>3. Swing client, Applet, Midlet和其他客户端可以通过其他途径呼叫EJB, 比如WEB SERVICES, 而没有必要用RMI. 事实上RMI-IIOP往往无法通过放火墙, 而SOAP-HTTP则没有这个问题. 对于广域分布应用, web services比RMI更适合做传输手段. 而WEB SERVICES又是可以部署在WEB容器上的, 和EJB同在一个JVM内. <BR>4. 容器生产者往往采用一些优化措施省略不必要的marshalling. 比如在weblogic上, 即使使用了remote EJB, 如果这个ejb部署在本地(JVM), 那么呼叫操作是不通过Socket进行的, 事实上等同于后来的local interface. </P>
<P>在这些发现的指导下, 第一代"核心J2EE设计模式"的主要目的是抵消EJB1的缺点. 比如 Session Facade, Transfer Object的主要目的在于粗糙通信粒度, 减少 Socket 和 marshalling. 而EJB2标准进一步引入了"local interface". 容器生产商也推荐开发者使用这些模式. 自此, EJB 层 与 WEB 层的"共同部署"作为J2EE模式的核心, 成了天经地义. </P>
<P>问题是: 如果WEB LAYER和BUSINESS LAYER"共同部署"在一个JVM里, 那EJB除了"事物处理"和"容器管理永久化"之外, 还有什么意义? </P>
<P>于是产生了SPRING, 于是产生了HIBERNATE. 在TomCat + Struts + Spring + hibernate的领域里, "Business Tier" 成为一个纯粹的设计概念, 不再具备"物理部署"层面的含义. </P>
<P>可是有些传统J2EE的模式保留了下来. 在一个单JVM系统里: Session Facade 和 Transfer Object 已经不具备性能上的必要性, 而更多地是一个审美取向和习惯做法. 当然, Session Facade作为事物处理的起点(spring)的意义仍然存在. </P>
<P>说这么多废话的意思, 是有时我们容易忘记, 分离"状态"和"方法"的"模式", 是为了一个理由而存在的. 在回答"BO"该不该具备"方法"或者"状态"的时候, 我们似乎更应该关心下面的这些问题:</P>
<P>1. 这个BO是代表"交易事物"的, 还是具有"实体身份"的?<BR>2. 这个BO的界面具备什么语意? 有状态? 无状态?<BR>3. 这个BO和下面的永久层什么界面? 如何绑定?<BR>4. 这个BO的部署有什么物理约束? <BR>5. 这个BO现在和未来的客户端是什么, 在哪里?</P>
<P>个人看法, 对于多数中小型, 单层JVM (横向可以集群, 但是纵向"共同部署"在一个JVM里), WEB表达层和EJB业务层"共同部署"的应用而言, 一个完全作为"业务交易中间数据属性集合"而不具备"业务逻辑方法"的BO属于杀鸡的钝牛刀. 原因如下: </P>
<P>1. 没有必要剥离"状态"和方法. "业务逻辑对象"的客户在这个环境里肯定是同一个JVM里, 作为WEB代理的的"SessionFacade", 作为Service代理的ApplicationService, 或者其他复合BO. 在"共同部署"前提下, 它们与BO之间的通信是纯JAVA方法调用. 从性能角度分析, 不必用"无状态"的设计原则来优化通信粒度. 事实上, Core J2EE Pattern里BO的第一个战略"composite entity strategy"也推荐使用 local entity bean 实现BO. </P>
<P>2. 从降低偶合角度看, 复杂数据结构而不具备相应逻辑方法的对象是难以使用的. 可以想象, "业务逻辑对象"的客户程序将需要复杂的代码来维护这个BO的数据完整性. 这样的设计极大地增加了客户程序和业务逻辑对象之间的偶合度. <BR>相反, 如果BO和业务逻辑对象合而为一, 那么BO本身可以负责自身属性数据的检验. 比如, 可以用有限状态模式来设计BO, 既增加BO界面的一致性, 也降低客户程序的复杂度. </P>
<P>EJB3, AJAX, XAML, XUL, 还有其他平台和客户端技术的出现, 应该让我们不断质疑和反思现在"传统"和"模式". 跟随"模式"是不可能有创新的. </P>
<P> <BR> </P>
<P> Re: 关于BO的问题  发表时间: Oct 14, 2005 4:14 PM   回复  <BR> <BR>发表人: windfromsky    发表文章: 2 / 注册时间: 2005-10  <BR>用文件系统来比方一下:<BR>BusinessObject是程序比如Word程序, 包含业务逻辑<BR>DbObject 是文件, 只包含属性<BR>DbObjectManager 是资源管理器, 管理DbObject</P>
<P>delete,list的操作应该属于DbObjectManager<BR>insert,load,update以及其他业务逻辑应该属于Word程序即BO</P>
<P> <BR> <BR></P><img src ="http://www.blogjava.net/qq13367612/aggbug/16250.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/qq13367612/" target="_blank">Sung</a> 2005-10-21 11:40 <a href="http://www.blogjava.net/qq13367612/archive/2005/10/21/16250.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>