﻿<?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-桔子园-随笔分类-J2EE</title><link>http://www.blogjava.net/orangelizq/category/24143.html</link><description>orangelizq</description><language>zh-cn</language><lastBuildDate>Sun, 30 Aug 2009 04:01:48 GMT</lastBuildDate><pubDate>Sun, 30 Aug 2009 04:01:48 GMT</pubDate><ttl>60</ttl><item><title>[转]组件-框架-容器</title><link>http://www.blogjava.net/orangelizq/archive/2009/08/29/293060.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sat, 29 Aug 2009 02:08:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2009/08/29/293060.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/293060.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2009/08/29/293060.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/293060.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/293060.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 组件是抽象的概念而已，<span style="color: #2708ff;">通俗的说是一些符合某种规范的类组合在一起就构成了组件</span>。他可以提供某些特定的功能。<br />
&nbsp;&nbsp;&nbsp; 拿J2EE来说，有什么servlet，jsp, javabean，ejb都是组件。但实际他们都是类，只不过有他们特殊的规定。举个例子，那个javabean来说：javabean也就是个类，但你的类想成为javabean你必须，给你的类里的变量 （如xxx）,添两个函数，getXxx()和setXxx()并且类里要有无参的构造函数。 有了这些就是JAVABEAN了。 <br />
<br />
&nbsp;&nbsp;&nbsp; 什么是框架那，用《设计模式》中的定义来说就是，<span style="color: #2708ff;">框架是构成一类特定软件可复用设计的一组相互协作的类</span>。<strong>框架规定了你的应用程序的体系结构。它定义了整体结构，类和对象的分割，各部分的主要责任，类和对象怎么协作，以及控制流程。 </strong><br />
<br />
&nbsp;&nbsp;&nbsp; 框架实现了对具体实现细节的反向控制（IOC），实现者无须考虑框架层已经实现好的设计，只要按照框架的要求开发就可以了，然后把开发好的东西放到框架中就可以了。框架其实就是一组组件，供你选用完成你自己的系统。简单说就是使用别人搭好的舞台，你来做表演。而且，框架一般是成熟的，不断升级的软件。 <br />
<br />
比如Struts框架就是一组相互协作的类、servlet 和 JSP 标记组成的一个可重用的 MVC设计。它有自己实现好的模型，视图，控制器。 <br />
<br />
&nbsp;&nbsp;&nbsp; 所谓<span style="color: #2708ff;">容器就是指符合一定的规范能提供一系列服务的管理器</span>，方便别人使用它来完成一系列的功能。<br />
&nbsp;&nbsp;&nbsp; 例如tomcat，使用tomcat可以为我们提供servlet.jsp等服务，我们俗称叫servlet服务器，在服务器中会有相关的容器，servlet容器可以调用servlet和jsp动态的为我们生成html <br />
&nbsp;&nbsp;&nbsp; 对于刚刚接触的人来说，可以把服务器就理解成一个容器也可以，不过两者的确不是一回事，是服务器为我们提供一个容器使我们的程序能够在容器里运行使用服务器提供的一系列功能。<br />
<img src ="http://www.blogjava.net/orangelizq/aggbug/293060.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2009-08-29 10:08 <a href="http://www.blogjava.net/orangelizq/archive/2009/08/29/293060.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]Java杂谈</title><link>http://www.blogjava.net/orangelizq/archive/2009/08/27/292851.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Thu, 27 Aug 2009 09:09:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2009/08/27/292851.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/292851.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2009/08/27/292851.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/292851.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/292851.html</trackback:ping><description><![CDATA[原文：http://home.phpchina.com/space.php?uid=108994&amp;do=blog&amp;id=69516<br />
<br />
<strong>接口&amp; 组件、容器</strong><br />
<br />
1. J2ee与接口
<br />
<br />
每一个版本的J2ee都对应着一个确定版本的JDK，J2ee1.4对应Jdk1.4，现在比较新的是JDK5.0，自然也会有J2EE
5.0。其实笔者一直在用的是J2EE1.4，不过没什么关系，大家可以下任何一个版本的J2ee
api来稍微浏览一下。笔者想先声明一个概念，J2ee也是源自Java，所以底层的操作依然调用到很多J2se的库，所以才建议大家先牢牢掌握J2se 的主流技术。
<br />
<br />
J2ee
api有一个特点，大家比较熟悉的几个包java.jms、javax.servlet.http、javax.ejb等都以interface居多，实现类较少。其实大家真正在用的时候百分之六十以上都在反复的查着javax.servlet.http这个包下面几个实现类的api函数，其他的包很少问津。笔者建议在学习一种技术之前，对整体的框架有一个了解是很有必要的，J2ee旨在通过interface的声明来规范实现的行为，任何第三方的厂商想要提供自己品牌的实现前提也是遵循这些接口定义的规则。如果在从前J2se学习的道路上对接口的理解很好的话，这里的体会将是非常深刻的，举个简单的例子：
<br />
<br />
public interface Mp3{ <br />
public void play(); <br />
public void record();
<br />
public void stop(); <br />
}
<br />
<br />
如果我定义这个简单的接口，发布出去，规定任何第三方的公司想推出自己的名字为Mp3的产品都必须实现这个接口，也就是至少提供接口中方法的具体实现。这个意义已经远远不止是面向对象的多态了，只有厂商遵循J2ee的接口定义，世界上的J2ee程序员才能针对统一的接口进行程序设计，最终不用改变代码只是因为使用了不同厂商的实现类而有不同的特性罢了，本质上说，无论哪一种厂商实现都完成了职责范围内的工作。这个就是笔者想一直强调的，针对接口编程的思想。
<br />
<br />
接口到底有什么好处呢？我们这样设想，现在有AppleMp3、SonyMp3、SamsungMp3都实现了这个Mp3的接口，于是都有了play、
record、stop这三个功能。我们将Mp3产品座位一个组件的时候就不需要知道它的具体实现，只要看到接口定义知道这个对象有3个功能就可以使用了。那么类似下面这样的业务就完全可以在任何时间从3个品牌扩展到任意个品牌，开个玩笑的说，项目经理高高在上的写完10个接口里的方法声明，然后就丢给手下的程序员去写里面的细节，由于接口已经统一（即每个方法传入和传出的格式已经统一），经理只需关注全局的业务就可以天天端杯咖啡走来走去了，^_^：
<br />
public Mp3 create(); <br />
public void copy(Mp3 mp3); <br />
public Mp3 getMp3();
<br />
<br />
最后用一个简单的例子说明接口：一个5号电池的手电筒，可以装入任何牌子的5号电池，只要它符合5号电池的规范，装入之后任何看不到是什么牌子，只能感受到手电筒在完成它的功能。那么生产手电筒的厂商和生产5号电池的厂商就可以完全解除依赖关系，可以各自自由开发自己的产品，因为它们都遵守5号电池应有的形状、正负极位置等约定。这下大家能对接口多一点体会了么？
<br />
<br />
2. 组件和容器
<br />
针对接口是笔者特意强调的J2ee学习之路必备的思想，另外一个就是比较常规的组件和容器的概念了。很多教材和专业网站都说J2EE的核心是一组规范与指南，强调J2ee的核心概念就是组件+容器，这确实是无可厚非的。随着越来越多的J2ee框架出现，相应的每种框架都一般有与之对应的容器。
<br />
<br />
容器，是用来管理组件行为的一个集合工具，组件的行为包括与外部环境的交互、组件的生命周期、组件之间的合作依赖关系等等。J2ee包含的容器种类大约有
Web容器、Application
Client容器、EJB容器、Applet客户端容器等。但在笔者看来，现在容器的概念变得有点模糊了，大家耳熟能详是那些功能强大的开源框架，比如
Hibernate、Struts2、Spring、JSF等，其中Hibernate就基于JDBC的基础封装了对事务和会话的管理，大大方便了对数据库操作的繁琐代码，从这个意义上来说它已经接近容器的概念了，EJB的实体Bean也逐渐被以Hibernate为代表的持久化框架所取代。
<br />
<br />
组件，本意是指可以重用的代码单元，一般代表着一个或者一组可以独立出来的功能模块，在J2ee中组件的种类有很多种，比较常见的是EJB组件、DAO组件、客户端组件或者应用程序组件等，它们有个共同特点是分别会打包成.war，.jar，.jar，.ear，每个组件由特定格式的xml描述符文件进行描述，而且服务器端的组件都需要被部署到应用服务器上面才能够被使用。
<br />
<br />
稍微理解完组件和容器，还有一个重要的概念就是分层模型，最著名的当然是MVC三层模型。在一个大的工程或项目中，为了让前台和后台各个模块的编程人员能够同时进行工作提高开发效率，最重要的就是实现层与层之间的耦合关系，许多分层模型的宗旨和开源框架所追求的也就是这样的效果。在笔者看来，一个完整的
Web项目大概有以下几个层次： <br />
<br />
a) 表示层（Jsp、Html、Javascript、Ajax、Flash等等技术对其支持） <br />
b)
控制层（Struts、JSF、WebWork等等框架在基于Servlet的基础上支持，负责把具体的请求数据（有时卸载重新装载）导向适合处理它的模型层对象）
<br />
c) 模型层（笔者认为目前最好的框架是Spring，实质就是处理表示层经由控制层转发过来的数据，包含着大量的业务逻辑） <br />
d)
数据层（Hibernate、JDBC、EJB等，由模型层处理完了持久化到数据库中） <br />
<br />
<br />
<strong>Servlet/Jsp</strong><br />
<font face="Arial ">终于正式进入J2ee的细节部分了，首当其冲的当然是Servlet和Jsp了，上篇曾经提到过J2ee只是一
个规范和指南，定义了一组必须要遵循的接口，核心概念是组件和容器。曾经有的人问笔者Servlet的Class文件是哪里来的？他认为是J2ee官方提
供的，我举了一个简单的反例：稍微检查了一下Tomcat5.0里面的Servlet.jar文件和JBoss里面的Servlet.jar文件大小，很
明显是不一样的，至少已经说明了它们不是源自同根的吧。其实Servlet是由容器根据J2ee的接口定义自己来实现的，实现的方式当然可以不同，只要都
遵守J2ee规范和指南。 <br />
<br />
上述只是一个常见的误区罢了，告诉我们要编译运行Servlet，是要依赖于实现它的容器的，不然连jar文件都没有，编译都无法进
行。那么Jsp呢？ Java Server
Page的简称，是为了开发动态网页而诞生的技术，其本质也是Jsp，在编写完毕之后会在容器启动时经过编译成对应的Servlet。只是我们利用Jsp
的很多新特性，可以更加专注于前后台的分离，早期Jsp做前台是满流行的，毕竟里面支持Html代码，这让前台美工人员可以更有效率的去完成自己的工作。
然后Jsp将请求转发到后台的Servlet，由Servlet处理业务逻辑，再转发回另外一个Jsp在前台显示出来。这似乎已经成为一种常用的模式，最
初笔者学习J2ee的时候，大量时间也在编写这样的代码。 <br />
<br />
尽管现在做前台的技术越来越多，例如Flash、Ajax等，已经有很多人不再认为Jsp重要了。笔者觉得Jsp带来的不仅仅是前后
端分离的设计理念，它的另外一项技术成就了我们今天用的很多框架，那就是Tag标签技术。所以与其说是在学习Jsp，不如更清醒的告诉自己在不断的理解
Tag标签的意义和本质。 <br />
<br />
1． Servlet以及Jsp的生命周期
<br />
Servlet是Jsp的实质，尽管容器对它们的处理有所区别。Servlet有init()方法初始化，service()方法进行Web服
务，
destroy()方法进行销毁，从生到灭都由容器来掌握，所以这些方法除非你想自己来实现Servlet，否则是很少会接触到的。正是由于很少接触，才
容易被广大初学者所忽略，希望大家至少记住Servlet生命周期方法都是回调方法。回调这个概念简单来说就是把自己注入另外一个类中，由它来调用你的方
法，所谓的另外一个类就是Web容器，它只认识接口和接口的方法，注入进来的是怎样的对象不管，它只会根据所需调用这个对象在接口定义存在的那些方法。由
容器来调用的Servlet对象的初始化、服务和销毁方法，所以叫做回调。这个概念对学习其他J2ee技术相当关键！ <br />
<br />
那么Jsp呢？本事上是Servlet，还是有些区别的，它的生命周期是这样的： <br />
a) 一个客户端的Request到达服务器 -&gt;
<br />
b) 判断是否第一次调用 -&gt; 是的话编译Jsp成Servlet <br />
c) 否的话再判断此Jsp是否有改变 -&gt;
是的话也重新编译Jsp成Servlet <br />
d) 已经编译最近版本的Servlet装载所需的其他Class <br />
e)
发布Servlet，即调用它的Service()方法
<br />
<br />
所以Jsp号称的是第一次Load缓慢，以后都会很快的运行。从它的生命的周期确实不难看出来这个特点，客户端的操作很少会改变Jsp的
源码，所以它不需要编译第二次就一直可以为客户端提供服务。这里稍微解释一下Http的无状态性，因为发现很多人误解，Http的无状态性是指每次一张页
面显示出来了，与服务器的连接其实就已经断开了，当再次有提交动作的时候，才会再次与服务器进行连接请求提供服务。当然还有现在比较流行的是Ajax与服
务器异步通过 xml交互的技术，在做前台的领域潜力巨大，笔者不是Ajax的高手，这里无法为大家解释。 <br />
<br />
2． Tag标签的本质
<br />
笔者之前说了，Jsp本身初衷是使得Web应用前后台的开发可以脱离耦合分开有效的进行，可惜这个理念的贡献反倒不如它带来的Tag技术对
J2ee的贡献要大。也许已经有很多人开始使用Tag技术了却并不了解它。所以才建议大家在学习J2ee开始的时候一定要认真学习Jsp，其实最重要的就
是明白标签的本质。 <br />
<br />
Html标签我们都很熟悉了，有 &lt;html&gt; 、 &lt;head&gt; 、 &lt;body&gt; 、
&lt;title&gt; ，Jsp带来的Tag标签遵循同样的格式，或者说更严格的Xml格式规范，例如 &lt;jsp:include&gt; 、
&lt;jsp:useBean&gt; 、 &lt;c:if&gt; 、 &lt;c:forEach&gt;
等等。它们没有什么神秘的地方，就其源头也还是Java
Class而已，Tag标签的实质也就是一段Java代码，或者说一个Class文件。当配置文件设置好去哪里寻找这些Class的路径后，容器负责将页面中存在的标签对应到相应的Class上，执行那段特定的Java代码，如此而已。
<br />
说得明白一点的话还是举几个简单的例子说明一下吧： <br />
<br />
&lt;jsp:include&gt;
去哪里找执行什么class呢?首先这是个jsp类库的标签，当然要去jsp类库寻找相应的class了，同样它也是由Web容器来提供，例如
Tomcat就应该去安装目录的lib文件夹下面的jsp-api.jar里面找，有兴趣的可以去找一找啊！ <br />
<br />
&lt;c:forEach&gt;
又去哪里找呢？这个是由Jsp2.0版本推荐的和核心标记库的内容，例如 &lt;c:if&gt;
就对应在页面中做if判断的功能的一断Java代码。它的class文件在jstl.jar这个类库里面，往往还需要和一个standard.jar类库一起导入，放在具体Web项目的WEB-INF的lib目录下面就可以使用了。
<br />
<br />
顺便罗唆一句，Web
Project的目录结构是相对固定的，因为容器会按照固定的路径去寻找它需要的配置文件和资源，这个任何一本J2ee入门书上都有，这里就不介绍了。了
解Tag的本质还要了解它的工作原理，所以大家去J2ee的API里找到并研究这个包：javax.servlet.jsp.tagext。它有一些接
口，和一些实现类，专门用语开发Tag，只有自己亲自写出几个不同功能的标签，才算是真正理解了标签的原理。别忘记了自己开发的标签要自己去完成配置文
件，容器只是集成了去哪里寻找jsp标签对应class的路径，自己写的标签库当然要告诉容器去哪里找啦。 <br />
<br />
说了这么多，我们为什么要用标签呢？完全在Jsp里面来个 &lt;% %&gt;
就可以在里面任意写Java代码了，但是长期实践发现页面代码统一都是与html同风格的标记语言更加有助于美工人员进行开发前台，它不需要懂Java，
只要Java程序员给个列表告诉美工什么标签可以完成什么逻辑功能，他就可以专注于美工，也算是进一步隔离了前后台的工作吧！ <br />
<br />
3． 成就Web框架
<br />
框架是什么？曾经看过这样的定义：与模式类似，框架也是解决特定问题的可重用方法，框架是一个描述性的构建块和服务集合，开发人员可以用来达成某个目标。一般来说，框架提供了解决某类问题的基础设施，是用来创建解决方案的工具，而不是问题的解决方案。
<br />
<br />
正是由于Tag的出现，成就了以后出现的那么多Web框架，它们都开发了自己成熟实用的一套标签，然后由特定的Xml文件来配置加载信息，力图使得Web
应用的开发变得更加高效。下面这些标签相应对很多人来说相当熟悉了： <br />
&lt;html:password&gt;
<br />
&lt;logic:equal&gt; <br />
&lt;bean:write&gt; <br />
&lt;f:view&gt;
<br />
&lt;h:form&gt; <br />
&lt;h:message&gt;
<br />
<br />
它们分别来自Struts和JSF框架，最强大的功能在于控制转发，就是MVC三层模型中间完成控制器的工作。Struts-1实际上并未做到真正的三层隔离，这一点在Struts-2上得到了很大的改进。而Jsf向来以比较完善合理的标签库受到人们推崇。
<br />
<br />
今天就大概讲这么多吧，再次需要强调的是Servlet/Jsp是学习J2ee必经之路，也是最基础的知识，希望大家给与足够的重视！</font><br />
<br />
<br />
<strong>Struts</strong><br />
<font face="Arial ">J2ee的开源框架很多，笔者只能介绍自己熟悉的几个，其他的目前在中国IT行业应用得不是很多。希望大家对新出的框架不要盲目的推崇，首先一定要熟悉它比旧的到底好在哪里，新的理念和特性是什么？然后再决定是否要使用它。
<br />
<br />
这期的主题是Struts，直译过来是支架。Struts的第一个版本是在2001年5月发布的，它提供了一个Web应用的解决方案，如何让Jsp和
servlet共存去提供清晰的分离视图和业务应用逻辑的架构。在Struts之前，通常的做法是在Jsp中加入业务逻辑，或者在Servlet中生成视图转发到前台去。Struts带着MVC的新理念当时退出几乎成为业界公认的Web应用标准，于是当代IT市场上也出现了众多熟悉Struts的程序员。即使有新的框架再出来不用，而继续用Struts的理由也加上了一条低风险，因为中途如果开发人员变动，很容易的招进新的会Struts的IT民工啊，
^_^!
<br />
<br />
笔者之前说的都是Struts-1，因为新出了Struts-2，使得每次谈到Struts都必须注明它是Struts-1还是2。笔者先谈比较熟悉的
Struts-1，下次再介绍一下与Struts－2的区别： <br />
<br />
1． Struts框架整体结构
<br />
Struts-1的核心功能是前端控制器，程序员需要关注的是后端控制器。前端控制器是是一个Servlet，在Web.xml中间配置所有
Request都必须经过前端控制器，它的名字是ActionServlet，由框架来实现和管理。所有的视图和业务逻辑隔离都是应为这个 ActionServlet，
它就像一个交通警察，所有过往的车辆必须经过它的法眼，然后被送往特定的通道。所有，对它的理解就是分发器，我们也可以叫做Dispatcher，其实了解Servlet编程的人自己也可以写一个分发器，加上拦截request的Filter，其实自己实现一个struts框架并不是很困难。主要目的就是让编写视图的和后台逻辑的可以脱离紧耦合，各自同步的完成自己的工作。
<br />
<br />
那么有了ActionServlet在中间负责转发，前端的视图比如说是Jsp，只需要把所有的数据Submit，这些数据就会到达适合处理它的后端控制器Action，然后在里面进行处理，处理完毕之后转发到前台的同一个或者不同的视图Jsp中间，返回前台利用的也是Servlet里面的forward
和redirect两种方式。所以到目前为止，一切都只是借用了Servlet的API搭建起了一个方便的框架而已。这也是Struts最显著的特性?? 控制器。
<br />
<br />
那么另外一个特性，可以说也是Struts-1带来的一个比较成功的理念，就是以xml配置代替硬编码配置信息。以往决定Jsp往哪个servlet提交，是要写进Jsp代码中的，也就是说一旦这个提交路径要改，我们必须改写代码再重新编译。而Struts提出来的思路是，编码的只是一个逻辑名字，它对应哪个class文件写进了xml配置文件中，这个配置文件记录着所有的映射关系，一旦需要改变路径，改变xml文件比改变代码要容易得多。这个理念可以说相当成功，以致于后来的框架都延续着这个思路，xml所起的作用也越来越大。
<br />
<br />
大致上来说Struts当初给我们带来的新鲜感就这么多了，其他的所有特性都是基于方便的控制转发和可扩展的xml配置的基础之上来完成它们的功能的。
<br />
下面将分别介绍Action和FormBean， 这两个是Struts中最核心的两个组件。 <br />
<br />
2． 后端控制器Action
<br />
Action就是我们说的后端控制器，它必须继承自一个Action父类，Struts设计了很多种Action，例如DispatchAction、
DynaValidationAction。它们都有一个处理业务逻辑的方法execute()，传入的request, response,
formBean和actionMapping四个对象，返回actionForward对象。到达Action之前先会经过一个
RequestProcessor来初始化配置文件的映射关系，这里需要大家注意几点： <br />
<br />
1)
为了确保线程安全，在一个应用的生命周期中，Struts框架只会为每个Action类创建一个Action实例，所有的客户请求共享同一个Action
实例，并且所有线程可以同时执行它的execute()方法。所以当你继承父类Action，并添加了private成员变量的时候，请记住这个变量可以被多个线程访问，它的同步必须由程序员负责。(所有我们不推荐这样做)。在使用Action的时候，保证线程安全的重要原则是在Action类中仅仅使用局部变量，谨慎的使用实例变量。局部变量是对每个线程来说私有的，execute方法结束就被销毁，而实例变量相当于被所有线程共享。
<br />
<br />
2) 当ActionServlet实例接收到Http请求后，在doGet()或者doPost()方法中都会调用process()方法来处理请求。
RequestProcessor类包含一个HashMap，作为存放所有Action实例的缓存，每个Action实例在缓存中存放的属性key为
Action类名。在RequestProcessor类的processActionCreate()方法中，首先检查在HashMap中是否存在
Action实例。创建Action实例的代码位于同步代码块中，以保证只有一个线程创建Action实例。一旦线程创建了Action实例并把它存放到
HashMap中，以后所有的线程会直接使用这个缓存中的实例。 <br />
<br />
3) &lt;action&gt; 元素的 &lt;roles&gt;
属性指定访问这个Action用户必须具备的安全角色，多个角色之间逗号隔开。RequestProcessor类在预处理请求时会调用自身的
processRoles()方法，检查配置文件中是否为Action配置了安全角色，如果有，就调用HttpServletRequest的
isUserInRole()方法来判断用户是否具备了必要的安全性角色，如果不具备，就直接向客户端返回错误。(返回的视图通过 &lt;input&gt;
属性来指定) <br />
<br />
3． 数据传输对象FormBean <br />
Struts并没有把模型层的业务对象直接传递到视图层，而是采用DTO（Data
Transfer Object）来传输数据，这样可以减少传输数据的冗余，提高传输效率；还有助于实现各层之间的独立，使每个层分工明确。Struts的DTO就是
ActionForm，即formBean。由于模型层应该和Web应用层保持独立。由于ActionForm类中使用了Servlet API，
因此不提倡把ActionForm传递给模型层， 而应该在控制层把ActionForm Bean的数据重新组装到自定义的DTO中，
再把它传递给模型层。它只有两个scope，分别是session和request。（默认是session）一个ActionForm标准的生命周期是： <br />
1)
控制器收到请求 -&gt; <br />
2) 从request或session中取出ActionForm实例，如不存在就创建一个 -&gt; <br />
3)
调用ActionForm的reset()方法 -&gt; <br />
4) 把实例放入session或者request中 -&gt; <br />
5)
将用户输入表达数据组装到ActionForm中 -&gt; <br />
6) 如眼张方法配置了就调用validate()方法 -&gt; <br />
7)
如验证错误就转发给 &lt;input&gt; 属性指定的地方，否则调用execute()方法 <br />
<br />
validate()方法调用必须满足两个条件：
<br />
1) ActionForm 配置了Action映射而且name属性匹配 <br />
2) &lt;aciton&gt; 元素的validate属性为true
<br />
<br />
如果ActionForm在request范围内，那么对于每个新的请求都会创建新的ActionForm实例，属性被初始化为默认值，那么reset
()方法就显得没有必要；但如果ActionForm在session范围内，同一个ActionForm实例会被多个请求共享，reset()方法在这种情况下极为有用。
<br />
<br />
4． 验证框架和国际化
<br />
Struts有许多自己的特性，但是基本上大家还是不太常用，说白了它们也是基于JDK中间的很多Java基础包来完成工作。例如国际化、验证框架、插件自扩展功能、与其他框架的集成、因为各大框架基本都有提供这样的特性，Struts也并不是做得最好的一个，这里也不想多说。Struts的验证框架，是通过一个validator.xml的配置文件读入验证规则，然后在validation-rules.xml里面找到验证实现通过自动为Jsp插入
Javascript来实现，可以说做得相当简陋。弹出来的JavaScript框不但难看还很多冗余信息，笔者宁愿用formBean验证或者
Action的saveErrors()，验证逻辑虽然要自己写，但页面隐藏/浮现的警告提示更加人性化和美观一些。
<br />
<br />
至于Struts的国际化，其实无论哪个框架的国际化，java.util.Locale类是最重要的Java
I18N类。在Java语言中，几乎所有的对国际化和本地化的支持都依赖于这个类。如果Java类库中的某个类在运行的时候需要根据Locale对象来调整其功能，那么就称这个类是本地敏感的(Locale-Sensitive)，
例如java.text.DateFormat类就是，依赖于特定Locale。
<br />
<br />
创建Locale对象的时候，需要明确的指定其语言和国家的代码，语言代码遵从的是ISO-639规范，国家代码遵从ISO-3166规范，可以从
<br />
</font><a href="http://www.unicode.org/unicode/onlinedat/languages.html" target="_blank"><font face="Arial "><font color="#0000ff">http://www.unicode.org/unicode/onlinedat/languages.html</font></font></a><font face="Arial "> <br />
</font><a href="http://www.unicode.org/unicode/onlinedat/countries.htm" target="_blank"><font face="Arial "><font color="#0000ff">http://www.unicode.org/unicode/onlinedat/countries.htm</font></font></a><font face="Arial ">
<br />
<br />
Struts的国际化是基于properties的message/key对应来实现的，笔者曾写过一个程序，所有Jsp页面上没有任何Text文本串，全部都用的是
&lt;bean:message&gt;
去Properties文件里面读，这个时候其实只要指定不同的语言区域读不同的Properties文件就实现了国际化。需要注意的是不同语言的字符写进Properties文件的时候需要转化成Unicode码，JDK已经带有转换的功能。JDK的bin目录中有native2ascii这个命令，可以完成对*.txt和*.properties的Unicode码转换。</font><br />
<br />
这次准备继续上次的话题先讲讲Struts-2，手下简短回顾一段历史：随着时间的推移，Web应用框架经常变化的需求，产生了几个下一代
Struts的解决方案。其中的Struts Ti 继续坚持 MVC模式的基础上改进，继续Struts的成功经验。
WebWork项目是在2002年3月发布的，它对Struts式框架进行了革命性改进，引进了不少新的思想，概念和功能，但和原Struts代码并不兼
容。WebWork是一个成熟的框架，经过了好几次重大的改进与发布。在2005年12月，WebWork与Struts Ti决定合拼， 再此同时， Struts
Ti 改名为 Struts Action Framework 2.0,成为Struts真正的下一代。 <br />
<br />
看看Struts-2的处理流程：
<br />
1) Browser产生一个请求并提交框架来处理：根据配置决定使用哪些拦截器、action类和结果等。 <br />
2)
请求经过一系列拦截器：根据请求的级别不同拦截器做不同的处理。这和Struts-1的RequestProcessor类很相似。 <br />
3) 调用Action:
产生一个新的action实例，调用业务逻辑方法。 <br />
4) 调用产生结果：匹配result class并调用产生实例。 <br />
5)
请求再次经过一系列拦截器返回：过程也可配置减少拦截器数量 <br />
6) 请求返回用户：从control返回servlet，生成Html。
<br />
<br />
这里很明显的一点是不存在FormBean的作用域封装，直接可以从Action中取得数据。 这里有一个Strut-2配置的web.xml文件：
<br />
&lt;filter&gt; <br />
&lt;filter-name&gt; controller &lt;/filter-name&gt;
<br />
&lt;filter-class&gt; org.apache.struts.action2.dispatcher.FilterDispatcher
&lt;/filter-class&gt; <br />
&lt;/filter&gt; <br />
&lt;filter-mapping&gt;
<br />
&lt;filter-name&gt; cotroller &lt;/filter-name&gt; <br />
&lt;url-pattern&gt;
/* &lt;/url-pattern&gt; <br />
&lt;/filter-mapping&gt;
<br />
<br />
注意到以往的servlet变成了filter，ActionServlet变成了FilterDispatcher，*.do变成了/*。filter
配置定义了名称(供关联)和filter的类。filter mapping让URI匹配成功的的请求调用该filter。默认情况下，扩展名为 ".action
"。这个是在default.properties文件里的 "struts.action.extension "属性定义的。
<br />
<br />
default.properties是属性定义文件，通过在项目classpath路径中包含一个名为&#8220;struts.properties&#8221;的文件来设置不同的属性值。而Struts-2的默认配置文件名为struts.xml。由于1和2的action扩展名分别为.do和.action，所以很方便能共存。我们再来看一个Struts-2的action代码：
<br />
public class MyAction { <br />
public String execute() throws Exception {
<br />
//do the work <br />
return "success "; <br />
} <br />
}
<br />
<br />
很明显的区别是不用再继承任何类和接口，返回的只是一个String，无参数。实际上在Struts-2中任何返回String的无参数方法都可以通过配置来调用action。所有的参数从哪里来获得呢？答案就是Inversion
of
Control技术（控制反转）。笔者尽量以最通俗的方式来解释，我们先试图让这个Action获得reuqest对象，这样可以提取页面提交的任何参数。那么我们把request设为一个成员变量，然后需要一个对它的set方法。由于大部分的action都需要这么做，我们把这个set方法作为接口来实现。
<br />
public interface ServletRequestAware { <br />
public void
setServletRequest(HttpServletRequest request); <br />
} <br />
<br />
public class
MyAction implements ServletRequestAware { <br />
private HttpServletRequest
request; <br />
<br />
public void setServletRequest(HttpServletRequest request) {
<br />
this.request = request; <br />
} <br />
<br />
public String execute() throws
Exception { <br />
// do the work directly using the request <br />
return
Action.SUCCESS; <br />
} <br />
}
<br />
<br />
那么谁来调用这个set方法呢？也就是说谁来控制这个action的行为，以往我们都是自己在适当的地方写上一句
action.setServletRequest(&#8230;)，也就是控制权在程序员这边。然而控制反转的思想是在哪里调用交给正在运行的容器来决定，只要利用Java反射机制来获得Method对象然后调用它的invoke方法传入参数就能做到，这样控制权就从程序员这边转移到了容器那边。程序员可以减轻很多繁琐的工作更多的关注业务逻辑。Request可以这样注入到action中，其他任何对象也都可以。为了保证action的成员变量线程安全，
Struts-2的action不是单例的，每一个新的请求都会产生一个新的action实例。
<br />
<br />
那么有人会问，到底谁来做这个对象的注入工作呢？答案就是拦截器。拦截器又是什么东西？笔者再来尽量通俗的解释拦截器的概念。大家要理解拦截器的话，首先一定要理解GOF23种设计模式中的Proxy模式。
<br />
<br />
A对象要调用f()，它希望代理给B来做，那么B就要获得A对象的引用，然后在B的f()中通过A对象引用调用A对象的f()方法，最终达到A的f()被调用的目的。有没有人会觉得这样很麻烦，为什么明明只要A.f()就可以完成的一定要封装到B的f()方法中去？有哪些好处呢？
<br />
<br />
1) 这里我们只有一个A，当我们有很多个A的时候，只需要监视B一个对象的f()方法就可以从全局上控制所有被调用的f()方法。 <br />
2)
另外，既然代理人B能获得A对象的引用，那么B可以决定在真正调A对象的f()方法之前可以做哪些前置工作，调完返回前可有做哪些后置工作。
<br />
<br />
讲到这里，大家看出来一点拦截器的概念了么？它拦截下一调f()方法的请求，然后统一的做处理（处理每个的方式还可以不同，解析A对象就可以辨别），处理完毕再放行。这样像不像对流动的河水横切了一刀，对所有想通过的水分子进行搜身，然后再放行？这也就是AOP（Aspect
of Programming面向切面编程）的思想。
<br />
<br />
Anyway，Struts-2只是利用了AOP和IoC技术来减轻action和框架的耦合关系，力图到最大程度重用action的目的。在这样的技术促动下，Struts-2的action成了一个简单被框架使用的POJO（Plain
Old Java
Object）罢了。实事上AOP和IoC的思想已经遍布新出来的每一个框架上，他们并不是多么新的技术，利用的也都是JDK早已可以最到的事情，它们代表的是更加面向接口编程，提高重用，增加扩展性的一种思想。Struts-2只是部分的使用这两种思想来设计完成的，另外一个最近很火的框架
Spring，更大程度上代表了这两种设计思想，笔者将于下一篇来进一步探讨Spring的结构。<br />
<br />
<br />
<strong>Spring</strong><br />
引用《Spring2.0技术手册》上的一段话：
<br />
Spring的核心是个轻量级容器，它是实现IoC容器和非侵入性的框架，并提供AOP概念的实现方式；提供对持久层、事务的支持；提供MVC
Web框架的实现，并对于一些常用的企业服务API提供一致的模型封装，是一个全方位的应用程序框架，除此之外，对于现存的各种框架，Spring也提供了与它们相整合的方案。
<br />
接下来笔者先谈谈自己的一些理解吧，Spring框架的发起者之前一本很著名的书名字大概是《J2ee Development without
EJB》，他提倡用轻量级的组件代替重量级的EJB。笔者还没有看完那本著作，只阅读了部分章节。其中有一点分析觉得是很有道理的：
<br />
<br />
EJB里在服务器端有Web Container和EJB Container，从前的观点是各层之间应该在物理上隔离，Web
Container处理视图功能、在EJB Container中处理业务逻辑功能、然后也是EBJ
Container控制数据库持久化。这样的层次是很清晰，但是一个很严重的问题是Web Container和EJB
Container毕竟是两个不同的容器，它们之间要通信就得用的是RMI机制和JNDI服务，同样都在服务端，却物理上隔离，而且每次业务请求都要远程调用，有没有必要呢？看来并非隔离都是好的。
<br />
<br />
再看看轻量级和重量级的区别，笔者看过很多种说法，觉得最有道理的是轻量级代表是POJO + IoC，重量级的代表是Container +
Factory。（EJB2.0是典型的重量级组件的技术）我们尽量使用轻量级的Pojo很好理解，意义就在于兼容性和可适应性，移植不需要改变原来的代码。而Ioc与Factory比起来，Ioc的优点是更大的灵活性，通过配置可以控制很多注入的细节，而Factory模式，行为是相对比较封闭固定的，生产一个对象就必须接受它全部的特点，不管是否需要。其实轻量级和重量级都是相对的概念，使用资源更少、运行负载更小的自然就算轻量。
<br />
<br />
话题扯远了，因为Spring框架带来了太多可以探讨的地方。比如它的非侵入性：指的是它提供的框架实现可以让程序员编程却感觉不到框架的存在，这样所写的代码并没有和框架绑定在一起，可以随时抽离出来，这也是Spring设计的目标。Spring是唯一可以做到真正的针对接口编程，处处都是接口，不依赖绑定任何实现类。同时，Spring还设计了自己的事务管理、对象管理和Model2
的MVC框架，还封装了其他J2ee的服务在里面，在实现上基本都在使用依赖注入和AOP的思想。由此我们大概可以看到Spring是一个什么概念上的框架，代表了很多优秀思想，值得深入学习。笔者强调，学习并不是框架，而是框架代表的思想，就像我们当初学Struts一样&#8230;&#8230;
<br />
<br />
1．Spring MVC
<br />
关于IoC和AOP笔者在上篇已经稍微解释过了，这里先通过Spring的MVC框架来给大家探讨一下Spring的特点吧。（毕竟大部分人已经很熟悉Struts了，对比一下吧）
<br />
众所周知MVC的核心是控制器。类似Struts中的ActionServlet，Spring里面前端控制器叫做DispatcherServlet。里面充当Action的组件叫做Controller，返回的视图层对象叫做ModelAndView，提交和返回都可能要经过过滤的组件叫做
Interceptor。 <br />
<br />
让我们看看一个从请求到返回的流程吧： <br />
(1)
前台Jsp或Html通过点击submit，将数据装入了request域 <br />
(2)
请求被Interceptor拦截下来，执行preHandler()方法出前置判断 <br />
(3) 请求到达DispathcerServlet <br />
(4)
DispathcerServlet通过Handler Mapping来决定每个reuqest应该转发给哪个后端控制器Controlle<br />
<br />
<br />
<strong>ORM</strong><br />
其实J2ee的规范指南里面就已经包括了一些对象持久化技术，例如JDO（Java Data
Object）就是Java对象持久化的新规范，一个用于存取某种数据仓库中的对象的标准化API，提供了透明的对象存储，对开发人员来说，存储数据对象完全不需要额外的代码（如JDBC
API的使用）。这些繁琐的工作已经转移到JDO产品提供商身上，使开发人员解脱出来，从而集中时间和精力在业务逻辑上。另外，JDO很灵活，因为它可以在任何数据底层上运行。JDBC只是面向关系数据库（RDBMS）JDO更通用，提供到任何数据底层的存储功能，比如关系数据库、文件、XML以及对象数据库（ODBMS）等等，使得应用可移植性更强。我们如果要理解对象持久化技术，首先要问自己一个问题：为什么传统的JDBC来持久化不再能满足大家的需求了呢？
<br />
<br />
笔者认为最好是能用JDBC真正编写过程序了才能真正体会ORM的好处，同样的道理，真正拿Servlet/Jsp做过项目了才能体会到Struts、
Spring等框架的方便之处。很幸运的是笔者这两者都曾经经历过，用混乱的内嵌Java代码的Jsp加Servlet转发写过完整的Web项目，也用
JDBC搭建过一个完整C/S项目的后台。所以现在接触到新框架才更能体会它们思想和实现的优越之处，回顾从前的代码，真是丑陋不堪啊。^_^
<br />
<br />
回到正题，我们来研究一下为什么要从JDBC发展到ORM。简单来说，传统的JDBC要花大量的重复代码在初始化数据库连接上，每次增删改查都要获得
Connection对象，初始化Statement，执行得到ResultSet再封装成自己的List或者Object，这样造成了在每个数据访问方法中都含有大量冗余重复的代码，考虑到安全性的话，还要加上大量的事务控制和log记录。虽然我们学习了设计模式之后，可以自己定义Factory来帮助减少一部分重复的代码，但是仍然无法避免冗余的问题。其次，随着OO思想深入人心，连典型的过程化语言Perl等都冠冕堂皇的加上了OO的外壳，何况是
Java中繁杂的数据库访问持久化技术呢？强调面向对象编程的结果就是找到一个桥梁，使得关系型数据库存储的数据能准确的映射到Java的对象上，然后针对Java对象来设计对象和方法，如果我们把数据库的Table当作Class，Record当作Instance的话，就可以完全用面向对象的思想来编写数据层的代码。于是乎，Object
Relationship Mapping的概念开始普遍受到重视，尽管很早很早就已经有人提出来了。
<br />
缺点我们已经大概清楚了，那么如何改进呢？对症下药，首先我们要解决的是如何从Data Schema准备完美的映射到Object
Schema，另外要提供对数据库连接对象生命周期的管理，对事务不同粒度的控制和考虑到扩展性后提供对XML、Properties等可配置化的文件的支持。到目前为止，有很多框架和技术在尝试着这样做。例如似乎是封装管理得过了头的EJB、很早就出现目前已经不在开发和升级了的Apache
OJB、首先支持Manual SQL的iBATIS，还有公认非常优秀的Hibernate等等。在分别介绍它们之前，我还想反复强调这些框架都在试图做什么：
<br />
<br />
毕竟Java
Object和数据库的每一条Record还是有很大的区别，就是类型上来说，DB是没有Boolean类型的。而Java也不得不用封装类（Integer、Double等）为了能映射上数据库中为null的情况，毕竟Primitive类型是没有null值的。还有一个比较明显的问题是，数据库有主键和外键，而Java中仍然只能通过基本类型来对应字段值而已，无法规定Unique等特征，更别提外键约束、事务控制和级联操作了。另外，通过Java
Object预设某Field值去取数据库记录，是否在这样的记录也是不能保证的。真的要设计到完全映射的话，Java的Static被所有对象共享的变量怎么办？在数据库中如何表现出来&#8230;&#8230;
<br />
我们能看到大量的问题像一座座大山横在那些框架设计者们面前，他们并不是没有解决办法，而是从不同的角度去考虑，会得到很多不同的解决方案，问题是应该采取哪一种呢？甚至只有等到真正设计出来了投入生产使用了，才能印证出当初的设想是否真的能为项目开发带来更多的益处。笔者引用一份文档中提到一个健壮的持久化框架应该具有的特点：
<br />
A robust persistence layer should support---- <br />
1. Several types of
persistence mechanism <br />
2. Full encapsulation of the persistence mechanism.
<br />
3. Multi-object actions <br />
4. Transactions Control <br />
5. Extensibility
<br />
6. Object identifiers <br />
7. Cursors: logical connection to the persistence
mechanism <br />
8. Proxies: commonly used when the results of a query are to be
displayed in a list <br />
9. Records: avoid the overhead of converting database
records to objects and then back to records <br />
10. Multi architecture <br />
11.
Various database version and/or vendors <br />
12. Multiple connections <br />
13.
Native and non-native drivers <br />
14. Structured query language queries(SQL) <br />
<br />
<font face="Arial ">现在来简短的介绍一下笔者用过的一些持久化框架和技术，之所以前面强调那么多共通的知识，是希望大家不要盲从流行框架，一定要把握它的本质和卓越的思想好在哪里。
<br />
1． Apache OJB <br />
OJB代表Apache Object Relational
Bridge，是Apache开发的一个数据库持久型框架。它是基于J2ee规范指南下的持久型框架技术而设计开发的，例如实现了ODMG
3.0规范的API，实现了JDO规范的API， 核心实现是Persistence Broker API。OJB使用XML文件来实现映射并动态的在Metadata
layer听过一个Meta-Object-Protocol(MOP)来改变底层数据的行为。更高级的特点包括对象缓存机制、锁管理机制、 Virtual
代理、事务隔离性级别等等。举个OJB Mapping的简单例子ojb-repository.xml： <br />
<br />
&lt;class-descriptor
class=&#8221;com.ant.Employee&#8221; table=&#8221;EMPLOYEE&#8221;&gt; <br />
&lt;field-descriptor name=&#8221;id&#8221;
column=&#8221;ID&#8221; <br />
jdbc-type=&#8221;INTEGER&#8221; primarykey=&#8221;true&#8221; autoincrement=&#8221;true&#8221;/&gt;
<br />
<br />
&lt;field-descriptor name=&#8221;name&#8221; column=&#8221;NAME&#8221; jdbc-type=&#8221;VARCHAR&#8221;/&gt;
<br />
&lt;/class-descrptor&gt; <br />
<br />
&lt;class-descriptor
class=&#8221;com.ant.Executive&#8221; table=&#8221;EXECUTIVE&#8221;&gt; <br />
&lt;field-descriptor
name=&#8221;id&#8221; column=&#8221;ID&#8221; <br />
jdbc-type=&#8221;INTEGER&#8221; primarykey=&#8221;true&#8221;
autoincrement=&#8221;true&#8221;/&gt; <br />
<br />
&lt;field-descriptor name=&#8221;department&#8221;
column=&#8221;DEPARTMENT&#8221; jdbc-type=&#8221;VARCHAR&#8221;/&gt; <br />
<br />
&lt;reference-descriptor
name=&#8221;super&#8221; class-ref=&#8221;com.ant.Employee&#8221;&gt; <br />
&lt;foreignkey
field-ref=&#8221;id&#8221;/&gt; <br />
&lt;/reference-descriptor&gt;
<br />
&lt;/class-descrptor&gt; <br />
<br />
2． iBATIS
<br />
iBATIS最大的特点就是允许用户自己定义SQL来组配Bean的属性。因为它的SQL语句是直接写入XML文件中去的，所以可以最大程度上利用到
SQL语法本身能控制的全部特性，同时也能允许你使用特定数据库服务器的额外特性，并不局限于类似SQL92这样的标准，它最大的缺点是不支持枚举类型的持久化，即把枚举类型的几个对象属性拼成与数据库一个字段例如VARCHAR对应的行为。这里也举一个Mapping文件的例子sqlMap.xml：
<br />
&lt;sqlMap&gt; <br />
&lt;typeAlias type=&#8221;com.ant.Test&#8221; alias=&#8221;test&#8221;/&gt;
<br />
<br />
&lt;resultMap class=&#8221;test&#8221; id=&#8221;result&#8221;&gt; <br />
&lt;result
property=&#8221;testId&#8221; column=&#8221;TestId&#8221;/&gt; <br />
&lt;result property=&#8221;name&#8221;
column=&#8221;Name&#8221;/&gt; <br />
&lt;result property=&#8221;date&#8221; column=&#8221;Date&#8221;/&gt;
<br />
&lt;/resultMap&gt; <br />
<br />
&lt;select id=&#8221;getTestById&#8221; resultMap=&#8221;result&#8221;
parameterClass=&#8221;int&#8221;&gt; <br />
select * from Test where TestId=#value#
<br />
&lt;/select&gt; <br />
<br />
&lt;update id=&#8221;updateTest&#8221; parameterClass=&#8221;test&#8221;&gt;
<br />
Update Tests set Name=#name#, Date=&#8221;date&#8221; where TestId=#testId#
<br />
&lt;/update&gt; <br />
&lt;/sqlMap&gt; <br />
<br />
3． Hibernate
<br />
Hibernate无疑是应用最广泛最受欢迎的持久型框架，它生成的SQL语句是非常优秀。虽然一度因为不能支持手工SQL而性能受到局限，但随着新一代
Hibernate
3.x推出，很多缺点都被改进，Hibernate也因此变得更加通用而时尚。同样先看一个Mapping文件的例子customer.hbm.xml来有一个大概印象：
<br />
<br />
&lt;hibernate-mapping&gt; <br />
&lt;class name=&#8221;com.ant.Customer&#8221;
table=&#8221;Customers&#8221;&gt; <br />
&lt;id name=&#8221;customerId&#8221; column=&#8221;CustomerId&#8221;
type=&#8221;int&#8221; unsaved-value=&#8221;0&#8221;&gt; <br />
&lt;generator class=&#8221;sequence&#8221;&gt;
<br />
&lt;param name=&#8221;sequence&#8221;&gt; Customers_CustomerId_Seq &lt;/param&gt;
<br />
&lt;/generator&gt; <br />
&lt;/id&gt; <br />
<br />
&lt;property name=&#8221;firstName&#8221;
column=&#8221;FirstName&#8221;/&gt; <br />
&lt;property name=&#8221;lastName&#8221; column=&#8221;LastName&#8221;/&gt;
<br />
<br />
&lt;set name=&#8221;addresses&#8221; outer-join=&#8221;true&#8221;&gt; <br />
&lt;key
column=&#8221;Customer&#8221;/&gt; <br />
&lt;one-to-many class=&#8221;com.ant.Address&#8221;/&gt;
<br />
&lt;/set&gt; <br />
&lt;/class&gt;<br />
&lt;/hibernate-mapping&gt;
<br />
<br />
Hibernate有很多显著的特性，最突出的就是它有自己的查询语言叫做HQL，在HQL中select
from的不是Table而是类名，一方面更加面向对象，另外一方面通过在hibernate.cfg.xml中配置Dialect为HQL可以使得整个后台与数据库脱离耦合，因为不管用那种数据库我都是基于HQL来查询，Hibernate框架负责帮我最终转换成特定数据库里的SQL语句。另外
Hibernate在Object-Caching这方面也做得相当出色，它同时管理两个级别的缓存，当数据被第一次取出后，真正使用的时候对象被放在一级缓存管理，这个时候任何改动都会影响到数据库；而空闲时候会把对象放在二级缓存管理，虽然这个时候与数据库字段能对应上但未绑定在一起，改动不会影响到数据库的记录，主要目的是为了在重复读取的时候更快的拿到数据而不用再次请求连接对象。其实关于这种缓存的设计建议大家研究一下Oracle的存储机制（原理是相通的），Oracle牺牲了空间换来时间依赖于很健壮的缓存算法来保证最优的企业级数据库访问速率。
<br />
<br />
以上是一些Mapping的例子，真正在Java代码中使用多半是继承各个框架中默认的Dao实现类，然后可以通过Id来查找对象，或者通过
Example来查找，更流行的是更具Criteria查找对象。Criteria是完全封装了SQL条件查询语法的一个工具类，任何一个查询条件都可以在Criteria中找到方法与之对应，这样可以在Java代码级别实现SQL的完全控制。另外，现在许多ORM框架的最新版本随着JDk
5.0加入Annotation特性都开始支持用XDoclet来自动根据Annotation来生成XML配置文件了。 <br />
</font><br />
<br />
<img src ="http://www.blogjava.net/orangelizq/aggbug/292851.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2009-08-27 17:09 <a href="http://www.blogjava.net/orangelizq/archive/2009/08/27/292851.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>消息中间件原理及JMS简介(2) </title><link>http://www.blogjava.net/orangelizq/archive/2008/01/27/178030.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sun, 27 Jan 2008 08:17:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2008/01/27/178030.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/178030.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2008/01/27/178030.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/178030.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/178030.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 消息中间件原理及JMS简介之二&nbsp;作者：orangelizq&nbsp; &nbsp;2.3 消息中间件的传递模式消息中间件一般有两种传递模型：点对点模型（PTP）和发布-订阅模型（Pub/Sub）[2]。1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 点对点模型（PTP）点对点模型用于消息生产者和消息消费者之间点到...&nbsp;&nbsp;<a href='http://www.blogjava.net/orangelizq/archive/2008/01/27/178030.html'>阅读全文</a><img src ="http://www.blogjava.net/orangelizq/aggbug/178030.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2008-01-27 16:17 <a href="http://www.blogjava.net/orangelizq/archive/2008/01/27/178030.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>消息中间件原理及JMS简介(1)</title><link>http://www.blogjava.net/orangelizq/archive/2008/01/27/178026.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sun, 27 Jan 2008 08:10:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2008/01/27/178026.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/178026.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2008/01/27/178026.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/178026.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/178026.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp; 消息中间件原理及JMS简介之一&nbsp;作者：orangelizq摘要：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 现今，越来越多的企业面临着各种各样的数据集成和系统整合，CORBA、DCOM、RMI等RPC中间件技术也应运而生，但由于采用RPC同步处理技术，在性能、健壮性、可扩展性上都存在着诸多缺点。而基于消息的异步处理模型采用非...&nbsp;&nbsp;<a href='http://www.blogjava.net/orangelizq/archive/2008/01/27/178026.html'>阅读全文</a><img src ="http://www.blogjava.net/orangelizq/aggbug/178026.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2008-01-27 16:10 <a href="http://www.blogjava.net/orangelizq/archive/2008/01/27/178026.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]了解Java ClassLoader</title><link>http://www.blogjava.net/orangelizq/archive/2007/09/02/142069.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sun, 02 Sep 2007 06:38:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/09/02/142069.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/142069.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/09/02/142069.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/142069.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/142069.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 什么是 ClassLoader?在流行的商业化编程语言中，Java 语言由于在 Java 虚拟机 (JVM) 上运行而显得与众不同。这意味着已编译的程序是一种特殊的、独立于平台的格式，并非依赖于它们所运行的机器。在很大程度上，这种格式不同于传统的可执行程序格式。 与 C 或 C++ 编写的程序不同，Java 程序并不是一个可执行文件，而是由许多独立的类文件组成，每一个文件对应于一个 Jav...&nbsp;&nbsp;<a href='http://www.blogjava.net/orangelizq/archive/2007/09/02/142069.html'>阅读全文</a><img src ="http://www.blogjava.net/orangelizq/aggbug/142069.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-09-02 14:38 <a href="http://www.blogjava.net/orangelizq/archive/2007/09/02/142069.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]Java泛型</title><link>http://www.blogjava.net/orangelizq/archive/2007/08/30/141452.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Thu, 30 Aug 2007 09:25:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/08/30/141452.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/141452.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/08/30/141452.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/141452.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/141452.html</trackback:ping><description><![CDATA[<p><strong>一 泛型简介</strong><br><br>什么是泛型？</p>
<p>泛型（Generic type 或者 generics）是对 Java 语言的类型系统的一种扩展，以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符，就像方法的形式参数是运行时传递的值的占位符一样。 </p>
<p>可以在集合框架（Collection framework）中看到泛型的动机。例如，Map 类允许您向一个 Map 添加任意类的对象，即使最常见的情况是在给定映射（map）中保存某个特定类型（比如 String）的对象。</p>
<p>因为 Map.get() 被定义为返回 Object，所以一般必须将 Map.get() 的结果强制类型转换为期望的类型，如下面的代码所示：</p>
<p>Map m = new HashMap();<br>m.put("key", "blarg");<br>String s = (String) m.get("key");</p>
<p>要让程序通过编译，必须将 get() 的结果强制类型转换为 String，并且希望结果真的是一个 String。但是有可能某人已经在该映射中保存了不是 String 的东西，这样的话，上面的代码将会抛出 ClassCastException。 </p>
<p>理想情况下，您可能会得出这样一个观点，即 m 是一个 Map，它将 String 键映射到 String 值。这可以让您消除代码中的强制类型转换，同时获得一个附加的类型检查层，该检查层可以防止有人将错误类型的键或值保存在集合中。这就是泛型所做的工作。</p>
<p>&nbsp;</p>
<p>泛型的好处</p>
<p>Java 语言中引入泛型是一个较大的功能增强。不仅语言、类型系统和编译器有了较大的变化，以支持泛型，而且类库也进行了大翻修，所以许多重要的类，比如集合框架，都已经成为泛型化的了。这带来了很多好处：</p>
<p>类型安全。 泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制，编译器可以在一个高得多的程度上验证类型假设。没有泛型，这些假设就只存在于程序员的头脑中（或者如果幸运的话，还存在于代码注释中）。</p>
<p>Java 程序中的一种流行技术是定义这样的集合，即它的元素或键是公共类型的，比如&#8220;String 列表&#8221;或者&#8220;String 到 String 的映射&#8221;。通过在变量声明中捕获这一附加的类型信息，泛型允许编译器实施这些附加的类型约束。类型错误现在就可以在编译时被捕获了，而不是在运行时当作 ClassCastException 展示出来。将类型检查从运行时挪到编译时有助于您更容易找到错误，并可提高程序的可靠性。 </p>
<p>消除强制类型转换。 泛型的一个附带好处是，消除源代码中的许多强制类型转换。这使得代码更加可读，并且减少了出错机会。</p>
<p>尽管减少强制类型转换可以降低使用泛型类的代码的罗嗦程度，但是声明泛型变量会带来相应的罗嗦。比较下面两个代码例子。</p>
<p>该代码不使用泛型： </p>
<p>List li = new ArrayList();<br>li.put(new Integer(3));<br>Integer i =&nbsp; (Integer) li.get(0);</p>
<p><br>该代码使用泛型： </p>
<p>List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br>li.put(new Integer(3));<br>Integer i =&nbsp; li.get(0);</p>
<p><br>在简单的程序中使用一次泛型变量不会降低罗嗦程度。但是对于多次使用泛型变量的大型程序来说，则可以累积起来降低罗嗦程度。</p>
<p>潜在的性能收益。 泛型为较大的优化带来可能。在泛型的初始实现中，编译器将强制类型转换（没有泛型的话，程序员会指定这些强制类型转换）插入生成的字节码中。但是更多类型信息可用于编译器这一事实，为未来版本的 JVM 的优化带来可能。</p>
<p>由于泛型的实现方式，支持泛型（几乎）不需要 JVM 或类文件更改。所有工作都在编译器中完成，编译器生成类似于没有泛型（和强制类型转换）时所写的代码，只是更能确保类型安全而已。 </p>
<p><br>泛型用法的例子</p>
<p>泛型的许多最佳例子都来自集合框架，因为泛型让您在保存在集合中的元素上指定类型约束。考虑这个使用 Map 类的例子，其中涉及一定程度的优化，即 Map.get() 返回的结果将确实是一个 String：</p>
<p><br>Map m = new HashMap();<br>m.put("key", "blarg");<br>String s = (String) m.get("key");</p>
<p><br>如果有人已经在映射中放置了不是 String 的其他东西，上面的代码将会抛出 ClassCastException。泛型允许您表达这样的类型约束，即 m 是一个将 String 键映射到 String 值的 Map。这可以消除代码中的强制类型转换，同时获得一个附加的类型检查层，这个检查层可以防止有人将错误类型的键或值保存在集合中。 </p>
<p>下面的代码示例展示了 JDK 5.0 中集合框架中的 Map 接口的定义的一部分： </p>
<p><br>public interface Map&lt;K, V&gt; {<br>&nbsp; public void put(K key, V value);<br>&nbsp; public V get(K key);<br>}</p>
<p>注意该接口的两个附加物： </p>
<p>类型参数 K 和 V 在类级别的规格说明，表示在声明一个 Map 类型的变量时指定的类型的占位符。</p>
<p>在 get()、put() 和其他方法的方法签名中使用的 K 和 V。 </p>
<p>为了赢得使用泛型的好处，必须在定义或实例化 Map 类型的变量时为 K 和 V 提供具体的值。以一种相对直观的方式做这件事：</p>
<p>Map&lt;String, String&gt; m = new HashMap&lt;String, String&gt;();<br>m.put("key", "blarg");<br>String s = m.get("key");</p>
<p>当使用 Map 的泛型化版本时，您不再需要将 Map.get() 的结果强制类型转换为 String，因为编译器知道 get() 将返回一个 String。</p>
<p>在使用泛型的版本中并没有减少键盘录入；实际上，比使用强制类型转换的版本需要做更多键入。使用泛型只是带来了附加的类型安全。因为编译器知道关于您将放进 Map 中的键和值的类型的更多信息，所以类型检查从执行时挪到了编译时，这会提高可靠性并加快开发速度。 </p>
<p><br>向后兼容</p>
<p>在 Java 语言中引入泛型的一个重要目标就是维护向后兼容。尽管 JDK 5.0 的标准类库中的许多类，比如集合框架，都已经泛型化了，但是使用集合类（比如 HashMap 和 ArrayList）的现有代码将继续不加修改地在 JDK 5.0 中工作。当然，没有利用泛型的现有代码将不会赢得泛型的类型安全好处。 </p>
<p>&nbsp;</p>
<p><br><strong>二 泛型基础</strong></p>
<p>类型参数</p>
<p>在定义泛型类或声明泛型类的变量时，使用尖括号来指定形式类型参数。形式类型参数与实际类型参数之间的关系类似于形式方法参数与实际方法参数之间的关系，只是类型参数表示类型，而不是表示值。 </p>
<p>泛型类中的类型参数几乎可以用于任何可以使用类名的地方。例如，下面是 java.util.Map 接口的定义的摘录：</p>
<p>public interface Map&lt;K, V&gt; {<br>&nbsp; public void put(K key, V value);<br>&nbsp; public V get(K key);<br>}</p>
<p>Map 接口是由两个类型参数化的，这两个类型是键类型 K 和值类型 V。（不使用泛型）将会接受或返回 Object 的方法现在在它们的方法签名中使用 K 或 V，指示附加的类型约束位于 Map 的规格说明之下。 </p>
<p>当声明或者实例化一个泛型的对象时，必须指定类型参数的值： </p>
<p>Map&lt;String, String&gt; map = new HashMap&lt;String, String&gt;();</p>
<p>注意，在本例中，必须指定两次类型参数。一次是在声明变量 map 的类型时，另一次是在选择 HashMap 类的参数化以便可以实例化正确类型的一个实例时。</p>
<p>编译器在遇到一个 Map&lt;String, String&gt; 类型的变量时，知道 K 和 V 现在被绑定为 String，因此它知道在这样的变量上调用 Map.get() 将会得到 String 类型。</p>
<p>除了异常类型、枚举或匿名内部类以外，任何类都可以具有类型参数。 </p>
<p><br>命名类型参数</p>
<p>推荐的命名约定是使用大写的单个字母名称作为类型参数。这与 C++ 约定有所不同（参阅 附录 A：与 C++ 模板的比较），并反映了大多数泛型类将具有少量类型参数的假定。对于常见的泛型模式，推荐的名称是： </p>
<p>K —— 键，比如映射的键。 <br>V —— 值，比如 List 和 Set 的内容，或者 Map 中的值。 <br>E —— 异常类。 <br>T —— 泛型。 </p>
<p><br>泛型不是协变的 </p>
<p>关于泛型的混淆，一个常见的来源就是假设它们像数组一样是协变的。其实它们不是协变的。List&lt;Object&gt; 不是 List&lt;String&gt; 的父类型。 </p>
<p>如果 A 扩展 B，那么 A 的数组也是 B 的数组，并且完全可以在需要 B[] 的地方使用 A[]： </p>
<p>Integer[] intArray = new Integer[10];&nbsp; <br>Number[] numberArray = intArray;</p>
<p>上面的代码是有效的，因为一个 Integer 是 一个 Number，因而一个 Integer 数组是 一个 Number 数组。但是对于泛型来说则不然。下面的代码是无效的： </p>
<p>List&lt;Integer&gt; intList = new ArrayList&lt;Integer&gt;();<br>List&lt;Number&gt; numberList = intList; // invalid</p>
<p>最初，大多数 Java 程序员觉得这缺少协变很烦人，或者甚至是&#8220;坏的（broken）&#8221;，但是之所以这样有一个很好的原因。如果可以将 List&lt;Integer&gt; 赋给 List&lt;Number&gt;，下面的代码就会违背泛型应该提供的类型安全： </p>
<p>List&lt;Integer&gt; intList = new ArrayList&lt;Integer&gt;();<br>List&lt;Number&gt; numberList = intList; // invalid<br>numberList.add(new Float(3.1415));</p>
<p>因为 intList 和 numberList 都是有别名的，如果允许的话，上面的代码就会让您将不是 Integers 的东西放进 intList 中。但是，正如下一屏将会看到的，您有一个更加灵活的方式来定义泛型。</p>
<p><br>类型通配符</p>
<p>假设您具有该方法： </p>
<p>void printList(List l) { <br>&nbsp; for (Object o : l) <br>&nbsp;&nbsp;&nbsp; System.out.println(o); <br>}</p>
<p>上面的代码在 JDK 5.0 上编译通过，但是如果试图用 List&lt;Integer&gt; 调用它，则会得到警告。出现警告是因为，您将泛型（List&lt;Integer&gt;）传递给一个只承诺将它当作 List（所谓的原始类型）的方法，这将破坏使用泛型的类型安全。</p>
<p>如果试图编写像下面这样的方法，那么将会怎么样？ </p>
<p>void printList(List&lt;Object&gt; l) { <br>&nbsp; for (Object o : l) <br>&nbsp;&nbsp;&nbsp; System.out.println(o); <br>}</p>
<p>它仍然不会通过编译，因为一个 List&lt;Integer&gt; 不是 一个 List&lt;Object&gt;（正如前一屏 泛型不是协变的 中所学的）。这才真正烦人 —— 现在您的泛型版本还没有普通的非泛型版本有用！</p>
<p>解决方案是使用类型通配符： </p>
<p>void printList(List&lt;?&gt; l) { <br>&nbsp; for (Object o : l) <br>&nbsp;&nbsp;&nbsp; System.out.println(o); <br>}</p>
<p>上面代码中的问号是一个类型通配符。它读作&#8220;问号&#8221;。List&lt;?&gt; 是任何泛型 List 的父类型，所以您完全可以将 List&lt;Object&gt;、List&lt;Integer&gt; 或 List&lt;List&lt;List&lt;Flutzpah&gt;&gt;&gt; 传递给 printList()。 </p>
<p><br>类型通配符的作用</p>
<p>前一屏 类型通配符 中引入了类型通配符，这让您可以声明 List&lt;?&gt; 类型的变量。您可以对这样的 List 做什么呢？非常方便，可以从中检索元素，但是不能添加元素。原因不是编译器知道哪些方法修改列表哪些方法不修改列表，而是（大多数）变化的方法比不变化的方法需要更多的类型信息。下面的代码则工作得很好： </p>
<p>List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br>li.add(new Integer(42));<br>List&lt;?&gt; lu = li;<br>System.out.println(lu.get(0));</p>
<p>为什么该代码能工作呢？对于 lu，编译器一点都不知道 List 的类型参数的值。但是编译器比较聪明，它可以做一些类型推理。在本例中，它推断未知的类型参数必须扩展 Object。（这个特定的推理没有太大的跳跃，但是编译器可以作出一些非常令人佩服的类型推理，后面就会看到（在 底层细节 一节中）。所以它让您调用 List.get() 并推断返回类型为 Object。 </p>
<p>另一方面，下面的代码不能工作： </p>
<p>List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br>li.add(new Integer(42));<br>List&lt;?&gt; lu = li;<br>lu.add(new Integer(43));&nbsp; // error</p>
<p>在本例中，对于 lu，编译器不能对 List 的类型参数作出足够严密的推理，以确定将 Integer 传递给 List.add() 是类型安全的。所以编译器将不允许您这么做。</p>
<p>以免您仍然认为编译器知道哪些方法更改列表的内容哪些不更改列表内容，请注意下面的代码将能工作，因为它不依赖于编译器必须知道关于 lu 的类型参数的任何信息：</p>
<p>List&lt;Integer&gt; li = new ArrayList&lt;Integer&gt;();<br>li.add(new Integer(42));<br>List&lt;?&gt; lu = li;<br>lu.clear();</p>
<p><br>泛型方法</p>
<p>（在 类型参数 一节中）您已经看到，通过在类的定义中添加一个形式类型参数列表，可以将类泛型化。方法也可以被泛型化，不管它们定义在其中的类是不是泛型化的。</p>
<p>泛型类在多个方法签名间实施类型约束。在 List&lt;V&gt; 中，类型参数 V 出现在 get()、add()、contains() 等方法的签名中。当创建一个 Map&lt;K, V&gt; 类型的变量时，您就在方法之间宣称一个类型约束。您传递给 add() 的值将与 get() 返回的值的类型相同。</p>
<p>类似地，之所以声明泛型方法，一般是因为您想要在该方法的多个参数之间宣称一个类型约束。例如，下面代码中的 ifThenElse() 方法，根据它的第一个参数的布尔值，它将返回第二个或第三个参数：</p>
<p>public &lt;T&gt; T ifThenElse(boolean b, T first, T second) {<br>&nbsp; return b ? first : second;<br>}</p>
<p>注意，您可以调用 ifThenElse()，而不用显式地告诉编译器，您想要 T 的什么值。编译器不必显式地被告知 T 将具有什么值；它只知道这些值都必须相同。编译器允许您调用下面的代码，因为编译器可以使用类型推理来推断出，替代 T 的 String 满足所有的类型约束：</p>
<p>String s = ifThenElse(b, "a", "b");</p>
<p>类似地，您可以调用： </p>
<p>Integer i = ifThenElse(b, new Integer(1), new Integer(2));</p>
<p>但是，编译器不允许下面的代码，因为没有类型会满足所需的类型约束： </p>
<p>String s = ifThenElse(b, "pi", new Float(3.14));</p>
<p>为什么您选择使用泛型方法，而不是将类型 T 添加到类定义呢？（至少）有两种情况应该这样做： </p>
<p>当泛型方法是静态的时，这种情况下不能使用类类型参数。</p>
<p>当 T 上的类型约束对于方法真正是局部的时，这意味着没有在相同类的另一个 方法签名中使用相同 类型 T 的约束。通过使得泛型方法的类型参数对于方法是局部的，可以简化封闭类型的签名。 </p>
<p><br>有限制类型 </p>
<p>在前一屏 泛型方法 的例子中，类型参数 V 是无约束的或无限制的 类型。有时在还没有完全指定类型参数时，需要对类型参数指定附加的约束。</p>
<p>考虑例子 Matrix 类，它使用类型参数 V，该参数由 Number 类来限制：</p>
<p>public class Matrix&lt;V extends Number&gt; { ... }</p>
<p>编译器允许您创建 Matrix&lt;Integer&gt; 或 Matrix&lt;Float&gt; 类型的变量，但是如果您试图定义 Matrix&lt;String&gt; 类型的变量，则会出现错误。类型参数 V 被判断为由 Number 限制 。在没有类型限制时，假设类型参数由 Object 限制。这就是为什么前一屏 泛型方法 中的例子，允许 List.get() 在 List&lt;?&gt; 上调用时返回 Object，即使编译器不知道类型参数 V 的类型。</p>
<p>&nbsp;</p>
<p><strong>三 一个简单的泛型类</strong></p>
<p>编写基本的容器类</p>
<p>此时，您可以开始编写简单的泛型类了。到目前为止，泛型类最常见的用例是容器类（比如集合框架）或者值持有者类（比如 WeakReference 或 ThreadLocal）。我们来编写一个类，它类似于 List，充当一个容器。其中，我们使用泛型来表示这样一个约束，即 Lhist 的所有元素将具有相同类型。为了实现起来简单，Lhist 使用一个固定大小的数组来保存值，并且不接受 null 值。</p>
<p>Lhist 类将具有一个类型参数 V（该参数是 Lhist 中的值的类型），并将具有以下方法：</p>
<p>public class Lhist&lt;V&gt; { <br>&nbsp; public Lhist(int capacity) { ... }<br>&nbsp; public int size() { ... }<br>&nbsp; public void add(V value) { ... }<br>&nbsp; public void remove(V value) { ... }<br>&nbsp; public V get(int index) { ... }<br>}</p>
<p>要实例化 Lhist，只要在声明时指定类型参数和想要的容量： </p>
<p>Lhist&lt;String&gt; stringList = new Lhist&lt;String&gt;(10);</p>
<p><br>实现构造函数 </p>
<p>在实现 Lhist 类时，您将会遇到的第一个拦路石是实现构造函数。您可能会像下面这样实现它： </p>
<p>public class Lhist&lt;V&gt; { <br>&nbsp; private V[] array;<br>&nbsp; public Lhist(int capacity) {<br>&nbsp;&nbsp;&nbsp; array = new V[capacity]; // illegal<br>&nbsp; }<br>}</p>
<p>这似乎是分配后备数组最自然的一种方式，但是不幸的是，您不能这样做。具体原因很复杂，当学习到 底层细节 一节中的&#8220;擦除&#8221;主题时，您就会明白。分配后备数组的实现方式很古怪且违反直觉。下面是构造函数的一种可能的实现（该实现使用集合类所采用的方法）：</p>
<p>public class Lhist&lt;V&gt; { <br>&nbsp; private V[] array;<br>&nbsp; public Lhist(int capacity) {<br>&nbsp;&nbsp;&nbsp; array = (V[]) new Object[capacity];<br>&nbsp; }<br>}</p>
<p><br>另外，也可以使用反射来实例化数组。但是这样做需要给构造函数传递一个附加的参数 —— 一个类常量，比如 Foo.class。后面在 Class&lt;T&gt; 一节中将讨论类常量。</p>
<p><br>实现方法 </p>
<p>实现 Lhist 的方法要容易得多。下面是 Lhist 类的完整实现：</p>
<p>public class Lhist&lt;V&gt; {<br>&nbsp;&nbsp;&nbsp; private V[] array;<br>&nbsp;&nbsp;&nbsp; private int size;</p>
<p>&nbsp;&nbsp;&nbsp; public Lhist(int capacity) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; array = (V[]) new Object[capacity];<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public void add(V value) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (size == array.length)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new IndexOutOfBoundsException(Integer.toString(size));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (value == null)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new NullPointerException();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; array[size++] = value;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public void remove(V value) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int removalCount = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i=0; i&lt;size; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (array[i].equals(value))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ++removalCount;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (removalCount &gt; 0) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; array[i-removalCount] = array[i];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; array[i] = null;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; size -= removalCount;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; public int size() { return size; }</p>
<p>&nbsp;&nbsp;&nbsp; public V get(int i) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (i &gt;= size)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new IndexOutOfBoundsException(Integer.toString(i));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return array[i];<br>&nbsp;&nbsp;&nbsp; }<br>}</p>
<p>注意，您在将会接受或返回 V 的方法中使用了形式类型参数 V，但是您一点也不知道 V 具有什么样的方法或域，因为这些对泛型代码是不可知的。 </p>
<p><br>使用 Lhist 类</p>
<p>使用 Lhist 类很容易。要定义一个整数 Lhist，只需要在声明和构造函数中为类型参数提供一个实际值即可：</p>
<p>Lhist&lt;Integer&gt; li = new Lhist&lt;Integer&gt;(30);</p>
<p>编译器知道，li.get() 返回的任何值都将是 Integer 类型，并且它还强制传递给 li.add() 或 li.remove() 的任何东西都是 Integer。除了实现构造函数的方式很古怪之外，您不需要做任何十分特殊的事情以使 Lhist 是一个泛型类。 </p>
<p>&nbsp;</p>
<p><strong>四 Java类库中的泛型</strong></p>
<p>集合类</p>
<p>到目前为止，Java 类库中泛型支持存在最多的地方就是集合框架。就像容器类是 C++ 语言中模板的主要动机一样（参阅 附录 A：与 C++ 模板的比较）（尽管它们随后用于很多别的用途），改善集合类的类型安全是 Java 语言中泛型的主要动机。集合类也充当如何使用泛型的模型，因为它们演示了泛型的几乎所有的标准技巧和方言。 </p>
<p>所有的标准集合接口都是泛型化的 —— Collection&lt;V&gt;、List&lt;V&gt;、Set&lt;V&gt; 和 Map&lt;K,V&gt;。类似地，集合接口的实现都是用相同类型参数泛型化的，所以 HashMap&lt;K,V&gt; 实现 Map&lt;K,V&gt; 等。</p>
<p>集合类也使用泛型的许多&#8220;技巧&#8221;和方言，比如上限通配符和下限通配符。例如，在接口 Collection&lt;V&gt; 中，addAll 方法是像下面这样定义的： </p>
<p>interface Collection&lt;V&gt; {<br>&nbsp; boolean addAll(Collection&lt;? extends V&gt;);<br>}</p>
<p>该定义组合了通配符类型参数和有限制类型参数，允许您将 Collection&lt;Integer&gt; 的内容添加到 Collection&lt;Number&gt;。</p>
<p>如果类库将 addAll() 定义为接受 Collection&lt;V&gt;，您就不能将 Collection&lt;Integer&gt; 的内容添加到 Collection&lt;Number&gt;。不是限制 addAll() 的参数是一个与您将要添加到的集合包含相同类型的集合，而有可能建立一个更合理的约束，即传递给 addAll() 的集合的元素 适合于添加到您的集合。有限制类型允许您这样做，并且使用有限制通配符使您不需要使用另一个不会用在其他任何地方的占位符名称。 </p>
<p>应该可以将 addAll() 的类型参数定义为 Collection&lt;V&gt;。但是，这不但没什么用，而且还会改变 Collection 接口的语义，因为泛型版本的语义将会不同于非泛型版本的语义。这阐述了泛型化一个现有的类要比定义一个新的泛型类难得多，因为您必须注意不要更改类的语义或者破坏现有的非泛型代码。 </p>
<p>作为泛型化一个类（如果不小心的话）如何会更改其语义的一个更加微妙的例子，注意 Collection.removeAll() 的参数的类型是 Collection&lt;?&gt;，而不是 Collection&lt;? extends V&gt;。这是因为传递混合类型的集合给 removeAll() 是可接受的，并且更加限制地定义 removeAll 将会更改方法的语义和有用性。 </p>
<p>&nbsp;</p>
<p>其他容器类 </p>
<p>除了集合类之外，Java 类库中还有几个其他的类也充当值的容器。这些类包括 WeakReference、SoftReference 和 ThreadLocal。它们都已经在其包含的值的类型上泛型化了，所以 WeakReference&lt;T&gt; 是对 T 类型的对象的弱引用，ThreadLocal&lt;T&gt; 则是到 T 类型的线程局部变量的句柄。 </p>
<p><br>泛型不止用于容器 </p>
<p>泛型最常见最直观的使用是容器类，比如集合类或引用类（比如 WeakReference&lt;T&gt;）。Collection&lt;V&gt; 中类型参数的含义很明显 —— &#8220;一个所有值都是 V 类型的集合&#8221;。类似地，ThreadLocal&lt;T&gt; 也有一个明显的解释 —— &#8220;一个其类型是 T 的线程局部变量&#8221;。但是，泛型规格说明中没有指定容积。 </p>
<p>像 Comparable&lt;T&gt; 或 Class&lt;T&gt; 这样的类中类型参数的含义更加微妙。有时，就像 Class&lt;T&gt; 中一样，类型变量主要是帮助编译器进行类型推理。有时，就像隐含的 Enum&lt;E extends Enum&lt;E&gt;&gt; 中一样，类型变量只是在类层次结构上加一个约束。</p>
<p><br>Comparable&lt;T&gt; </p>
<p>Comparable 接口已经泛型化了，所以实现 Comparable 的对象声明它可以与什么类型进行比较。（通常，这是对象本身的类型，但是有时也可能是父类。）</p>
<p>public interface Comparable&lt;T&gt; { <br>&nbsp; public boolean compareTo(T other);<br>} </p>
<p>所以 Comparable 接口包含一个类型参数 T，该参数是一个实现 Comparable 的类可以与之比较的对象的类型。这意味着如果定义一个实现 Comparable 的类，比如 String，就必须不仅声明类支持比较，还要声明它可与什么比较（通常是与它本身比较）： </p>
<p>public class String implements Comparable&lt;String&gt; { ... }</p>
<p>现在来考虑一个二元 max() 方法的实现。您想要接受两个相同类型的参数，二者都是 Comparable，并且相互之间是 Comparable。幸运的是，如果使用泛型方法和有限制类型参数的话，这相当直观： </p>
<p>public static &lt;T extends Comparable&lt;T&gt;&gt; T max(T t1, T t2) {<br>&nbsp; if (t1.compareTo(t2) &gt; 0)<br>&nbsp;&nbsp;&nbsp; return t1;<br>&nbsp; else <br>&nbsp;&nbsp;&nbsp; return t2;<br>}</p>
<p>在本例中，您定义了一个泛型方法，在类型 T 上泛型化，您约束该类型扩展（实现） Comparable&lt;T&gt;。两个参数都必须是 T 类型，这表示它们是相同类型，支持比较，并且相互可比较。容易！ </p>
<p>更好的是，编译器将使用类型推理来确定当调用 max() 时 T 的值表示什么意思。所以根本不用指定 T，下面的调用就能工作：</p>
<p>String s = max("moo", "bark");</p>
<p>编译器将计算出 T 的预定值是 String，因此它将进行编译和类型检查。但是如果您试图用不实现 Comparable&lt;X&gt; 的 类 X 的参数调用 max()，那么编译器将不允许这样做。 </p>
<p>&nbsp;</p>
<p>Class&lt;T&gt; </p>
<p>类 Class 已经泛型化了，但是很多人一开始都感觉其泛型化的方式很混乱。Class&lt;T&gt; 中类型参数 T 的含义是什么？事实证明它是所引用的类接口。怎么会是这样的呢？那是一个循环推理？如果不是的话，为什么这样定义它？</p>
<p>在以前的 JDK 中，Class.newInstance() 方法的定义返回 Object，您很可能要将该返回类型强制转换为另一种类型：</p>
<p>class Class { <br>&nbsp; Object newInstance();<br>}</p>
<p>但是使用泛型，您定义 Class.newInstance() 方法具有一个更加特定的返回类型： </p>
<p>class Class&lt;T&gt; { <br>&nbsp; T newInstance();<br>}</p>
<p>如何创建一个 Class&lt;T&gt; 类型的实例？就像使用非泛型代码一样，有两种方式：调用方法 Class.forName() 或者使用类常量 X.class。Class.forName() 被定义为返回 Class&lt;?&gt;。另一方面，类常量 X.class 被定义为具有类型 Class&lt;X&gt;，所以 String.class 是 Class&lt;String&gt; 类型的。 </p>
<p>让 Foo.class 是 Class&lt;Foo&gt; 类型的有什么好处？大的好处是，通过类型推理的魔力，可以提高使用反射的代码的类型安全。另外，还不需要将 Foo.class.newInstance() 强制类型转换为 Foo。</p>
<p>考虑一个方法，它从数据库检索一组对象，并返回 JavaBeans 对象的一个集合。您通过反射来实例化和初始化创建的对象，但是这并不意味着类型安全必须完全被抛至脑后。考虑下面这个方法：</p>
<p>public static&lt;T&gt; List&lt;T&gt; getRecords(Class&lt;T&gt; c, Selector s) {<br>&nbsp; // Use Selector to select rows<br>&nbsp; List&lt;T&gt; list = new ArrayList&lt;T&gt;();<br>&nbsp; for (/* iterate over results */) {<br>&nbsp;&nbsp;&nbsp; T row = c.newInstance();<br>&nbsp;&nbsp;&nbsp; // use reflection to set fields from result<br>&nbsp;&nbsp;&nbsp; list.add(row);&nbsp; <br>&nbsp; }<br>&nbsp; return list;<br>}</p>
<p>可以像下面这样简单地调用该方法： </p>
<p>List&lt;FooRecord&gt; l = getRecords(FooRecord.class, fooSelector);</p>
<p>编译器将会根据 FooRecord.class 是 Class&lt;FooRecord&gt; 类型的这一事实，推断 getRecords() 的返回类型。您使用类常量来构造新的实例并提供编译器在类型检查中要用到的类型信息。</p>
<p>&nbsp;</p>
<p>用 Class&lt;T&gt; 替换 T[]</p>
<p>Collection 接口包含一个方法，用于将集合的内容复制到一个调用者指定类型的数组中： </p>
<p>public Object[] toArray(Object[] prototypeArray) { ... }</p>
<p>toArray(Object[]) 的语义是，如果传递的数组足够大，就会使用它来保存结果，否则，就会使用反射分配一个相同类型的新数组。一般来说，单独传递一个数组作为参数来提供想要的返回类型是一个小技巧，但是在引入泛型之前，这是与方法交流类型信息最方便的方式。</p>
<p>有了泛型，就可以用一种更加直观的方式来做这件事。不像上面这样定义 toArray()，泛型 toArray() 可能看起来像下面这样：</p>
<p>public&lt;T&gt; T[] toArray(Class&lt;T&gt; returnType)</p>
<p>调用这样一个 toArray() 方法很简单： </p>
<p>FooBar[] fba = something.toArray(FooBar.class);</p>
<p>Collection 接口还没有改变为使用该技术，因为这会破坏许多现有的集合实现。但是如果使用泛型从新构建 Collection，则当然会使用该方言来指定它想要返回值是哪种类型。 </p>
<p>&nbsp;</p>
<p>Enum&lt;E&gt;</p>
<p>JDK 5.0 中 Java 语言另一个增加的特性是枚举。当您使用 enum 关键字声明一个枚举时，编译器就会在内部为您生成一个类，用于扩展 Enum 并为枚举的每个值声明静态实例。所以如果您说： </p>
<p>public enum Suit {HEART, DIAMOND, CLUB, SPADE};</p>
<p>编译器就会在内部生成一个叫做 Suit 的类，该类扩展 java.lang.Enum&lt;Suit&gt; 并具有叫做 HEART、DIAMOND、CLUB 和 SPADE 的常量（public static final）成员，每个成员都是 Suit 类。 </p>
<p>与 Class 一样，Enum 也是一个泛型类。但是与 Class 不同，它的签名稍微更复杂一些： </p>
<p>class Enum&lt;E extends Enum&lt;E&gt;&gt; { . . . }</p>
<p>这究竟是什么意思？这难道不会导致无限递归？ </p>
<p>我们逐步来分析。类型参数 E 用于 Enum 的各种方法中，比如 compareTo() 或 getDeclaringClass()。为了这些方法的类型安全，Enum 类必须在枚举的类上泛型化。 </p>
<p>所以 extends Enum&lt;E&gt; 部分如何理解？该部分又具有两个部分。第一部分指出，作为 Enum 的类型参数的类本身必须是 Enum 的子类型，所以您不能声明一个类 X 扩展 Enum&lt;Integer&gt;。第二部分指出，任何扩展 Enum 的类必须传递它本身 作为类型参数。您不能声明 X 扩展 Enum&lt;Y&gt;，即使 Y 扩展 Enum。 </p>
<p>总之，Enum 是一个参数化的类型，只可以为它的子类型实例化，并且这些子类型然后将根据子类型来继承方法。幸运的是，在 Enum 情况下，编译器为您做这些工作，一切都很好。 </p>
<p>&nbsp;</p>
<p>与非泛型代码相互操作</p>
<p>数百万行现有代码使用已经泛型化的 Java 类库中的类，比如集合框架、Class 和 ThreadLocal。JDK 5.0 中的改进不要破坏所有这些代码是很重要的，所以编译器允许您在不指定其类型参数的情况下使用泛型类。</p>
<p>当然，以&#8220;旧方式&#8221;做事没有新方式安全，因为忽略了编译器准备提供的类型安全。如果您试图将 List&lt;String&gt; 传递给一个接受 List 的方法，它将能够工作，但是编译器将会发出一个可能丧失类型安全的警告，即所谓的&#8220;unchecked conversion（不检查转换）&#8221;警告。 </p>
<p>没有类型参数的泛型，比如声明为 List 类型而不是 List&lt;Something&gt; 类型的变量，叫做原始类型。原始类型与参数化类型的任何实例化是赋值兼容的，但是这样的赋值会生成 unchecked-conversion 警告。</p>
<p>为了消除一些 unchecked-conversion 警告，假设您不准备泛型化所有的代码，您可以使用通配符类型参数。使用 List&lt;?&gt; 而不使用 List。List 是原始类型；List&lt;?&gt; 是具有未知类型参数的泛型。编译器将以不同的方式对待它们，并很可能发出更少的警告。 </p>
<p>无论在哪种情况下，编译器在生成字节码时都会生成强制类型转换，所以生成的字节码在每种情况下都不会比没有泛型时更不安全。如果您设法通过使用原始类型或类文件来破坏类型安全，就会得到与不使用泛型时得到的相同的 ClassCastException 或 ArrayStoreException。 </p>
<p><br>已检查集合</p>
<p>作为从原始集合类型迁移到泛型集合类型的帮助，集合框架添加了一些新的集合包装器，以便为一些类型安全 bug 提供早期警告。就像 Collections.unmodifiableSet() 工厂方法用一个不允许任何修改的 Set 包装一个现有 Set 一样，Collections.checkedSet()（以及 checkedList() 和 checkedMap()）工厂方法创建一个包装器（或者视图）类，以防止您将错误类型的变量放在集合中。</p>
<p>checkedXxx() 方法都接受一个类常量作为参数，所以它们可以（在运行时）检查这些修改是允许的。典型的实现可能像下面这样： </p>
<p><br>public class Collections {&nbsp; <br>&nbsp; public static &lt;E&gt; Collection&lt;E&gt; checkedCollection(Collection&lt;E&gt; c, Class&lt;E&gt; type ) { <br>&nbsp;&nbsp;&nbsp; return new CheckedCollection&lt;E&gt;(c, type); <br>&nbsp; } </p>
<p>&nbsp; private static class CheckedCollection&lt;E&gt; implements Collection&lt;E&gt; { <br>&nbsp;&nbsp;&nbsp; private final Collection&lt;E&gt; c; <br>&nbsp;&nbsp;&nbsp; private final Class&lt;E&gt; type; </p>
<p>&nbsp;&nbsp;&nbsp; CheckedCollection(Collection&lt;E&gt; c, Class&lt;E&gt; type) { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.c = c; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.type = type; <br>&nbsp;&nbsp;&nbsp; } </p>
<p>&nbsp;&nbsp;&nbsp; public boolean add(E o) { <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!type.isInstance(o)) <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new ClassCastException(); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return c.add(o); <br>&nbsp;&nbsp;&nbsp; } <br>&nbsp; } <br>}&nbsp; </p>
<p>&nbsp;</p>
<p><strong>五 底层细节</strong></p>
<p><br>擦除</p>
<p>也许泛型最具挑战性的方面是擦除（erasure），这是 Java 语言中泛型实现的底层技术。擦除意味着编译器在生成类文件时基本上会抛开参数化类的大量类型信息。编译器用它的强制类型转换生成代码，就像程序员在泛型出现之前手工所做的一样。区别在于，编译器开始已经验证了大量如果没有泛型就不会验证的类型安全约束。</p>
<p>通过擦除实现泛型的含意是很重要的，并且初看也是混乱的。尽管不能将 List&lt;Integer&gt; 赋给 List&lt;Number&gt;，因为它们是不同的类型，但是 List&lt;Integer&gt; 和 List&lt;Number&gt; 类型的变量是相同的类！要明白这一点，请评价下面的代码：</p>
<p>new List&lt;Number&gt;().getClass() == new List&lt;Integer&gt;().getClass()</p>
<p>编译器只为 List 生成一个类。当生成了 List 的字节码时，将很少剩下其类型参数的的跟踪。</p>
<p>当生成泛型类的字节码时，编译器用类型参数的擦除 替换类型参数。对于无限制类型参数（&lt;V&gt;），它的擦除是 Object。对于上限类型参数（&lt;K extends Comparable&lt;K&gt;&gt;），它的擦除是其上限（在本例中是 Comparable）的擦除。对于具有多个限制的类型参数，使用其最左限制的擦除。 </p>
<p>如果检查生成的字节码，您无法说出 List&lt;Integer&gt; 和 List&lt;String&gt; 的代码之间的区别。类型限制 T 在字节码中被 T 的上限所取代，该上限一般是 Object。</p>
<p>&nbsp;</p>
<p>多重限制</p>
<p>一个类型参数可以具有多个限制。当您想要约束一个类型参数比如说同时为 Comparable 和 Serializable 时，这将很有用。多重限制的语法是用&#8220;与&#8221;符号分隔限制： </p>
<p><br>class C&lt;T extends Comparable&lt;? super T&gt; &amp; Serializable&gt; </p>
<p>通配符类型可以具有单个限制 —— 上限或者下限。一个指定的类型参数可以具有一个或多个上限。具有多重限制的类型参数可以用于访问它的每个限制的方法和域。 </p>
<p><br>类型形参和类型实参 </p>
<p>在参数化类的定义中，占位符名称（比如 Collection&lt;V&gt; 中的 V）叫做类型形参（type parameter），它们类似于方法定义中的形式参数。在参数化类的变量的声明中，声明中指定的类型值叫做类型实参（type argument），它们类似于方法调用中的实际参数。但是实际中二者一般都通称为&#8220;类型参数&#8221;。所以给出定义： </p>
<p>interface Collection&lt;V&gt; { ... }</p>
<p>和声明： </p>
<p>Collection&lt;String&gt; cs = new HashSet&lt;String&gt;();</p>
<p>那么，名称 V（它可用于整个 Collection 接口体内）叫做一个类型形参。在 cs 的声明中，String 的两次使用都是类型实参（一次用于 Collection&lt;V&gt;，另一次用于 HashSet&lt;V&gt;）。</p>
<p>关于何时可以使用类型形参，存在一些限制。大多数时候，可以在能够使用实际类型定义的任何地方使用类型形参。但是有例外情况。不能使用它们创建对象或数组，并且不能将它们用于静态上下文中或者处理异常的上下文中。还不能将它们用作父类型（class Foo&lt;T&gt; extends T），不能用于 instanceof 表达式中，不能用作类常量。 </p>
<p>类似地，关于可以使用哪些类型作为类型实参，也存在一些限制。类型实参必须是引用类型（不是基本类型）、通配符、类型参数，或者其他参数化类型的实例化。所以您可以定义 List&lt;String&gt;（引用类型）、List&lt;?&gt;（通配符）或者 List&lt;List&lt;?&gt;&gt;（其他参数化类型的实例化）。在带有类型形参 T 的参数化类型的定义中，您也可以声明 List&lt;T&gt;（类型形参）。</p>
<p>&nbsp;</p>
<p><strong>六 结束语</strong> </p>
<p>泛型的引入是对 Java 语言和 Java 类库的一个主要改变。泛型可以提高 Java 应用程序的类型安全、可维护性和可靠性，代价是一些附加的复杂性。</p>
<p>已经做了非常小心的处理，以确保现有的类将继续与 JDK 5.0 中的泛型化类库一起工作，所以您可以根据自己的意愿选择从什么时候开始使用泛型。</p>
<p><br>&nbsp;</p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/141452.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-08-30 17:25 <a href="http://www.blogjava.net/orangelizq/archive/2007/08/30/141452.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]世界各地程序开发高手谈Java</title><link>http://www.blogjava.net/orangelizq/archive/2007/08/28/140661.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Tue, 28 Aug 2007 13:17:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/08/28/140661.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/140661.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/08/28/140661.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/140661.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/140661.html</trackback:ping><description><![CDATA[<p><strong>一、简介</strong> </p>
<p>2005年5月标记着自从Sun第一个引入Java技术以来经历了整整10个年头。在过去的10年中，Java语言已经变为一个平台，一个社团和一个生态系统。在这些环境下，软件用户、开源工程以及个体开发者等茁壮成长。今天，大约有四百五十万Java开发者和大约1.4亿台设备上使用着Java。 </p>
<p>我们不空谈Java的成功故事。代替的是通过分发调查问卷（每发展一年提问一个问题）来搜集个人的Java使用经验，这些人大都是精于Java技术的开发者。回答者包括各个层面的人，从咨询师、作家到BEA、IBM、Sun等大公司的CTO和资深技术人员。 </p>
<p>下面是一些我们收到的令人瞠目的回答。 </p>
<p>1. 你是如何开始使用Java编程的？ </p>
<p>Java吸引了每个人的注意，从程序开发人员到&#8230;&#8230; </p>
<p>当Java还称为Oak的时候我就用它进行编程，大约是在1993年的下半年或者是1994年的上半年。当时我在Sun Labs率领一个研究小组。一旦我们使用Java开发，我们就注意到使用Java的最大优点是，它能以一种合理的安全的方式从一个虚拟机迁移到另一台虚拟机。，这是Jim Waldo（SunLabs的杰出工程师）的回答。 </p>
<p>Rod Smith，作为IBM软件开发小组中处理突发技术的副主席，写道：我们看到了，Java平台是一种足够好的技术，它具有在计算机工业界成为一支重大的独成系列的力量的潜力。我们认为，我们最好要赶上这班时代列车并采纳Sun的Java技术而不再依赖于以前的模式-自己开发每一件东西。 </p>
<p>Ed Cobb，BEA Systems公司CTO办公室架构标准的副主席，写道：我们可以选择其它主流的面向对象语言，但是Java提供了一种更好的组合特点使它适合于团队环境下的大规模开发。 </p>
<p>在过去十年中的大部分时间里，我受雇于Sun。因此，我不得不说，Java突然来到我的身边。如果不是这一突然性的到来，我们也会需要另外一种似Java的东西来叩开业界中正迅速发展的网络计算环境的大门。-Rob Gingell，Cassatt Corporation的执行副主席和CTO。 </p>
<p>...对那些勤奋的计算机系的大学生们... </p>
<p>Michael Pilone，作为Blueprint Technologies的资深软件工程师，他的回答是：当时我盘算着我将来得找一份体面的工作，所以虽然我在大学中所学全部是C++，但是我还是另外自学了Java。 </p>
<p>在我上硕士期间，我的指导教授让我用Java工作，结果我用Java 1.0 beta版完成了我所有的功课。-Raghu Donepudi，环球计算机公司的系统开发领导者。 </p>
<p>...对那些热衷于编程的狂热者... </p>
<p>我一毕业即用Java开始工作，我惊喜于Java的WORA的前景和Applets。-Jack Herrington,作为Code Generation in Action (Manning)的作者和Code Generation Network的编辑。 </p>
<p>我甚至在1.0版本之前就开始学习Java了，因为其开发目标是作为微软工具（VB和Visual C++）与Pascal语言的可选替代者，Laurent Ploix写道，他是SunGard-Finance公司的工程总管和技术架构师。 </p>
<p>在1997年，我带着一本手册作为度假读物，在海滩度假的日子里，我沉浸在Java的优美之中。我转向了Java，并宣布C++是一种传统的语言，并发誓一旦选择了Java，永不回头。-Vlad Patryshev,Borland公司Java Business Unit的前任R&amp;D工程师。 </p>
<p>2.Java宣称的编写一次，到处运行效果怎么样（WORA）？一直以来，WORA的重要性改变了多少？ </p>
<p>Java虚拟机，至少在概念上，是Java背后最强有力的思想。它的确实现了它许诺的轻便性。-Bruce Tate，J2Life, LLC（一家Java技术咨询公司）的主席。 </p>
<p>应用服务器和J2EE应用程序可以在多种平台上良好地迁移。我认为在客户端上实现WORA还是相当值得怀疑的，也许永远不会实现。-Michael Pilone </p>
<p>它对我简直是一个不可捉摸的平台。-Vlad Patryshev </p>
<p>Java的早期成功根本上在于WORA。与其它可选择工具相比，Java带给了（并将进一步带给）SI（系统集成商），ISV（独立软件开发商）和软件工程师们一个根本不同的经济环境。-Rod Smith </p>
<p>Java在WORA方面的性能：a)比任何它之前的工具要好；b)就目前而言，与另外一些可选工具相比仍然要好得多；c)将作为Java价值的一个关键部分继续保持下去。-Rob Gingell </p>
<p>WORA每次都带给我极大的便利。我总是在Windows平台上进行我的Java开发；但是，我总是毫不费劲地把这些代码发布到Solaris 或者Linux 环境中去。-Eric Bruno，一个独立的咨询师，擅长于软件架构，企业Java和C++开发。 </p>
<p>你可以把字节代码转换成MSIL（MS中间语言），而且你可以在J#中运行Java程序。这使我们可以针对我们产品的Java和.NET环境只保留一份代码即可。-Michael R. Smialek，Knowledge Dynamics的主席和CEO </p>
<p>我经常跨Windows，Linux和Sun Solaris开发，测试和发布Java代码，而仅需对XML配置文件作较少的改动。但是，随着面向服务的结构的出现，WORA的重要性已经减弱了。-Kyle Gabhart，作家和独立咨询师 </p>
<p>象Perl，Ruby和Python等语言一样轻便。-Jack Herrington </p>
<p>一些人认为，由于通用操作系统的数目的下降，WORA将变得不再如以前那么重要。事实上，即使只有两种合理的可能的目标平台存在，WORA就一直是重要的。-Ed Cobb </p>
<p><strong>二、客户端Java及开发工具选择</strong> </p>
<p>3.你用Java编写过多少代码？估计你将来的工作有多少会用Java实现？ </p>
<p>几乎所的的回答者声称Java是他们主要的产品编码工具，大多数人选择他们的Java使用率超过70%。没有人认为他们下一步的Java开发使用率会降低。 </p>
<p>去年，我们利用Java 技术开发出了800多个商业产品。几乎我们所有的中间件都依赖于Java运行时刻库。-IBM的Rod Smith </p>
<p>在我们的顾客中，我们仍看到具有可以预料的潜在需求的大量C/C++功能第一型的应用程序。他们经常愿意用Java进行开发，而Java虚拟机技术目前正发展到正好能够处理这些类型系统的时候。 </p>
<p>在Web应用程序开发中，我想，当人们的应用程序变得越来越大且越复杂时，我们将看到针对动态类型语言会出现一点后推力作用。他们将经受运行时刻类型异常--事实上，他们早已认识到，如果采用象Java一样的强类型语言的话，这是可以避免的。-BEA公司的Ed Cobb </p>
<p>4.你用Java开发桌面应用和服务器应用的比例为多少？如果你开发过桌面应用程序，你更喜欢用SWT还是Swing，为什么？ </p>
<p>Bruce Tate对这一问题的回答总结了所有其他人的观点：服务器端Java正是它应有的位置。 </p>
<p>至于，客户端Java开发，众说纷纭... </p>
<p>Swing太复杂，太不可预测，太难学。SWT则好一些，但一般而言，Java在用户接口设计上很不成功。-Tate，《Better, Faster, Lighter Java》和《Bitter Java》的作者 </p>
<p>我认为SWT有更好的方法，它链接到本地lib库文件以达到加快速度和一致性的目的，但是我并不喜欢这些API，因为它们暴露出太多的老式的编码技术。相比之下，Swing有一个更好些的API，但是其中充满错误、性能低下且设计糟糕。-Michael Pilone </p>
<p>我更喜欢SWT...它比Swing更具本地化，Swing而可以说只是粗略地实现了本地化。-Ed Cobb </p>
<p>我写Swing应用程序，然后使用Java Web Start来进行发布。我还没有出卖过我的SWT型程序，因为它仅有有限的跨平台支持并缺乏可靠的MVC设计。-Kyle Gabhart </p>
<p>5.你使用的Java开发环境是什么？ </p>
<p>很明显，当前流行的Eclipse框架和集成开发环境是大多数对这一问题的回答，因为其是Windows和Linux平台的主流环境。只有另外少数的回答者指定了其它几个选择： </p>
<p>我选择的平台是WinXP Pro。在安装了Cygwin和另外几个开发工具后，你就可以得到一个具有硬件支持的非常有用的系统了。-Michael Pilone </p>
<p>我特别喜欢J2SE 5。说到IDE，我更喜欢Jbuilder，其次是IDEA。IDEA中有一些巧妙的实现，但是良好的经典的Jbuilder具有我需要的任何东西。-Vlad Patryshev </p>
<p>我一直使用emacs开发而用println进行调试。最近我在使用NetBeans，已惊奇于它给我带来的巨大帮助。-Jim Waldo </p>
<p>请不要使用EJB！-Laurent Ploix </p>
<p>Caf&#233;。-Smialek </p>
<p>在必要的时候，我都使用vi进行开发。-Kyle Gabhart </p>
<p><strong>三、开源，JCP和对Java的希望列举</strong> </p>
<p>6.JCP和开源社群谁在Java更新上的贡献更大？ </p>
<p>到目前为止，应该说是开源的贡献更大。而JCP在进行实际的开发实践之前，推崇标准化的作用。EJB,日志以及持久性一直是JCP中的灾难。实际上JCP在抛弃着Java的根基。很难的问题在拐弯抹角变得易于解决，而容易的问题反而在变得越来越难于解决了。-Bruce Tate </p>
<p>开放源码的执行领导着开发过程，而JCP仅仅是定义了一些标准。-Laurent Ploix </p>
<p>如果说纯粹的革新，我将选择开源。当工程中存在漏洞需要补全时，开源是能够迅速得到响应的。而JCP目前是一种太慢的方式，以至于根本跟不上工业发展的步伐。-Michael Pilone </p>
<p>多数的革新经历了JCP模式。但是，在过去的几年中，我们看到了在开源模式中的活动不断增加的迹象。-Rod Smith (IBM) </p>
<p>为使得开源运动进行下去，JCP值得广泛的信任，它对于Java生态系统的发展起到提供一个群落中心的作用。任何一些非JCP标准的开源工程已经探索了各式各样的思想-一些是糟糕的，一些却取得了令人惊喜的成绩。JCP可以说是一场伟大的创新运动的火车头。-Ed Cobb (BEA) </p>
<p>JCP本身仅仅是定义了一些标准及相应的说明书，这在已有的工程实现中被得到支持。作为编程者，我们不想根据一段现成的Java编码来指导我们编程，而要根据一套成熟的说明书进行开发。真正有用的编程最开始往往是零碎的代码片断，经常经历一个先有代码然后有规范的说明书的过程。-Rob Gingell </p>
<p>我找到了开源的主动性，特别是从Apache到当前最具创新性的有用的软件中找到的。-Eric Bruno </p>
<p>7.Sun应该开放Java的源代码吗？ </p>
<p>你知道这个问题肯定会出现在问卷之中的。 </p>
<p>这一点并不重要。Java在走自己路的过程中，自身已经建设得足够强大。-Bruce Tate </p>
<p>如果Sun在做这样一件有益的工作，为什么要打扰他呢？-Raghu Donepudi </p>
<p>不应该。如果它实现开源，那么我们能够看到Java的许多技巧，这最终将导致一些问题的出现，例如我们现在使用应用程序服务器时所遇到的问题。-Rahul Kumar Gupta </p>
<p>是的。Sun拒绝这样做的唯一原因就是，Java完全是他们自己的产品，他们使用它来使自己的公司得以维持下去。-Jack Herrington </p>
<p>一方面，开源的思想是很有吸引力的，因为它将导致更多的错误在很短的时间内就得以修改。另一方面，它可能导致分支分派的不匹配的JAVA虚拟机的出现。-Michael Pilone </p>
<p>不。我不相信一群普普通通的所谓天才人物就能取代那些负责管理极为健全的基本概念的大家们的科学思想-其中的大多数人甚至还不能明白这个问题。-Vlad Patryshev </p>
<p>开源社团的加入将会加速创新并推动该平台的竞争性。-Rod Smith(IBM) </p>
<p>我们需要一个开源Java的主要原因是确保该平台的生命力。如果将来Sun公司发生什么事情的话，开源的Java将列入我们的保险计划中。-Ed Cobb(BEA) </p>
<p>我认为是应该的。&#8216;开源&#8217;Java并不要求Sun做什么事情，它只是要求另外一些公司或个人做一些事情。&#8216;开源&#8217;Java是不可避免的。我建议Sun积极地实现这一不可避免并为此带来的益处做一些工作。-Rob Gingell </p>
<p>我对此并不关心-Kyle Gabhart </p>
<p>8.你希望Java有怎样的改进？ </p>
<p>从较低层次上，Java需要代码的模块化，扩展和一个更具动态化的模型，以及还需要增加很多的功能以使得应用程序开发更为容易，因为你不可能仅仅利用库来实现一切。-Bruce Tate </p>
<p>内省机制（用一个类来分析JavaBean的特性）使用起来太难且过于繁重。-Laurent Ploix </p>
<p>需要加入代码使用许可证机制。-Greg Magnusson，Cyborg Spiders的Web技术开发的奠基者 </p>
<p>应该提供由开发者来进行内存管理的功能。-Raghu Donepudi </p>
<p>应该加入运算符重载功能。-Jack Herrington </p>
<p>目前迫切需要在Java中加入Jar版本机制。我记不清有多少次遇到XML分析库冲突或者日志库冲突了。-Michael Pilone </p>
<p>类对象。十年了，没有任何改变。有大量的类对象需要加上去。-Vlad Patryshev </p>
<p>Java平台目前已经变得过于复杂。我们认为Java社团需要做出更好的工作来满足独立的和中小型的商业需要，这也是为了Java继续发展、繁荣和成功的目的。-Rod Smith（IBM） </p>
<p>Java非常需要一个更为强壮的模块化的系统。当前，我们所拥有的是一些.jar文件，其结果是成了&#8216;.jar文件地狱&#8217;。但是今天还很难描述一个互有联系的模块化的系统。-Ed Cobb </p>
<p>我原先希望Java能够实现的，过去的Java并没有做到：关于日期和时间问题上有好几处从一开始就是错误的；RMI/IIOP的引入在理论上是正确的但完全没有必要；它自一开始就是自我封闭的。-Rob Gingell </p>
<p>对于类的加载和对象的Java运行时刻类型的关系的处理是一个错误，现在我们还在为之付出代价。你不可能真正确定出是否你的程序在编译时刻是类型安全的。而且，如果你在做适当动态的任何操作，你经常需要对一个给定类的正确加载作出猜测分析。-Jim Waldo </p>
<p>垃圾回收简直是个噩梦。它有可能使得没有经过良好训练的，懒散的编程人员进入到这个工业领域中。 </p>
<p>Java需要增加的另外一些功能有：操作符重载；预编译指令（#define，等等）；把声明与定义（头文件和源文件）相分离的能力；唯一的、非本机的机器标志符（用于认证之目的）。-Michael Smialek </p>
<p>迫切需要加入Code-Behind（页面代码分离）技术！！！ASP.NET和页面代码分离技术所带来的重用性与灵活性是巨大的。我希望JSP 3.0能够朝这个方向发展。-Kyle Gabhart </p>
<p>我更愿意使用Java对象来存取操作系统而不是用JNI（Java本机接口），因为大多数的Win32/Linux API都包含在Java中了。-Alexi Jordanov，OSGi技术公司ProSyst Bulgaria的项目领导者 </p>
<p><strong>四、Java带给人们的最伟大的礼物与Java的未来</strong> </p>
<p>9.Java对软件社群最大的贡献是什么？ </p>
<p>我们收到的大部分反映结果认为是平台独立性，Java平台和该语言本身的创建以及他们创建的各种社团。 </p>
<p>有两点：它使得更广泛的用户群可以接受垃圾收集语言；围绕该语言涌现出了各种集成开发环境。-Jack Herrington </p>
<p>Java真正地震撼了许多东西，这包括迫使微软设计出新的产品，诸如Visual Studio的进一步改进和.NET产品的出现等。由于Netscape选择了Java,这使得客户浏览器能够执行JavaScript。它使当今世界服务器端开发也迈出了巨大的一步，受此技术影响的站点数以百万计。-Michael Pilone </p>
<p>数百万的学生不需要学习C++。-Rob Gingell </p>
<p>能使多家厂商贯彻实施的标准化API的建立。很多厂家竞相提供该标准化接口的最好实现，这给业界创造高质量的解决方案带来了强大发展动力。-Kyle Gabhart </p>
<p>.NET-Michael Smialek </p>
<p>10.Java的未来会如何？ </p>
<p>任何一种语言总是存在其有限的技术领先周期，Java也不例外。在某种意义上看，能超过Java技术会是一种非常有趣的事情。-Bruce Tate </p>
<p>当桌面和膝上电脑要被手持设备取代时，我们可能需要一种更简单的更强有力的语言。-Raghu Donepudi </p>
<p>C#具有挤垮Java的潜力，它是一种更好的语言。-Jack Herrington </p>
<p>微软在通过.NET以一种令人难以置信的速度发展。由于拥有Windows操作系统的大量客户端用户，所以他们比Java具有更大的优势。-Michael Pilone </p>
<p>在一些新技术被编程世界接受之前，至少要经过几年的时间。例如，看起来不超过5%的Java程序员能掌握Java泛型编程。请问，又有多少人可以掌握Lock/Condition？-Vladimir Patryshev </p>
<p>当技术朝着围绕交互性的结构化（如面向服务的结构化）方向发展的时候，Java语言将继续作为一种语言在计算机工业发展中占有重要地位。-Rod Smith </p>
<p>在某种意义上看，Java 虚拟机体系所要求的限制可能有点太严肃了。但是，任何一种新的挑战者虚拟机登上舞台并用之取代Java平台都是一件很好的事情。-Ed Cobb </p>
<p>实际上我们可以开发出许多种不同的语言，其中一些可能与我们熟知的Java根本不同，但是仍旧保持应用程序二进制接口（ABI）。这是为什么Java将会保持长时期的重要性的原因。-Rob Gingell </p>
<p>Java将继续作为一股巨大的力量存在于IT业中，但是一些瞄准市场的脚本语言将会在某些行业中进一步发展壮大。-Doug Tillman，Grainger.com站点Java和Python技术的开发者 </p>
<p>任何一个人，只要他在该界业干过多年，都不会相信，存在一种技术会对要推翻自己的更新、更快或者更有效的技术产生免疫力。-Kyle Gabhart </p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/140661.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-08-28 21:17 <a href="http://www.blogjava.net/orangelizq/archive/2007/08/28/140661.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]Java Web层的下一个王者是谁？</title><link>http://www.blogjava.net/orangelizq/archive/2007/08/28/140329.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Tue, 28 Aug 2007 02:04:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/08/28/140329.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/140329.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/08/28/140329.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/140329.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/140329.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 经过数年的&#8220;框架大战&#8221;，Java</a>界的各种框架找到了自己应有的位置。
<p>　　Spring+Hibernate</a>+Struts已成为Java开发的主流体系。在这个体系中，Spring+Hibernate的地位应该说短期内是难以撼动了。除了新兴的Jboss Seam作为挑战者之外，几乎难有劲敌。有趣的是当初Spring、Hibernate作为挑战者，将官方的EJB</a>成功挑落马下;这次反倒是官方的EBJ3成了挑战者，不知结局如何。</p>
<p>　　Java B/S编程中历来战火最激烈的其实还在Web</a>层，框架的数量最多，争议最大。</p>
<p>　　一切由Struts而起，而Struts最终也坐稳了第一个时代的王座。在技术层面，Struts 1.x已经被无数人抱怨过、批评过，但终于还是稳坐王位，这充分说明了习惯的力量。&#8220;稳定压倒一切&#8221;，这句话在IT技术领域仍旧适用。</p>
<p>　　其实IT应用技术，什么新鲜玩意并不难学。难的是标准化和规范化。每个程序员都有自己的思路和习惯，写出来的代码自然是五花八门。Java何以成为编程界的老大，很重要的一点在于Java的规范化。这种规范化很高的语言适用于多人合作的大型项目，便于沟通和理解，也就便于集成和维护。Java世界为什么会框架横飞，说到底还是规范化的需要。纯JSP</a>和Struts写Web谁快，摆明了是JSP。那撑饱了用Struts?原因在于100个人写出来的JSP，有100种写法;而100个人写出来的Struts，基本相似。Struts之成功，正缘于其在Java Web层的规范化方面所做出的贡献。</p>
<p>　　然而长江后浪推前浪，Struts 1.x的技术缺陷毕竟是隐患。</p>
<p>　　Sun力推JSF，打算一雪Web层框架缺失之耻。可惜JSF既要沿用Swing</a>的技术路线，又要学ASP</a>.net</a>，还要照顾产商的IDE，结果搞了个四不象，弄得里外不是人。当然Sun的技术实力毕竟是超强的，只要别重蹈EJB的覆辙，拿出点专断的精神(像这两年的NetBeans)，做出像Swing那样水准的东西，JSF当大有作为。JSF现在比较有优势的是对Ajax的集成，这一点走在了其他框架的前面。</p>
<p>　　而Struts就更没有志气了，把WebWork换了个标签，凑出个Struts2，Bug多多。说实在话，根本不如原版的WebWork。如果不是靠了原先的fans捧场，根本就没得混。不过Struts原本就不是以技术取胜的，靠的是抢占先机带来的习惯优势。如果原先的fans们在这两年内都能转到Struts2，那么Struts二世仍将雄霸天下。</p>
<p>　　综上所述，未来两年，JSF与Struts将展开Java Web框架的最终战争。</p>
<p>　　以笔者愚见，结局有二：一是不论Struts还是JSF获胜，Java Web层都将结束混战的局面，这对Java Web开发的标准化是非常有利的，并有助于巩固Java在B/S界的地位;二是Struts1.x、Struts2、JSF三分天下，必然从整体上削弱Java在B/S界的竞争力，并将进一步被RoR、ASP.NET</a>、PHP</a>所蚕食。<br></p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/140329.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-08-28 10:04 <a href="http://www.blogjava.net/orangelizq/archive/2007/08/28/140329.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] 谈谈Unicode编码，简要解释UCS、UTF、BMP、BOM等名词</title><link>http://www.blogjava.net/orangelizq/archive/2007/08/02/134021.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Thu, 02 Aug 2007 08:54:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/08/02/134021.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/134021.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/08/02/134021.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/134021.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/134021.html</trackback:ping><description><![CDATA[<h2><span style="FONT-SIZE: 12pt"><font face=#ce_temp_font#>
<h2></font>&nbsp;</span>谈谈Unicode编码，简要解释UCS、UTF、BMP、BOM等名词</h2>
</h2>
<p>这是一篇程序员写给程序员的趣味读物。所谓趣味是指可以比较轻松地了解一些原来不清楚的概念，增进知识，类似于打RPG游戏的升级。整理这篇文章的动机是两个问题：</p>
<dl>
<dt>问题一：
<dd>
<p>使用Windows记事本的&#8220;另存为&#8221;，可以在GBK、Unicode、Unicode big endian和UTF-8这几种编码方式间相互转换。同样是txt文件，Windows是怎样识别编码方式的呢？</p>
<p>我很早前就发现Unicode、Unicode big endian和UTF-8编码的txt文件的开头会多出几个字节，分别是FF、FE（Unicode）,FE、FF（Unicode big endian）,EF、BB、BF（UTF-8）。但这些标记是基于什么标准呢？</p>
<dt>问题二：
<dd>最近在网上看到一个ConvertUTF.c，实现了UTF-32、UTF-16和UTF-8这三种编码方式的相互转换。对于Unicode(UCS2)、GBK、UTF-8这些编码方式，我原来就了解。但这个程序让我有些糊涂，想不起来UTF-16和UCS2有什么关系。 </dd></dl>
<p>查了查相关资料，总算将这些问题弄清楚了，顺带也了解了一些Unicode的细节。写成一篇文章，送给有过类似疑问的朋友。本文在写作时尽量做到通俗易懂，但要求读者知道什么是字节，什么是十六进制。</p>
<h3>0、big endian和little endian</h3>
<p>big endian和little endian是CPU处理多字节数的不同方式。例如&#8220;汉&#8221;字的Unicode编码是6C49。那么写到文件里时，究竟是将6C写在前面，还是将49写在前面？如果将6C写在前面，就是big endian。如果将49写在前面，就是little endian。</p>
<p>&#8220;endian&#8221;这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开，由此曾发生过六次叛乱，一个皇帝送了命，另一个丢了王位。</p>
<p>我们一般将endian翻译成&#8220;字节序&#8221;，将big endian和little endian称作&#8220;大尾&#8221;和&#8220;小尾&#8221;。</p>
<h3>1、字符编码、内码，顺带介绍汉字编码</h3>
<p>字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用7位的ASCII编码，为了处理汉字，程序员设计了用于简体中文的GB2312和用于繁体中文的big5。</p>
<p>GB2312(1980年)一共收录了7445个字符，包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7，低字节从A1-FE，占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。</p>
<p>GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号，它分为汉字区和图形符号区。汉字区包括21003个字符。</p>
<p>从ASCII、GB2312到GBK，这些编码方法是向下兼容的，即同一个字符在这些方案中总是有相同的编码，后面的标准支持更多的字符。在这些编码中，英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼，GB2312、GBK都属于双字节字符集 (DBCS)。</p>
<p>2000年的GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字，同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。从汉字字汇上说，GB18030在GB13000.1的20902个汉字的基础上增加了CJK扩展A的6582个汉字（Unicode码0x3400-0x4db5），一共收录了27484个汉字。</p>
<p>CJK就是中日韩的意思。Unicode为了节省码位，将中日韩三国语言中的文字统一编码。GB13000.1就是ISO/IEC 10646-1的中文版，相当于Unicode 1.1。</p>
<p>GB18030的编码采用单字节、双字节和4字节方案。其中单字节、双字节和GBK是完全兼容的。4字节编码的码位就是收录了CJK扩展A的6582个汉字。 例如：UCS的0x3400在GB18030中的编码应该是8139EF30，UCS的0x3401在GB18030中的编码应该是8139EF31。</p>
<p>微软提供了GB18030的升级包，但这个升级包只是提供了一套支持CJK扩展A的6582个汉字的新字体：新宋体-18030，并不改变内码。Windows 的内码仍然是GBK。</p>
<p>这里还有一些细节：</p>
<ul>
    <li>
    <p>GB2312的原文还是区位码，从区位码到内码，需要在高字节和低字节上分别加上A0。</p>
    <li>
    <p>对于任何字符编码，编码单元的顺序是由编码方案指定的，与endian无关。例如GBK的编码单元是字节，用两个字节表示一个汉字。 这两个字节的顺序是固定的，不受CPU字节序的影响。UTF-16的编码单元是word（双字节），word之间的顺序是编码方案指定的，word内部的字节排列才会受到endian的影响。后面还会介绍UTF-16。</p>
    <li>
    <p>GB2312的两个字节的最高位都是1。但符合这个条件的码位只有128*128=16384个。所以GBK和GB18030的低字节最高位都可能不是1。不过这不影响DBCS字符流的解析：在读取DBCS字符流时，只要遇到高位为1的字节，就可以将下两个字节作为一个双字节编码，而不用管低字节的高位是什么。</p>
    </li>
</ul>
<h3>2、Unicode、UCS和UTF</h3>
<p>前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容（更准确地说，是与ISO-8859-1兼容），与GB码不兼容。例如&#8220;汉&#8221;字的Unicode编码是6C49，而GB码是BABA。</p>
<p>Unicode也是一种字符编码方法，不过它是由国际组织设计，可以容纳全世界所有语言文字的编码方案。Unicode的学名是"Universal Multiple-Octet Coded Character Set"，简称为UCS。UCS可以看作是"Unicode Character Set"的缩写。</p>
<p>根据维基百科全书(http://zh.wikipedia.org/wiki/)的记载：历史上存在两个试图独立设计Unicode的组织，即国际标准化组织（ISO）和一个软件制造商的协会（unicode.org）。ISO开发了ISO 10646项目，Unicode协会开发了Unicode项目。</p>
<p>在1991年前后，双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果，并为创立一个单一编码表而协同工作。从Unicode2.0开始，Unicode项目采用了与ISO 10646-1相同的字库和字码。</p>
<p>目前两个项目仍都存在，并独立地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode 4.1.0。ISO的最新标准是ISO 10646-3:2003。</p>
<p>UCS只是规定如何编码，并没有规定如何传输、保存这个编码。例如&#8220;汉&#8221;字的UCS编码是6C49，我可以用4个ascii数字来传输、保存这个编码；也可以用utf-8编码:3个连续的字节E6 B1 89来表示它。关键在于通信双方都要认可。UTF-8、UTF-7、UTF-16都是被广泛接受的方案。UTF-8的一个特别的好处是它与ISO-8859-1完全兼容。UTF是&#8220;UCS Transformation Format&#8221;的缩写。</p>
<p>IETF的RFC2781和RFC3629以RFC的一贯风格，清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。我总是记不得IETF是Internet Engineering Task Force的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。</p>
<h4>2.1、内码和code page</h4>
<p>目前Windows的内核已经支持Unicode字符集，这样在内核上可以支持全世界所有的语言文字。但是由于现有的大量程序和文档都采用了某种特定语言的编码，例如GBK，Windows不可能不支持现有的编码，而全部改用Unicode。</p>
<p>Windows使用代码页(code page)来适应各个国家和地区。code page可以被理解为前面提到的内码。GBK对应的code page是CP936。</p>
<p>微软也为GB18030定义了code page：CP54936。但是由于GB18030有一部分4字节编码，而Windows的代码页只支持单字节和双字节编码，所以这个code page是无法真正使用的。</p>
<h3>3、UCS-2、UCS-4、BMP</h3>
<p>UCS有两种格式：UCS-2和UCS-4。顾名思义，UCS-2就是用两个字节编码，UCS-4就是用4个字节（实际上只用了31位，最高位必须为0）编码。下面让我们做一些简单的数学游戏：</p>
<p>UCS-2有2^16=65536个码位，UCS-4有2^31=2147483648个码位。</p>
<p>UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个plane。每个plane根据第3个字节分为256行 (rows)，每行包含256个cells。当然同一行的cells只是最后一个字节不同，其余都相同。</p>
<p>group 0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中，高两个字节为0的码位被称作BMP。</p>
<p>将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节，就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。</p>
<h3>4、UTF编码</h3>
<p>&nbsp;</p>
<p>UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下：</p>
<table width="75%" border=1>
    <tbody>
        <tr>
            <td>UCS-2编码(16进制)</td>
            <td>UTF-8 字节流(二进制)</td>
        </tr>
        <tr>
            <td>0000 - 007F</td>
            <td>0xxxxxxx</td>
        </tr>
        <tr>
            <td>0080 - 07FF</td>
            <td>110xxxxx 10xxxxxx</td>
        </tr>
        <tr>
            <td>0800 - FFFF</td>
            <td>1110xxxx 10xxxxxx 10xxxxxx</td>
        </tr>
    </tbody>
</table>
<p>例如&#8220;汉&#8221;字的Unicode编码是6C49。6C49在0800-FFFF之间，所以肯定要用3字节模板了：1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是：0110 110001 001001， 用这个比特流依次代替模板中的x，得到：11100110 10110001 10001001，即E6 B1 89。</p>
<p>读者可以用记事本测试一下我们的编码是否正确。需要注意，UltraEdit在打开utf-8编码的文本文件时会自动转换为UTF-16，可能产生混淆。你可以在设置中关掉这个选项。更好的工具是Hex Workshop。</p>
<p>UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码，UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于0x10000的UCS码，定义了一个算法。不过由于实际使用的UCS2，或者UCS4的BMP必然小于0x10000，所以就目前而言，可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案，UTF-16却要用于实际的传输，所以就不得不考虑字节序的问题。</p>
<h3>5、UTF的字节序和BOM</h3>
<p>UTF-8以字节为编码单元，没有字节序的问题。UTF-16以两个字节为编码单元，在解释一个UTF-16文本前，首先要弄清楚每个编码单元的字节序。例如&#8220;奎&#8221;的Unicode编码是594E，&#8220;乙&#8221;的Unicode编码是4E59。如果我们收到UTF-16字节流&#8220;594E&#8221;，那么这是&#8220;奎&#8221;还是&#8220;乙&#8221;？</p>
<p>Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是&#8220;Bill Of Material&#8221;的BOM表，而是Byte Order Mark。BOM是一个有点小聪明的想法：</p>
<p>在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符，它的编码是FEFF。而FFFE在UCS中是不存在的字符，所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前，先传输字符"ZERO WIDTH NO-BREAK SPACE"。</p>
<p>这样如果接收者收到FEFF，就表明这个字节流是Big-Endian的；如果收到FFFE，就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。</p>
<p>UTF-8不需要BOM来表明字节顺序，但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF（读者可以用我们前面介绍的编码方法验证一下）。所以如果接收者收到以EF BB BF开头的字节流，就知道这是UTF-8编码了。</p>
<p>Windows就是使用BOM来标记文本文件的编码方式的。</p>
<h3>6、进一步的参考资料</h3>
<p>本文主要参考的资料是 "Short overview of ISO-IEC 10646 and Unicode" (http://www.nada.kth.se/i18n/ucs/unicode-iso10646-oview.html)。</p>
<p>我还找了两篇看上去不错的资料，不过因为我开始的疑问都找到了答案，所以就没有看：</p>
<ol>
    <li>"Understanding Unicode A general introduction to the Unicode Standard" (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;item_id=IWS-Chapter04a)
    <li>"Character set encoding basics Understanding character set encodings and legacy encodings" (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;item_id=IWS-Chapter03) </li>
</ol>
<p>我写过UTF-8、UCS-2、GBK相互转换的软件包，包括使用Windows API和不使用Windows API的版本。以后有时间的话，我会整理一下放到我的个人主页上(http://fmddlmyy.home4u.china.com)。</p>
<p>我是想清楚所有问题后才开始写这篇文章的，原以为一会儿就能写好。没想到考虑措辞和查证细节花费了很长时间，竟然从下午1:30写到9:00。希望有读者能从中受益。</p>
<h3>附录1 再说说区位码、GB2312、内码和代码页</h3>
<p>有的朋友对文章中这句话还有疑问：<br>&#8220;GB2312的原文还是区位码，从区位码到内码，需要在高字节和低字节上分别加上A0。&#8221;</p>
<p>我再详细解释一下：</p>
<p>&#8220;GB2312的原文&#8221;是指国家1980年的一个标准《中华人民共和国国家标准 信息交换用汉字编码字符集 基本集 GB 2312-80》。这个标准用两个数来编码汉字和中文符号。第一个数称为&#8220;区&#8221;，第二个数称为&#8220;位&#8221;。所以也称为区位码。1-9区是中文符号，16-55区是一级汉字，56-87区是二级汉字。现在Windows也还有区位输入法，例如输入1601得到&#8220;啊&#8221;。（这个区位输入法可以自动识别16进制的GB2312和10进制的区位码，也就是说输入B0A1同样会得到&#8220;啊&#8221;。）</p>
<p>内码是指操作系统内部的字符编码。早期操作系统的内码是与语言相关的。现在的Windows在系统内部支持Unicode，然后用代码页适应各种语言，&#8220;内码&#8221;的概念就比较模糊了。微软一般将缺省代码页指定的编码说成是内码。</p>
<p>内码这个词汇，并没有什么官方的定义，代码页也只是微软这个公司的叫法。作为程序员，我们只要知道它们是什么东西，没有必要过多地考证这些名词。</p>
<p>Windows中有缺省代码页的概念，即缺省用什么编码来解释字符。例如Windows的记事本打开了一个文本文件，里面的内容是字节流：BA、BA、D7、D6。Windows应该去怎么解释它呢？</p>
<p>是按照Unicode编码解释、还是按照GBK解释、还是按照BIG5解释，还是按照ISO8859-1去解释？如果按GBK去解释，就会得到&#8220;汉字&#8221;两个字。按照其它编码解释，可能找不到对应的字符，也可能找到错误的字符。所谓&#8220;错误&#8221;是指与文本作者的本意不符，这时就产生了乱码。</p>
<p>答案是Windows按照当前的缺省代码页去解释文本文件里的字节流。缺省代码页可以通过控制面板的区域选项设置。记事本的另存为中有一项ANSI，其实就是按照缺省代码页的编码方法保存。</p>
<p>Windows的内码是Unicode，它在技术上可以同时支持多个代码页。只要文件能说明自己使用什么编码，用户又安装了对应的代码页，Windows就能正确显示，例如在HTML文件中就可以指定charset。</p>
<p>有的HTML文件作者，特别是英文作者，认为世界上所有人都使用英文，在文件中不指定charset。如果他使用了0x80-0xff之间的字符，中文Windows又按照缺省的GBK去解释，就会出现乱码。这时只要在这个html文件中加上指定charset的语句，例如：<br>&lt;meta http-equiv="Content-Type" content="text/html; charset=ISO8859-1"&gt;<br>如果原作者使用的代码页和ISO8859-1兼容，就不会出现乱码了。</p>
<p>再说区位码，啊的区位码是1601，写成16进制是0x10,0x01。这和计算机广泛使用的ASCII编码冲突。为了兼容00-7f的ASCII编码，我们在区位码的高、低字节上分别加上A0。这样&#8220;啊&#8221;的编码就成为B0A1。我们将加过两个A0的编码也称为GB2312编码，虽然GB2312的原文根本没提到这一点。 <br><br>文章二，本文转载自:http://www.donews.net/holen/archive/2004/11/30/188182.aspx<br></p>
<p>Unicode: <br><br>unicode.org制定的编码机制, 要将全世界常用文字都函括进去.<br>在1.0中是16位编码, 由U+0000到U+FFFF. 每个2byte码对应一个字符; 在2.0开始抛弃了16位限制, 原来的16位作为基本位平面, 另外增加了16个位平面, 相当于20位编码, 编码范围0到0x10FFFF.<br><br>UCS: <br><br>ISO制定的ISO10646标准所定义的 Universal Character Set, 采用4byte编码.<br><br>Unicode与UCS的关系:<br><br>ISO与unicode.org是两个不同的组织, 因此最初制定了不同的标准; 但自从unicode2.0开始, unicode采用了与ISO 10646-1相同的字库和字码, ISO也承诺ISO10646将不会给超出0x10FFFF的UCS-4编码赋值, 使得两者保持一致.<br><br>UCS的编码方式:<br><br></p>
<li>UCS-2, 与unicode的2byte编码基本一样. <br>
<li>UCS-4, 4byte编码, 目前是在UCS-2前加上2个全零的byte.<br><br>UTF: Unicode/UCS Transformation Format<br>---------------------------------------原文------------------------------&nbsp;
<li>UTF-8, 8bit编码, ASCII不作变换, 其他字符做变长编码, 每个字符1-3 byte. 通常作为外码. 有以下优点:<br>* 与CPU字节顺序无关, 可以在不同平台之间交流<br>* 容错能力高, 任何一个字节损坏后, 最多只会导致一个编码码位损失, 不会链锁错误(如GB码错一个字节就会整行乱码) <br>
<li>UTF-16, 16bit编码, 是变长码, 大致相当于20位编码, 值在0到0x10FFFF之间, 基本上就是unicode编码的实现. 它是变长码, 与CPU字序有关, 但因为最省空间, 常作为网络传输的外码.
<p>---------------------------------------原文------------------------------&nbsp; <br>---------------------------------------纠正后---------------------------- </p>
<li>UTF-8, 8bit编码, ASCII不作变换, 其他字符做变长编码, 每个字符1-3 byte. 通常作为外码. 有以下优点:<br>* 与CPU字节顺序无关, 可以在不同平台之间交流<br>* 容错能力高, 任何一个字节损坏后, 最多只会导致一个编码码位损失, 不会链锁错误(如GB码错一个字节就会整行乱码) <br>
<li>UTF-16, 16bit编码, 是定长码,&nbsp; 基本上就是unicode编码的实现. 与CPU字序有关
<p>---------------------------------------纠正后---------------------------- </p>
<li>UTF-16是unicode的preferred encoding. <br>
<li>UTF-32, 仅使用了unicode范围(0到0x10FFFF)的32位编码, 相当于UCS-4的子集.<br><br>UTF与unicode的关系:<br><br>Unicode是一个字符集, 可以看作为内码.<br>而UTF是一种编码方式, 它的出现是因为unicode不适宜在某些场合直接传输和处理. UTF-16直接就是unicode编码, 没有变换, 但它包含了0x00在编码内, 头256字节码的第一个byte都是0x00, 在操作系统(C语言)中有特殊意义, 会引起问题. 采用UTF-8编码对unicode的直接编码作些变换可以避免这问题, 并带来一些优点.<br><br>中国国标编码:<br>
<li>GB 13000: 完全等同于ISO 10646-1/Unicode 2.1, 今后也将随ISO 10646/Unicode的标准更改而同步更改.<br>
<li>GBK: 对GB2312的扩充, 以容纳GB2312字符集范围以外的Unicode 2.1的统一汉字部分, 并且增加了部分unicode中没有的字符. <br>
<li>GB 18030-2000: 基于GB 13000, 作为Unicode 3.0的GBK扩展版本, 覆盖了所有unicode编码, 地位等同于UTF-8, UTF-16, 是一种unicode编码形式. 变长编码, 用单字节/双字节/4字节对字符编码. GB18030向下兼容GB2312/GBK. <br>GB 18030是中国所有非手持/嵌入式计算机系统的强制实施标准.
<p><br>-------------------------------</p>
<h2>什么是 UCS 和 ISO 10646?</h2>
<p>国际标准 ISO 10646 定义了 通用字符集 (Universal Character Set, UCS). UCS 是所有其他字符集标准的一个超集. 它保证与其他字符集是双向兼容的. 就是说, 如果你将任何文本字符串翻译到 UCS格式, 然后再翻译回原编码, 你不会丢失任何信息.</p>
<p>UCS 包含了用于表达所有已知语言的字符. 不仅包括拉丁语,希腊语, 斯拉夫语,希伯来语,阿拉伯语,亚美尼亚语和乔治亚语的描述, 还包括中文, 日文和韩文这样的象形文字, 以及 平假名, 片假名, 孟加拉语, 旁遮普语果鲁穆奇字符(Gurmukhi), 泰米尔语, 印.埃纳德语(Kannada), Malayalam, 泰国语, 老挝语, 汉语拼音(Bopomofo), Hangul, Devangari, Gujarati, Oriya, Telugu 以及其他数也数不清的语. 对于还没有加入的语言, 由于正在研究怎样在计算机中最好地编码它们, 因而最终它们都将被加入. 这些语言包括 Tibetian, 高棉语, Runic(古代北欧文字), 埃塞俄比亚语, 其他象形文字, 以及各种各样的印-欧语系的语言, 还包括挑选出来的艺术语言比如 Tengwar, Cirth 和 克林贡语(Klingon). UCS 还包括大量的图形的, 印刷用的, 数学用的和科学用的符号, 包括所有由 TeX, Postscript, MS-DOS，MS-Windows, Macintosh, OCR 字体, 以及许多其他字处理和出版系统提供的字符.</p>
<p>ISO 10646 定义了一个 31 位的字符集. 然而, 在这巨大的编码空间中, 迄今为止只分配了前 65534 个码位 (0x0000 到 0xFFFD). 这个 UCS 的 16位子集称为 基本多语言面 (Basic Multilingual Plane, BMP). 将被编码在 16 位 BMP 以外的字符都属于非常特殊的字符(比如象形文字), 且只有专家在历史和科学领域里才会用到它们. 按当前的计划, 将来也许再也不会有字符被分配到从 0x000000 到 0x10FFFF 这个覆盖了超过 100 万个潜在的未来字符的 21 位的编码空间以外去了. ISO 10646-1 标准第一次发表于 1993 年, 定义了字符集与 BMP 中内容的架构. 定义 BMP 以外的字符编码的第二部分 ISO 10646-2 正在准备中, 但也许要过好几年才能完成. 新的字符仍源源不断地加入到 BMP 中, 但已经存在的字符是稳定的且不会再改变了.</p>
<p>UCS 不仅给每个字符分配一个代码, 而且赋予了一个正式的名字. 表示一个 UCS 或 Unicode 值的十六进制数, 通常在前面加上 "U+", 就象 U+0041 代表字符"拉丁大写字母A". UCS 字符 U+0000 到 U+007F 与 US-ASCII(ISO 646) 是一致的, U+0000 到 U+00FF 与 ISO 8859-1(Latin-1) 也是一致的. 从 U+E000 到 U+F8FF, 已经 BMP 以外的大范围的编码是为私用保留的.</p>
<h2>什么是组合字符?</h2>
<p>UCS里有些编码点分配给了 组合字符.它们类似于打字机上的无间隔重音键. 单个的组合字符不是一个完整的字符. 它是一个类似于重音符或其他指示标记, 加在前一个字符后面. 因而, 重音符可以加在任何字符后面. 那些最重要的被加重的字符, 就象普通语言的正字法(orthographies of common languages)里用到的那种, 在 UCS 里都有自己的位置, 以确保同老的字符集的向后兼容性. 既有自己的编码位置, 又可以表示为一个普通字符跟随一个组合字符的被加重字符, 被称为 预作字符(precomposed characters). UCS 里的预作字符是为了同没有预作字符的旧编码, 比如 ISO 8859, 保持向后兼容性而设的. 组合字符机制允许在任何字符后加上重音符或其他指示标记, 这在科学符号中特别有用, 比如数学方程式和国际音标字母, 可能会需要在一个基本字符后组合上一个或多个指示标记.</p>
<p>组合字符跟随着被修饰的字符. 比如, 德语中的元音变音字符 ("拉丁大写字母A 加上分音符"), 既可以表示为 UCS 码 U+00C4 的预作字符, 也可以表示成一个普通 "拉丁大写字母A" 跟着一个"组合分音符":U+0041 U+0308 这样的组合. 当需要堆叠多个重音符, 或在一个基本字符的上面和下面都要加上组合标记时, 可以使用多个组合字符. 比如在泰国文中, 一个基本字符最多可加上两个组合字符.</p>
<h2>什么是 UCS 实现级别?</h2>
<p>不是所有的系统都需要支持象组合字符这样的 UCS 里所有的先进机制. 因此 ISO 10646 指定了下列三种实现级别: </p>
<dl>
<dt>级别1
<dd>不支持组合字符和 Hangul Jamo 字符 (一种特别的, 更加复杂的韩国文的编码, 使用两个或三个子字符来编码一个韩文音节)
<dt>级别2
<dd>类似于级别1, 但在某些文字中, 允许一列固定的组合字符 (例如, 希伯来文, 阿拉伯文, Devangari, 孟加拉语, 果鲁穆奇语, Gujarati, Oriya, 泰米尔语, Telugo, 印.埃纳德语, Malayalam, 泰国语和老挝语). 如果没有这最起码的几个组合字符, UCS 就不能完整地表达这些语言.
<dt>级别3
<dd>支持所有的 UCS 字符, 例如数学家可以在任意一个字符上加上一个 tilde(颚化符号,西班牙语字母上面的～)或一个箭头(或两者都加). </dd></dl>
<h2>什么是 Unicode?</h2>
<p>历史上, 有两个独立的, 创立单一字符集的尝试. 一个是<a href="http://www.iso.ch/">国际标准化组织(ISO)</a>的 ISO 10646 项目, 另一个是由(一开始大多是美国的)多语言软件制造商组成的协会组织的 <a href="http://www.unicode.org/">Unicode 项目</a>. 幸运的是, 1991年前后, 两个项目的参与者都认识到, 世界不需要两个不同的单一字符集. 它们合并双方的工作成果, 并为创立一个单一编码表而协同工作. 两个项目仍都存在并独立地公布各自的标准, 但 Unicode 协会和 ISO/IEC JTC1/SC2 都同意保持 Unicode 和 ISO 10646 标准的码表兼容, 并紧密地共同调整任何未来的扩展.</p>
<h2>那么 Unicode 和 ISO 10646 不同在什么地方?</h2>
<p>Unicode 协会公布的 <a href="http://www.unicode.org/unicode/standard/standard.html">Unicode 标准</a> 严密地包含了 ISO 10646-1 实现级别3的基本多语言面. 在两个标准里所有的字符都在相同的位置并且有相同的名字.</p>
<p>Unicode 标准额外定义了许多与字符有关的语义符号学, 一般而言是对于实现高质量的印刷出版系统的更好的参考. Unicode 详细说明了绘制某些语言(比如阿拉伯语)表达形式的算法, 处理双向文字(比如拉丁与希伯来文混合文字)的算法和 排序与字符串比较 所需的算法, 以及其他许多东西.</p>
<p>另一方面, ISO 10646 标准, 就象广为人知的 ISO 8859 标准一样, 只不过是一个简单的字符集表. 它指定了一些与标准有关的术语, 定义了一些编码的别名, 并包括了规范说明, 指定了怎样使用 UCS 连接其他 ISO 标准的实现, 比如 ISO 6429 和 ISO 2022. 还有一些与 ISO 紧密相关的, 比如 ISO 14651 是关于 UCS 字符串排序的.</p>
<p>考虑到 Unicode 标准有一个易记的名字, 且在任何好的书店里的 Addison-Wesley 里有, 只花费 ISO 版本的一小部分, 且包括更多的辅助信息, 因而它成为使用广泛得多的参考也就不足为奇了. 然而, 一般认为, 用于打印 ISO 10646-1 标准的字体在某些方面的质量要高于用于打印 Unicode 2.0的. 专业字体设计者总是被建议说要两个标准都实现, 但一些提供的样例字形有显著的区别. ISO 10646-1 标准同样使用四种不同的风格变体来显示表意文字如中文, 日文和韩文 (CJK), 而 Unicode 2.0 的表里只有中文的变体. 这导致了普遍的认为 Unicode 对日本用户来说是不可接收的传说, 尽管是错误的.</p>
<h2>什么是 UTF-8?</h2>
<p>首先 UCS 和 Unicode 只是分配整数给字符的编码表. 现在存在好几种将一串字符表示为一串字节的方法. 最显而易见的两种方法是将 Unicode 文本存储为 2 个 或 4 个字节序列的串. 这两种方法的正式名称分别为 UCS-2 和 UCS-4. 除非另外指定, 否则大多数的字节都是这样的(Bigendian convention). 将一个 ASCII 或 Latin-1 的文件转换成 UCS-2 只需简单地在每个 ASCII 字节前插入 0x00. 如果要转换成 UCS-4, 则必须在每个 ASCII 字节前插入三个 0x00.</p>
<p>在 Unix 下使用 UCS-2 (或 UCS-4) 会导致非常严重的问题. 用这些编码的字符串会包含一些特殊的字符, 比如 '\0' 或 '/', 它们在 文件名和其他 C 库函数参数里都有特别的含义. 另外, 大多数使用 ASCII 文件的 UNIX 下的工具, 如果不进行重大修改是无法读取 16 位的字符的. 基于这些原因, 在文件名, 文本文件, 环境变量等地方, UCS-2 不适合作为 Unicode 的外部编码.</p>
<p>在 ISO 10646-1 <a href="http://www.cl.cam.ac.uk/~mgk25/ucs/ISO-10646-UTF-8.html">Annex R</a> 和 <a href="ftp://ftp.funet.fi/mirrors/nic.nordu.net/rfc/rfc2279.txt">RFC 2279</a> 里定义的 UTF-8 编码没有这些问题. 它是在 Unix 风格的操作系统下使用 Unicode 的明显的方法.</p>
<p>UTF-8 有一下特性: </p>
<ul>
    <li>UCS 字符 U+0000 到 U+007F (ASCII) 被编码为字节 0x00 到 0x7F (ASCII 兼容). 这意味着只包含 7 位 ASCII 字符的文件在 ASCII 和 UTF-8 两种编码方式下是一样的.
    <li>所有 &gt;U+007F 的 UCS 字符被编码为一个多个字节的串, 每个字节都有标记位集. 因此, ASCII 字节 (0x00-0x7F) 不可能作为任何其他字符的一部分.
    <li>表示非 ASCII 字符的多字节串的第一个字节总是在 0xC0 到 0xFD 的范围里, 并指出这个字符包含多少个字节. 多字节串的其余字节都在 0x80 到 0xBF 范围里. 这使得重新同步非常容易, 并使编码无国界, 且很少受丢失字节的影响.
    <li>可以编入所有可能的 2<sup>31</sup>个 UCS 代码
    <li>UTF-8 编码字符理论上可以最多到 6 个字节长, 然而 16 位 BMP 字符最多只用到 3 字节长.
    <li>Bigendian UCS-4 字节串的排列顺序是预定的.
    <li>字节 0xFE 和 0xFF 在 UTF-8 编码中从未用到. </li>
</ul>
<p>下列字节串用来表示一个字符. 用到哪个串取决于该字符在 Unicode 中的序号.</p>
<div align=center>
<center>
<table border=1>
    <tbody>
        <tr>
            <td>U-00000000 - U-0000007F: </td>
            <td>0xxxxxxx</td>
        </tr>
        <tr>
            <td>U-00000080 - U-000007FF: </td>
            <td>110xxxxx 10xxxxxx</td>
        </tr>
        <tr>
            <td>U-00000800 - U-0000FFFF: </td>
            <td>1110xxxx 10xxxxxx 10xxxxxx</td>
        </tr>
        <tr>
            <td>U-00010000 - U-001FFFFF: </td>
            <td>11110xxx 10xxxxxx 10xxxxxx 10xxxxxx</td>
        </tr>
        <tr>
            <td>U-00200000 - U-03FFFFFF: </td>
            <td>111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx</td>
        </tr>
        <tr>
            <td>U-04000000 - U-7FFFFFFF: </td>
            <td>1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx</td>
        </tr>
    </tbody>
</table>
</center></div>
<p>xxx 的位置由字符编码数的二进制表示的位填入. 越靠右的 x 具有越少的特殊意义. 只用最短的那个足够表达一个字符编码数的多字节串. 注意在多字节串中, 第一个字节的开头"1"的数目就是整个串中字节的数目.</p>
<p>例如: Unicode 字符 U+00A9 = 1010 1001 (版权符号) 在 UTF-8 里的编码为:</p>
<blockquote>
<p>11000010 10101001 = 0xC2 0xA9</p>
</blockquote>
<p>而字符 U+2260 = 0010 0010 0110 0000 (不等于) 编码为:</p>
<blockquote>
<p>11100010 10001001 10100000 = 0xE2 0x89 0xA0</p>
</blockquote>
<p>这种编码的官方名字拼写为 UTF-8, 其中 UTF 代表 UCS Transformation Format. 请勿在任何文档中用其他名字 (比如 utf8 或 UTF_8) 来表示 UTF-8, 当然除非你指的是一个变量名而不是这种编码本身.</p>
<h2>什么编程语言支持 Unicode?</h2>
<p>在大约 1993 年之后开发的大多数现代编程语言都有一个特别的数据类型, 叫做 Unicode/ISO 10646-1 字符. 在 Ada95 中叫 Wide_Character, 在 Java 中叫 char.</p>
<p>ISO C 也详细说明了处理多字节编码和宽字符 (wide characters) 的机制, 1994 年 9 月 <a href="http://www.lysator.liu.se/c/na1.html">Amendment 1 to ISO C</a> 发表时又加入了更多. 这些机制主要是为各类东亚编码而设计的, 它们比处理 UCS 所需的要健壮得多. UTF-8 是 ISO C 标准调用多字节字符串的编码的一个例子, wchar_t 类型可以用来存放 Unicode 字符.</p>
</li>
<img src ="http://www.blogjava.net/orangelizq/aggbug/134021.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-08-02 16:54 <a href="http://www.blogjava.net/orangelizq/archive/2007/08/02/134021.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] J2EE vs .NET (下)</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/27/132900.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Fri, 27 Jul 2007 14:56:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/27/132900.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/132900.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/27/132900.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/132900.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/132900.html</trackback:ping><description><![CDATA[<div class=postText><strong>三. 异中有同同中有异</strong> <br>——J2EE与.NET平台体系架构的异同 <br>南京邮电学院 李建忠 <br>中南大学 毕文杰 <br><br>作为彼此竞争的应用平台，J2EE和.NET开发平台在目标和体系结构上极其相似，但在实现上又完全不同。平台的体系架构是支撑平台的基础，平台各方面的性能也会因平台架构实现的不同而有差异。对两个平台产生至关重要影响的三个方面是：系统平台基础构造、三层/多层体系结构和移植/性能/扩展。J2EE是一个平台规范而非产品，对等而论，在这里述及的.NET也专注于该平台的架构规范，而较少地涉及到具体产品，尽管对.NET而言有时候这方面并不能被区分得很清楚。 <br>类似的平台基础构造 <br>一个平台在语言编译、代码执行、编程支持等基础构造方面往往会对平台的可用性、生产性、移植性等产生重要的影响，也是我们评判一个平台是否适合特定应用的重要依据。J2EE和.NET两个平台在底层的执行引擎都源于托管的虚拟机概念，但.NET的CLR沿着Java虚拟机(JVM)走得更远。CLR在借鉴了JVM的自动垃圾收集、异常处理等机制的同时，又为.NET平台添加了多语言支持、组件自描述等新的特性。 <br>在.NET和 J2EE平台上，程序的编译都经过两个类似的过程。首先特定高级语言编译器将C#（及其他.NET语言）和Java源代码分别翻译成中间语言(IL)和字节代码(ByteCode)。.NET在中间语言设计时通盘考虑了多个主流高级语言，在这一层面实现了.NET平台的跨语言承诺。J2EE的基石是Java语言，它最典型的特征是：一次编写，多次运行。跨平台是J2EE一直引以为豪的关键，这是通过JVM来实现的。 <br>其次，在执行时，中间语言被即时编译器(JIT)编译成特定平台的二进制代码，字节代码则通过JVM解释执行，完成各自语言的指令功能。鉴于微软在&#8220;Wintel平台&#8221;上的代码优化功底，.NET代码的执行速度较之于Java有明显的优势是不争的事实。但在Unix/Linux平台上，由于.NET迟迟未能实现其跨平台的承诺，J2EE几乎成了惟一的选择，执行效率的比较也就无所谓。在代码执行的同时，通用语言运行时和Java虚拟机也都提出了异常捕捉、类型安全、内存分配、垃圾收集等自动化内存管理工作，大大减轻了现代软件的内存泄漏问题和程序员繁重的负担。 <br>面向对象程序设计在J2EE和.NET平台中都获得了直接的支持，单根继承加多接口实现是它们共有的特征。但在面向对象之外，.NET对现代组件编程提供了直接支持。当然，当下的很多企业中间件都是基于J2EE平台的,只是.NET从设计、编码、配置到运行给予了组件编程更多、更直接的支持。 <br>一个能够为编程提供广泛服务的、可从玫腁PI类库对于现代软件平台非常重要。从基础的集合、字符串操作到企业级的API接口，如JMS、JDBC、JAX、JNDI等，可以看到J2EE在这方面有着非常坚实的结构。微软.NET框架类库也不示弱，提供了从图画、网络、线程到ADO.NET、ADSI、Windows表单、ASP.NET等一系列的API。在这些基础的和企业级的服务上两个平台很难一决高下，而且对功能集合的支持很多时候是一个时间问题，往往是一个平台推出了某一子功能集，另一个平台马上推出类似的功能集。 <br>除去API类库的无缝的功能复用外，对本地平台的调用操作也是值得关注的一点。CLR和Java虚拟机都支持本地方法的调用。在异构平台方面，J2EE更钟情于IIOP（Internet InterORB Protocol），而.NET则使用SOAP。 <br>相同的三层/多层体系 <br>基于三层/多层分布式计算结构已毋庸置疑地成为当今企业应用的主流模式，也是两个平台较量的着力点。 <br>在客户端，表示层负责用户与系统的交互。对于不同的处理要求，.NET和J2EE都提出了基于桌面的应用程序和基于浏览器的Web应用的开发组件：Java Application与Windows表单、Java Servlet/JSP与ASP.NET双双形成犄角之势。但Windows表单依赖微软桌面系统的天然优势，不管在交互速度还是在界面的表现性能上都较Java Application稍胜一筹。Servlet/JSP与ASP.NET是目前企业在&#8220;瘦客户端&#8221;应用的重点，两者都基于HTTP请求/响应模型，通过HTML浏览器页面完成用户交互。虽然ASP.NET声称在底层通过编译执行获得了相当高的处理速度，以及服务器方控件的浏览器自适应能力，但目前并没有这方面的硬性数据，很难据此而论高下。在缓存、状态优化等方面两者可谓旗鼓相当。另一个和客户端应用相关的技术是ActiveX与Applet，但从目前的趋势来看，它们在两个平台上的地位逐渐边缘化，也不为大多数企业所接受。 <br>在中间层，分布式业务组件负责企业应用的商业逻辑部署。由于这些业务组件经常负责处理数据库连接、网络资源、线程等高昂的资源，所以一直是三层/多层架构的关键和企业应用的核心。J2EE的EJB是一个成熟的、得到业界广泛支持的大型企业级组件框架，而.NET组件则是建立在新型的COM+服务之上，两者在组件与操作系统的交互、客户端资源共享等方面都有很好的支持。EJB的核心是容器，容器是一个为组件提供服务的运行时环境，负责为组件提供诸如事务处理、持久性、安全性、组建状态自动化管理等服务，它分离了商业逻辑和系统底层逻辑，使开发人员的工作大为简化。.NET则通过元数据支持自描述性的组件开发、XCOPY部署以及多版本共存，而无需注册表和描述文件，对企业客户有一定的吸引力。 <br>在后端数据层，两个平台都为数据库连接量身定做了一套数据存取模型：J2EE的JDBC和.NET的ADO.NET。它们在支持传统SQL数据源的同时，也都支持新型的XML数据源。这方面由于更多地涉及到具体的数据库产品，很难说那种数据模型更有优势。 <br>值得指出的是，在打造三层/多层体系结构的同时，Web服务作为新一代企业计算模型也得到了J2EE和.NET平台相当的关注，在后面的文章会有这方面的详细评述。 <br>不同的移植、性能和扩展 <br>在移植性方面，微软通过.NET 通用语言运行时来消除编程语言的差别，而J2EE则通过Java虚拟机来消除平台差别。&#8220;选择.NET平台就意味着选择Windows&#8221;，这句话至少在可预见的一段时间里仍然是一个基本事实。跨平台是J2EE的一大卖点，也是在选择企业应用开发平台时的一个重要参考因素，几乎所有的主流操作系统都提供了对J2EE的支持。实际上如果要搭建跨Unix、Windows等多个操作系统平台，J2EE平台几乎是惟一的选择。J2EE更关注跨平台而不是跨语言。但微软认为，如果企业的应用都能通过标准协议以Web服务的方式发布，那么平台都是中立的。跨平台甚至是微软所不想的。为了吸引更多的开发者和鼓励广大企业厂商转到.NET平台，微软提出了多语言支持，希望用跨语言的交互性来平衡跨平台的互操作。 <br>性能是J2EE和.NET喋喋不休的话题。二者之间著名的论战是一个关于宠物店的范例应用。宠物店是Sun一度以来作为J2EE典型应用的展示范例，但.NET&#8220;自告奋勇&#8221;地在自己的平台上实现了该宠物店应用，且声称代码行是J2EE的1/3，效率却是J2EE的30倍。但Sun的理由是这个范例根本不适合用来做性能比较，该范例实现也没有做针对性能的优化，而且指责微软通过后端数据库优化和缓存虚抬了.NET平台的效率。这样的争吵当然不能作为我们判断的依据，目前也没有见到更客观的第三方评测报告。在&#8220;Wintel平台&#8221;上我们也许没有理由怀疑.NET的性能，而至于非Windows平台，.NET和J2EE也不再具有可比性。 <br>在平台的成熟度方面，两者也有一拼。J2EE在1999年形成了其成熟的架构，并且到今天已经有相当成熟的经过检验的企业应用系统。而.NET究其渊源是源自微软以前开发企业应用程序的平台DNA（Distributed Network Architecture），其中包括了许多已经被证实的技术，并且这些技术已经在产品中得到实现，包括微软的事务服务器、COM+、消息队列、SQL Server数据库等。而对于扩展性，广为业界接受的事实是.NET平台的扩展思想是基于软件的横向扩展，而J2EE平台的扩展思想则是基于硬件的纵向扩展。这也符合微软和Sun各自的产品利益。 <br>J2EE另一个重要特征就是它的架构开放性，它本身是一系列规范，而不是产品，任何符合这一规范的产品都是J2EE兼容的。这使得J2EE从制订之初就得到了广泛的支持。BEA、IBM、Oracle等都相继开发了符合J2EE的应用服务器，它们的产品相互之间甚至可以兼容。而.NET在设计之初就紧紧地把平台规范与产品胶合在一起，虽然.NET架构的一小部分具有开放性（如C#语言、通用语言基础构造CLI 和Web服务标准），但至少目前很难想像会有一个非微软的.NET实现。 <br><br><br><strong>四。Web服务谁主沉浮？</strong> <br>■ 柴晓路 <br>现在已经是2002年第二季度，Gartner Group对Web服务发展的预测似乎被产品提供商稍稍延误了，最近微软的.NET Framework及其开发工具VS .NET刚刚正式发布。而作为Web服务世界中另一个重量级角色Sun，也为它的J2EE Framework增添了开发Web服务的强有力的工具包Java Web Services Developer Pack(WSDP)。从2002年起到2005年，Gartner Group所预测的B2C、B2B以及e-Government领域Web服务的开发和部署将会大量依赖J2EE和.NET这两个平台及其上的开发工具。 <br>.NET与J2EE 对Web服务的支持 <br>从.NET和J2EE这两个平台的发展历程来看， .NET从一开始就深深打上了Web服务技术的烙印，在它的市场推广活动中，无时无刻不凸显其作为Web服务的开发和部署平台的特征。可以说，.NET天生就是为Web服务准备的开发和部署平台。相对.NET而言，J2EE是一个比较&#8220;老&#8221;的东西，最初它是为了将Java平台拓展到企业级应用领域而制订的一个平台框架规范。随着Web服务的兴起和发展，J2EE平台作为一个企业级应用的开发和部署平台，无法回避业界的重大技术革命——Web服务。随着Web服务技术的发展，J2EE也不断地引入了对Web服务的支持。 <br>从服务描述、服务实现和服务的发布、发现与绑定，以及服务的调用和执行这些不同的角度看，J2EE和.NET的支持基本不相上下，惟一的区别可能是.NET的开发工具更为方便一些，集成度更高一些。.NET是一个在J2EE之后出现的平台，所有的重量级技术产品无一例外地都会吸收先前成功者的优点，.NET就大量地吸收了J2EE平台的优点。其中，最重要的一点就是.NET不再完全沿袭微软先前的技术，从.NET开始，其应用不再以本地机器代码运行，而是编译成中间代码，由称为CLR的虚拟机来运行，这样，.NET也具备了跨平台的可能。不过.NET的跨平台特性主要体现在支持多种开发语言上，VB.NET、C#、C++、JScript等都可以被编译成相同的中间代码，使用相同的运行库执行。 <br>第三方厂商的支持 <br>J2EE作为一种开放的规范，从一开始就得到了众多厂商的支持，IBM、BEA、HP、Oracle等在J2EE的实施上都有较大的投入。目前市场上最好的J2EE应用服务器并不是Sun与Netscape合资的iPlanet，而是BEA的WebLogic和IBM的Webshpere。一年一度的JavaONE就有成千上万的开发商参加。由于J2EE是开放的规范框架，任意厂商只要有实力都可以按照规范来开发实现，不同厂商的组件也可以在一起协同使用，当然最关键的是这些参与J2EE的厂商都具有很强的实力。除了微软以外，基本上所有的软件业巨擎都钟情于J2EE。 <br>然而，J2EE虽然是开放的规范，但是它的使用却不是那么开放，每家使用J2EE技术的公司都不得不为此向Sun支付一笔不小的费用。同时也正因为Sun对J2EE规范的独家控制，使得J2EE规范的开发进度缓慢，迄今为止，J2EE规范中并不包含对Web服务的支持，Sun推出的WSDP只是一种插件形式的扩展支持。有消息表明，在今年年底前，Sun和Java领域的其他支持商，包括IBM、Bea、Silverstream等会就J2EE如何支持Web服务达成一致，然而这一切均存在变数，其中的根结就在于Sun对Java技术的独家控制。 <br>同时，由于J2EE对Web服务支持的步履维艰，各大厂商分别自行开发Java平台的Web服务支持，IBM在这个领域的步伐是飞快的，它的WSAD（Webshpere Studio Application Developer）集成了大量自行开发（部分来自于Apache.org，不过这个项目的前身是IBM发起，而后移交给Apache.org）的Web服务组件，业已成为Java领域开发Web服务的最佳开发工具，同时IBM的Websphere也慢慢向Web服务开发部署应用平台的角色转化。 <br>而对于微软的.NET而言，虽然从一开始，微软就以独占、垄断、不开放的形象出现在平台市场上。然而，它的.NET却表现出了前所未有的开放姿态。 <br>.NET的主力开发语言C# 已经提交给 ECMA，开始标准化，ECMA是一个致力于推动行业范围内采用信息和通信技术的非特定供应商的国际标准组织。C#的标准化使希望在任何平台上都可以实现 C# 编程工具的公司能够实现其愿望。微软 还向 ECMA 提交了微软.NET框架的一个子集，叫做CLR（公共语言架构，Common Language Infrastructure）。这将使其他供应商能够在各种平台上实现 CLI，以便用.NET框架提供的基本体系结构模型编写的软件可以在各种平台上用各种工具来创建。美国Ximian公司已于2001年7月启动了一个名为Mono 的开放源码版.NET开发项目，计划内容包括一个C#编译器、与微软的CLI兼容的类库和Linux版CLR编译器。虽然这只是起步，然而谁也不能肯定，它不会像当初的Java那样，从Sun的小玩具，变成了今天如此重要的开发平台。 <br>Web服务规范的控制 <br>由于Web服务的各种技术都是先以规范的形式制订，然后再交付各大开发商进行实施。所以，某个开发商如果从一开始就参与某种Web服务规范的开发，那么它的平台就能够以最快的速度支持这一Web服务规范。在这一点上，微软给人以非常积极进取的印象。在Web服务领域，微软与IBM共同主推了大量的Web服务规范，在一段时间内，两家公司Web服务技术的市场推广活动都是联合举行的，不难看出这两家公司在这个领域背后的战略合作关系。最初的Web服务核心技术SOAP、WSDL主要由这两家公司制订;后来的UDDI是由这两家为首的多家核心企业共同制订;再后来的一些不是核心的Web服务规范，如WS-Inspection、WSFL、WS-Security、WS-Routing、WS-License、WS-Referral等，则完全由这两家来制订，不难看出IBM和微软对于Web服务的贡献以及它们对Web服务规范的控制。 <br>而Sun自从在XML规范的制订中发挥了重要的作用之后，在其后的Internet规范，尤其是Web服务规范的制订中，声音变得非常微弱，而且似乎并没有改善的趋势。最近在Web服务领域中的一件大事是WS-I.org的成立。WS-I.org是为保证Web服务所承诺的互操作性而成立的一个组织，主要工作就是开发保障Web服务互操作性的相关规范，并进行规范实施的测试。WS-I.org的核心成员包括Accenture、BEA、HP、Intel、IBM、Microsoft、Oracle、SAP等，Sun不在其中，甚至都不在非核心成员的列表中。是Sun的发展战略的问题，还是受盈利问题的困扰，我们不得而知，不过我们可以知道的是，Sun再一次在Web服务领域中落后了，由它控制的J2EE规范的状况也就可想而知。 <br>潜在的市场 <br>从技术的发展来看，大型的企业用户或有着成功实施经验的企业用户，并不会因为新技术的推出而盲目地否定旧技术，它们总是在保护投资的前提下，在不推翻现有架构的前提下，有选择地挑选适合的技术。 <br>J2EE已经是一个成熟的、成功的企业级应用解决方案，拥有大量的客户，已经实施了J2EE的企业不太可能在Web服务的时代全面否定J2EE而去接受.NET。.NET是一个全新的架构，虽然它的开发语言中已经包含了诸如VB、C++等传统开发语言，刚刚接触.NET的开发人员会以为能将以前使用VB开发的代码平滑地转移到.NET平台上来。其实不然，VB.NET的语法与VB 6.0已经有了根本性的差别，与其说VB.NET是VB 6.0的升级，不如说VB.NET是C#的Basic版。由于采用了CLI的结构，VB.NET将很难兼容以前的VB 6.0的代码，大量的VB代码无法顺利地转移到.NET上，我们期待着微软能够提供转换程序以实现代码的升级。虽然在源代码级别上的升级变得不是那么容易，不过开发人员仍然可以在.NET平台下，将原有的COM组件进行重新包装，形成 .NET平台下的Web服务组件，而且.NET的整个平台、开发工具的高集成性和友好的开发环境还是会给开发人员留下深刻印象。在Java领域中，无论是Borland的JBuilder 6，还是Sun的Forte for Java，或是IBM的WebShpere Studio Application Developer、VisualAge for Java都无法达到VS .NET的生产效率。开发工具是.NET的一大优势，同时.NET平台对Web服务规范的支持力度也仅有IBM的J2EE平台能够与之相媲美。 <br>因此，笔者认为在大型企业级应用场合，如果已经采用了J2EE架构，应该会在Web服务的时代继续使用J2EE架构。而原先就是采用微软架构的，出于技术延续性的考虑，大多数仍然会选用微软的.NET。那些采用其他技术的企业级应用则会在开发效率、安全性、可靠性、维护代价等不同指标上对两种架构进行考察，应该说机会是均等的，J2EE强在有大量的应用实例，而.NET强在整合集成的优秀开发部署环境。 <br>在中小级别的应用领域，J2EE的占有率优势不再那么明显，一方面，长期以来微软专长于这个领域;另一方面，Java解决方案已经是如此地深入人心，即使是中小企业也会考虑J2EE架构，在这个领域，两者平分秋色。 <br>而在桌面应用（Web服务客户端）领域，除了一些管理客户端会采用Java开发以外，绝大多数的应用毫无疑问地会在微软平台上开发和部署。 <br>谁主沉浮 <br>下面这张表格概括了对两者的比较： <br>比较项目? J2EE? .NET? <br>对Web服务的支持? <br>服务描述? 好? 好? <br>服务实现? 好? 很好? <br>服务发布、发现与绑定? 好? 很好? <br>服务调用和执行? 好? 好? <br>第三方支持? <br>平台提供商? 很好? 有待考察? <br>软件开发商? 很好? 好? <br>对Web服务规范的控制? 情况复杂(注）? 很好? <br>市场前景? <br>企业级大型应用? 很好? 一般? <br>中小级别应用? 好? 好? <br>桌面应用? 差? 很好? <br>注：J2EE的控制者Sun对Web服务规范几乎没有什么控制能力，然而Sun在J2EE上的合作伙伴IBM等对Web服务规范却具备强大的控制力，所以表格中显示&#8220;情况复杂&#8221;。 <br>从表格中，不难看出两者是旗鼓相当的对手，现在就断言谁主沉浮还为时过早。应该说，J2EE目前需要做的是尽快真正将Web服务规范融入到J2EE规范中去，从规范出发统一对Web服务的支持。而.NET迫切需要进行的则是加大平台的开放力度，争取改善微软在用户心目中独断、单方控制、不开放的形象。 <br>在未来相当长时期内，J2EE和.NET都将是企业构建应用系统的重要选择，两个平台将相互共存，两者本身也在不断地相互借鉴和完善，并且有望通过Web服务实现互操作。真正的市场，正是需要强大的竞争者之间的较量，这样用户才能得到最好的技术和解决方案。 <br>小资料&#183; <br>Gartner Group对未来Web服务发展状况的预测： <br>2001年，Web服务的架构平台、开发工具将基本被各大开发商开发完毕。开发人员能够购买到这些面向服务的开发工具，同时将开始构建实际使用的Web服务。 <br>2002年，商业Web服务将大量出现，大量的面向消费者的B2C Web服务将投入使用。 <br>2003年，UDDI注册中心随着Web服务的发展，将变得越来越重要，其中的商业数据也越来越丰富。私有的UDDI注册中心将被投入使用，以支持内部服务信息的交换。而政府的Web服务应用也将不断出现。 <br>2004年，各类企业将会普遍接受基于Web服务的商务应用模式，而服务集中的计算模式将进入青年期。私有的UDDI注册中心仍然在各类应用中处于优势地位，新的赢利模式和商业渠道将到处可见。40％的金融财务服务事务将使用Web服务模式，而35%的在线政府服务将以Web服务的形式提供。 <br>2005年，公共的UDDI注册中心作为公共商务信息的交换机制将大量应用。动态服务同样将大量投入使用。<br></div>
<img src ="http://www.blogjava.net/orangelizq/aggbug/132900.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-27 22:56 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/27/132900.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] J2EE VS .NET (上)</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/27/132899.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Fri, 27 Jul 2007 14:54:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/27/132899.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/132899.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/27/132899.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/132899.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/132899.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 看到这个标题，也许会有人表示疑惑，J2EE和.NET并不在一个层次上，怎么能将它们放在一起呢？需要指出的是，通常所说的.NET包含了一个相当广泛的产品家族，包括开发平台、操作系统、服务器、终端设备等，此外还包括服务平台。开发平台只是整个.NET战略中的一部分，所以确切地说，放在这里的.NET应该算是.NET开发平台。<br>随着三层/多层企业信息系统结构的深度发展和<span style="COLOR: #0000ff">下一代分布式计算模型Web 服务</span>的出现，企业应用中关于平台、框架、语言的竞争也愈演烈。J2EE平台在过去几年里一直引领着企业应用的潮流，但最近微软强力推出的.NET平台也开始吸引着众多IT企业和开发人员的注意力，向J2EE平台提出了强有力的挑战。企业应用领域的技术对抗也因此拉开了架势。<br>需要强调的是，.NET是战略产品，而<span style="COLOR: #0000ff">J2EE是描述产品的标准</span>，现在有很多符合J2EE标准的产品。在可以预见的未来，它们都将是构建企业信息系统应用的基础性平台，尤其是开发和部署Web服务的重要平台。<br>尽管可以同时使用几种系统平台和语言，但对于企业来说，还需要选择一个战略性的平台来实现数据的无缝集成，加速企业应用的部署。而要做出正确的选择，首先需要充分了解两个平台的特点和优势。本期专题将为您细说J2EE和.NET。 <br><br><br>一、群力所至的J2EE<br>二、.NET开发平台留住Windows开发者<br>三、 J2EE与.NET平台体系架构的异同<br>四、 J2EE vs .NET：Web服务谁主沉浮？<br><br><br><strong>一、群力所至的J2EE <br><br></strong>中南大学 罗新星 毕文杰 <br><br>企业应用系统的开发一直面临着重大挑战：一方面，企业应用系统面对的是一个异构的分布式环境，它必须支持与已有系统的集成性和与其他系统的互操作性；另一方面，作为为客户、合作伙伴和企业内部提供信息服务的平台，企业系统还必须具有高可用性、安全性、可靠性和可伸缩性。这些要求再加上复杂多变的用户需求和不断伸缩的交付时间，使得企业系统的开发越来越困难。开发商和广大程序员一直在努力推动和殷切期待一个成熟、标准的企业平台来简化和规范企业系统的开发和部署。Java技术的出现，尤其是J2EE（Java 2 Platform Enterprise Edition）平台的推出正是这种努力的结果，也使得企业系统的开发由此变得更加快速和方便。需要指出的是，J2EE本身是一个标准，它为不同厂商创建平台产品提供了标准，使不同J2EE平台产品之间的交互成为可能。 <br><br><strong>J2EE旅程 </strong><br>Java于1996年由Sun公司推出，当时它的主要用途是制作产生动态网页的Applet。后来，人们发现Java的&#8220;一次开发，多次运行&#8221;、纯面向对象的特性、垃圾回收机制和内置的安全特别适合于开发企业应用系统。于是，企业应用开发商纷纷在Java标准版的基础上各自扩展出许多企业应用API，其结果导致基于Java的企业应用呈爆炸式增长。但是各企业系统API之间又不能相互兼容，破坏了Java的平台独立性。鉴于此，Sun公司联合IBM、Oracle、BEA等大型企业应用系统开发商于1998年共同制订了一个基于Java组件技术的企业应用系统开发规范，该规范定义了一个多层企业信息系统的标准平台，旨在简化和规范企业应用系统的开发和部署。这一规范和其定义的平台就构成了J2EE。目前J2EE的最新版本是J2EE 1.3。需要注意的是，J2EE本身是一个标准，而不是一个现成的产品（虽然现在有很多符合J2EE标准的产品），它由以下几个部分组成： <br>J2EE规范。该规范定义了J2EE平台的体系结构、平台角色及J2EE中每种服务和核心API的实现要求。它是J2EE应用服务器开发商的大纲。 <br>J2EE兼容性测试站点。Sun公司提供的一个测试J2EE应用服务器是否符合J2EE规范的站点，对通过该站点测试的产品，Sun公司将发放兼容性证书。 <br>J2EE参考实现。即J2EE SDK，它既是Sun公司自己对J2EE规范的一个非商业性实现，又是为开发基于J2EE企业级应用系统原型提供的一个免费的底层开发环境。 <br>J2EE实施指南。即BluePrints文档，该文档通过实例来指导开发人员如何去开发一个基于J2EE的多层企业应用系统。 <br>组件-容器 搭建体系架构 <br>J2EE规范定义了一个基于组件的多层企业应用系统开发平台，其逻辑结构如图1所示。图中的椭圆形表示组件，大矩形表示容器，包含向下文字的小矩形表示API，箭头表示访问，箭头线上的文字表示相应的协议。 <br>J2EE是一个基于组件-容器模型的系统平台，其核心概念是容器。容器是指为特定组件提供服务的一个标准化的运行时环境，Java虚拟机就是一个典型的容器。组件是一个可以部署的程序单元，它以某种方式运行在容器中，容器封装了J2EE底层的API，为组件提供事务处理、数据访问、安全性、持久性等服务。在J2EE中组件和组件之间并不直接访问，而是通过容器提供的协议和方法来相互调用。组件和容器间的关系通过&#8220;协议&#8221;来定义。容器的底层是J2EE服务器，它为容器提供J2EE中定义的各种服务和API。一个J2EE服务器（也叫J2EE应用服务器）可以支持一种或多种容器。在图1中，你可能已经注意到每个容器的服务包括两部分：J2SE（Java 2 Platform Standard Edition）和一组扩展的服务。这是因为J2EE是以Java标准版为基础的，各容器在J2SE之上再根据需要提供一些扩展的服务，如目录服务、事务管理、数据访问、消息机制、安全性等。 <br><br><strong>J2ee的核心——EJB</strong> <br>J2EE定义了四种组件：Applet组件、Application客户组件、Web组件及EJB（Enterprise JavaBeans）组件。其中Applet和Application客户组件在客户端运行，J2EE通过Java插件为Applet提供运行环境，Application客户的容器就是本地Java虚拟机。Web及EJB组件在服务端运行。J2EE中包含两种Web组件：JSP和Servlet。它们是Web服务器的功能扩展，都能生成动态Web页面。不同的是JSP是将Java代码嵌入到HTML中，服务器负责解释执行，生成结果返回用户（与ASP技术相似）。而Servlet是单独的Java类，它动态生成HTML文件返回给客户。Web组件的容器比较典型的就是基于Java的Web服务器。 <br>EJB是J2EE平台的核心，也是J2EE得到业界广泛关注和支持的主要原因。我们知道，J2EE的一个主要目的就是简化企业应用系统的开发，使程序员将主要精力放在商业逻辑的开发上。EJB正是基于这种思想的服务器端技术，它本身也是一种规范，该规范定义了一个可重用的组件框架来实现分布式的、面向对象的商业逻辑。EJB的核心思想是将商业逻辑与底层的系统逻辑分开，使开发者只需关心商业逻辑，而由EJB容器实现目录服务、事务处理、持久性、安全性等底层系统逻辑。 <br>一个可部署的EJB组件包含3个部分： <br>Remote 接口 Remote接口定义EJB组件中提供的可供用户调用的方法，也就是通常所说的实现商业逻辑的函数或过程（如计算商品价格的函数），以供远程客户端调用。在EJB组件部署到容器的时候，容器会自动生成Remote接口相应的实例，即EJB对象，它负责代理用户的调用请求。 <br>Home接口 Home接口定义一组方法来创建新的EJB对象，查找、定位和清除已有的EJB对象。在EJB组件部署时容器也会自动生成相应的Home对象，该对象负责查找和创建EJB对象，返回EJB对象的引用给客户；用户利用该引用调用EJB组件的方法，得到结果；最后Home对象清除EJB对象。我们可以形象地称Home接口为EJB对象的工厂。 <br>Enterprise Beans类 Enterprise Beans类是商业逻辑的具体实现类。其可供用户调用的方法在Remote接口中定义。根据功能不同，EJB 2.0规范中定义了三种Enterprise Beans:会话Beans（Session Beans）、实体Beans（Entity Beans）和消息驱动Beans（Message-driven Beans）。 <br>会话Beans分无状态和有状态两种。一般无状态的会话Beans模拟商业逻辑，比如计算价格等。有状态的会话Beans通常模拟一个客户会话，它会临时保存客户信息，根据客户要求调用其他Beans来存取数据。两种会话Beans都不保存状态信息或数据，当客户断开连接或服务器关闭时，会话Beans也随之消失。一个会话Beans的典型例子是网站上的购物车。 <br>实体Beans模拟商业数据，它表示一个数据存储，可以是状态信息或数据库中的一条纪录。实体Beans在客户断开连接或服务器关闭后，仍有服务保证其数据得以保存。一个实体Beans的典型例子就是客户账号信息。 <br>消息驱动Beans在行为上很像会话Beans。不同的是仅在需要向这些Beans发送消息时才调用消息驱动Beans,比如在需要的时候发送用户确认信息等。 <br>另外，在提交和部署EJB组件时，还需要两个文件：部署描述文件，容器根据该文件来部署Enterprise Beans，提供所要求的服务；EJB jar文件，它是提交给EJB容器的一个部署单元，容器（应用服务器）在部署时解开它，装入Enterprise Beans。 <br>EJB容器非常复杂，一般由专业的J2EE应用服务器开发商提供，比较流行的EJB容器由IBM的WebShpere、BEA公司的WebLogic Server、Sun公司的iPlant等应用服务器提供。EJB容器除了为EJB提供事务处理、目录服务、持久性管理和安全性服务外，还负责EJB的部署、发布和生命周期管理。 <br><br><strong>平台标准服务</strong> <br>服务是组件和容器之间，以及容器和J2EE服务器之间的接口，在实现层面上它就是一系列API和协议。J2EE平台定义了一组标准的服务，其中有些服务是由J2SE提供的，有些则是J2EE对Java的扩展。 <br>目录服务　JNDI（Java Name and Directory） API为应用程序提供了一个统一的接口来完成标准的目录操作，由于JNDI是独立于目录协议的，应用程序可以用它访问各种目录服务，如LDAP、NDS、DNS等。 <br>数据访问 JDBC（Java Database Connectivity) API为访问不同类型的数据库提供了统一的途径，屏蔽了不同数据库的细节，具有平台无关性。J2EE平台除了要求核心的JDBC API（包含在J2SE中）外，还要求扩展的JDBC API 2.0，它支持行集、连接池和分布式的事务处理。 <br>事务处理 JTA（Java Transaction Architecture）定义了一组标准的接口，为应用系统提供可靠的事务处理支持。JTS（Java Transaction Service）是CORBA OTS事务监控的Java实现。JTS规定了事务管理器的实现方式，该事务管理器在高层支持JTA标准，在底层实现了OMG OTS规范的Java映射。 <br>消息服务　JMS（Java Message Service）是一组用于和面向消息的中间件相互通信的API。<br>它既支持点对点的消息通信，也支持发布/订阅式的消息通信。 电子邮件 JavaMail API允许在应用程序中以独立于平台、独立于协议的方式收发电子邮件。JAF（JavaBeans Activation Framework）负责处理MIME编码，JavaMail利用JAF来处理MIME编码的邮件附件。 <br>CORBA兼容接口　RMI（远程方法调用）是在分布式对象间通信的Java本地方法，它使应用程序调用远程方法像调用本地方法一样，不需要考虑所调用对象的位置。RMI-IIOP是RMI的扩展，是符合CORBA标准的对象通信协议，也是J2EE默认的组件通信协议。Java IDL允许J2EE应用组件通过IIOP协议访问外部的CORBA对象。 <br>安全服务　JAAS（Java Authentication and Authorization Service）用两个步骤实现安全性：认证，即由用户提供认证信息（如用户名和密码）来获得系统认证，这一过程又称之为登录；授权，在被确认为合法用户后，系统根据用户的角色授予其相应的权限。J2EE的授权是基于安全角色的概念，一个安全角色是一个拥有相同权限的逻辑组。J2EE的安全角色由应用组件提供商来定义。 <br>Web服务支持 目前J2EE还不提供对Web服务的支持。Sun提供了一套API及其实现WSDP作为对J2EE的扩展，但目前还不是J2EE规范的内容。在WSDP中，JAXP用来解析XML文档;JAXR向UDDI服务器注册Web Services；JTX/RPC用基于XML的协议（如SOAP）来发送和接收XML文档；JWSDL处理WSDL文档。虽然J2EE不是为Web服务而生，但它现在正在努力追赶Web服务的脚步。 <br><br><strong>多层应用模型</strong> <br>从应用的角度来看，J2EE为企业应用系统的开发提供了一种多层分布式企业应用模型。在J2EE中，应用逻辑按功能不同可以划分为不同类型的组件，各组件根据它们所在的层分布在不同的机器上，共同组成一个基于组件的分布式系统。 <br>如图2所示，J2EE定义了一个典型的四层结构，分别是客户层、Web层、商业逻辑层和企业信息系统层。 <br>在应用开发时，J2EE定义的四层模型可根据实际情况灵活运用。由于除了Applet外其他的组件都可以访问数据库、EJB组件和企业信息系统，所以通过不同层的取舍及组合，可以衍生出许多应用软件开发模型，如基于Web的四层模型、基于桌面应用的三层模型（不包括Web层）、B2B模型（不包括客户层）等。如果应用系统比较简单，一般不用EJB作为逻辑层，而直接用Web组件来实现商业逻辑和数据访问，毕竟EJB的开发和部署费用还相当高。<br>&nbsp;<br><br><strong>二、.NET开发平台留住Windows开发者</strong> <br><br>南京邮电学院 李建忠 <br><br>.NET开发平台一推出，就开始了与J2EE平台的竞争。它的绝大部分是微软Windows DNA(Distributed Network Architecture)的重写，DNA是微软以前开发企业应用程序的平台。Windows DNA中包括了许多已经被证实的技术，新的.NET框架取代了这些技术，并包含了Web服务层和改良的语言支持。从战略角度看，.NET开发平台担负着整合.NET战略的重任，但它最直接的目标则是努力为微软保留住庞大的Windows用户基础。 <br>微软的Windows开发用户群是微软通过Windows操作系统获得的最大财富。对于为什么要推出.NET开发平台，微软表示，主要原因之一就是由于Java向开发者承诺的硬件和操作系统无关性，可能会导致这些用户转向其他平台。虽然开发平台本身不会给微软带来很多收益，但Windows程序员是企业内部对微软产品的主要支持力量，商用软件的开发者形成了向客户销售微软产品的重要渠道。如果微软可以让开发者在.NET开发平台上编写应用程序，那么就会有更多的公司购买微软的其他产品。 <br>认识.NET <br>认识.NET最好的方法是看它做什么。.NET战略将互联网本身作为构建新一代操作系统的基础，并对互联网和操作系统的设计思想进行合理延伸，使开发人员能够创建出与设备无关的应用程序，以便轻松实现互联网连接。.NET包括一个相当广泛的产品家族，它们构建于XML和互联网产业标准之上，为用户提供Web服务的开发、管理、应用和体验。图1是对.NET战略的总体描述。组成.NET战略的五个方面包括： <br>.NET开发平台 这是一组用于建立Web服务应用程序和Windows桌面应用程序的软件组件，包括 .NET Framework（框架）、.NET开发者工具和ASP.NET。于今年3月发布的Visual Studio .NET将是RAD开发工具中一个重要的产品。 <br>.NET服务器 能够提供广泛聚合和集成Web服务的服务器是搭建.NET平台的后端基础。 .NET基础服务 密码认证、日历、文件存储、用户信息等基础服务是必不可少的。微软正在着力建设的.NET My Services等基础性服务平台是这方面可以借鉴的例子。 <br>.NET终端设备 广泛的连接互联网并体验Web服务的终端设备是实现.NET的前端基础。PC、PDA以及各种嵌入式设备将在这个广阔的天地里发挥作用。 <br>.NET用户体验 能够满足人们各种各样需求的用户体验是.NET的最终目标，也是.NET的价值实现。 <br>在这五个组成部分当中，.NET开发平台中的 .net框架是.NET软件构造中最具挑战性的部分，其他四个部分则紧紧围绕.NET框架来进行组织整合。<br>&nbsp;<br><strong>.NET 框架内核 </strong><br>.NET框架实现了语言开发、代码编译、组件配置、程序运行、对象交互等各个层面的功能，为Web服务及普通应用程序提供了一个托管、安全、高效的执行环境。所有在.NET平台上创建的应用程序运行都需要两个核心模块:Common Language Runtime（CLR，通用语言运行时）和.NET Framework类库。CLR是一个软件引擎，用来加载应用程序，确认它们可以没有错误地运行，并进行相应的安全许可验证，执行应用程序，然后将被清除。<br>.NET Framework类库则向程序员提供软件组件，来编写在CLR的控制下运行的代码，它们按照单一有序的分级组织提供了一个庞大的功能集，包括从文件系统到对XML功能的网访问的每一样功能。该类库为开发提供了三种基本编程模板：基于ASP.NET的Web表单应用、基于ASP.NET的Web服务应用和基于传统GUI交互的Windows应用。图2描述了 .NET开发平台的组成。 <br>CLR——.NET的虚拟机 <br>CLR为.NET应用程序提供了一个托管的代码执行环境。托管意味着将原来由程序员或操作系统做的工作剥离出来交由CLR来完成，从而使程序运行获得更高的安全性和稳定性。这些工作包括内存管理、即时编译、组件自描述、安全管理和代码验证，以及其他一些系统服务。CLR提供一个技术规范，无论程序使用什么语言编写，只要能编译成中间语言，就可以在它的支持下运行，这样.NET应用程序就可以独立于语言。CLR还在应用程序运行环境中为基于组件的编程提供了直接支持，比如它支持属性、事件、对象、继承性、多态性、接口等组件编程特性。 <br>CLR中的自动垃圾收集器负责.NET应用程序运行时的内存分配、对象布局、内存释放等内存管理问题，彻底解决了多年来困扰程序员的内存泄漏问题，大大增强了应用程序的健壮性。 <br>即时编译器在运行时将中间语言以调用的对象方法为单位动态编译成本地二进制代码。<br>中间语言是在.NET平台下编译器输出PE文件（Windows可执行文件）的语言，它为.NET平台提供了多语言支持，允许开发者使用20多种不同的编程语言。而元数据是一个内嵌于PE文件的表的集合，描述了代码中数据类型等在代码执行时CLR需要知道的信息。元数据使得.NET应用程序代码具备自描述特性，提供了类型安全保障，而这在以前需要额外的类型库或接口定义语言（IDL）。 <br>CLR根据托管组件的来源（如互联网、企业局域网、本地机器）等因素确定各组件的信任度，并根据信任度来限定它们执行诸如读取文件、修改注册表等敏感操作的权限。此外，CLR借助通用类型系统对代码类型进行严格的安全检查，可以避免不同组件之间可能存在的类型不匹配问题。通过代码访问安全机制，开发人员可以为应用程序指定完成工作所必需的权限。CLR不仅规定了代码访问安全，还规定了基于角色的安全。基于角色的认证为互联网上分布式组件的执行提供了安全保证。 <br>值得指出的是，CLR通常寄宿在其他高性能服务器的应用程序中，比如互联网信息服务器（IIS）、SQL Server数据库服务器等。这样，开发者可以充分利用CLR诸多安全、高效的优点来部署自己的商业逻辑。<br>&nbsp;<br><strong>类库——组件和服务的家园</strong> <br>.NET Framework类库由一组广泛的、面向对象的、可被开发者用于任何编程语言的可重用类集合组成。它提供了几乎所有应用程序都需要的公共代码;在此之上是许多应用程序模板，这些模板为开发网络站点和网络服务提供特定的高级组件和服务，不管是传统的命令行程序还是Windows图形界面程序，亦或是面向下一代互联网分布式计算平台的ASP.NET或Web服务应用。与在Windows和它的SDK中发送的代码库一样，.NET框架类库将程序员从繁重的编程细节中解放出来，而专注于程序的商业逻辑。它将核心Win32 API最常用的功能和外挂SDK的功能封装到了一个统一的包中，并采用清晰而有条理的方式对类库进行分组和描述，这样开发者就能够更方便地找到其应用程序所需要的大多数功能。下面是它所提供的一些核心服务： <br>系统框架服务 <br>服务框架包括一套开发人员希望在标准语言库中存在的基类库，如集合、输入/输出、字符串、数据等基类。基类库还提供访问操作系统服务的类，如图画、网络、线程、加密等类型。此外，服务框架也包括数据访问类库以及开发工具。 <br>ADO.NET组件 <br>ADO.NET为基于网络的、可扩展的应用程序和服务提供数据访问服务。它不仅支持传统的基于链接指针风格的数据访问，而且对于更适合于把数据返回到客户端应用程序的无链接数据模板，它也提供高性能的访问支持。 <br>XML数据组件 <br>通过它开发人员可以对任何数据进行XML转换、传输和确认，所有数据都可以被看做是XML格式的。同时，系统也支持ADO.NET数据与XML数据之间的通用转换。 <br>Windows表单组件 <br>Windows表单组件为开发人员提供了强大的Windows应用程序模型和丰富的Windows用户口，包括传统的ActiveX控件和Windows XP的新界面，如透明的、分层的浮动窗口。对CLR的强大支持也是Windows表单组件令人兴奋的地方之一。 <br>ASP.NET应用服务 <br>ASP.NET的核心是其用于处理基于低级结构HTTP请求的高性能的运行语言，其编译运行的方式大大提高了它的性能。ASP.NET使用基于构件的.NET框架配制模板，因此它获得了诸如XCOPY配制、构件并行配制、基于XML配制之类的优点。它还支持应用程序的实时更新，同时提供高速缓冲服务，以改善性能。 <br>ASP.NET Web表单 <br>ASP.NET Web表单把VB表单高效率的优点带到了Web应用程序的开发中。ASP.NET Web单支持传统的将HTML内容与脚本代码混合的ASP语法，但是它提出了一种将应用程序代码和用户接口内容分离的、更加结构化的方法。它提供一套映射传统HTML用户接口部件（包括列表框、文本框和按钮）的ASP.NET Web表单控件和一套更加复杂的Web应用控件（如日历和广告转板）。 <br>对Web服务的支持 <br>ASP.NET应用服务体系架构为用ASP.NET建立Web服务提供了一个高级的可编程模板。虽然建立Web服务并不限定使用特定的服务平台，但是ASP.NET的许多优点将简化其开发过程。使用这个编程模型，开发人员甚至无需理解HTTP、SOAP或其他任何网络服务规范。ASP.NET可以利用现存的体系架构和应用程序，为在互联网上绑定应用程序提供了一个简单的、灵活的、基于产业标准的模型。 
<img src ="http://www.blogjava.net/orangelizq/aggbug/132899.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-27 22:54 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/27/132899.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAF 学习</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/27/132893.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Fri, 27 Jul 2007 14:34:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/27/132893.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/132893.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/27/132893.html#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/132893.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/132893.html</trackback:ping><description><![CDATA[<p>The JavaBeans Activation Framework 1.1 contains a few small enhancements and bugs fixes. The enhancements are described <a href="http://java.sun.com/products/javabeans/jaf/downloads/JAF-1.1-changes.txt"><u><font color=#0000ff>here</font></u></a> and have been approved by the JCP as <a href="http://jcp.org/en/jsr/detail?id=925"><u><font color=#0000ff>JSR-925</font></u></a>. The primary enhancements are:
<ul>
    <li>Provide a list of all MIME types known to a MailcapCommandMap.
    <li>Provide access to the native commands known to a MailcapCommandMap.
    <li>Support for fallback entries in mailcap file. </li>
</ul>
<p>The JavaBeans Activation Framework 1.1 requires J2SE 1.4 or greater.</p>
<p>The JavaBeans Activation Framework 1.1 final release is included with the <a href="http://jcp.org/en/jsr/detail?id=270"><u><font color=#0000ff>Java SE 6</font></u></a> release and the <a href="http://java.sun.com/javaee/"><u><font color=#0000ff>Java EE 5</font></u></a> release, and is also available separately. </p>
For a detailed description see the <a href="http://java.sun.com/products/javabeans/glasgow/jaf-changes.txt"><u><font color=#0000ff>jaf-changes.txt</font></u></a> document. <br>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ... ...</p>
<p>JavaBeans Activation Framework（JAF）主页 　　 <a href="http://java.sun.com/beans/glasgow/jaf.html" target=_blank><font color=#014da2>http://java.sun.com/beans/glasgow/jaf.html</font></a>　 <br><br><br><br><br></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=FR style="mso-ansi-language: FR">JavaBeans</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">激活框架</span><span style="FONT-FAMILY: 宋体; mso-ansi-language: FR; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">（</span><span lang=FR style="mso-ansi-language: FR">JavaBeans Activation Framework</span><span style="FONT-FAMILY: 宋体; mso-ansi-language: FR; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">）</span><span lang=FR style="mso-ansi-language: FR"><o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=FR style="mso-ansi-language: FR"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang=EN-US>JAF</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的目的在于统一处理不同数据格式的方法（不管数据格式为简单文本还是由图片、声音、视频甚至其它</span><span lang=EN-US>"</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">活动</span><span lang=EN-US>"</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">内容共同组成的复合文档）。在这个意义上，</span><span lang=EN-US>JAF</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对</span><span lang=EN-US>Java</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的作用正如插件对</span><span lang=EN-US>Web</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">浏览器的作用。</span><span lang=EN-US>JAF</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">使得</span><span lang=EN-US style="COLOR: blue">Java</span><span style="COLOR: blue; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">与<span style="COLOR: blue">编码数据流</span>之间的映射变得非常容易。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>JAF</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">完成于</span><span lang=EN-US>1998</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">年，当时</span><span lang=EN-US>JavaBean</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">非常流行，</span><span lang=EN-US>Swing</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">非常热门，</span><span lang=EN-US>Servlet</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">刚刚出现，</span><span lang=EN-US>J2EE</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">平台还没有。那时</span><span lang=EN-US>Sun</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">需要一个用于文件浏览器和</span><span lang=EN-US>JavaMail</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的标准发现</span><span lang=EN-US>API</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，此需要推动了</span><span lang=EN-US>JAF</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的创建。<span style="COLOR: blue">&#8220;发现&#8221;指可以动态找到正确的组件以处理任意但类型化了的数据的机制</span>。特别是，作为发现</span><span lang=EN-US>API</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">来开发</span><span lang=EN-US>JAF</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，以便使</span><span lang=EN-US>GUI</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">开发人员能够为文档和图像数据动态地发现浏览器和编辑器。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">发现取决于注册的类型，注册可以将每一文件类型与对应的应用程序建立关联。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">简单的说，</span><span lang=EN-US>JAF</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">提供了用于动态发现可视窗口部件以处理由</span><span lang=EN-US>MIME</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件头描述的各种数据的架构。当用于</span><span lang=EN-US>SAAJ</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">这样的非可视系统时，</span><span lang=EN-US>JAF</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">可以将</span><span lang=EN-US style="COLOR: blue">Java</span><span style="COLOR: blue; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类型</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">映射到将它们无缝转换到数据流的<span style="COLOR: blue">特殊处理程序</span>，从而允许</span><span lang=EN-US>SAAJ</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">将</span><span lang=EN-US>Java</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象自动转换成由</span><span lang=EN-US>SwA MIME</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">部分包含的<span style="COLOR: blue">原始数据</span>。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><strong style="mso-bidi-font-weight: normal"><span lang=EN-US style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">1.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span></strong><strong style="mso-bidi-font-weight: normal"><span lang=EN-US>DataHandler </span></strong><strong style="mso-bidi-font-weight: normal"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类</span><span lang=EN-US><o:p></o:p></span></strong></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>JAF</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">架构的核心是</span><span lang=EN-US>javax.activation.DataHandler</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类。</span><span lang=EN-US>DataHandler</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类定义了三个构造函数：</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Public DataHandler(DataSource ds)</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Public DataHandler(java.net.URL url)</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Public DataHandler(Object obj, String mimeType)</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">当使用前两个构造函数时，数据处理会委托到</span><span lang=EN-US>DataSource</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">接口的一个子类型，使用第三个构造函数时，数据处理委托到一个动态发现的内容处理程序并使用此处理程序。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">如果</span><span lang=EN-US>DataHandler</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">是用</span><span lang=EN-US>DataSource</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象构造的，那么它将首先试图试图将</span><span lang=EN-US>getContent()</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">方法委托到一个</span><span lang=EN-US>DCH</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，如果它没有找到</span><span lang=EN-US>DCH</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，会返回一个从</span><span lang=EN-US>DataSource</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中得到的</span><span lang=EN-US>InputStream</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。有趣的是，</span><span lang=EN-US style="COLOR: blue">DataHandler</span><span style="COLOR: blue; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">总是试图首先委托到一个</span><span lang=EN-US style="COLOR: blue">DCH</span><span style="COLOR: blue; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">，即便是用</span><span lang=EN-US style="COLOR: blue">DataSource</span><span style="COLOR: blue; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">创建</span><span lang=EN-US style="COLOR: blue">DataHandler</span><span style="COLOR: blue; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">也是如此</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">上述说明的问题看似简单，但实际上非常重要，它说明了</span><span lang=EN-US>getContent()</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">方法的作用。</span><span lang=EN-US style="COLOR: blue">getContent()</span><span style="COLOR: blue; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">方法要返回一个表示附件的</span><span lang=EN-US style="COLOR: blue">Java</span><span style="COLOR: blue; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象，</span><span lang=EN-US style="COLOR: blue">DataSource</span><span style="COLOR: blue; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的</span><span lang=EN-US style="COLOR: blue">InputStream</span><span style="COLOR: blue; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">只作为最后一种手段来返回</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。从</span><span lang=EN-US>DCH</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中返回的对象类型取决于</span><span lang=EN-US>DCH</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的编码方式。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><strong style="mso-bidi-font-weight: normal"><span lang=EN-US style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">2.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span></strong><strong style="mso-bidi-font-weight: normal"><span lang=EN-US>DataContentHandler</span></strong><strong style="mso-bidi-font-weight: normal"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类</span><span lang=EN-US><o:p></o:p></span></strong></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">此接口用于实现将</span><span lang=EN-US>Java</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象流转换成数据流，或将数据流转换成</span><span lang=EN-US>Java</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>DataContentHandler</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类型的每一个子类</span><span lang=EN-US>(</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">即每一个</span><span lang=EN-US>DCH)</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">均用于自动将</span><span lang=EN-US>Java</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象的专用类型转换为流，以及将流转换成</span><span lang=EN-US>Java</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象。如某个</span><span lang=EN-US>DCH</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">能够将</span><span lang=EN-US>java.awt.Image</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象转换为</span><span lang=EN-US>JPEG</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">或</span><span lang=EN-US>GIF</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">编码流，或者进行反向转换。</span><span lang=EN-US>DCH</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">的缺点是它们的使用范围通常受到很大的限制，每一个</span><span lang=EN-US>DCH</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">只能讲一个<span style="COLOR: blue">对象类型</span>转换成一种类型的<span style="COLOR: blue">数据流</span>。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">与</span><span lang=EN-US>DCH</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">不同，</span><span lang=EN-US>DataSource</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象只处理数据流。用户要用</span><span lang=EN-US>DataSource</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">从某种类型的资源中读数据流，并将数据流写入</span><span lang=EN-US>DataSource</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。，此时重点放在了流上而不是</span><span lang=EN-US>Java</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象类型上。从而</span><span lang=EN-US>DataSource</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">具有很大的通用性，其缺点为不宜于使用，因为用户直接处理的是流而不是</span><span lang=EN-US>Java</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1; tab-stops: list 18.0pt"><strong style="mso-bidi-font-weight: normal"><span lang=EN-US style="mso-fareast-font-family: 'Times New Roman'"><span style="mso-list: Ignore">3.<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span></strong><strong style="mso-bidi-font-weight: normal"><span lang=EN-US>DataSource </span></strong><strong style="mso-bidi-font-weight: normal"><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类</span><span lang=EN-US><o:p></o:p></span></strong></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>DataSource</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类拥有完成委托操作所需要的全部功能。当使用</span><span lang=EN-US>DataSource</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">或</span><span lang=EN-US>URL</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">参数构造</span><span lang=EN-US>DataHandler</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">时，</span><span lang=EN-US>DataHandler</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">要使用</span><span lang=EN-US>DataSource</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">而不是</span><span lang=EN-US>DCH</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">。</span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>JAF</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">定义了两个标准的</span><span lang=EN-US>DataSource</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象，即</span><span lang=EN-US>FileDataSource</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象和</span><span lang=EN-US>URLDataSource</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">对象。</span><span lang=EN-US><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">某些</span><span lang=EN-US>DataSource</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">实现用于动态发现它们的</span><span lang=EN-US>MIME</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类型。例如，</span><span lang=EN-US>FileDataSource</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">用</span><span lang=EN-US>MIME</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类型注册表发现它的</span><span lang=EN-US>MIME</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类型。</span><span lang=EN-US>JAF</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">中的默认</span><span lang=EN-US>MIME</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">类型注册表是</span><span lang=EN-US>mimetypes.default</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件，该文件包含在</span><span lang=EN-US>JAF</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">二进制</span><span lang=EN-US>JAR</span><span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">文件中。</span></p>
<p><br>&nbsp;</p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/132893.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-27 22:34 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/27/132893.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java十年的发展轨迹和历史变迁</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/27/132776.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Fri, 27 Jul 2007 06:07:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/27/132776.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/132776.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/27/132776.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/132776.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/132776.html</trackback:ping><description><![CDATA[<div class=item-content><font color=#dc6900>【正文】</font><br><img height=1 src="http://blog.sohu.com/manage/entry.do?m=edit&amp;id=40685812&amp;t=shortcut#" width=1>
<div>　　从JDK诞生到现在已经有11年的时间了。沧海桑田一瞬间。转眼11年过去了，JDK已经发布了6个版本。在这11年里诞生了无数和Java相关的技术和标准。现在让我们进入时间隧道，重新回到1995年，再来回顾一下Java的发展轨迹和历史变迁。 <br><br><font color=#ff0000>　　一、 JDK前传 </font><br><br>　　在这个世界上，熟悉Java历史的人非常多，如果要问一个人Java是从哪年诞生的，也许大多数人都会回答是1995年（这个非常好记，因为微软的Windows95也是在这一年发布的）。但事实上Java早在上个世纪90年代初就开始酝酿了。 <br><br>　　1991年4月，Sun公司的James Gosling领导的绿色计划(Green Project)开始着力发展一种分布式系统结构，使其能够在各种消费性电子产品上运行。而Green项目组的成员一开始使用C++语言来完成这个项目，由于Green项目组的成员都具有C++背景，所以他们首先把目光锁定了C++编译器，Gosling首先改写了C++编译器，但很快他就感到C++的很多不足，需要研发一种新的语言Java来替代它，一杯飘香的咖啡成为了它的标志。 <br><br>　　在17 个月后，整个系统完成了，这个系统是更注重机顶盒式的操作系统，不过在当时市场不成熟的情况下，他们的项目没有获得成功，但Java语言却得到了Sun总裁McNealy的赏识。 <br><br>　　直至 1994年下半年，由于Internet的迅猛发展和环球信息网 WWW的快速增长，第一个全球信息网络浏览器Mosaic诞生了；此时，工业界对适合在网络异构环境下使用的语言有一种非常急迫的需求；Games Gosling决定改变绿色计划的发展方向，他们对Oak进行了小规模的改造，就这样，<font color=#ff0000>Java在1995年的3月23日诞生了</font>！Java的诞生标志着互联网时代的开始，它能够被应用在全球信息网络的平台上编写互动性及强的Applet程序，而1995年的Applet无疑能给人们无穷的视觉和脑力震荡。我们姑且将Java的这段历史称为Java前传吧。 <br><br>　　其实Java的诞生颇有那么一股&#8220;有心栽花花不开，无心插柳柳成阴&#8221;的味道。就象当年Unix和它的前身MULTICS系统一样。 <br><br>　<font color=#ff0000>　二、JDK的幼年时期(1995～1998) <br></font><br>　　Sun继Green项目后又经过了几年的研究，终于在1995年5月23日在SunWorld'95上正式发布Java和HotJava浏览器。在同年，有很多公司先后获得了Java许可证，如Netscape在1995年8月，Oracle在1995年10月分别获得了Java许可证。Sun在1995年发布第一个Java版本后，于1996年1月宣布成立新的业务部门──JavaSoft部，这个部分主要负责开发、销售并支持基于Java技术的产品，由AlanBaratz先生任总裁。 <br><br>　　在1995年Sun虽然推出了Java，但这只是一种语言，而要想开发复杂的应用程序，必须要有一个的强大的开发库支持还行。因此，<font color=#ff0000>Sun在1996年1月23日发布了JDK1.0</font>。这个版本包括了两部分：<font color=#ff0000>运行环境（即JRE）和开发环境(即JDK)</font>。在<font color=#ff0000>运行环境中包括了核心API、集成API，用户界面API，发布技术，Java虚拟机(JVM)五个部分</font>。而<font color=#ff0000>开发环境还包括了编译Java程序的编译器（即javac）</font>。在JDK1.0时代，JDK除了AWT（一种用于开发图形用户界面的API）外，其它的库并不完整。 <br><br>　　Sun在推出JDK1.0后，紧跟着，<font color=#ff0000>Sun在1997年2月18日发布了JDK1.1</font>。JDK1.1相对于JDK1.0最大的改进就是为JVM增加了JIT(即时编译)编译器。JIT和传统的编译器不同，传统的编译器是编译一条，运行完后再将其扔掉，而JIT会将经常用到的指令保存在内容中，在下次调用时就不需要再编译了。这样JDK在效率上有了非常大的提升。 <br><br>　　Sun在推出JDK1.1后，接着又推出了数个JDK1.x版本。自从Sun推出Java后，JDK的下载量不断彪升，在1997年，JDK的下载量突破了220,000，而在1998年，JDK的下载量已经超过了2,000,000。 <br><br>　　虽然在1998年之前，Java被众多的软件企业所采用，但由于当时硬件环境和JVM的技术原因，它的应用却很有限。当时Java主要只使用在前端的Applet以及一些移动设备中。然而这并不等于Java的应用只限于这些领域。<font color=#ff0000>在1998年是Java开始迅猛发展的一年。在这一年中Sun发布了JSP/Servlet、EJB规范以及将Java分成了J2EE、J2SE和J2ME</font>。标志着Java已经吹响了向企业、桌面和移动3个领域进军的号角。 </div>
<div>&nbsp;</div>
<div><font color=#ff0000>&nbsp;&nbsp;&nbsp; 三、JDK的青少年时期(1998～2004) </font><br><br>　　到1998年，Java已经走过了3个年头。从JDK1.0到JDK1.1.8。JDK1.x经过了9个小版本的发展，已经初具规模。至此，它已经走出了摇篮，可以去独闯世界了。 <br><br>　　<font color=#ff0000>在1998年12月4日。Sun发布了Java的历史上最重要的一个JDK版本：JDK1.2</font>。这个版本标志着Java已经进入Java2时代。这个时期也是Java飞速发展的时期。 <br><br>　　在Java2时代Sun对Java进行了很多革命性的变化 ，而这些革命性的变化一直沿用到现在，对Java的发展形成了深远的影响。 <br><br>　　<font color=#ff0000>JDK1.2自从被分成了J2EE、J2SE和J2ME三大块</font>，得到了市场的强烈反响。不仅如此，JDK1.2还对它的API分成了三大类。 <br><br>　　核心API <br><br>　　&nbsp;&nbsp;&nbsp; 由Sun公司制定的基本的API，所有的Java平台都应该提供。这就是我们平常所说的Java核心类库。 <br><br>　　可选API <br><br>　　　　这是Sun为JDK提供的扩充API，这些API因平台的不同而不同。 <br><br>　　特殊API <br><br>　　　　用于满足特殊要求的API。如用于JCA和JCE的第三方加密类库。 <br><br>　　Java2除了上述的一些改进外，还增加了很多新的特性。其中最吸引眼球的当属Swing了。Swing是Java的另一个图形库。它不但有各式各样先进的组件，而且连组件风格都可抽换。在Swing出现后，很快就抢了AWT的风头。但Swing并不是为取代AWT而存在的，事实上Swing是建立在AWT之上的。就象JFace是建立在SWT之上一样。另外Java2还在多线程、集合类和非同步类上做了大量的改进。 <br><br>　　从JDK1.2开始，Sun以平均2年一个版本的速度推出新的JDK。<font color=#ff0000>在2000年5月8日。Sun对JDK1.2进行了重大升级。推出了JDK1.3</font>。 <br><br>　　Sun在JDK1.3中同样进行了大量的改进，主要表现在一些类库上（如数学运算、新的Timer API等）、在JNDI接口方面增加了一些DNS的支持、增加了JNI的支持，这使得Java可以访问本地资源了、支持XML以及使用新的Hotspot虚拟机代替了传统的虚拟机。 <br><br>　　在JDK1.3时代，相应的应用程序服务器也得到了广泛的应用，如<font color=#ff0000>第一个稳定版本Tomcat3.x在这一时期得到了广泛的应用</font>，WebLogic等商业应用服务器也渐渐被接受。 <br><br>　　时间如水、生命如歌。<font color=#ff0000>转眼到了2002年。Sun在这一年的2月13日发布了JDK历史上最为成熟的版本：JDK1.4</font>。在进入21世纪以来，曾经在.NET平台和Java平台之间发生了一次声势浩大的孰优孰劣的论战，Java的主要问题就是性能。 <br><br>　　因此，这次Sun将主要精力放到了Java的性能上。在JDK1.4中，Sun放言要对Hotspot虚拟机的锁机制进行了改进，使JDK1.4的性能有了质的飞跃。同时由于Compaq、Fujitsu、 SAS、 Symbian、 IBM等公司的参与，使JDK1.4成为发展最快的一个JDK版本。到JDK1.4为止，我们已经可以使用Java实现大多数的应用了。 <br><br><font color=#ff0000>　　四、JDK的壮年时期(2004～至今) </font><br><br>　　虽然从JDK1.4开始，Java的性能有了显著的提高，但Java又面临着另一个问题，那就是复杂。 <br><br>　　虽然Java是纯面向对象语言，但它对一些高级的语言特性（如泛型、增强的for语句）并不支持。而且和Java相关的技术，如EJB2.x，也由于它们的复杂而很少有人问津。也许是Sun意识到了这一点。因此，<font color=#ff0000>在2004年10月，Sun发布了我们期待已久的版本：JDK1.5，同时，Sun将JDK1.5改名为J2SE5.0</font>。和JDK1.4不同，JDK1.4的主题是性能，而J2SE5.0的主题是易用。Sun之所以将版本号1.5改为5.0，就是预示着J2SE5.0较以前的J2SE版本有着很大的改过。 <br><br>　　Sun不仅为J2SE5.0增加了诸如泛型、增强的for语句、可变数目参数、注释(Annotations)、自动拆箱（unboxing）和装箱等功能，同时，也更新的企业级规范，如通过注释等新特性改善了EJB的复杂性，并推出了EJB3.0规范。同时又针对JSP的前端界面设计而推出了JSF。这个JSF类似于ASP.NET的服务端控件。通过它可以很快地建立起复杂的JSP界面。 <br><br>　　<font color=#ff0000>到2006年年底Sun也再接再厉地推出了J2SE6.0的测试版，预计在2007年初将推出它的正式版</font>。 <br><br>　　正象J2SE6.0的开发代号&#8220;野马（Mustang）&#8221;一样，我们已经隐约听到了野马的嘶鸣。据Sun发言人透露，J2SE6.0不仅在性能、易用性方面得到了前所未有的提高，而且还提供了如脚本、全新的API（Swing和AWT等API已经被更新）的支持。<font color=#ff0000>而且J2SE6.0是专为Vista而设计的</font>，它在Vista上将会拥有更好的性能。在推出J2SE6.0的同时，J2SE7.0项目也已经启动。 <br><br>　　在Java发展的十几年的时间里，经历了无数的风风雨雨。现在Java已经成为一种相当成熟的语言了。在这10年的发展中，Java平台吸引了数百万的开发者，在网络计算遍及全球的今天，更是有20亿台设备使用了Java技术。作为Java技术的基础，J2SE功不可没，让我们期望J2SE伴随Java平台一路走好！ </div>
<div>&nbsp;</div>
<div><font color=#ff0000>&nbsp;&nbsp;&nbsp; 五、JDK各版的发布时间表 <br></font><br>　　到现在为止我们已经重新走了一遍Java的历史轨迹。在这一部分，为了有一个总体的认识，让我们来看一看Java发展的时间表。 （版本号 名称 中文名 发布日期） <br><br>　　JDK 1.1.4 Sparkler 宝石 1997-09-12 <br><br>　　JDK 1.1.5 Pumpkin 南瓜 1997-12-13 <br><br>　　JDK 1.1.6 Abigail 阿比盖尔--女子名 1998-04-24 <br><br>　　JDK 1.1.7 Brutus 布鲁图--古罗马政治家和将军 1998-09-28 <br><br>　　JDK 1.1.8 Chelsea 切尔西--城市名 1999-04-08 <br><br>　　J2SE 1.2 Playground 运动场 1998-12-04 <br><br>　　J2SE 1.2.1 none 无 1999-03-30 <br><br>　　J2SE 1.2.2 Cricket 蟋蟀 1999-07-08 <br><br>　　J2SE 1.3 Kestrel 美洲红隼 2000-05-08 <br><br>　　J2SE 1.3.1 Ladybird 瓢虫 2001-05-17 <br><br>　　J2SE 1.4.0 Merlin 灰背隼 2002-02-13 <br><br>　　J2SE 1.4.1 grasshopper 蚱蜢 2002-09-16 <br><br>　　J2SE 1.4.2 Mantis 螳螂 2003-06-26 <br><br>　　J2SE 5.0 (1.5.0) Tiger 老虎 2004-10 <br><br>　　J2SE 6.0 (Beta) Mustang 野马 2006-04 <br><br>　　从这个表中我们可以看出一个非常有意思的现象，就是JDK的每一个版本号都使用一个开发代号表示（就是表中的中文名）。而且从JDK1.2.2开始,主要版本(如1.3,1.4,5.0)都是以鸟类或哺乳动物来命名的. 而它们的bug修正版本(如1.2.2,1.3.1,1.4.2)都是以昆虫命名的。 <br><br><font color=#ff0000>　　六、Java的未来10年 <br></font><br>　　在2005年的Java One开发者大会上，James Gosling作了题为&#8220;Java技术下一个10年贡献&#8221;的演讲。谈到未来Java的发展时，James Gosling提到了有关Java软件的性能和复杂性问题。鉴于许多机器运行着大量进程的实际情况，人们对线程模型投以越来越多的关注。 <br><br>　　随着人们对桌面应用的要求越来越高，系统将变得越来越复杂。他指出： &#8220;<font color=#ff0000>从工程的角度来看，未来10年内我们所面临的最大挑战就是复杂性问题</font>，&#8221; James Gosling说， &#8220;目前，我们开展了许多工作以解决应用编程接口、语言以及工具中所涉及的复杂性问题。在工具和用户界面(UI)中都会遇到复杂性问题，Java技术设计人员必须处理好大小尺寸调整和国际化的问题。&#8221; <br><br>　　在这次大会上，James Gosling还同Java技术先驱，现任Kleiner, Perkins Caulfield and Byers合伙人的Bill Joy先生，Sun公司首席科学家John Gage先生，未来研究所主任Paul Saffo先生，Sun杰出工程师Guy Steele先生以及Applied Mindes公司主席及首席技术官Danny Hillis先生等一起探讨了讨论Java语言的过去和未来发展情况。 <br><br>　　他们认为，<font color=#ff0000>Java技术提高了计算的&#8220;流动性&#8221;，</font>就如同货币的发明提高了商品的流动性一样。无所不在的网络丰富了每个人的信息，就如同可以兑换的货币产生了财富一样。由于从前的网络是很慢的，所以计算被束缚在特定的计算机上，而这种情况将一去不复返了。 <br><br>　　目前，全球Java开发人员已经超过450万，而与之相对应的是Java社区充满活力和创新精神，这正是Java下一个10年更加繁荣的保障。为了保持Java的增长和推进Java社区的参与, <font color=#ff0000>Sun在Java One开发者大会上宣布开放Java核心源代码</font>，以鼓励更多的人参与到社团活动中来，这是Sun为推进社团发展和维护Java技术兼容性而迈出的重要一步。 <br><br>　　Sun公司总裁兼首席运营官Jonathan Schwartz先生指出，来自Java社团和IBM等全球技术合作伙伴两方面的支持，乃是Java技术在创新和社会进步上继续发挥重要作用的强有力的标志。<font color=#ff0000>技术开放和社团建设降低了技术应用的壁垒，其结果是为参与和增长创造了更多的商机，这就形成了价值上千亿美元的Java产业。</font> <br><br>　　有很多人认为Java开源后，在众多开发人员的参与之下，Java会变得更加强大。随着Java和IT业界的关系变得更加紧密，Sun公司也将更容易卖出自己兼容Java良好的WEB服务器和操作系统。这个举动将会给软件开发群体带来新的活力，改善Sun公司的公众形象，并同时证明Sun可以成为一个开源社会的&#8220;良民&#8221;。 <br><br>　　随着Java的开源，Java的未来似乎变得更加明朗。在未来，Java的应用范围有可能变得更广。Sun董事长麦克里尼在2006年的JavaOne会议上说，&#8220;全球有3/4的人还不能接入Internet，这对Java技术伙伴来说是一个巨大的经济机会。瘦客户机、微小的传感器以及其它Java驱动的小装置，可以帮助我们改善人们的生活。他希望Java社区通过他们的工作能够弥合数字鸿沟&#8221;。 <br><br>　　Sun认为，数字媒体将是Java的下一个重点市场，同时，教育和健康将是未来Java发展过程中的两大重点应用领域。但愿Java的未来真能象Sun宣称的那样，成为我们未来生活的一部分。</div>
<div>&nbsp;&nbsp;PS：Java之父，Sun公司副总裁James Gosling博士&nbsp; <img alt="" src="http://img114.pp.sohu.com/images/blog/2007/4/4/23/10/112545644e2.jpg" border=0></div>
</div>
<img src ="http://www.blogjava.net/orangelizq/aggbug/132776.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-27 14:07 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/27/132776.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2EE简介</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/27/132775.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Fri, 27 Jul 2007 06:06:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/27/132775.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/132775.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/27/132775.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/132775.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/132775.html</trackback:ping><description><![CDATA[&nbsp;J2EE是一个开放的、基于标准的开发和部署的平台，用于构建N层的、基于Web的、以服务端计算为核心的、模块化的企业应用。J2EE同时也是所有兼容J2EE标准的应用服务器产品的统一标识。Sun公司领导着J2EE规范和标准的制定，但同时很多公司如IBM、BEA也为该标准的制定贡献了很多力量，所以J2EE每一个新规范的推出，都体现着整个业界对技术的共同认同。大家都遵守着&#8220;在标准上进行合作，在产品上进行竞争&#8221;的原则，从而使J2EE在技术规范上日臻完善和进步，同时又有各厂家实现J2EE规范的应用服务器产品在市场上推出，在性能上、价格上互相竞争，为最终用户提供多样化的选择。<br><br>　　从图1中可以看出，J2EE技术是在J2SE的基础之上，提供了企业计算所必须的服务如事务、安全性、消息服务等。J2SE平台提供Java运行时环境的标准功能，如对跨平台开发的支持和内存管理等。J2EE应用的组件如企业JavaBean（EJB）、JSP和Servlet运行于J2EE容器之中，通过连接器访问企业信息系统，如数据库系统、ERP系统和其他应用程序系统。J2EE应用可以集成一系列的客户端，包括独立运行的台式客户端、无线客户端，以及基于Web浏览器的客户端等。J2EE平台为开发企业应用提供了高性能、高可靠性和可伸缩性的运行支撑环境。<br><br><img alt="" src="http://img64.pp.sohu.com/images/blog/2007/4/4/22/20/112544474a5.JPG" border=0>
<p><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 图1 Ja2EEa平台体系架构<br><br>　　J2EE提供了一个多层的端到端的应用系统架构，如图2所示。在客户端层，多种客户端可通过多种不同的协议来访问中间层的J2EE服务，在设计J2EE应用的时候，首先需要选择的就是J2EE客户端的类型，应根据用户的使用模式及应用环境的特点来选择适合的客户端类型和通信协议，如Java Applet客户端和Java Web Start客户端适合于复杂的图形用户界面，如大量的数据录入、数据统计等应用，能满足高效的用户交互需求，根据具体应用环境的不同，可选择HTTP(S)、RMI/IIOP或JNLP等通信协议。<br><br></span></p>
<p><span><img alt="" src="http://img45.pp.sohu.com/images/blog/2007/4/4/22/21/11254512d3f.JPG" border=0><br>图2 端到端的多层应用系统架构<br><br>　　在中间层，主要包括Web服务器和应用服务器。在实际部署时，它们可以运行于单一的或多个物理平台上。从可靠性和可伸缩性考虑，应该采用多个Web服务器和应用服务器。Web服务器接受从客户端发来的请求，通过JSP和Servlet技术动态生成响应的内容，JSP或Servlet可以接着调用运行于应用服务器EJB容器中的企业 JavaBean进行相应的业务处理和运算。而中间层通过标准的协议访问企业信息系统层来读取数据和调用服务。<br><br>　　总之，J2EE为我们提供了一个建立在开放和标准的技术之上、非常灵活的端到端的多层体系架构，从而满足各种不同企业应用的需求。<br></span></p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/132775.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-27 14:06 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/27/132775.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]J2EE学习流程</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/27/132774.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Fri, 27 Jul 2007 06:05:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/27/132774.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/132774.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/27/132774.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/132774.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/132774.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp; 在这里我谈谈我在学习j2ee流程，并谈到在此过程中领会的经验和教训。以便后来者少走弯路。 <br>Java发展到现在，按应用来分主要分为三大块：J2SE,J2ME和J2EE。这三块相互补充，应用范围不同。 <br>&nbsp; 1.J2SE就是Java2的标准版，主要用于桌面应用软件的编程； <br>&nbsp; 2.J2ME主要应用于嵌入是系统开发，如手机和PDA的编程； <br>&nbsp; 3.J2EE是Java2的企业版，主要用于分布式的网络程序的开发，如电子商务网站和ERP系统。
<p>&nbsp;&nbsp;&nbsp; 先学习j2se <br>&nbsp;&nbsp;&nbsp; 要学习j2ee就要先学习j2se，刚开始学习j2se先建议不要使用IDE，然后渐渐的过渡到使用IDE开发，毕竟用它方便嘛。学习j2se推荐两本书，《java2核心技术一二卷》，《java编程思想》，《java模式》。其中《java编程思想》要研读，精读。这一段时间是基本功学习，时间会很长，也可能很短，这要看学习者自身水平而定。 </p>
<p>&nbsp;&nbsp;&nbsp; 不要被IDE纠缠 <br>&nbsp;&nbsp;&nbsp; 在学习java和j2ee过程中，你会遇到五花八门的IDE，不要被他们迷惑，学JAVA的时候，要学语言本身的东西，不要太在意IDE的附加功能，JAVA编程在不同IDE之间的转换是很容易的，过于的在意IDE的功能反而容易耽误对语言本身的理解。目前流行的IDE有jbuilder，eclipse和eclipse的加强版WSAD。用好其中一个就可以了，推荐从eclipse入手j2ee。因为Jbuilder更适合于写j2se程序。 </p>
<p>&nbsp;&nbsp;&nbsp; 选择和学习服务器使用配置 <br>&nbsp;&nbsp;&nbsp; 当你有了j2se和IDE的经验时，可以开始j2ee的学习了，web服务器：tomcat，勿庸置疑，tomcat为学习web服务首选。而应用服务器目前主要有三个：jboss、weblogic、websphere。有很多项目开始采用jboss，并且有大量的公司开始做websphere或weblogic向jboss应用服务器的移植（节省成本），这里要说的是，学习tomcat和jboss我认为是首选，也是最容易上手的。学习服务器使用配置最好去询问有经验的人（有条件的话），因为他们或许一句话就能解决问题，你自己上网摸索可能要一两天（我就干过这种傻事），我们应该把主要时间放在学习原理和理论上，<font color=#ff0000>一项特定技术的使用永远代替不了一个人的知识和学问</font>。 </p>
<p>&nbsp;&nbsp;&nbsp; 学习web知识 <br>&nbsp;&nbsp;&nbsp; 如果你是在做电子商务网站等时，你可能要充当几个角色，这是你还要学习：html，可能要用到dreamwave等IDE。Javascript，学会简单的数据校验，数据联动显示等等 </p>
<p>&nbsp;&nbsp;&nbsp; J2EE API学习 <br>&nbsp;&nbsp;&nbsp; 学习j2EE API和学习服务器应该是一个迭代的过程。 <br>&nbsp;&nbsp;&nbsp; 先学习jsp和servlet编程，这方面的书很多，我建立看oreilly公司的两本&nbsp; 《jsp设计》和&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 《java servlet编程》，oreilly出的书总是那本优秀，不得不佩服。 <br>&nbsp;&nbsp;&nbsp; 学习jdbc数据库编程，j2EE项目大多都是MIS系统，访问数据库是核心。这本应属于j2se学习中，这里拿出来强调一下。 <br>&nbsp;&nbsp;&nbsp; 学习JNDI API，它和学习ejb可以结合起来。 <br>&nbsp;&nbsp;&nbsp; 学习EJB API，推荐书《精通ejb》 <br>&nbsp;&nbsp;&nbsp; 经过上面的这些的学习，大概可以对付一般的应用了。 <br>&nbsp;&nbsp;&nbsp; 有人说跟着sun公司的《j2ee tutorial》一路学下来，当然也可以。 </p>
<p>&nbsp;&nbsp;&nbsp;<font color=#ff0000> 学习ejb设计模式和看代码（最重要） <br></font>&nbsp;&nbsp;&nbsp; 设计模式是练内功，其重要性可以这么说吧，如果你不会用设计模式的话，你将写出一堆使用了ejb的垃圾，有慢又是一堆bug，其结果不如不用ejb实现（ejb不等于j2ee） <br>&nbsp;&nbsp;&nbsp; 无论学习什么语言，都应该看大量代码，你看的代码量不到一定数量，是学不好j2ee的 <br>&nbsp;&nbsp;&nbsp; 目前有很多开源的工程可以作为教材： <br>&nbsp;&nbsp;&nbsp; jive论坛 <br>&nbsp;&nbsp;&nbsp; petstore sun公司 <br>&nbsp;&nbsp;&nbsp; dune sun公司 <br>&nbsp;&nbsp;&nbsp; 等等，研读一个，并把它用到自己的工程中来。 </p>
<p>&nbsp;&nbsp;&nbsp; J2EE 其他学习 <br>&nbsp;&nbsp;&nbsp; 当你渐渐对j2ee了解到一定深度时，你要开始关注当前领域中的一些技术变化，J2ee是一块百家争鸣的领域，大家都在这里提出自己的解决方案，例如structs，hiberate，ofbiz等等，学习这些东西要你的项目和目标而定，预先补充一下未尝不可，但不用涉及太深，毕竟学习原理和理论是最最重要的事。 </p>
<p>目前常见j2eeAPI <br>JavaServer Pages(JSP)技术1.2 <br>Java Servlet技术2.3 <br>JDBC API 2.0 <br>Java XML处理API(JAXP)1.1 <br>Enterprise JavaBeans技术2.0 <br>Java消息服务(JMS)1.0 <br>Java命名目录接口(JNDI)1.2 <br>Java事务API(JTA) 1.0 <br>JavaMail API 1.2 <br>JavaBeans激活架构(JAF)1.0 <br>J2EE连接器体系结构(JCA)1.0 <br>Java认证和授权服务(JAAS)1.0&nbsp;</p>
<p><br>&nbsp;&nbsp; 学习上面的某些API要以你的项目而定，了解所有他们总之是有好处的 <br>&nbsp; 上面印证了大家说的一句话，java语言本身不难学，但是技术太多，所以学java很费劲。回想一下，基本上每个初学者，在刚学习java的时候可能都会问别人这么一句话，你怎么知道的哪个方法(api)在哪个包里的？<font color=#ff0000>呵呵，无他，唯手熟尔</font>。 <br></p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/132774.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-27 14:05 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/27/132774.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] RCP能否取代WEB技术？</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/26/132429.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Thu, 26 Jul 2007 01:02:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/26/132429.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/132429.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/26/132429.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/132429.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/132429.html</trackback:ping><description><![CDATA[<p>[转帖]RCP能否取代WEB技术？</p>
<p>作者：IT168 IT168 卒<br><br>随着Eclipse IDE的出现和不断发展，Java社区又进入了一个新的春天，Eclipse强大而又灵活扩展的插件机制成为了吸引开发人员的天堂，使其成为一款优秀的IDE。Eclipse项目的初衷并不打算构建成一个胖客户端平台(RCP)，而只是想构建一个集成开发工具的平台。Eclipse RCP始于Eclipse2.1版本，到了3.0版本的时候，Eclipse作为一个RCP迈出了巨大的一步， 在3.0后的版本，Eclipse项目小组废除了以前的插件管理模型，通过引入OSGi(Open Service Gateway Initiative) R4标准来实现插件的动态安装，移除和更新，这使Eclipse IDE实现了革命性的变革。Eclipse3.1版本正式的引入了RCP发布功能，一时间RCP技术成为Java开发人员中最流行的词汇，那究竟什么是Eclipse RCP呢？ </p>
<p>&nbsp;&nbsp;&nbsp; 什么是Eclipse RCP？ </p>
<p>&nbsp;&nbsp;&nbsp; Eclipse RCP （Rich Client Platform）是基于Eclipse项目的一个开发胖客户端应用程序的框架，它提供了一个通用的 Eclipse 工作台，允许开发人员扩展和构造强大的，跨平台应用程序。 </p>
<p>&nbsp;&nbsp;&nbsp; RCP和Plug-in工程的差别并不大，插件项目使用的工作台是Eclipse IDE本身；对于构建RCP项目，开发人员可以定义应用程序外观，商标和其它Eclipse基础组件等，实现自己的桌面应用程序。 </p>
<p>&nbsp;&nbsp;&nbsp; 作者最开始接触到RCP的时候并没有感觉到RCP的强大之处，只觉得用RCP开发出来的应用程序的界面比用Swing开发出来的应用程序界面漂亮许多。但随着需求变得复杂，需要实现更多的功能，RCP的易开发性和强大的插件管理功能就显得越来越突出，真正有一种做产品的感觉，着实让人兴奋。事实上使用RCP开发的产品很多，如IBM Lotus Workspace, Eclipse trader, NASA Maestro，而Eclipse3.1本身就是使用RCP开发出的杰出代表产品。 </p>
<p>&nbsp;&nbsp;&nbsp; Web和Eclipse RCP技术孰优孰劣？ </p>
<p>&nbsp;&nbsp;&nbsp; 随着RCP的强势出现，许多开发人员自然会拿Eclipse RCP技术和现在流行的Web技术对比，孰优孰劣？如果撇开业务需求，单单要对比两种技术是困难的，我们就主要对比两种技术如何适应业务的需求变化。 </p>
<p>&nbsp;&nbsp;&nbsp; Web技术的出现,不仅为Internet的广泛普及起到关键性的作用,而且还在Intranet、电子商务等其他相关计算机网络应用技术发展中起到关键作用,特别是Web动态技术.例如J2EE平台的Servlet技术,Servlet容器会将来自于客户端的HTTP请求封装为一个HttpServletRequest对象，然后根据请求的URI和servlet的键值关系，调用相应的servlet处理；最后，将处理的结果转换成HTTP响应发送回客户端。 </p>
<p>&nbsp;&nbsp;&nbsp; 系统的所有业务逻辑都放在服务器端了，用户无须安装客户端程序，只需要通过浏览器就能访问，这样用户方就变成了&#8220;瘦&#8221;客户端。这种模式很好的适应了业务的变化，对于业务的改变都发生在服务器端，而客户端没有任何影响，这就是Web技术现在非常流行的重要原因之一。 </p>
<p>&nbsp;&nbsp;&nbsp; 不能适应快速的业务变化，就成了传统的基于C/S模式的RCP系统最大弊端。 </p>
<p>&nbsp;&nbsp;&nbsp; Eclipse RCP的插件机制是其最大的亮点，基于Eclipse RCP构建的应用系统的表现出相当出色的扩展性，用户只需要下载一个插件，简单的部署到应用系统中，就能为应用程序添加新功能。Eclipse RCP的插件管理机制较好的解决了应用程序的扩展问题，适应业务需求的变化。 </p>
<p>&nbsp;&nbsp;&nbsp; 使用RCP开发的桌面应用程序其主要应用于如科学计算，数据管理等业务领域，而Eclipse RCP与其它RCP系统相比，屏蔽了底层操作系统的差异，真正实现了跨平台；和Web应用系统相比，使用Eclipse RCP系统的开发人员还可以根据需要，实现自己的插件，部署到与有的RCP产品，增强其业务功能的。 </p>
<p>&nbsp;&nbsp;&nbsp; Web技术和RCP在各自的应用领域都扮演着非常重要的作用。这个时候可能会有人问&#8220;那Web技术会被Eclipse RCP技术取代吗&#8221;？ </p>
<p>&nbsp;&nbsp;&nbsp; Web技术会被Eclipse RCP技术取代吗？ </p>
<p>&nbsp;&nbsp;&nbsp; 技术的兴起与消亡都与它关注的业务领域息息相关的，如果这种技术适合这个业务领域的发展需求，那自然会被普及推广，反之则会被淘汰。 </p>
<p>&nbsp;&nbsp;&nbsp; 其实早在90年代初就提出了&#8220;Rich Client&#8221;的概念用来构造客户端应用程序，常用的开发语言有Visual Basic和Delphi，出现了大量的桌面应用程序，如ERP，财务管理系统等。而随着Internet的普及，Web技术的发展，一时间诸如CGI,ASP,JSP等技术成为了开发人员的必须要掌握的技术，越来越多的企业使用Web技术在Internet上构建应用系统，典型的系统有办公自动化系统。 </p>
<p>&nbsp;&nbsp;&nbsp; 经过多年的发展，虽然有些应用开发方式已经从C/S模式演变成B/S模式，但是桌面应用程序并没有被完全被B/S系统替代，它们都在各自的业务领域中发挥着不可替代的作用，并且还不断涌现新的Web技术和RCP技术，促进各自领域的发展。如今Eclipse RCP的横空出世，备受业界关注，仿佛要打破这种平衡。 </p>
<p>&nbsp;&nbsp;&nbsp; 而事实上Eclipse RCP的出现只是增强了Java领域在桌面应用的开发能力，只是Eclipse RCP优秀的插件管理机制让所有人耳目一新。我们可以理性的回过头看，当Web技术大兴其道的时候，桌面应用程序开发技术并没有消失；同样，Eclipse RCP的出现也不能够取代Web技术，两者是相互依存的关系，RCP的不断创新，将为Web2.0注入新的生机。<br></p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/132429.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-26 09:02 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/26/132429.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] J2SDK1.4新增Java日志框架</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/22/131725.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sun, 22 Jul 2007 07:07:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/22/131725.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/131725.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/22/131725.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/131725.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/131725.html</trackback:ping><description><![CDATA[<span id="ArticleContent1_ArticleContent1_lblContent"><span><span><span><span id="ArticleContent1_ArticleContent1_lblContent"><font size="3"><span lang="EN-US" style="font-family: 宋体"><font size="3">
<p>引言 <br />
作为一名Java 程序员，最熟悉的、使用最多的调用恐怕莫过于System.out.print(&#8220;&#8230;&#8221;)。当你没有调试工具而要跟踪一个变量的值得时候；当你需要显示捕获的Exception、Error的时候；当你想知道程序在运行的时候究竟发生了什么的时候，通常的做法就是调用System.out.print把他们在终端、控制台上打印出来。这种方式对于输出信息的分类、格式化及永久保存带来诸多不便。虽然我们可以把它写入一个文件然后进行分析，但是这要需要编写额外的程序代码，其成本不可忽视！而由此给目标系统本身增加的复杂程度不可避免的使开发、调试陷入一个深深的迷潭。</p>
<p>JDK1.4的推出，使得这一切即将成为历史。让我们向System.out.print()告别吧，使用Java Logging API为自己的程序构建一个完整的日志记录系统！</p>
<p>&nbsp;</p>
<p>一、第一个实例</p>
<p>&nbsp;&nbsp;&nbsp; 先看一个简单的实例：SimpleLoggingTest.java</p>
<p>1&nbsp; import java.util.logging.*;</p>
<p>2 public class SimpleLoggingTest {</p>
<p>3&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void main(String args[]) {</p>
<p>4&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //程序的其它处理</p>
<p>5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //使用Logger的静态方法获得一个匿名&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Logger</p>
<p>6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Logger logger1 = Logger.getAnonymousLogger();</p>
<p>7&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //记录消息</p>
<p>8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logger1.log(Level.INFO,"第一条日志记录");</p>
<p>9&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //程序的其它处理</p>
<p>10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>11 }</p>
<p>注意：编译、执行该程序需要JDK1.4及以上版本的支持。</p>
<p><br />
运行该程序，可以在控制台看到程序运行结果：</p>
<p><br />
2003-1-14 15:09:40 SimpleLoggingTest main</p>
<p>信息: 第一条日志记录</p>
<p>&nbsp;</p>
<p>首先，程序引用java.util.Logging包（第1行）。接着，在适当的时候获得一个Logger(记录器)类的实例（第6行，获取一个匿名的Logger）。最后，在程序需要记录信息的地方调用Logger类的log方法进行记录（第8行，记录一个INFO级别的消息）。</p>
<p>&nbsp;</p>
<p>二、Java Logging API</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; Java Logging API封装在JDK1.4.0的java.util.Logging包中。它通过产生便于最终用户、系统管理员、故障维护工程师以及软件开发团队（工程师）进行分析的日志记录为软件的开发调试和维护提供便利的手段。它捕获操作系统平台和执行程序的安全故障、配置错误、执行瓶颈和（或）Bug等数据信息，以纯文本、XML或程序员自定的某种方式将其格式化成日志记录，然后传递给内存、系统输出流、控制台、文件、Sockets等多种系统资源进行缓存和输出。</p>
<p>&nbsp;</p>
<p>(一)该软件包中的关键类：</p>
<p>&nbsp;&nbsp; Logger:&nbsp; 应用程序进行日志记录调用的主要实体。 Logger对象用于记录特定系统或应用程序的消息。</p>
<p>&nbsp;&nbsp; LogRecord:&nbsp; 用于在日志框架和单个记录处理程序之间传递记录请求。</p>
<p>&nbsp;&nbsp; Handler:&nbsp; 日志数据的最终输出处理器。它将LogRecord对象导出到各种目标，包括内存、输出流、控制台、文件和套接字。多种 Handler子类可供用于这种用途。</p>
<p>&nbsp;&nbsp; Level:&nbsp; 定义一组标准的记录级别，可用于控制记录的输出。可以把程序配置为只输出某些级别的记录，而忽略其他级别的输出。</p>
<p>&nbsp;&nbsp; Filter:&nbsp; 精细过滤、控制记录的内容，比记录级别所提供的控制准确得多。记录API支持通用的过滤器机制，这种机制允许应用程序代码添加任意过滤器以便控制记录的输出。</p>
<p>&nbsp;&nbsp; Formatter:&nbsp; 为LogRecord对象的格式化提供支持。</p>
<p>&nbsp;&nbsp; LogManager: Java Logging框架中唯一的、全局的对象，用于维护与Logger记录器及日志服务的一系列共享的数据结构及状态。它负责整个日志框架的初始化、维护一组全局性的Handle对象、维护一个树形结构的Logger的名字空间、诊测日志框架配置文件的改变从而重新读入并应用相关的参数以及负责程序停止运行时整个日志框架的清理工作。</p>
<p>&nbsp;</p>
<p>(二)Logger </p>
<p>1、Logger的命名空间</p>
<p>在SimpleLoggingTest.java实例中，我们使用了一个匿名的（没有命名的）Logger对象。在Java Logging 框架中，Logger是可以命名的。Logger的名字空间与java类的名字空间相同的结构相同：使用&#8220;.&#8221;间隔的字符串。Logger的名字空间体现了Logger的层次结构。例如：命名为&#8220;a.b&#8221;的Logger是命名为&#8220;a.b.c&#8221;的&#8220;父&#8221;（上一级）Logger记录器。Logger的命名可以是任意的字符串，一般情况下，使用包或类的名字为Logger 进行命名。</p>
<p>Logger的名字空间由全局单列类LogManager的实例进行创建、维护。</p>
<p>匿名Logger不被存储在命名空间中。</p>
<p>2、创建Logger实例</p>
<p>Logger对象可以通过调用工厂方法getLogger或getAnonymousLogger获取。</p>
<p><br />
//获取一个名为&#8220;A&#8221;的Logger对象</p>
<p>Logger loggerA= Logger.getLogger(&#8220;A&#8221;); </p>
<p>// 获取一个名为&#8220;A.B&#8221;的Logger对象，其上级记录器为loggerA。</p>
<p>Logger loggerAB= Logger.getLogger(&#8220;A.B&#8221;);</p>
<p>//获取一个匿名Logger对象</p>
<p>Logger loggerTmp = Logger.getAnonymousLogger();</p>
<p>&nbsp;</p>
<p>对非匿名Logger，getLogger先在命名空间中查找同名的Logger对象，如果有，则返回该Logger对象；如果不存在，则在命名空间中创建注册一个新的Logger对象，并与其上级Logger对象相关联。</p>
<p>匿名Logger对象属于创建它的对象的私有对象，只能由创建它的对象使用，记录一些临时性的日志信息。而命名Logger对象使全局性的，在日志框架的生存期内，除了创建它的对象外还，可由其它对象用于记录日志信息。</p>
<p>匿名的Logger对象由一个全局的的root Logger &#8220;&#8221; 对象（root Logger的名字为空）。这意味着所有匿名Logger对象将从root Logger &#8220;&#8221;中继承行为。</p>
<p>匿名Logger对象通常用于java Applet应用中。它去掉了在运行过程中的一班性的安全检查，允许其创建类对象对Logger的控制、状态信息进行修改，如：setLevel设置Logger的日志消息记录级别；addHandle增加Logger的Handle（处理器）对象等。</p>
<p>一个Logger对象可以拥有有零个到多个Handler实例。当没有Handler时，如不禁止日志记录沿名字空间向上传递，那该Logger对象的日志消息记录将有其拥有Handler实例的上级Logger进行处理。当一个Logger对象拥有多个Handler实例对象时，其记录的日志数据将被所有的Handler逐一进行处理。</p>
<p><br />
（三）Handler<br />
&nbsp;Handler对象接收传来的日志消息将其输出。Handler可以把日志消息输出到多种目标资源，如：输出到控制台进行显示、写入日志文件、传送到网络上的远程日志服务进行处理、写入系统日志等任何物理资源。<br />
Handler对象在创建时使用LogManager对象的相关属性的默认值（如Handler的Filter、Formatter、Level等对象属性）进行初始化。<br />
Handler对象可通过调用setLevel(Level.OFF)暂停工作；通过调用setLevel设置适当的记录日志消息级别恢复工作。<br />
Handler是一个抽象类。在J2SDK1.4中，其子类及它们之间的关系见图一。</p>
<p>1、&nbsp;&nbsp; MemoryHandler Handler的子类，在内存中的一个循环缓冲区用于缓存日志记录请求。通常MemoryHandler只简单的把传入的LogRecords存储到它的内存中。这种缓存的开销非常低廉，它去掉了格式化所产生的系统消耗。当某个触发条件满足时，MemoryHandler将其缓冲的数据push(发布)到目标Handler，由后者执行实际的输出。有三种模式触发MemoryHandler进行push操作：a、传入的LogRecords的级别高于MemoryHandler预先定义的push级别；b、有其他对象显式的调用其push方法；c、其子类重载了log方法，逐一检索每个传入的LogRecords，若符合特定的标准则进行push操作</p>
<p>实例：假设我们需要跟踪一个生产环境中的一个很少出现的Bug。在大多数场合，系统化产生大量的日志记录，而我们仅只关心记录中最近的几条，那么我们只需要使用MemoryHandler对日志记录进行缓存，当且仅当某个事件发生时将最近的几条记录从内存中 dump到制定的文件中。</p>
<p>&nbsp; //MemoryHandlerTest.java</p>
<p>import java.util.logging.*;</p>
<p>import java.io.*;</p>
<p>public class MemoryHandlerTest {</p>
<p>&nbsp;&nbsp; FileHandler fhandler;</p>
<p>&nbsp;&nbsp; Logger logger;</p>
<p>&nbsp;&nbsp; MemoryHandler mhandler;</p>
<p>&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp; MemoryHandlerTest() {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //构造名为my.log的日志记录文件</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fhandler = new FileHandler("my.log");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int numRec = 5;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //构造一个5个日志记录的MemoryHandler，</p>
<p>//其目标Handler为一个FileHandler</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mhandler = new MemoryHandler (fhandler, numRec, Level.OFF) ;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //构造一个记录器</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logger = Logger.getLogger("com.mycompany");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //为记录器添加一个MemoryHandler</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logger.addHandler(mhandler);</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (IOException e) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp; public static void main(String args[]) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MemoryHandlerTest mt = new MemoryHandlerTest();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int trigger = (int)(Math.random()*100);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i=1;i&lt;100;i++) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //在MemoryHandler中缓存日志记录</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mt.logger.log(Level.INFO,"日志记录"+i);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (i==trigger) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //触发事件成立，显式调用MemoryHandler的</p>
<p>//push方法触发目标Handler输出日志记录到</p>
<p>//my.log文件中</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mt.mhandler.push(); </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp; }</p>
<p><br />
2、FileHandler 文件处理器</p>
<p>StreamHandler流处理器将日志记录以流的形式输出。FileHandler、ConsoleHandler、SocketHandler为StreamHandler的子类。 ConsoleHandler将日志记录输出到控制终端。前面的实例（实例2除外）都将日记记录数据输出到控制台。</p>
<p><br />
FileHandler将日志记录输出到特定的文件，或循环的几个日志文件中。日志文件可以设置容量大小。当日志文件达到限定的容量时将被自动清空，重头开始写入新的日志记录数据。</p>
<p>例：创建一个容量为1Mb的文件处理器</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int limit = 1000000; // 1 Mb</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FileHandler fh = new FileHandler("my.log", limit, 1);</p>
<p>&nbsp;</p>
<p>对于循环的日志文件，每个文件将被指定容量限制。当当前的日志文件的长度达到制定值后该文件被关闭，新的日志文件被创建，旧的文件将在文件名模板后追加序号。如此产生多个顺序编号的日志记录文件。</p>
<p>例：</p>
<p>try {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 创建一个拥有3个日志文件，每个容量为1Mb的文件处理器</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String pattern = "my%g.log";</p>
<p>&nbsp;int limit = 1000000; // 1 Mb</p>
<p>&nbsp;int numLogFiles = 3;</p>
<p>&nbsp; FileHandler fh = new FileHandler(pattern, limit, numLogFiles);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#8230;</p>
<p>&nbsp;} catch (IOException e) {</p>
<p>&nbsp;}</p>
<p><br />
（四）Level</p>
<p>&nbsp;&nbsp; Level对象定义了一组日志消息的级别，用于控制日志消息的输出。一个级别对应一个整型值。日志消息级别按照其整数值的大小排定优先级。在Logger对象中设定一个级别，则大于等于该级别的日志消息将会被传递到某个Handler对象进行输出处理。</p>
<p>J2SDK1.4的Java Logging框架中定义了以下消息级别：<o:p></o:p></font></span></p>
<p>
<table style="border-right: medium none; border-top: medium none; margin-left: 65.15pt; border-left: medium none; border-bottom: medium none; border-collapse: collapse; mso-border-alt: solid windowtext .5pt; mso-padding-alt: 0cm 5.4pt 0cm 5.4pt" cellspacing="0" cellpadding="0" border="1">
    <tbody>
        <tr style="height: 15.45pt">
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: windowtext 0.5pt solid; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 0.5pt solid; width: 142.3pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 15.45pt; background-color: transparent" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span style="font-family: 宋体">级别名称<span lang="EN-US"><o:p></o:p></span></span></p>
            </td>
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: windowtext 0.5pt solid; padding-left: 5.4pt; padding-bottom: 0cm; border-left: #d4d0c8; width: 142.25pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 15.45pt; background-color: transparent; mso-border-left-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">int值（Windows 2000环境）<o:p></o:p></span></p>
            </td>
        </tr>
        <tr style="height: 14.7pt">
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 0.5pt solid; width: 142.3pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 14.7pt; background-color: transparent; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">OFF<o:p></o:p></span></p>
            </td>
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: #d4d0c8; width: 142.25pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 14.7pt; background-color: transparent; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">2147483647<o:p></o:p></span></p>
            </td>
        </tr>
        <tr style="height: 14.7pt">
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 0.5pt solid; width: 142.3pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 14.7pt; background-color: transparent; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">SEVERE<o:p></o:p></span></p>
            </td>
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: #d4d0c8; width: 142.25pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 14.7pt; background-color: transparent; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">1000<o:p></o:p></span></p>
            </td>
        </tr>
        <tr style="height: 15.45pt">
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 0.5pt solid; width: 142.3pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 15.45pt; background-color: transparent; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">WARNING<o:p></o:p></span></p>
            </td>
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: #d4d0c8; width: 142.25pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 15.45pt; background-color: transparent; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">900<o:p></o:p></span></p>
            </td>
        </tr>
        <tr style="height: 14.7pt">
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 0.5pt solid; width: 142.3pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 14.7pt; background-color: transparent; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">INFO<o:p></o:p></span></p>
            </td>
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: #d4d0c8; width: 142.25pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 14.7pt; background-color: transparent; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">800<o:p></o:p></span></p>
            </td>
        </tr>
        <tr style="height: 14.7pt">
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 0.5pt solid; width: 142.3pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 14.7pt; background-color: transparent; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">CONFIG<o:p></o:p></span></p>
            </td>
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: #d4d0c8; width: 142.25pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 14.7pt; background-color: transparent; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">700<o:p></o:p></span></p>
            </td>
        </tr>
        <tr style="height: 15.45pt">
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 0.5pt solid; width: 142.3pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 15.45pt; background-color: transparent; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">FINE<o:p></o:p></span></p>
            </td>
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: #d4d0c8; width: 142.25pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 15.45pt; background-color: transparent; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">500<o:p></o:p></span></p>
            </td>
        </tr>
        <tr style="height: 15.45pt">
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 0.5pt solid; width: 142.3pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 15.45pt; background-color: transparent; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">FINER<o:p></o:p></span></p>
            </td>
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: #d4d0c8; width: 142.25pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 15.45pt; background-color: transparent; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">400<o:p></o:p></span></p>
            </td>
        </tr>
        <tr style="height: 14.7pt">
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 0.5pt solid; width: 142.3pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 14.7pt; background-color: transparent; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">FINEST<o:p></o:p></span></p>
            </td>
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: #d4d0c8; width: 142.25pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 14.7pt; background-color: transparent; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">300<o:p></o:p></span></p>
            </td>
        </tr>
        <tr style="height: 14.7pt">
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: windowtext 0.5pt solid; width: 142.3pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 14.7pt; background-color: transparent; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">ALL<o:p></o:p></span></p>
            </td>
            <td style="border-right: windowtext 0.5pt solid; padding-right: 5.4pt; border-top: #d4d0c8; padding-left: 5.4pt; padding-bottom: 0cm; border-left: #d4d0c8; width: 142.25pt; padding-top: 0cm; border-bottom: windowtext 0.5pt solid; height: 14.7pt; background-color: transparent; mso-border-left-alt: solid windowtext .5pt; mso-border-top-alt: solid windowtext .5pt" valign="top" width="237">
            <p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体">-2147483648<o:p></o:p></span></p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p class="MsoNormal" style="margin: 0cm 0cm 0pt; text-indent: 18pt; text-align: left" align="left"><span lang="EN-US" style="font-family: 宋体"><font size="3"><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>表一<o:p></o:p></font></span></p>
<p>Level.OFF具有最高的级别。将Logger的Level级别设置成Level.OFF</p>
<p>让我们看看消息级别是怎样工作的：LoggingLevelTest.java</p>
<p>import java.util.logging.*;</p>
<p>public class LoggingLevelTest {</p>
<p>public static void main(String args[]) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //使用Logger的静态方法获得一个匿名Logger</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; Logger logger1 = Logger.getAnonymousLogger();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //设置Logger对象记录的最低日志消息级别</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; logger1.setLevel(Level.FINER);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; //记录消息</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; logger1.severe("SEVERE级消息");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; logger1.warning("WARNING级消息");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; logger1.config("CONFIG级消息");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; logger1.info("INFO级消息");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; logger1.fine("FINE级消息");</p>
<p>&nbsp;&nbsp;&nbsp; logger1.finer("FINER级消息");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; logger1.finest("FINEST级消息");</p>
<p>}</p>
<p>}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>运行结果：</p>
<p>2003-1-15 7:02:03 LoggingLevelTest main</p>
<p>服务器: SEVERE级消息</p>
<p>2003-1-15 7:02:04 LoggingLevelTest main</p>
<p>警告: WARNING级消息</p>
<p>2003-1-15 7:02:04 LoggingLevelTest main</p>
<p>&nbsp;&nbsp; 配置: CONFIG级消息 </p>
<p><br />
2003-1-15 7:02:04 LoggingLevelTest main</p>
<p>信息: INFO级消息</p>
<p>&nbsp;</p>
<p>可以看出，优先级低于INFO的日志消息不被记录。</p>
<p>Level的构造函数为protected便于程序员开发自己的消息级别类。</p>
<p>&nbsp;</p>
<p>import java.util.logging.*;</p>
<p>//自定义消息级别</p>
<p>class myLevel extends Level{</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 定义自己的消息级别SYSE</p>
<p>p&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static final Level SYSE = <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new myLevel("SYSE", Level.SEVERE.intValue()+10);</p>
<p>&nbsp;&nbsp;&nbsp; public myLevel(String ln,int v) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super(ln,v);</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>public class MyLevelTest {</p>
<p>public static void main(String args[]) {</p>
<p>&nbsp;&nbsp;&nbsp; Logger logger1 = Logger.getAnonymousLogger();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; //设置消息级别</p>
<p>&nbsp;&nbsp;&nbsp; logger1.setLevel(myLevel.SYSE);</p>
<p>&nbsp;&nbsp;&nbsp; //记录消息</p>
<p>&nbsp;&nbsp;&nbsp; logger1.log(myLevel.SYSE,"SYSE消息");</p>
<p>&nbsp;&nbsp;&nbsp; logger1.severe("SVERE消息");</p>
<p>}</p>
<p>}&nbsp;&nbsp; </p>
<p><br />
运行结果：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>2003-1-15 15:40:04 MyLevelTest main</p>
<p>SYSE: SYSE消息</p>
<p>&nbsp;</p>
<p>只有SYSE消息被记录，SVERE消息不被记录，因为自定义级别SYSE高于SEVERE.</p>
<p><br />
（五）Formatter<br />
&nbsp; <br />
&nbsp;&nbsp; Formatter负责对LogRecords进行格式化。每个记录处理器Handler同一个Formatter对象相关联。Formatter对象接收从Handler传来的LogRecord，将其格式化成字符串后返回给Handler进行输出。<br />
&nbsp; <br />
&nbsp;&nbsp; Formatter是一个抽象类。在J2SDK1.4中，其子类及它们之间的关系见图二。<br />
&nbsp;&nbsp; 自定义扩展Formatter类。实例：MyFormatterTest.java</p>
<p>&nbsp;</p>
<p>import java.util.Date;</p>
<p>import java.util.logging.*;</p>
<p>&nbsp;</p>
<p>//创建每条日志记录以行的日志格式：</p>
<p>//时间&lt;空格&gt;消息级别&lt;空格&gt;消息ID&lt;空格&gt;日志信息内容&lt;换行&gt;</p>
<p>class MyFormatter extends Formatter {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public String format(LogRecord rec) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StringBuffer buf = new StringBuffer(1000);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf.append(new Date().toLocaleString()); //时间</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf.append(' ');</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf.append(rec.getLevel()); //消息级别</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf.append(' ');</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf.append(rec.getMillis()); //作为消息ID</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf.append(' ');</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf.append(formatMessage(rec));//格式化日志记录数据</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; buf.append('\n');&nbsp;&nbsp;&nbsp; //换行</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return buf.toString();</p>
<p>&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>}</p>
<p>&nbsp;</p>
<p>public class MyFormatterTest {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void main(String args[]){</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //创建记录器</p>
<p>Logger log1 = Logger.getLogger("MyLogger");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //创建记录处理器</p>
<p>Handler mh = new ConsoleHandler();</p>
<p>//为记录处理器设置Formatter</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mh.setFormatter(new MyFormatter());</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //为记录器添加记录处理器</p>
<p>log1.addHandler(mh);</p>
<p>//禁止消息处理将日志消息上传给父级处理器</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log1.setUseParentHandlers(false);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //记录消息</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log1.severe("消息1");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log1.warning("消息2");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log1.info("消息3");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log1.config("消息4");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>程序运行结果：</p>
<p>&nbsp;</p>
<p>2003-1-15 16:59:38 SEVERE 1042621178968 消息1</p>
<p>2003-1-15 16:59:40 WARNING 1042621178985 消息2</p>
<p>2003-1-15 16:59:41 INFO 1042621179105 消息3</p>
<p>&nbsp;</p>
<p>三、配置文件</p>
<p>&nbsp;&nbsp; J2SDK1.4的Java Logging框架的配置文件(Windows)：</p>
<p>%J2SDK1.4_HOME%/jre/lig/logging.properties</p>
<p>&nbsp;&nbsp;&nbsp; 从配置文件可以看到：</p>
<p>&nbsp;（一） 自定义日志配置文件：</p>
<p>java -Djava.util.logging.config.file=myfile</p>
<p>&nbsp;&nbsp;&nbsp; （二）全局Handler在Java VM启动时被加载。</p>
<p>&nbsp;（二） 全局Handler默认为java.util.logging.ConsoleHandler。</p>
<p>handlers= java.util.logging.ConsoleHandler</p>
<p>所以我们的任何日志记录动作都会在控制台进行显示。</p>
<p>&nbsp;（三） 缺省的消息记录级别为：INFO</p>
<p>.level= INFO&nbsp; </p>
<p>在缺省情况下我们在控制台看不见低于INFO级别的日志消息。</p>
<p>&nbsp;（四） 缺省的Handler消息格式为java.util.logging.SimpleFormatter</p>
<p>&nbsp;</p>
<p>四、日志框架在程序测试中的应用</p>
<p>&nbsp;&nbsp;&nbsp; Logger类提供了两个的方法：Logger.entering() Logger.exiting() 。这对我们调试自己的方法调用提供了便利的方式。</p>
<p>&nbsp;&nbsp;&nbsp; 例子：</p>
<p>&nbsp;&nbsp;&nbsp; 记录方法调用的输入参数和输出参数 方法myMethod将一个int 追加在一个对象之后。</p>
<p>运行该程序应将logging.properties的</p>
<p>java.util.logging.ConsoleHandler.level = INFO</p>
<p>改为：</p>
<p>java.util.logging.ConsoleHandler.level = ALL</p>
<p>&nbsp;</p>
<p>import java.util.logging.*;</p>
<p>public class MyClass {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public String myMethod(int p1, Object p2) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Logger logger = Logger.getLogger("com.mycompany.MyClass");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (logger.isLoggable(Level.FINER)) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logger.entering(this.getClass().getName(), "myMethod",</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; new Object[]{new Integer(p1), p2});</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String tmp = p2.toString() + p1;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (logger.isLoggable(Level.FINER)) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; logger.exiting(this.getClass().getName(), "myMethod", tmp);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return tmp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void main(String args[]) {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyClass mc = new MyClass();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String rslt = mc.myMethod(123,"Hello");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>&nbsp;</p>
<p>后记</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; J2SDK1.4引入的日志记录框架为构建简易的日志记录系统提供了便利的解决方案。虽然还有期它的一些专用日志包如Log4j，但从简单的打印输出到严密的、可扩展的日志记录框架，J2SDK1.4的日志系统已经足以满足一般的系统开发的要求。 </p>
<p>&nbsp;</p>
</span></span></span></span></font></span>
<img src ="http://www.blogjava.net/orangelizq/aggbug/131725.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-22 15:07 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/22/131725.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java开源日记工具</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/22/131722.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sun, 22 Jul 2007 06:47:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/22/131722.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/131722.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/22/131722.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/131722.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/131722.html</trackback:ping><description><![CDATA[<p>Java开源日记工具<br>-------------------------------</p>
<p><strong>Log4j</strong><br>Log4j是Apache的一个开放源代码项目，通过使用Log4j，我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等；用户也可以控制每一条日志的输出格式；通过定义每一条日志信息的级别，用户能够更加细致地控制日志的生成过程。这些可以通过一个配置文件来灵活地进行配置，而不需要修改程序代码。【Log4E：Log4j在Eclipse下的插件】</p>
<p><strong><br>MonoLog<br></strong>Monolog是一个用于监视和日志操作的API,国际化支持.</p>
<p><br><strong>JTraceDump<br></strong>这是工具用于保持记录程序运行期间的内容信息，并在发生错误时将其转储在一个单独的文件中（类似核心dump)。 </p>
<p><br><strong>Commons Logging</strong><br>Jakarta Commons Logging (JCL)提供的是一个日志(Log)接口(interface)，同时兼顾轻量级和不依赖于具体的日志实现工具。 它提供给中间件/日志工具开发者一个简单的日志操作抽象，允许程序开发人员使用不同的具体日志实现工具。用户被假定已熟悉某种日志实现工具的更高级别的细节。JCL提供的接口，对其它一些日志工具，包括Log4J, Avalon LogKit, and JDK 1.4等，进行了简单的包装，此接口更接近于Log4J和LogKit的实现. </p>
<p><br><strong>Lumberjack<br></strong>Lumberjack为1.2 和1.3版本的JDK实现了Java日志 API（在JDK1.4中引入）。</p>
<p><br><strong>Houston <br></strong>Houston是一个开放源码的轻量级日记工具包。它提供一个插件体系使得能在底层使用成熟的日记工具如Apache log4j 与JDK1.4 Logging。</p>
<p><br><strong>Just4Log</strong> <br>Just4Log 是为增强现有的日记系统（Log4j ,apache commons Logging ,JDK 1.4 Logging ）尽可能的在运行时进行配置而不需要在java源代码中进行过多的设置。</p>
<p><br><strong>SMTPHandler</strong>&nbsp;&nbsp; <br>SMTPHandler是一个java.util.logging处理器，可以通过SMTP来发送日志消息。</p>
<p><br><strong>jLo <br></strong>jLo是一个Java开源的logging框架。它有一些与其它框架不同的特性比如下：<br>*支持多log configurations <br>*当配置文件改变时可重新读取该文件*通过简单XML结构来配置jLo<br>*支持过滤限制日记输出等。</p>
<p><br><strong>qflog</strong>&nbsp; <br>qflog显示日记信息的工具。它在de.qfs.lib.log 类包上进行构建。</p>
<p><br><strong>Simple Log</strong><br>Simple Log是一个logging anti-framework.Simple Log是一个让日记操作变得简单但很小的类库并且几乎不需要你做任何操作就可以得到日记的输出.它与其它日记框架相比最大的特点是使用简单,特别是在条件配置方面.它并不打算在一个包中解决所有日记问题,但它提供足够的功能来满足大多数应用程序所需的日记操作.</p>
<p><br><strong>LN2</strong> <br>LN2是一个日记处理框架.它基于模式匹配(pattern matching)的日记处理过程序.LN2在Log4J上进行松散地设计,不需要与日记级别绑定的太紧.</p>
<p><br><strong>Log Bridge<br></strong>Log Bridge允许从具体的日记实现包中提取日记.它通过提供一个扮演桥角色的简单API来实现从开发者所选择的具体日记实现包中抽取日记.Log Bridge当前支持的日记包有:<br>Simple Log <br>J2SE Logging <br>Log4J <br>IBM's Logging Toolkit for Java <br>jLo <br>Protomatter Syslog</p>
<p><br><strong>Craftsman Spy <br></strong>Craftsman Spy是一个开源的JDBC日记框架.它实现了JDBC驱动.这个日记器将记录所有SQL连接,所有批处理,所有返回的记录集,所有带参数的存储过程和执行处理所发费的时间.</p>
<p><strong><br>log4j2me&nbsp;</strong> <br>log4j2me是用于J2ME 平台上的log4j。当把你的代码移到J2SE/J2EE平台上时,不需要对代码进行改变，只需把log4j2me.jar替换掉log4j jar文件。与log4j所不同的是不能在j2me环境中使用log4j.properties文件来配置log4j,但可以从 JAD文件读取配置。还有其它在J2ME平台中的不同之处，log4j2me都提供了相应的解决方案。</p>
<p><br><strong>SLF4J</strong>&nbsp;&nbsp; <br>简单日记门面(Facade)SLF4J是为各种loging APIs提供一个简单统一的接口，从而使得最终用户能够在部署的时候配置自己希望的loging APIs实现。 Logging API实现既可以选择直接实现SLF4J接的loging APIs如： NLOG4J、SimpleLogger。也可以通过SLF4J提供的API实现来开发相应的适配器如Log4jLoggerAdapter、JDK14LoggerAdapter。</p>
<p><br><strong>Logback</strong>&nbsp; <br>Logback是由log4j创始人设计的又一个开源日记组件。logback当前分成三个模块：logback-core,logback-classic和logback-access。logback-core是其它两个模块的基础模块。logback-classic是log4j的一个改良版本。此外logback-classic完整实现SLF4J API使你可以很方便地更换成其它日记系统如log4j或JDK14 Logging。logback-access访问模块与Servlet容器集成提供通过Http来访问日记的功能。 </p>
<p><strong><br>log4javascript</strong>&nbsp;&nbsp; <br>log4javascript是一个基于log4j的JavaScript日记框架。</p>
<p><br><a href="http://www.open-open.com/29.htm">http://www.open-open.com/29.htm</a></p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/131722.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-22 14:47 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/22/131722.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] Java 日志系统</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/22/131720.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sun, 22 Jul 2007 06:42:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/22/131720.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/131720.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/22/131720.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/131720.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/131720.html</trackback:ping><description><![CDATA[<p>1&nbsp;简介<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 日志系统是一种不可或缺的跟踪调试工具，特别是在任何无人职守的后台程序以及那些没有跟踪调试环境的系统中有着广泛的应用。长期以来，日志系统作为一种应用程序服务，对于跟踪调试、程序状态记录、崩溃数据恢复都有非常现实的意义。这种服务通常以两种方式存在：</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;日志系统作为服务进程存在。Windows中的的事件日志服务就属于这种类型，该类型的日志系统通常通过消息队列机制将所需要记录的日志由日志发送端发送给日志服务。日志发送端和日志保存端通常不在同一进程当中，日志的发送是异步过程。这种日志服务通常用于管理员监控各种系统服务的状态。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 日志系统作为系统调用存在。Java世界中的日志系统和Unix环境下诸多守护进程所使用的日志系统都属于这种类型。日志系统的代码作为系统调用被编译进日志发送端，日志系统的运行和业务代码的运行在同一进程空间。日志的发送多数属于同步过程。这种日志服务由于能够同步反映处系统运行状态，通常用于调试跟踪和崩溃恢复。</p>
<p>本文建立的日志系统基本属于第二种类型，但又有所不同。该日志系统将利用Java线程技术实现一个既能够反映统一线程空间中程序运行状态的同步日志发送过程，又能够提供快速的日志记录服务，还能够提供灵活的日志格式配置和过滤机制。</p>
<p>系统调试的误区<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在控制台环境上调试Java程序时，此时往控制台或者文本文件输出一段文字是查看程序运行状态最简单的做法，但这种方式并不能解决全部的问题。有时候，对于一个我们无法实时查看系统输出的系统或者一个确实需要保留我们输出信息的系统，良好的日志系统显得相当必要。</p>
<p>因此，不能随意的输出各种不规范的调试信息，这些随意输出的信息是不可控的，难以清除，可能为后台监控、错误排除和错误恢复带来相当大的阻力。</p>
<p><br>日志系统框架的基本功能<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个完备的日志系统框架通常应当包括如下基本特性：</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 所输出的日志拥有自己的分类。这样在调试时便于针对不同系统的不同模块进行查询，从而快速定位到发生日志事件的代码。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 日志按照某种标准分成不同级别。分级以后的日志，可以用于同一分类下的日志筛选。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;支持多线程。日志系统通常会在多线程环境中使用，特别是在Java系统当中，因此作为一种系统资源，日志系统应当保证是线程安全的。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;支持不同的记录媒介。不同的工程项目往往对日志系统的记录媒介要求不同，因此日志系统必须提供必要的开发接口，以保证能够比较容易的更换记录介质。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;高性能。日志系统通常要提供高速的日志记录功能以应对大系统下大请求流量下系统的正常运转。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 稳定性。日志系统必须是保持高度的稳定性，不能因为日志系统内部错误导致主要业务代码的崩溃。</p>
<p><br><strong>&nbsp;常用日志系统简介<br></strong>在Java世界中，以下三种日志框架比较优秀：</p>
<p>1)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Log4J</p>
<p>最早的Java日志框架之一，由Apache基金会发起，提供灵活而强大的日志记录机制。但是其复杂的配置过程和内部概念往往令使用者望而却步。</p>
<p>2)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JDK1.4 Logging Framework</p>
<p>继Log4J之后，JDK标准委员会将Log4J的基本思想吸收到JDK当中，在JDK1.4中发布了第一个日志框架接口，并提供了一个简单实现。</p>
<p>3)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Commons Logging Framwork</p>
<p>该框架同样是Apache基金会项目，其出现主要是为了使得Java项目能够在Log4J和JDK1.4 l Logging Framework的使用上随意进行切换，因此该框架提供了统一的调用接口和配置方法。<br></p>
<br><a href="http://www.dezai.cn/Article_Show.asp?ArticleID=11489&amp;ArticlePage=1">http://www.dezai.cn/Article_Show.asp?ArticleID=11489&amp;ArticlePage=1</a>
<img src ="http://www.blogjava.net/orangelizq/aggbug/131720.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-22 14:42 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/22/131720.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]  JMS开源比较</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/21/131677.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sat, 21 Jul 2007 15:19:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/21/131677.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/131677.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/21/131677.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/131677.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/131677.html</trackback:ping><description><![CDATA[<p>Java开源JMS消息中间件</p>
<p><strong>mom4j&nbsp;</strong><br>mom4j是一个完全实现JMS1.1规范的消息中间件并且向下兼容JMS1.0与1.02.它提供了自己的消息处理存储使它独立于关系数据与语言,所以它的客户端可以用任何语言开发.</p>
<p><strong>OpenJMS</strong><br>OpenJMS是一个开源的Java Message Service API 1.0.2 规范的实现,它包含有以下特性： <br>*. 它既支持点到点（point-to-point）（PTP）模型和发布/订阅（Pub/Sub）模型。 <br>*. 支持同步与异步消息发送 <br>*. JDBC持久性管理使用数据库表来存储消息 <br>*. 可视化管理界面。 <br>*. Applet支持。 <br>*. 能够与Jakarta Tomcat这样的Servlet容器结合。 <br>*. 支持RMI, TCP, HTTP 与SSL协议。 <br>*. 客户端验证 <br>*. 提供可靠消息传输、事务和消息过滤</p>
<p><br><strong>UberMQ</strong><br>UberMQ完全实现了Java Message Service 规范。UberMQ是因为现有的许多JMS提供商已经违背了分布式计算的核心原则：快速与简单而开发的。</p>
<p>&nbsp;<br><strong>Hermes JMS</strong><br>利用它提供的Swing UI可以很好的实现监控JMS providers。</p>
<p>&nbsp;<br><strong>ActiveMQ</strong>&nbsp;<br>ActiveMQ是一个开放源码基于Apache 2.0 licenced 发布并实现了JMS 1.1。它能够与Geronimo，轻量级容器和任Java应用程序无缝的给合。</p>
<p>&nbsp;<br><strong>Somnifugi</strong><br>Somnifugi使得工作在同一个java虚拟机中的线程能实现消息互发。</p>
<p>&nbsp;<br><strong>MantaRay</strong>&nbsp;<br>MantaRay基于peer-2-peer 技术。它具有以下特性: <br>1.它既支持点对点(point-to-point)的域，又支持发布/订阅(publish/subscribe)类型的域。 <br>2.并且提供对下列类型的支持：经认可的消息传递,事务型消息的传递，一致性消息和具有持久性的订阅者支持。 <br>3.消息过滤体制。 <br>4.能与WebLogic and WebSphere 给合。 <br>5.支持TCP, UDP 与 HTTP传输协。</p>
<p>&nbsp;<br><strong>Presumo</strong><br>Presumo也是一个实现Java Message Service API的JMS消息中间件。</p>
<p>&nbsp;<br><strong>JORAM</strong><br>JORAM一个类似于openJMS分布在ObjectWeb之下的JMS消息中间件。</p>
<p>&nbsp;<br><strong>JMS4Spread</strong><br>JMS4Spread是一个消息系统.它部分地实现了Java消息服务(JMS) API.<br><br>-------------------------------------------------------------------------------------------<br><br><strong>开源JMS简单比较</strong><br><br>我考虑在公司的项目中采用JMS来降低服务器之间的耦合性，但为了降低成本，商业软件是不考虑的，于是只能在开源的并且对商业友好的JMS服务器中选择一个了。选择条件主要基于： </p>
<p>支持JMS 1.1规范 <br>持久化，能满足商业应用所需的稳定性 <br>满足项目的性能需求 <br>最好本身提供JNDI服务 <br>最好支持JMX <br>最好本身提供一个友好的管理工具 <br>最好提供一份完整的文档<br>准备进行选择的JMS服务器有：OpenJMS、UberMQ、ActiveMQ、MantaRay、JORAM </p>
<p>OpenJMS：老牌的JMS服务器了，也是我最早知道的开源JMS服务器，不过只支持JMS 1.02，已经很长时间没有更新了，因此不予考虑。 </p>
<p>UberMQ：采用NIO的JMS服务器，以前我学习NIO的时候看过它的代码，写的蛮不错的，也支持JMS 1.1。由于采用了NIO，所以具有很高的弹性，在满足项目的性能需求上没有什么问题；本身也提供JNDI服务，但是遗憾的是我bind其他类型的数据时会出错；提供admin和viewer两个管理工具，但是在管理工具里不能创建ConnectionFactory和Destination并绑定到JNDI；文档不太完整；最头痛的对于持久化支持不好，如果关闭JMS服务器再开启，所有保存在JMS中的信息就全部丢失了，这点没有办法满足商业应用所需的稳定性。 </p>
<p>ActiveMQ：最近比较活跃的一个JMS服务器，主页上的介绍说在协议配置上可以选择支持NIO，但是我仔细看它所支持的协议，却并没有提到如何配置，并且在实际的测试中也并没有发现其有采用NIO的迹象，多连接一个Client端，服务器端就增多了一个线程。满足JMS 1.1，有多种方法进行持久化；本身不提供JNDI，也没有对JMX的支持，本身不带管理工具，采用Hermes进行管理（这个我会在以后提到），文档也相对较少。 </p>
<p>MantaRay：也是比较活跃的一个JMS服务器，采用的是P2P模型，但是我不喜欢这种模型，对于JMS服务来说，很大的一个特点就是客户端可以不用永远在线，比如在更新某一个客户端时需要暂停服务，等服务再度开启时，这段时间内所接收到的信息并不会丢失，保存在服务器上，所以我并不能看到P2P模型应用在JMS服务器上的优势，况且采用JMS服务就是为了解除耦合，速度并不是唯一需要考量的事情。出于我不喜欢其所采用模型，并且在运行其所带的示例时都出现了示例时都出现了问题，两个客户端互发互收，但是彼此之间都收不到消息，于是不予考虑。 </p>
<p>JORAM：支持JMS 1.1，可以持久化到文件，本身提供JNDI服务和提供对JMX的支持，自带的管理工具可以添加ConnectionFactory和Destination并绑定到JNDI，这点对实现动态管理来说非常有用；文档非常完备，100多页的PDF，包含了各种配置和调整信息。其稳定性考虑的尤其好，不仅考虑到JMS服务器的集群，甚至连JNDI的集群也考虑进去（尽管暂时对我而言还用不上），这点对于商业应用而言应该会有加分。 </p>
<p>ActiveMQ是Apache License，JORAM是LGPL，这两者对于商业应用都是友好的；UberMQ和MantaRay采用是Dual License，UberMQ的Dual License是只要你不分发，就可以允许使用；而MantaRay是商业使用需要应用一个商业的License。 </p>
<p>比较上面的这些JMS服务器，最终我是选择了JORAM，其满足了我的绝大部分要求，唯一比较遗憾的是其采用传统的IO模型，每连接一个Client端会在服务器端增加两个线程，这点稍微影响了服务器的弹性。不过考虑到我们的项目应用，这点暂时可以不用考虑，实在压力过大了，最多到时候采用JMS集群呗：）<br></p>
<p>&nbsp;<br><strong>开源JMS再比较</strong><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>四月份时我曾经比较了那时活跃度比较高的一些开源JMS——《开源JMS简单比较》，时隔四个月，重新回顾这些项目，发现与四个月以前的比较有一些出入，在这里再进行一些比较：）<br>&nbsp;<br>比较的项目没有变化，OpenJMS、UberMQ、ActiveMQ、MantaRay、JORAM，这段时间内没有出现什么JMS新秀，JBoss计划在今年第四季度发布JBoss Messaging，但只要还是捆绑发行，我对其就没什么兴趣。<br>&nbsp;<br>在上次的比较中，OpenJMS已经有比较长的一段时间没有更新了，但最近的四个月似乎又活跃了起来，其预备发行的0.7.7版计划支持JMS 1.1（这个来的太晚了些），其主页上的Changelog表明了接下来的这个版本有着较大的变化。这对那些以前将OpenJMS应用在项目中的人来说是一个不错的消息，但对正在选择JMS的人而言，OpenJMS的这些改进来的还是稍稍晚了些。<br>&nbsp;<br>UberMQ这段时间没有更新，我对它的评价与以前一样，没有任何变化。<br>&nbsp;<br>MantaRay在其主页上更新了一系列的Flash Demos，通过这些Demo，我更坚定了我的看法——MantaRay并不适合用于企业的JMS服务。<br>&nbsp;<br>P2P这个词虽然热，但是不是什么地方都需要P2P的，在我看来JMS就是用于解除各个应用之间的耦合，速度是个关键指标，但比起这个关键指标更重要的是它存在的意义。我更倾向于采用MantaRay在Flash中所反对的那种模型，通过中心服务器进行转发，可以存放离线消息以及解除耦合。更何况，企业应用中很少有类似MantaRay演示DEMO中出现的那种网络拓扑图，并不是任何两个节点之间都是互联互通的。当然，如果MantaRay能够做一些改进，先尝试采用点对点模型，如果点对点失败，这时将消息发送到中心服务器上（但这一切必须对用户透明），我会比较赞成，既具有传统优势，又能提高消息发送接收速度。<br>&nbsp;<br>至于上篇文章中提到的运行其自带的示例出现了问题，这次在Flash演示中终于找到了答案。看来MantaRay真应该提高其示例程序的易用性，这么复杂的操作，要是不看Flash演示，还真难想到该这样操作：（<br>&nbsp;<br>ActiveMQ是让我感到惊讶的一个项目，上次对它的评价似乎有失偏颇。 ActiveMQ支持多种网络拓扑模型，既可以采用传统JMS的Client-Server模型，也可以采用MantaRay的P2P模型，还可以仅仅支持同一JVM内的JMS应用。持久化机制一如既往的优秀，默认采用Apache Derby数据库持久化，也可以配置为各种主流数据库来持久。目前也提供了一简单的JNDI实现，对于JMS应用而言，这已经够用了。<br>&nbsp;<br>但是其缺点也同样明显，本身不提供管理工具；示例代码非常少；虽然主页上的文档看上去比较全面，但是一来缺乏一种有效的组织方式（文档凌乱，用户很难由浅入深进行了解，提高了门槛），二来文档整体的专业性太强（不了解ActiveMQ？看文档去吧，可是文档是写给了解ActiveMQ的用户看的&#8230;&#8230;），对于普通用户而言，门槛有点高。<br>&nbsp;<br>而且感觉ActiveMQ有点不安于JMS的本份，开始做一些周边应用了，看其主页就可以看出来，多了很多比较流行的词汇。说不上这是优点还是缺点，但就我的角度而言，我更希望其专注于做好它的JMS。<br>&nbsp;<br>&nbsp;JORAM在这段期间推出了4.3.x的版本，也是我们在应用中所采用的版本，我的评价和上次相比没有什么大的变化。主页上说其速度有了提高，但我们应用中JMS数据量相对较少，没有感觉出来。稍微遗憾的是在我们试用的过程中，从4.2.3升级到4.3，老版本的持久化消息都无法在新版本上识别出来，只能全部清空。在兼容性上，看来JORAM还得多下功夫。总而言之，我们在应用中采用JORAM，感觉就是波澜不惊，没碰到什么大问题，也没有什么惊喜。<br>&nbsp;<br>就目前情况，我觉得ActiveMQ和JORAM的竞争力相对较强。再次提醒，JMS的选择一定要慎重，一旦选择好，换起来可是要伤筋动骨的&#8230;&#8230;</p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/131677.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-21 23:19 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/21/131677.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] JMS 学习</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/21/131676.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Sat, 21 Jul 2007 15:11:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/21/131676.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/131676.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/21/131676.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/131676.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/131676.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; JMS，也就是Java消息服务，它是一种允许Java应用程序通过标准化的接口访问范围广泛的MQ服务器的API。就像JDBC允许程序通过一个公共接口访问许多不同的数据库服务器一样。<br><br><br>
<center>
<h4>消息中间件和JMS</h4>
</center><br>(from http://jmsmom.3322.net/mom_jms/jms.html) <br><br>当前，CORBA、DCOM、RMI等RPC中间件技术已广泛应用于各个领域。但是面对规模和复杂度都越来越高的分布式系统，这些技术也显示出其局限性：（1）同步通信：客户发出调用后，必须等待服务对象完成处理并返回结果后才能继续执行；（2）客户和服务对象的生命周期紧密耦合：客户进程和服务对象进程都必须正常运行；如果由于服务对象崩溃或者网络故障导致客户的请求不可达，客户会接收到异常；（3）点对点通信：客户的一次调用只发送给某个单独的目标对象。 <br>&nbsp;&nbsp; 面向消息的中间件（Message Oriented Middleware，MOM）较好的解决了以上问题。发送者将消息发送给消息服务器，消息服务器将消息存放在若干队列中，在合适的时候再将消息转发给接收者。这种模式下，发送和接收是异步的，发送者无需等待；二者的生命周期未必相同：发送消息的时候接收者不一定运行，接收消息的时候发送者也不一定运行；一对多通信：对于一个消息可以有多个接收者。 <br>&nbsp;&nbsp; 已有的MOM系统包括IBM的MQSeries、Microsoft的MSMQ和BEA的MessageQ等。由于没有一个通用的标准，这些系统很难实现互操作和无缝连接。Java Message Service（JMS）是SUN提出的旨在统一各种MOM系统接口的规范，它包含点对点（Point to Point，PTP）和发布/订阅（Publish/Subscribe，pub/sub）两种消息模型，提供可靠消息传输、事务和消息过滤等机制。
<p>&nbsp;</p>
<p><br><strong>1.JMS<br></strong>&nbsp;&nbsp; JAVA 消息服务(JMS)定义了Java 中访问消息中间件的接口。JMS 只是接口，并没有给予实现，实现JMS 接口的消息中间件称为JMS Provider，iLink实现了JMS接口，用户可以通过使用JMS接口，在iLink中进行JMS编程。 iLink支持JMS1.0.2版本。 <br><br><strong>2.JMS接口描述<br></strong>&nbsp;&nbsp; JMS 支持两种消息类型PTP 和Pub/Sub，分别称作：PTP Domain 和Pub/Sub Domain，这两种接口都继承统一的JMS父接口，JMS 主要接口如下所示：<br><br>
<table style="WIDTH: 638px; HEIGHT: 267px" cellSpacing=0 cellPadding=0 width=638 align=left border=0 NOF="TE">
    <tbody>
        <tr>
            <td>
            <table id=Table3 style="WIDTH: 634px; HEIGHT: 244px" cellSpacing=3 cellPadding=1 width=634 border=1>
                <tbody>
                    <tr>
                        <td width="22%" height=16>
                        <p><font size=-1>MS父接口</font></p>
                        </td>
                        <td width="29%">
                        <p><font size=-1>PTP</font></p>
                        </td>
                        <td width="48%">
                        <p><font size=-1>Pub/Sub</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="22%">
                        <p><font size=-1>ConnectionFactory</font></p>
                        </td>
                        <td width="29%">
                        <p><font size=-1>QueueConnectionFactory</font></p>
                        </td>
                        <td width="48%">
                        <p><font size=-1>TopicConnectionFactory</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="22%">
                        <p><font size=-1>Connection</font></p>
                        </td>
                        <td width="29%">
                        <p><font size=-1>QueueConnection</font></p>
                        </td>
                        <td width="48%">
                        <p><font size=-1>TopicConnection</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="22%">
                        <p><font size=-1>Destination</font></p>
                        </td>
                        <td width="29%">
                        <p><font size=-1>Queue</font></p>
                        </td>
                        <td width="48%">
                        <p><font size=-1>Topic</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="22%">
                        <p><font size=-1>Session</font></p>
                        </td>
                        <td width="29%">
                        <p><font size=-1>QueueSession</font></p>
                        </td>
                        <td width="48%">
                        <p><font size=-1>TopicSession</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="22%">
                        <p><font size=-1>MessageProducer</font></p>
                        </td>
                        <td width="29%">
                        <p><font size=-1>QueueSender</font></p>
                        </td>
                        <td width="48%">
                        <p><font size=-1>TopicPublisher</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="22%">
                        <p><font size=-1>MessageConsumer</font></p>
                        </td>
                        <td width="29%">
                        <p><font size=-1>QueueReceiver,QueueBrowser</font></p>
                        </td>
                        <td width="48%">
                        <p><font size=-1>TopicSubscriber</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;<br><br><br><br><br><br><br><br><br><br>&nbsp;&nbsp; <br><br>ConnectionFactory ：连接工厂，JMS 用它创建连接<br>&nbsp;&nbsp; Connection ：JMS 客户端到JMS Provider 的连接<br>&nbsp;&nbsp; Destination ：消息的目的地<br>&nbsp;&nbsp; Session： 一个发送或接收消息的线程<br>&nbsp;&nbsp; MessageProducer： 由Session 对象创建的用来发送消息的对象<br>&nbsp;&nbsp; MessageConsumer： 由Session 对象创建的用来接收消息的对象<br><br><strong>3.JMS消息模型<br></strong>JMS 消息由以下几部分组成：消息头，属性，消息体。<br>&nbsp;&nbsp; <br><strong>3.1 消息头(Header)</strong> - 消息头包含消息的识别信息和路由信息，消息头包含一些标准的属性如：JMSDestination,JMSMessageID 等。 <br><br>
<table style="WIDTH: 509px; HEIGHT: 387px" cellSpacing=0 cellPadding=0 width=509 align=left border=0 NOF="TE">
    <tbody>
        <tr>
            <td>
            <table id=Table4 style="WIDTH: 499px; HEIGHT: 386px" cellSpacing=3 cellPadding=1 width=499 border=1>
                <tbody>
                    <tr>
                        <td width="25%" height=16>
                        <p><font size=-1>&nbsp;消息头</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>&nbsp;由谁设置</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSDestination</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>send 或 publish 方法</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSDeliveryMode</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>send 或 publish 方法</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSExpiration</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>send 或 publish 方法</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSPriority</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>send 或 publish 方法</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSMessageID</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>send 或 publish 方法</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSTimestamp</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>send 或 publish 方法</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSCorrelationID</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>客户</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSReplyTo</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>客户</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSType</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>客户</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSRedelivered</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>JMS Provider</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp; &nbsp; &nbsp; <br>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br><strong><br><br><br><br><br><br><br><br><br><br>&nbsp;&nbsp;<br>&nbsp; <br><br><br><br><br><br><br>3.2 属性(Properties)</strong> - 除了消息头中定义好的标准属性外，JMS 提供一种机制增加新属性到消息头中，这种新属性包含以下几种：<br>&nbsp;&nbsp; 1. 应用需要用到的属性;<br>&nbsp;&nbsp; 2. 消息头中原有的一些可选属性;<br>&nbsp;&nbsp; 3. JMS Provider 需要用到的属性。<br>&nbsp;&nbsp; 标准的JMS 消息头包含以下属性：<br><br>
<table style="WIDTH: 786px; HEIGHT: 483px" cellSpacing=0 cellPadding=0 width=786 align=left border=0 NOF="TE">
    <tbody>
        <tr>
            <td>
            <table id=Table5 style="WIDTH: 809px; HEIGHT: 460px" cellSpacing=3 cellPadding=1 width=809 border=1>
                <tbody>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSDestination</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>消息发送的目的地</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSDeliveryMode</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>传递模式， 有两种模式： PERSISTENT 和NON_PERSISTENT，PERSISTENT 表示该消息一定要被送到目的地，否则会导致应用错误。NON_PERSISTENT 表示偶然丢失该消息是被允许的，这两种模式使开发者可以在消息传递的可靠性和吞吐量之间找到平衡点。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSMessageID</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>唯一识别每个消息的标识，由JMS Provider 产生。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSTimestamp</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>一个消息被提交给JMS Provider 到消息被发出的时间。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSCorrelationID</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>用来连接到另外一个消息，典型的应用是在回复消息中连接到原消息。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSReplyTo</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>提供本消息回复消息的目的地址</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSRedelivered</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>如果一个客户端收到一个设置了JMSRedelivered 属性的消息，则表示可能该客户端曾经在早些时候收到过该消息，但并没有签收(acknowledged)。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSType</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>消息类型的识别符。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSExpiration</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>消息过期时间，等于QueueSender 的send 方法中的timeToLive 值或TopicPublisher 的publish 方法中的timeToLive 值加上发送时刻的GMT 时间值。如果timeToLive值等于零，则JMSExpiration 被设为零，表示该消息永不过期。如果发送后，在消息过期时间之后消息还没有被发送到目的地，则该消息被清除。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="25%">
                        <p><font size=-1>JMSPriority</font></p>
                        </td>
                        <td width="74%">
                        <p><font size=-1>消息优先级，从0-9 十个级别，0-4 是普通消息，5-9 是加急消息。JMS 不要求JMS Provider 严格按照这十个优先级发送消息，但必须保证加急消息要先于普通消息到达。</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><br><strong><br><br><br><br><br><br><br><br><br><br><br><br>&nbsp; <br><br><br><br><br><br><br><br>3.3 消息体(Body)</strong> - JMS API 定义了5种消息体格式，也叫消息类型，你可以使用不同形式发送接收数据并可以兼容现有的消息格式，下面描述这5种类型： <br><br>
<table style="WIDTH: 622px; HEIGHT: 270px" cellSpacing=0 cellPadding=0 width=622 align=left border=0 NOF="TE">
    <tbody>
        <tr>
            <td>
            <table id=Table6 style="WIDTH: 629px; HEIGHT: 247px" cellSpacing=3 cellPadding=1 width=629 border=1>
                <tbody>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>消息类型</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>消息体</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>TextMessage</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>java.lang.String对象，如xml文件内容</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>MapMessage</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>名/值对的集合，名是String对象，值类型可以是Java任何基本类型</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>BytesMessage</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>字节流</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>StreamMessage</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>Java中的输入输出流</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>ObjectMessage</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>Java中的可序列化对象</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>Message</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>没有消息体，只有消息头和属性</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>下例演示创建并发送一个TextMessage到一个队列： <br>TextMessage message = queueSession.createTextMessage();<br>message.setText(msg_text); // msg_text is a String<br>queueSender.send(message);<br><br>下例演示接收消息并转换为合适的消息类型： <br>Message m = queueReceiver.receive();<br>if (m instanceof TextMessage) {<br>&nbsp;TextMessage message = (TextMessage) m;<br>&nbsp;System.out.println("Reading message: " + message.getText());<br>} else {<br>&nbsp;// Handle error<br>}<br><br><strong>4. 消息的同步异步接收<br></strong>&nbsp;&nbsp; 消息的同步接收是指客户端主动去接收消息，JMS 客户端可以采用MessageConsumer 的receive方法去接收下一个消息。<br>&nbsp;&nbsp; 消息的异步接收是指当消息到达时，主动通知客户端。JMS 客户端可以通过注册一个实 现MessageListener 接口的对象到MessageConsumer，这样，每当消息到达时，JMS Provider 会调用MessageListener中的onMessage 方法。 <br><br><br><strong>5. PTP模型<br></strong>PTP(Point-to-Point)模型是基于队列的，发送方发消息到队列，接收方从队列接收消息，队列的存在使得消息的异步传输成为可能。和邮件系统中的邮箱一样，队列可以包含各种消息，JMS Provider 提供工具管理队列的创建、删除。JMS PTP 模型定义了客户端如何向队列发送消息，从队列接收消息，浏览队列中的消息。<br>&nbsp;&nbsp; 下面描述JMS PTP 模型中的主要概念和对象：<br><br>
<table style="WIDTH: 820px; HEIGHT: 536px" cellSpacing=0 cellPadding=0 width=820 align=left border=0 NOF="TE">
    <tbody>
        <tr>
            <td>
            <table id=Table1 style="WIDTH: 826px; HEIGHT: 513px" cellSpacing=3 cellPadding=1 width=826 border=1>
                <tbody>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>名称</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>描述</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>Queue</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>由JMS Provider 管理，队列由队列名识别，客户端可以通过JNDI 接口用队列名得到一个队列对象。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>TemporaryQueue</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>由QueueConnection 创建，而且只能由创建它的QueueConnection 使用。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>QueueConnectionFactory</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>客户端用QueueConnectionFactory 创建QueueConnection 对象。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>QueueConnection</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>一个到JMS PTP provider 的连接，客户端可以用QueueConnection 创建QueueSession 来发送和接收消息。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>QueueSession</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>提供一些方法创建QueueReceiver 、QueueSender、QueueBrowser 和TemporaryQueue。如果在QueueSession 关闭时，有一些消息已经被收到，但还没有被签收(acknowledged)，那么，当接收者下次连接到相同的队列时，这些消息还会被再次接收。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>QueueReceiver</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>客户端用QueueReceiver 接收队列中的消息，如果用户在QueueReceiver 中设定了消息选择条件，那么不符合条件的消息会留在队列中，不会被接收到。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>QueueSender</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>客户端用QueueSender 发送消息到队列。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>QueueBrowser</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>客户端可以QueueBrowser 浏览队列中的消息，但不会收走消息。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>QueueRequestor</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>JMS 提供QueueRequestor 类简化消息的收发过程。QueueRequestor 的构造函数有两个参数：QueueSession 和queue，QueueRequestor 通过创建一个临时队列来完成最终的收发消息请求。</font> </p>
                        </td>
                    </tr>
                    <tr>
                        <td width="26%">
                        <p><font size=-1>可靠性(Reliability)</font></p>
                        </td>
                        <td width="73%">
                        <p><font size=-1>队列可以长久地保存消息直到接收者收到消息。接收者不需要因为担心消息会丢失而时刻和队列保持激活的连接状态，充分体现了异步传输模式的优势。</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br>&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; <br>&nbsp;&nbsp; &nbsp; &nbsp; <br>&nbsp;&nbsp; <br>&nbsp;
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><br><br><br><br><br><br><br><br><br><br><br><br>6. PUB/SUB模型<br></strong>JMS Pub/Sub 模型定义了如何向一个内容节点发布和订阅消息，这些节点被称作主题(topic)。<br>&nbsp;&nbsp; 主题可以被认为是消息的传输中介，发布者(publisher)发布消息到主题，订阅者(subscribe) 从主题订阅消息。主题使得消息订阅者和消息发布者保持互相独立，不需要接触即可保证消息的传送。<br>&nbsp;&nbsp; 下面描述JMS Pub/Sub 模型中的主要概念和对象：<br><br>
<table style="WIDTH: 760px; HEIGHT: 688px" cellSpacing=0 cellPadding=0 width=760 align=left border=0 NOF="TE">
    <tbody>
        <tr>
            <td>
            <table id=Table2 style="WIDTH: 826px; HEIGHT: 648px" cellSpacing=3 cellPadding=1 width=826 border=1>
                <tbody>
                    <tr>
                        <td width="27%">
                        <p><font size=-1>名称</font></p>
                        </td>
                        <td width="72%">
                        <p><font size=-1>描述</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="27%">
                        <p><font size=-1>订阅(subscription)</font></p>
                        </td>
                        <td width="72%">
                        <p><font size=-1>消息订阅分为非持久订阅(non-durable subscription)和持久订阅(durable subscrip-tion)，非持久订阅只有当客户端处于激活状态，也就是和JMS Provider 保持连接状态才能收到发送到某个主题的消息，而当客户端处于离线状态，这个时间段发到主题的消息将会丢失，永远不会收到。持久订阅时，客户端向JMS 注册一个识别自己身份的ID，当这个客户端处于离线时，JMS Provider 会为这个ID 保存所有发送到主题的消息，当客户再次连接到JMS Provider时，会根据自己的ID 得到所有当自己处于离线时发送到主题的消息。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="27%">
                        <p><font size=-1>Topic</font></p>
                        </td>
                        <td width="72%">
                        <p><font size=-1>主题由JMS Provider 管理，主题由主题名识别，客户端可以通过JNDI 接口用主题名得到一个主题对象。JMS 没有给出主题的组织和层次结构的定义，由JMS Provider 自己定义。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="27%">
                        <p><font size=-1>TemporaryTopic</font></p>
                        </td>
                        <td width="72%">
                        <p><font size=-1>临时主题由TopicConnection 创建，而且只能由创建它的TopicConnection 使用。临时主题不能提供持久订阅功能。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="27%">
                        <p><font size=-1>TopicConnectionFactory</font></p>
                        </td>
                        <td width="72%">
                        <p><font size=-1>客户端用TopicConnectionFactory 创建TopicConnection 对象。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="27%">
                        <p><font size=-1>TopicConnection</font></p>
                        </td>
                        <td width="72%">
                        <p><font size=-1>TopicConnection 是一个到JMS Pub/Sub provider 的连接，客户端可以用TopicConnection创建TopicSession 来发布和订阅消息。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="27%">
                        <p><font size=-1>TopicSession</font></p>
                        </td>
                        <td width="72%">
                        <p><font size=-1>TopicSession 提供一些方法创建TopicPublisher、TopicSubscriber、TemporaryTopic 。它还提供unsubscribe 方法取消消息的持久订阅。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="27%">
                        <p><font size=-1>TopicPublisher</font></p>
                        </td>
                        <td width="72%">
                        <p><font size=-1>客户端用TopicPublisher 发布消息到主题。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="27%">
                        <p><font size=-1>TopicSubscriber</font></p>
                        </td>
                        <td width="72%">
                        <p><font size=-1>客户端用TopicSubscriber 接收发布到主题上的消息。可以在TopicSubscriber 中设置消息过滤功能，这样，不符合要求的消息不会被接收。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="27%">
                        <p><font size=-1>Durable TopicSubscriber</font></p>
                        </td>
                        <td width="72%">
                        <p><font size=-1>如果一个客户端需要持久订阅消息，可以使用Durable TopicSubscriber，TopSession 提供一个方法createDurableSubscriber创建Durable TopicSubscriber 对象。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="27%">
                        <p><font size=-1>恢复和重新派送(Recovery and Redelivery)</font></p>
                        </td>
                        <td width="72%">
                        <p><font size=-1>非持久订阅状态下，不能恢复或重新派送一个未签收的消息。只有持久订阅才能恢复或重新派送一个未签收的消息。</font></p>
                        </td>
                    </tr>
                    <tr>
                        <td width="27%">
                        <p><font size=-1>TopicRequestor</font></p>
                        </td>
                        <td width="72%">
                        <p><font size=-1>JMS 提供TopicRequestor 类简化消息的收发过程。TopicRequestor 的构造函数有两个参数：TopicSession 和topic。TopicRequestor 通过创建一个临时主题来完成最终的发布和接收消息请求。</font> </p>
                        </td>
                    </tr>
                    <tr>
                        <td width="27%">
                        <p><font size=-1>可靠性(Reliability)</font></p>
                        </td>
                        <td width="72%">
                        <p><font size=-1>当所有的消息必须被接收，则用持久订阅模式。当丢失消息能够被容忍，则用非持久订阅模式。</font></p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><br>&nbsp;</p>
<p>&nbsp;</p>
<p><br>&nbsp;&nbsp;<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;<br>&nbsp;&nbsp;<br><strong>&nbsp;&nbsp;&nbsp;&nbsp;<br><br><br><br><br><br><br><br><br><br><br><br><br><br>7. 开发JMS的步骤<br></strong>&nbsp;&nbsp; 广义上说，一个JMS 应用是几个JMS 客户端交换消息，开发JMS 客户端应用由以下几步构成： <br>用JNDI 得到ConnectionFactory 对象； <br>用JNDI 得到目标队列或主题对象，即Destination 对象； <br>用ConnectionFactory 创建Connection 对象； <br>用Connection 对象创建一个或多个JMS Session； <br>用Session 和Destination 创建MessageProducer 和MessageConsumer； <br>通知Connection 开始传递消息。 </p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/131676.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-21 23:11 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/21/131676.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] JNDI常见问题</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/16/130653.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Mon, 16 Jul 2007 11:48:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/16/130653.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/130653.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/16/130653.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/130653.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/130653.html</trackback:ping><description><![CDATA[<p>谁应该使用 JNDI？ <br>任何需要访问有关用户、机器、网络和服务的信息的 Java 应用程序。用户信息包括安全凭证、电话、电子邮件地址、通信地址和应用程序首选项（application preferences）。机器信息包括网络地址、机器配置等。另外就是任何需要导出对象或者需要访问其他应用程序或服务导出的对象的 Java 应用程序。这样的例子包括打印程序、日历和联网的文件系统。</p>
<p>现在我可以使用 JNDI 吗？ </p>
<p>是的，Sun Microsystems 已经将 JNDI 作为一个 Java Standard Extension 发布了。Sun Microsystems 还为很多命名服务和目录服务（如 LDAP, NIS, CORBA (COS) Naming 和文件）发布了无缝地插入在 JNDI 后面的服务提供程序。这些服务提供程序以及其他供应商提供的服务提供程序都可从 下载 处得到。 </p>
<p>JNDI 用于 Java 平台中的什么地方？</p>
<p>HotJava Views 1.1 使用 JNDI 来访问 LDAP。像 Enterprise JavaBeans, Java Message Service, JDBC 2.0 这样的 Enterprise API 将 JNDI 用于命名和目录用途。RMI over IIOP 应用程序可以使用 JNDI 来访问 CORBA (COS) 命名服务。 </p>
<p>谁将提供 JNDI 的实现？ </p>
<p>在写这篇文章时，IBM, Novell, Sun 和 WebLogic 已经为 JNDI 提供了服务提供程序。我们维护有一个公共可用的服务提供程序的 列表 。</p>
<p>JNDI 为哪些协议提供了接口？ </p>
<p>JNDI 本身独立于任何特定目录访问协议。单独的服务提供程序决定所支持的协议。将会有不同供应商提供的流行协议（比如 LDAP, NDS, DNS 和 NIS(YP)）的提供程序实现。</p>
<p>JNDI 与 LDAP 的关系如何？ </p>
<p>JNDI 提供目录和命名的一个优良的面向对象的抽象。开发人员使用 JNDI 可以开发出使用 LDAP 或其他访问协议的查询来检索结果；但是他们并不局限于 LDAP，也不是必须开发与 LDAP 相关的应用程序。JNDI 支持 LDAP v3 中的关键功能。</p>
<p>JNDI 与 Netscape 的 Java LDAP API 的关系如何？ </p>
<p>Netscape 的 API 是特定于 LDAP 的。它用于对 LDAP 目录的低级别访问。它暴露应用程序一般不需要知道的协议细节。 <br>JNDI 是 Java 程序的一个普通目录 API。它类似用于访问文件的 java.io.File 类。可能会有一些需要在协议级别处理文件的管理程序（比如 NFS），但是所有的 Java 应用程序一般都使用 File 类来访问文件系统。类似地，大多数 Java 程序应该使用 JNDI 来访问目录。需要在协议级别处理目录内容的应用程序可能会选择使用 Netscape 的 API。</p>
<p>JNDI 与用于命名的 OMG 的 CORBA 标准的关系如何？ </p>
<p>Java CORBA 应用程序可以使用 JNDI 来访问 CORBA (COS) 名称服务，以及其他命名服务和目录服务。它为应用程序提供一个接口，用于访问所有这些命名服务和目录服务。</p>
<p>通过使用 JNDI，Java CORBA 应用程序还可以使用像 LDAP 这样的分布式企业级服务来存储对象引用。</p>
<p>JNDI 与 Microsoft 的 ADSI 的关系如何？ </p>
<p>Java ADSI 包允许 Java 程序基于 COM 模型访问 Active Directory。尽管它可用于访问其他目录，但是它是一个以 Windows 为中心的解决方案。 </p>
<p>JNDI 提供 Java 应用程序使用 Java 对象模型来访问目录，而不管这些应用程序是运行在 Windows 上，还是在访问 Active Directory。例如，您可以处理像 AWT 和 JavaBeans 组件这样的对象，将它们绑定到目录中，然后再返回来查找它们，而不用做任何转换或者处理数据表示问题。</p>
<p>什么是 XFN，它与 JNDI 的关系如何？ </p>
<p>XFN 就是 X/Open Federated Naming，即一个基于 C 的标准，用于访问多个有可能联盟的命名服务和目录服务。熟悉 XFN 的程序员会发现使用 JNDI 很容易。</p>
<p>安全性如何？ </p>
<p>不同目录对待安全性的方式不同。JNDI 允许应用程序与特定于目录的安全系统协同工作。在未来，基于 JNDI 的应用程序将能够为 Java 平台开发的任何单点登录机制的优势。 </p>
<p>&nbsp;</p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/130653.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-16 19:48 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/16/130653.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转] 再谈JNDI</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/16/130651.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Mon, 16 Jul 2007 11:29:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/16/130651.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/130651.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/16/130651.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/130651.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/130651.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们知道jndi是java的命名和目录服务的api，为什么要有它了，是因为我们在网络条件下可能要查找和使用一些分布式的资源。好比我们现在使用的操作系统，它本身有一个类似于jndi的东西，这样我们才能找到和存放一些资源，如文件等。例如windows系统的分区和目录，它就是一个目录服务，还有linux的以文件夹的方式也是相当于一个目录服务；DNS就是一个命名服务等等，这些应用都有jndi的影子。考虑在网络条件下，我们要查找一个资源，我们不知道它所在的机器是什么操作系统，采用的什么目录和命名模式，所以sun提供了一个更高层次的接口，即jndi，让我们查找和使用资源是忽略这些不同的地方。否则试想一下以windows的目录结构试着去匹配linux的目录结构肯定是不行的。
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sun给的jndi只是个接口，各家都有自己的实现，这些实现就包括了一个统一的目录结构和查找（包括索引）。sun本身的jdk给了4种实现，还包含另一种简单的以文件系统为命名服务的实现。</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; 通常我们在程序中new出Context（在目录结构中的每一个结点称为context。每一个JNDI名字都是相对于context的）需要提前做些工作（如果是在j2ee容器中的代码则不必），需要两步，一是实现类的initcontextfactory,另一个就是provider_url,&nbsp; 它我感觉相当于给出具体资源在什么位置，并且以什么协议的形式作为其目录方案。有了这两个我们就能new出context,然后lookup出资源。以文件系统为命名服务的更简单，它只须一个factory就可以了。</p>
<img src ="http://www.blogjava.net/orangelizq/aggbug/130651.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-16 19:29 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/16/130651.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]J2EE的13种核心技术</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/16/130607.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Mon, 16 Jul 2007 08:22:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/16/130607.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/130607.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/16/130607.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/130607.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/130607.html</trackback:ping><description><![CDATA[Steven Gould在文中介绍了Java2平台企业版（J2EE）的13种核心技术：<span style="COLOR: #0000ff">JDBC, JNDI, EJBs, RMI, JSP, Java servlets, XML, JMS, Java IDL, JTS, JTA, JavaMail 和 JAF。</span>为了联系实际，Gould基于WebLogic应用服务器来自BEA Systems公司的一种广为应用的产品环境来介绍J2EE的这些技术。 <br><br>Java最初是在浏览器和客户端机器中粉墨登场的。当时，很多人质疑它是否适合做服务器端的开发。现在，随着对Java2平台企业版（J2EE）第三方支持的增多，Java被广泛接纳为开发企业级服务器端解决方案的首选平台之一。 <br><br>J2EE平台由一整套服务（Services）、应用程序接口（APIs）和协议构成，它对开发基于Web的多层应用提供了功能支持。 <br><br>在本文中我将解释支撑J2EE的13种核心技术：JDBC, JNDI, EJBs, RMI, JSP, Java servlets, XML, JMS, Java IDL, JTS, JTA, JavaMail 和 JAF，同时还将描述在何时、何处需要使用这些技术。当然，我还要介绍这些不同的技术之间是如何交互的。 <br><br>此外，为了让您更好地感受J2EE的真实应用，我将在WebLogic应用服务器,来自BEA Systems公司的一种广为应用的产品环境下来介绍这些技术。不论对于WebLogic应用服务器和J2EE的新手，还是那些想了解J2EE能带来什么好处的项目管理者和系统分析员，相信本文一定很有参考价值。 <br><br>宏观印象: 分布式结构和J2EE <br><br>过去，二层化应用 -- 通常被称为client/server应用 -- 是大家谈论的最多的。在很多情况下，服务器提供的惟一服务就是数据库服务。在这种解决方案中，客户端程序负责数据访问、实现业务逻辑、用合适的样式显示结果、弹出预设的用户界面、接受用户输入等。client/server结构通常在第一次部署的时候比较容易，但难于升级或改进，而且经常基于某种专有的协议，通常是某种数据库协议。它使得重用业务逻辑和界面逻辑非常困难。更重要的是，在Web时代，二层化应用通常不能体现出很好的伸缩性，因而很难适应Internet的要求。 <br><br>Sun设计J2EE的部分起因就是想解决二层化结构的缺陷。于是，J2EE定义了一套标准来简化N层企业级应用的开发。它定义了一套标准化的组件，并为这些组件提供了完整的服务。J2EE还自动为应用程序处理了很多实现细节，如安全、多线程等。 <br><br>用J2EE开发N层应用包括将二层化结构中的不同层面切分成许多层。一个N层化应用A能够为以下的每种服务提供一个分开的层： <br><br>显示：在一个典型的Web应用中，客户端机器上运行的浏览器负责实现用户界面。 <br><br>动态生成显示: 尽管浏览器可以完成某些动态内容显示，但为了兼容不同的浏览器，这些动态生成工作应该放在Web服务器端进行，使用JSP、Servlets，或者XML（可扩展标记语言）和（可扩展样式表语言）。 <br><br>业务逻辑：业务逻辑适合用Session EJBs（后面将介绍）来实现。 <br><br>数据访问：数据访问适合用Entity EJBs（后面将介绍）和JDBC来实现。 <br><br>后台系统集成: 同后台系统的集成可能需要用到许多不同的技术，至于何种最佳需要根据后台系统的特征而定。 <br><br>您可能开始诧异：为什么有这么多的层？事实上，多层方式可以使企业级应用具有很强的伸缩性，它允许每层专注于特定的角色。例如，让Web服务器负责提供页面，应用服务器处理应用逻辑，而数据库服务器提供数据库服务。 <br><br>由于J2EE建立在Java2平台标准版（J2SE）的基础上，所以具备了J2SE的所有优点和功能。包括&#8220;编写一次，到处可用&#8221;的可移植性、通过JDBC访问数据库、同原有企业资源进行交互的CORBA技术，以及一个经过验证的安全模型。在这些基础上，J2EE又增加了对EJB（企业级Java组件）、Java servlets、Java服务器页面（JSPs）和XML技术的支持。 <br><br>分布式结构与WebLogic应用服务器 <br><br>J2EE提供了一个框架--一套标准API--用于开发分布式结构的应用，这个框架的实际实现留给了第三方厂商。部分厂商只是专注于整个J2EE架构中的的特定组件，例如Apache的Tomcat提供了对JSP和servlets的支持，BEA系统公司则通过其WebLogic应用服务器产品为整个J2EE规范提供了一个较为完整的实现。 <br><br>WebLogic服务器已使建立和部署伸缩性较好的分布式应用的过程大为简化。WebLogic和J2EE代你处理了大量常规的编程任务，包括提供事务服务、安全领域、可靠的消息、名字和目录服务、数据库访问和连接池、线程池、负载平衡和容错处理等。 <br><br>通过以一种标准、易用的方式提供这些公共服务，象WebLogic服务器这样的产品造就了具有更好伸缩性和可维护性的应用系统，使其为大量的用户提供了增长的可用性。 <br><br>J2EE技术 <br><br>在接下来的部分里，我们将描述构成J2EE的各种技术，并且了解WebLogic服务器是如何在一个分布式应用中对它们进行支持的。最常用的J2EE技术应该是JDBC、JNDI、EJB、JSP和servlets，对这些我们将作更仔细的考察。 <br><br>Java Database Connectivity (JDBC) <br><br>JDBC API以一种统一的方式来对各种各样的数据库进行存取。和ODBC一样，JDBC为开发人员隐藏了不同数据库的不同特性。另外，由于JDBC建立在Java的基础上,因此还提供了数据库存取的平台独立性。 <br><br>JDBC定义了4种不同的驱动程序，现分述如下： <br><br>类型 1: JDBC-ODBC Bridge <br><br>在JDBC出现的初期，JDBC-ODBC桥显然是非常有实用意义的，通过JDBC-ODBC桥，开发人员可以使用JDBC来存取ODBC数据源。不足的是，他需要在客户端安装ODBC驱动程序，换句话说，必须安装Microsoft Windows的某个版本。使用这一类型你需要牺牲JDBC的平台独立性。另外，ODBC驱动程序还需要具有客户端的控制权限。 <br><br>类型 2: JDBC-native driver bridge <br><br>JDBC本地驱动程序桥提供了一种JDBC接口，它建立在本地数据库驱动程序的顶层，而不需要使用ODBC。 JDBC驱动程序将对数据库的API从标准的JDBC调用转换为本地调用。使用此类型需要牺牲JDBC的平台独立性，还要求在客户端安装一些本地代码。 <br><br>类型 3: JDBC-network bridge <br><br>JDBC网络桥驱动程序不再需要客户端数据库驱动程序。它使用网络上的中间服务器来存取数据库。这种应用使得以下技术的实现有了可能，这些技术包括负载均衡、连接缓冲池和数据缓存等。由于第3种类型往往只需要相对更少的下载时间，具有平台独立性，而且不需要在客户端安装并取得控制权，所以很适合于Internet上的应用。 <br><br>类型 4: Pure Java driver <br><br>第4种类型通过使用一个纯Java数据库驱动程序来执行数据库的直接访问。此类型实际上在客户端实现了2层结构。要在N-层结构中应用，一个更好的做法是编写一个EJB，让它包含存取代码并提供一个对客户端具有数据库独立性的服务。 <br><br>WebLogic服务器为一些通常的数据库提供了JDBC驱动程序，包括Oracle, Sybase, Microsoft SQL Server以及Informix。它也带有一种JDBC驱动程序用于Cloudscape，这是一种纯Java的DBMS，WebLogic服务器中带有该数据库的评估版本。 <br><br>以下让我们看一个实例。 <br><br>JDBC实例 <br><br>在这个例子中我们假定你已经在Cloudscape中建立了一个PhoneBook数据库，并且包含一个表，名为 CONTACT_TABLE ，它带有2个字段：NAME 和 PHONE。 开始的时候先装载Cloudscape JDBC driver，并请求 driver manager得到一个对PhoneBook Cloudscape数据库的连接。通过这一连接，我们可以构造一个 Statement 对象并用它来执行一个简单的SQL查询。最后，用循环来遍历结果集的所有数据，并用标准输出将NAME和PHONE字段的内容进行输出。 <br><br><ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
    <tbody>
        <tr>
            <td bgColor=#e6e6e6>
            <pre><ccid_code>import java.sql.*;
            public class JDBCExample
            {
            public static void main( String args[] )
            {
            try
            {
            Class.forName("COM.cloudscape.core.JDBCDriver");
            Connection conn = DriverManager.getConnection("jdbc:cloudscape:PhoneBook");
            Statement stmt = conn.createStatement();
            String sql = "SELECT name, phone FROM CONTACT_TABLE ORDER BY name";
            ResultSet resultSet = stmt.executeQuery( sql );
            String name;
            String phone;
            while ( resultSet.next() )
            {
            name = resultSet.getString(1).trim();
            phone = resultSet.getString(2).trim();
            System.out.println( name + ", " + phone );
            }
            }
            catch ( Exception e )
            {
            // Handle exception here
            e.printStackTrace();
            }
            }
            }</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</ccid_nobr><br><br>OK。接着我们来看一看JDBC是如何在企业应用中的进行使用。 <br><br>JDBC在企业级应用中的应用 <br><br>以上实例其实是很基本的，可能有些微不足道。它假定了一个2层结构。在一个多层的企业级应用中，更大的可能是在客户端和一个EJB进行通信，该EJB将建立数据库连接。为了实现和改进可伸缩性和系统性能， WebLogic服务器提供了对连接缓冲池connection pool的支持。 <br><br>Connection pool减少了建立和释放数据库连接的消耗。在系统启动以后即可建立这样的缓冲池，此后如故再有对数据库的请求，WebLogic服务器可以很简单地从缓冲池中取出数据。数据缓冲池可以在WebLogic服务器的 weblogic.properties 文件中进行定义。(可参考 weblogic.properties 文件中的例子，WebLogic服务器的文档中还有更详细的参考信息) <br><br>在企业级应用的另一个常见的数据库特性是事务处理。事务是一组申明statement，它们必须做为同一个statement来处理以保证数据完整性。缺省情况下JDBC使用 auto-commit 事务模式。这可以通过使用Connection类的 setAutoCommit() 方法来实现。 <br><br>现在我们已经对JDBC有了一些认识，下面该转向<a href="http://tech.ccidnet.com/pub/article/c1060_a105273_p1.html">JNDI</a>了。 <br><br>Java Naming and Directory Interface (JNDI) <br><br>JNDI API被用于执行名字和目录服务。它提供了一致的模型来存取和操作企业级的资源如DNS和LDAP，本地文件系统，后者在应用服务器中的对象。 <br><br>在JNDI中，在目录结构中的每一个结点称为context。每一个JNDI名字都是相对于context的。这里没有绝对名字的概念存在。对一个应用来说，它可以通过使用 InitialContext 类来得到其第一个context: <br><br><ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
    <tbody>
        <tr>
            <td bgColor=#e6e6e6>
            <pre><ccid_code>Context ctx = new InitialContext();</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</ccid_nobr><br><br>应用可以通过这个初始化的context经有这个目录树来定位它所需要的资源或对象。例如，假设你在Weblogic服务器中展开了一个EJB并将home接口绑定到名字 myApp.myEJB ，那么该EJB的某个客户在取得一个初始化context以后，可以通过以下语句定位home接口： <br><br><ccid_nobr>
<table cellSpacing=0 borderColorDark=#ffffff cellPadding=2 width=400 align=center borderColorLight=black border=1>
    <tbody>
        <tr>
            <td bgColor=#e6e6e6>
            <pre><ccid_code>MyEJBHome home = ctx.lookup( "myApp.myEJB" );</ccid_code></pre>
            </td>
        </tr>
    </tbody>
</table>
</ccid_nobr><br><br>在这个例子中，一旦你有了对被请求对象的参考，EJB的home接口就可以在它上面调用方法。我们将在下面的"Enterprise Java Beans"章节中做更多的介绍。 <br><br>以上关于JNDI的讨论只是冰山之一角而已。如果要更进一步地在context中查找对象，JNDI也提供了一些方法来进行以下操作：　 <br><br>将一个对象插入或绑定到context。这在你展开一个EJB的时候是很有效的。 <br><br>从context中移去对象。 <br><br>列出context中的所有对象。 <br><br>创建或删除子一级的context。 <br><br>接下来，我们要开始关注EJB了。 <br><br>Enterprise Java Beans (EJB) <br><br>J2EE技术之所以赢得某体广泛重视的原因之一就是EJB。它们提供了一个框架来开发和实施分布式商务逻辑，由此很显著地简化了具有可伸缩性和高度复杂的企业级应用的开发。EJB规范定义了EJB组件在何时如何与它们的容器进行交互作用。容器负责提供公用的服务，例如目录服务、事务管理、安全性、资源缓冲池以及容错性。 <br><br>EJB规范定义了3中基本的bean类型: <br><br>Stateless session beans: 提供某种单一的服务，不维持任何状态，在服务器故障发生时无法继续存在，生命期相对较短。例如，一个stateless session bean可能被用于执行温度转换计算。 <br><br>Stateful session bean: T提供了与客户端的会话交互，可以存储状态从而代表一个客户。典型例子是购物车。Stateful session bean在服务器故障时无法继续生存，生命气相对较短。每一个实例只用于一个单个的线程。 <br><br>Entity beans: 提供了一致性数据的表示-- 通常存放在数据库中 -- 在服务器故障发生后能继续存在。多用户情况下可以使用EJB来表示相同的数据。entity EJB的一个典型例子是客户的帐号信息。 <br><br>尽管有以上的区别，所有的EJB还是有许多的共同之处。它们都处理home interface。它定义了一个客户端是如何创建与消亡EJB的。可以在bean中对定义了客户端方法的远程接口进行调用；bean类则执行了主要的商务逻辑。 <br><br>描述EJB的开发已经超出了本文的范围。但是，如果一个EJB已经被开发了或者从第三方进行了购买，它就必须在应用服务器中进行发布。WebLogic Server 5.1带有一个EJB Deployer Tool来协助处理EJB的发布。当你使用EJB Deployer Tool的时候，你要定义客户端所用的JNDI名字来定位EJB。Deployer Tool将生成wrapper类来处理和容器的通信以及在一个jar文件中把被请求的Java类绑定在一起。 <br><br>一旦EJB被发布，客户端就可以使用它的JNDI名字来定位EJB。首先，它必须得到一个到home接口的reference。然后，客户端可以使用该接口，调用一个 create() 方法来得到服务器上运行的某个bean实例的句柄；最后，客户端可以使用该句柄在bean中调用方法。 <br><br>了解 EJB后，让我们再来看JSP。 <br><br>JavaServer Pages (JSPs) <br><br>我们中间可能已经有许多人已经熟悉Microsoft的Active Server Pages (ASP)技术了。JSP和ASP相对应的，但更具有平台对立性。他们被设计用以帮助Web内容开发人员创建动态网页，并且只需要相对较少的代码。 即使Web设计师不懂得如何编程也可以使用JSP，因为JSP应用是很方便的。 JSP页面由HTML代码和嵌入其中的Java代码所组成。服务器在页面被客户端所请求以后对这些Java代码进行处理，然后将生成的HTML页面返回给客户端的浏览器。 <br><br>下面我们来看一个JSP的简单实例。它只显示了服务器的当前日期和时间。虽然，对语法的具体解释已经超出了本文的范围，但我们还是可以很直观地看到，Java代码被放在符号的中间，而Java的表达式则放在符号之间。 <br><br>Date JSP sample <br><br>The current date is . <br><br>您可能有时候听说过JHTML。这是JSP以前的一种较老的标准。WebLogic服务器既可支持JSP，又可支持JHTML。请注意，在缺省状况下，JSP在WebLogic服务器中并没有处于有效状态。要使之有效，你可以编辑weblogic.properties文件。如果Web服务器还没有处于有效状态，则要先使之有效。Servlet的情况和JSP是一样的。 <br><br>下面是: Java servlets <br><br>Java servlets <br><br>servlet提供的功能大多与JSP类似，不过实现的方式不同。JSP通常是大多数HTML代码中嵌入少量的Java代码，而servlets全部由Java写成并且生成HTML。 <br><br>servlet是一种小型的Java程序，它扩展了Web服务器的功能。作为一种服务器端的应用，当被请求时开始执行，这和CGI Perl脚本很相似。Servlets和CGI脚本的一个很大的区别是：每一个CGI在开始的时候都要求开始一个新的进程 -- 而servlets是在servlet引擎中以分离的线程来运行的。因此servlets在可伸缩性上提供了很好的改进。 <br><br>在开发servlets的时候，您常常需要扩展javax.servlet.http.HttpServlet 类，并且override一些它的方法，其中包括： <br><br>service(): 作为dispatcher来实现命令-定义方法 <br><br>doGet(): 处理客户端的HTTP GET请求。 <br><br>doPost(): 进行HTTP POST操作 <br><br>其它的方法还包括处理不同类型的HTTP请求 -- 可以参考HttpServlet API文档。 <br><br>以上描述的是标准J2EE Servlet API的各种方法。WebLogic服务器提供了一个该API完整的实现途径。一旦你开发了一个servlet，你就可以在weblogic.properties 中加以注册并由此可以在WebLogic服务器中对它进行配置。 <br><br>通过Java servlets,我们已经到达了J2EE主要技术的末尾了。但J2EE所提供的并不止于这些。下面的段落中我们将简要地看一下现存的一些技术，包括RMI, Java IDL和CORBA, JTA, 以及XML，等等。 <br><br>Remote Method Invocation (RMI) <br><br>正如其名字所表示的那样，RMI协议是在远程对象上调用一些方法。它使用了连续序列方式在客户端和服务器端传递数据。RMI是一种被EJB使用的更下层的协议。 <br><br>Java IDL/CORBA <br><br>在Java IDL的支持下，开发人员可以将Java和CORBA集成在一起。 他们可以创建Java对象并使之可在CORBA ORB中展开, 或者他们还可以创建Java类并作为和其它ORB一起展开的CORBA对象的客户。后一种方法提供了另外一种途径，通过它Java可以被用于将你的新的应用和legacy系统相集成。 <br><br>Java Transaction Architecture (JTA)/Java Transaction Service (JTS) <br><br>JTA定义了一种标准的API，应用系统由此可以存取各种事务监控。 <br><br>JTS是CORBA OTS事务监控的基本的实现。JTS规定了事务管理器的实现方式。该事务管理器是在高层支持Java Transaction API (JTA)规范，并且在较底层实现OMG OTS specification的Java映像。JTS事务管理器为应用服务器、资源管理器、独立的应用以及通信资源管理器提供了事务服务。 <br><br>JavaMail and JavaBeans Activation Framework <br><br>JavaMail是用于存取邮件服务器的API，它提供了一套邮件服务器的抽象类。不仅支持SMTP服务器，也支持IMAP服务器。 <br><br>JavaMail利用JavaBeans Activation Framework (JAF)来处理MIME-编码的邮件附件。MIME的字节流可以被转换成Java对象，或者转换自Java对象。由此大多数应用都可以不需要直接使用JAF。 <br><br>Java Messaging Service (JMS) <br><br>JMS是用于和面向消息的中间件相互通信的应用程序接口(API)。它既支持点对点的域，有支持发布/订阅(publish/subscribe)类型的域，并且提供对下列类型的支持：经认可的消息传递,事务型消息的传递，一致性消息和具有持久性的订阅者支持。JMS还提供了另一种方式来对您的应用与legacy backend系统相集成。 <br><br>Extensible Markup Language (XML) <br><br>XML是一种可以用来定义其它标记语言的语言。它被用来在不同的商务过程中共享数据。XML的发展和Java是相互独立的，但是，它和Java具有的相同目标正是平台独立性。通过将Java和XML的组合，您可以得到一个完美的具有平台独立性的解决方案。目前正有许多不同的公司在为Java和XML的组合而努力。如果要了解更多的这方面的信息，可以访问Sun的Java-XML页面，或者IBM developerWorks的XML Zone。 <br><br>总结 <br><br>在本文中，我们介绍了建立在J2EE上的分布式应用结构，并且描述了WebLogic服务器对J2EE的各种支持。 然而，我们所揭示的仅仅是冰山之一角而已，要以一篇数千字的文章来展示J2EE潜在的对您的企业级应用的影响可是很不公平的。 <br><br>我们已经关注了在您开始用J2EE进行工作时最有可能遇到的各类技术：JDBC, JNDI, EJB, JSP和servlet。我们也为您提供了一些尚未常见的J2EE技术的背景知识。不管您是一名开发人员，商务应用分析师，或者项目经理，都应该对J2EE和WebLogic服务器所能提供给我们，给我们的企业以及我们的企业级应用所带来的意义有一个更好的认识。 <br><br>
<img src ="http://www.blogjava.net/orangelizq/aggbug/130607.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-16 16:22 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/16/130607.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JNDI 学习</title><link>http://www.blogjava.net/orangelizq/archive/2007/07/16/130605.html</link><dc:creator>桔子汁</dc:creator><author>桔子汁</author><pubDate>Mon, 16 Jul 2007 08:18:00 GMT</pubDate><guid>http://www.blogjava.net/orangelizq/archive/2007/07/16/130605.html</guid><wfw:comment>http://www.blogjava.net/orangelizq/comments/130605.html</wfw:comment><comments>http://www.blogjava.net/orangelizq/archive/2007/07/16/130605.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/orangelizq/comments/commentRss/130605.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/orangelizq/services/trackbacks/130605.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp; 基于JNDI的应用开发&nbsp;&nbsp;&nbsp; JNDI（The Java Naming and Directory Interface，Java命名和目录接口）是一组在Java应用中访问命名和目录服务的API.命名服务将名称和对象联系起来，使得我们可以用名称访问对象。目录服务是一种命名服务，在这种服务里，对象不但有名称，还有属性。&nbsp;&nbsp;&nbs...&nbsp;&nbsp;<a href='http://www.blogjava.net/orangelizq/archive/2007/07/16/130605.html'>阅读全文</a><img src ="http://www.blogjava.net/orangelizq/aggbug/130605.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/orangelizq/" target="_blank">桔子汁</a> 2007-07-16 16:18 <a href="http://www.blogjava.net/orangelizq/archive/2007/07/16/130605.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>