如何在鸡尾酒会上谈论Jini,J2EE和Web服务
2007-12-23 12:23:37 来源:WEB开发网版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
原文地址:
http://www.onjava.com/pub/a/onjava/2003/08/27/cocktails.Html
中文地址:
http://www.matrix.org.cn/resource/article/43/43781_Jini_J2EE.html
关键词: Jini J2EE Web
编辑说明:Kathy Sierra 和Bert Bates是O’Reilly的Head First系列的后台智囊。他们第一次写这篇文章是在2003年Head First Java发行的那时候。这本书变得十分流行,我们现在仍然收到订单。如此多的订单让我们决定重新带给读者,这就导致Head First Java,2nd Edition的发行。所以如果第一次发行的时候你没赶上,现在就让Kathy和Bert告诉你如何与Java初学者交谈。
这些都是可以预测到的:当你在一个宴会中,喝着次等的马提尼酒,不可避免的就会谈到分布式编程。怎么办?放松点,因为在这里我们会解决3种最有意思的分布式编程,就在用完的餐巾上画图,你可以用来提高你的 whuffie. (不知道 whuffie是什么? 读读 Richard Koman's interview with Cory Doctorow.)
但是首先,万一你真的现在就在某个宴会上,我们首先从几个即使你不懂Java也可以使用的短语。然后,对于那些确实知道一些Java技术的人,我们会谈得更深入一点。
“什么是动态发现?你知道,它的含义就是在网络上,尽管客户端和服务事先对对方一无所知,但能动态地发现对方。这所有都是基于ip广播的。”
“什么是自动诊断网络 (self-healing network) 呢?它指Jini网络总是能够反映当前所有可用的服务的状态--就像这样:‘OK,这个服务起来了,那个服务当掉了……’没有任何人的管理!”
当然,大多数路由器禁止了IP广播,所以你不准备在web上用Jini。但是Jini是为本地网络的工作,或者本地网络的集合而设计的,所以这并不是个大问题。
“J2EE最酷的东西是提供商独立,你可以专心于你的商业逻辑,将那些重担交给厂商。你可以在你特殊的商业规则上工作,把那些安全,事务,并行,持久化,甚至网络代码实现的任务交给服务器。而你只需要去学一个API,你可以重新部署你的J2EE程序给所有厂商的与J2EE兼容的服务器。所以现在厂商必须改变以前那种将你锁起来,并且你必须乞求厂商添加新的功能以解决bugs的方式。”
“Web服务中酷的东西是…嗯,OK,也许现在的情况Web services没有什么真正酷的东西。但是会变的,就在不久的未来,当所有标准都提出,工具成熟了,以及….”
“但是如果要说Web 服务酷,这也许因为你可以采用你已有的商业程序,甚至旧的程序,让他们通过xml那样的接口暴露在Web上。客户端通过可互操作的方式发送一个XML信息(通过一种叫SOAP的格式)给服务。”
“安全和事务是现在Web服务的两个明显的缺陷,这意味着所有人必须加入自己的解决方案。在Web上没有足够的事务管理设置,而你所拥有的唯一的安全是互动验证 (mutual authentication) 的Https。”
“Jini和J2EE都是Java技术。通过J2EE,就像规格说明说的那样:“哦,你不需要担心你这个小程序员去解决那些巨大的困难的东西。厂商会考虑这些问题,而你可专注于自己的特殊领域的需要(比如,如何卖出更多的妇女内衣)。但是,通过Jini,就好像规格说明说的那样:‘你必须靠自己,别指望其他人使用许多基础设施来救你。这是贫乏而又简陋的,宝贝,但你可以做最令人惊讶和体面的事情。当你在那时,检验一下Javaspaces。’”
“Web服务不是针对于java的技术,但是java可以让其更容易的使用,特别是如果你去使用J2EE 1.4。不,你说的对。J2EE 1.4还没发布,但是会在今年年底发布,你将在2004年初见到许多厂商支持。”
(在我们继续下去之前,先提出一些免责声明:首先,如果你对于这篇文章的高级含义有任何问题,重新读一下标题。对于这些内容我们有酒吧侍应的牌照,我们不会告诉你什么是错的,但是你也不会试图去了解整个故事。每个主题都需要相应的一本书(但如果我们不说出任意一本书来,这是很可恨的。比如,Head First EJB,或是哪一本呢?)让我们仅仅通过鸡尾酒会的观点来看这个问题并不是你想来操纵你的下个结构。(我们知道你知道这些,但对于那些可能会误解这篇文章是篇严肃的技术论文的人,我感到有必要谈谈我们的a**es).)[原文如此]
所以现在是时候进入下一个层次了。我们将首先开始看看什么是服务,而且更重要的是如何向别人显现你自己。换种说法,你如何将你的服务告诉你潜在的客户呢?
通过Jini,J2EE,和Web service,目的是让客户去访问服务。什么是服务呢?你所能做的事就是将消息从一个软件传递给另一软件(也许包含人工指向,也许也不包含)。你也许有一个为基因匹配进行大规模计算的服务。或者一个玩简陋的Go游戏的服务。或者一个让你买演唱会门票的程序。或者预定异国热带风情的巡游。或者甚至将你的文字传递给一台高容量(high-volume)打印机。换种说法,服务就是所有像软件一样开始但是结果并不一定留存在软件中的东西。
你也许有个服务来移动摄像机,利用打印机打印,拨电话。没关系。或者至少没关系。主要目标之一,就像OO的任何情况下的目标一样,尽可能减少耦合,—此例中,即客户端和服务器端之间的耦合。换种说法就是我们要让那些参与者(客户端和服务端)尽可能地少了解对方。
但是让我们说说你有一个服务…现在怎么办?客户端如何找到你?如果他们找到你,他们怎么知道你的服务可以干什么?换种说法,他们如何知道他们调用你的服务的方法?这样,你必须将你自己暴露给客户,我们的3种技术在这方面稍有不同。不考虑我们在讨论Jini,J2EE,或者Web服务,可是必须在某个地方有个接口。这个接口说明了你可以做的东西。
作为一个服务开发人员,你必须告诉人们你的服务可以做什么。或者说,你必须声明你的服务的方法。这包括声明客户端传递给你的方法什么(参数)以及服务返回什么(返回类型)。
上面这个接口是为一个叫Advice的服务。它具有一个方法,getAdvice()。你可以调用这个方法,得到一个字符串,里面包含极好的有用的随机选择的建议。(我们选择不在我们假的小类似于UML的图中显示返回类型。这是非常小的事)。
对于Jini和EJB(EnterPRise JavaBeans---J2EE的核心和灵魂),接口通常是Remote。一个Remote的接口通常意味着开发者写一个基于java.rmi.Remote的扩展java接口。耶,Remote所有方法(意思是Remote接口中的所有方法)必须声明一个java.rmi.RemoteException的异常。一个强制性的异常(checked exception),告诉客户端:“事情变得很糟糕,小心准备。”
对于Web Services,接口被定义在一个WSDL中(读作“wizdle”,与“fizdle”押韵)----一种特殊格式的XML文档。所以总括来说,你可以在随意的谈话中说以下这段话:
“可以说,在jini和ejb中,你通过java接口暴露你自己,但是在web服务中,你通过xml接口暴露你自己。”
(准确地说,如果你是一个java开发者,你通过写一个java接口来创建WSDL,按那个在你的开发工具中神奇的红色按钮,将你的java接口变成XML WSDL。这就够了,像他们说的,这超过了一个鸡尾酒会谈话的范围。)
OK,现在我们有了服务,有了暴露我们服务的接口。现在呢?客户端如何知道这个接口?他们如何真正取得这个接口,他们还需要和我们服务交流些什么呢?这要看服务的类型。这3种类型是不同的。你访问Jini,J2EE和Web服务将有少许不同,文章的其他部分将着重于一些细节和主要不同之处。
首先,让我们从一些Java的Remote Method Invocation(RMI)的背景开始。这是大部分的java分布式技术的主干,Jini和EJB都基于此。事实上,自从所有分布式技术在概念上如RMI的技术上运行时,你在这里了解的东西将帮助你弄清楚整个分布式的世界,Java或者非Java的。
通过RMI,你写你的服务并将此变成为Remote对象。让其成为Remote仅是小菜一碟---写分布式接口,创建Remote类来实现它。进入讨论组讨论。 创建remote接口
1. 扩展 java.rmi.Remote
2. 为每个方法声明java.rmi.RemoteException
1. public interface Advice
2. extends java.rmi.Remote {
3.
4. String getAdvice() throws
5. java.rmi.RemoteException;
6.
7. }
创建remote类
1. 实现你的Remote接口
2. 为接口方法写真正的商业逻辑
1. public class AdviceImplementation implements Advice {
2.
3. public String getAdvice() {
4.
5. // monumentally important
6. business logic here
7.
8. }
9.
10. }
(OK,所以代码对于鸡尾酒会有点多,因为在这都是初学者….)
所有东西的关键:The Stub
当你已经有了接口和类,你通过RMI编译器(rmic)运行你的类,RMI编译器已在J2SE中(如果你已经有javac,你就有rmic了)。那个程序建立了stub这个你可以几乎在所有现代分布式编程模型中发现的东西。Stub仅是客户端的助手,通过像服务那样实现远程接口来扮演远程对象的角色。但是事实上,Stub仅是一个取得方法调用,打包,并通过网线传递给真正的远程对象的小对象。或者说, stub知道如何打电话回家并叫真正的服务去做真正的工作。
Stub所有方法都是虚假的。他们是方法,有一大堆代码,包括用于服务联系的网络和I/O内容。但是他们并不是真正的商业代码。所以对于Advice服务, stub有getAdvice()方法,但是这个方法仅仅从客户的请求传递给在服务器上真正的AdviceImplementation。
那边,客户端假装他在调用远程对象,但是我们知道由于远程对象运行于不同的JVM堆中这并不真正发生。
(是的,在服务器端依然有些东西接受来自客户的Socket连接,解包方法调用,打包并装载返回值等等。但是我们并不准备在这篇文章中谈论这些。那是个更微不足道的过程,因为你不需要担心传递功能给客户。所以在服务器上Stub的功能有同伴,但是你并不需要担心这个。)
RMI基本结构
籍由rmi,远程对象是一个服务。
如果你有个客户端,你想通过远程服务(或者说远程对象)做些东西,你必须有远程服务的接口(在编译时和运行时),而且你必须有stub对象(运行时)。记住,是Stub真正知道如何传递你的方法调用给远程对象(并传递回返回值),所以你可不能没有它。很多时候,你将从服务开发者那得到接口和stub .class文件。
但是有个更酷的方式运行时取得stub类而不需要在运行前取得,那就是通过一个叫动态代码下载(Dynamic code downloading)的进程。这是你在java中可以使用的最强大的东西之一,而且也不难。但是,你也可以以守旧的方式从开发者的Email中取得stub类文件,或者从别人那里下载到你的电脑。
查找服务(例如拥有stub,这样你可以调服务中的商业方法)
在RMI中,你通过RMI登记(附在J2SE中)查找到stub。RMI登记就像一本小的电话簿,你找到名字,然后就得到stub。记得当stub对象过来的时候是串行化的。这意味着在其来到客户端时要使其反串行化。同时为了让其工作,the stub类必须在当前的classpath中或者可以用动态代码下载来找到。这是java基本知识---一个实例不能在没有其类时反串行化。
发现查找服务(rmi Registry)
通过RMI,你必须知道the stub在哪里?你必须为RMI Registry知道IP地址和TCP端口号(同时也恰好是远程对象/服务所在的服务器那里)。事实上我们大概并不一定要知道端口号,因为有默认的端口号,但是如果服务部署人员更改了,你则需要知道。客户端只做了简单的单行的查找,利用静态方法(java.rmi.Naming.lookup()),这时奇迹发生了,它有了一个即时的反串行化的stub。对象知道如何打电话回家了。
现在我们已经注意RMI,这部分将会更有意义。Jini就像是Extreme RMI。我们这里用Extreme这个词是指其运动含义,而不是XP中的意思。
动态发现:服务和查找服务如何发现对方
这是普通的RMI与Jini差别最多的地方。在RMI中,客户端必须知道很多东西,服务必须通过RMI Registry清楚地登记(用逻辑名称),注册必须在与服务的同个机器中。
但是通过Jini,所有东西仅是在那儿,在某处。没有人知道除了接口外的其他东西。这里有句话关于这个的,我们发现这放在这个对话中的战略时刻相当有意义。
“通过jini,服务通常尝试去发现查找服务,客户端也通常发现查找服务,而查找服务通常在让服务和客户端知道他们(查找服务)就在这。所有都是自动的。”
虽然在Jini中你也可以将整个服务发送给客户端本身,而不是让服务变成服务器上100%的远程对象,但是基本的Jini结构采用RMI。当客户端要求服务的参考时,将取得远程对象的stub(如果服务被实现为一个远程对象),或者重新得到整个该死的服务,在上面进行简单的旧的本地调用。事实上,通过Jini,客户端甚至可以一个混合体或者“智能代理”(公开说这句话会有特别的声望)。--那样,一个非远程java对象包含一个远程对象的stub(就好像,实例变量)。那样,客户端直接对服务进行本地调用,但是服务本身可能会将其跳转,让远程调用到其他地方。“智能代理”可以通过在客户端做某些工作提高在客户端和服务上的表现。进入讨论组讨论。
Jini架构
Jini和其他所有分布式技术(普通的RMI,EJB和Web services)有另外一个主要不同点是Jini利用分布式租约帮助建立自愈网络。如果Jini服务是远程的,例如,他给客户端一个租约并说:“这是你的租约。如果你不更新它,我将假设你已经离开,我将停止你在这里代表的任何资源。”最新的网络如何保持依赖于这些租约的时长。例如,一个两小时的租约将意味着客户端可能断开将近两个小时,而你(服务)却不知道。多么浪费!另一方面,一个一秒钟的租约意味着最近的网络,但是所有人花费其所有的周期和带宽去更新租约!所以那是笔交易,这依赖于服务的类型以及其他服务质量问题。
自愈型网络另一个很酷的东西是:既然Jini查找服务是一个Jini服务,查找服务给Jini服务一个租约。所以在这种情况下,Jini服务对于Jini查找服务是个客户。那样,当一个普通客户端(或者说,一个本身不是Jini服务的客户端,而是想用Jini服务的程序)来查找一个服务,比如打印机,如果服务没有通过查找服务更新客户端将不视其为“可用”。此外,租约时间越短,就越是最近的网络的写照,越不可能会令到一个客户选择一个不再可用的服务。
Jini暴露
如果你是一个客户端,你想从Jini服务中取得某些东西,你必须有对于这个服务的接口。不想RMI,Jini具有更灵活性,因为你除了接口外不需要知道其他东西。你并不需要知道注册的名字或者Service的位置。只要你知道接口以及你在一个可以发现查找服务的网络,你就可以工作了。
今天,幸运的是开发者给你接口。但是有少量的接口到处漂浮,变成某种标准,包括Bill Venner的名声不好的ServiceUI.
发现服务类
不像RMI,在Jini中你将不可以选择是否使用动态代码下载。你几乎被迫使用它。(你也可以不使用它,但是这违背了Jini的本来目的。)通过Jini,你不需要知道网络中内容的位置,你也不需要知道谁建立这个服务。记着,所有你要的只是接口,而且在很多情况下,你需要担心的你需要去实现服务的接口(也许是个stub,也许是真正的服务,或者集合体…)。
记着,在RMI中,你在客户端中需要的类通常是Stub类,而不是服务本身。在Jini中,如果服务被实现为远程对象,或者是真实的服务,你调用方法的应该在stub中---例如,一个实现了接口并真正工作的类就在客户端本地。进入讨论组讨论。 查找服务
通过Jini,你使用Jini 查找服务(lookup service),而不是RMI Registry。Jini查找服务更灵活和强大,而且它可以像服务对象那样提供信息给你,哪个,是或者不是远程对象stub,这依赖于服务是否是个服务器上的远程对象或者是转载在客户端上的非远程对象。最好的东西是你不需要知道查找服务在哪!在RMI中,你必须知道远程服务的IP地址和端口号来找到服务stub所在的RMI Registry。而在Jini中,你仅知道大概在某处,在那儿的网络上有不少于一个的查找服务,通过IP广播达到。
这就是Jini。但是Jini是个疯狂的边缘,这里没有人知道任何东西,而你就在你自己的模型上,J2EE正好是相反的东西。
J2EE的主要思想就是服务器(通过容器)给你一堆额外的服务,否则这些服务你也许要自己写,或者将不同的部分拼凑起来,或者从私人厂商那购买。我们现在谈论的是巨型的服务,像事务管理,并行管理,安全,资源/生命周期管理,查找服务,持久化,信息服务等等。
那样,你开始专注于你的自己的商业规则,而不是重新去做那些繁琐的重复的事情。其他应用服务器已经存在了一段时间了---如CICS和TUXEDO都是经典的例子。但是在那种环境下你必须学私有的API,你被厂商锁住了。J2EE是企业级商业中间层的标准,所以你仅仅需要学一个API,无需考虑你要选择的服务器。其中最好的就是你可以部署你的应用到任何符合J2EE标准的服务器,所以你可以对厂商的枷锁说拜拜了。
当然,事实上由于各种原因,将J2EE程序部署在不同的服务器上并不容易。但是你可以。而且如果你有足够的钱换掉你现在的提供商,知道这个不更好?更好的是,让厂商知道你可以这样做。
首先,澄清一下J2EE和EJB的不同:J2EE是一个为可运行EJB的服务器设立的规范。但是J2EE服务器同样要支持servlets和jsp。你可以认为J2EE就像一个整合的服务器—将servlet和支持JSP的Web服务器以及EJB服务器整合在一起的服务器。在J2EE领域,我们叫它子服务容器。所以我们在J2EE服务器中拥有Web容器和EJB容器。因为beans比servlets和JSP更吸引人,我们在这里专注于EJB方面。同时在EJB的世界,服务就是bean。
EJB有样很酷的东西就是基于组件的开发模型。你建立可重用组件,这些组件是可以在部署时定制而不需要改动java代码。组件比java类更易用,所以他是下一个层次的重用。你利用XML文件来部署你的组件(例如beans),而这些文件说明服务器应该如何处理bean,所以你可以配置安全,事务,资源使用等等,都声明在XML文件中,你只需要在开发工具中按几个按钮即可。(如果你真的想,你当然可以直接编写XML文件。但是现在的工具十分好,而你并不需要去做那些工作。)进入讨论组讨论。
J2EE架构
J2EE和普通RMI一个很大不同之处就是服务器必须介入客户的调用和服务(例如bean)的被调用中。如果客户端远程调用已开放的bean方法,服务器会介入并让其隐蔽的过多的bean与客户端直接联系。例如,假如客户端扮演特定的顾客调用bean中的getAddress()方法。首先,服务器必须验证客户端是否经授权去调用这个方法,但是假设客户通过了安全检查,现在服务器必须去做其他东西让bean准备好去接受调用。如果bean返回客户的地址,比方bean会重新去数据库记录中取得客户的资料。这需要变成事务的一部分,而其他客户也许准备好使用这个客户,还有….
J2EE的解决方案是让bean成为非远程对象,被远程对象保护。所以即使bean真的是服务(或者说,这里含有客户想调用的商业逻辑服务方法),而bean在RMI方式中并非是远程的。但是客户依然和RMI副本以及远程对象相互作用;就像和EJB相互作用那样,远程对象更像是bean的保镖。远程对象(在J2EE中叫做EJB对象)接受客户端远程调用(例如,客户端在stub上的调用),并同时让服务器加入来决定如何,何时和调用是否到达bean。
J2EE暴露
通过EJB,在RMI规划图(除了服务外)中加入另一层。EJBObject是远程对象,但不像RMI中,EJBObject并不是个服务。所以那样现在这里对于服务有两个帮手,而不像普通的RMI那样子有一个(stub)。你的服务接口依然是远程接口,而不是直接实现java.rmi.Remote,你的服务接口实现EJBObject---一个本身扩展自java.rmi.Remote的接口。尽管bean不含有相同的方法,注意bean如何不扩展服务接口(Advice)。(不要过于拘谨担心,绝大多数注重EJB的开发工具确认bean和服务接口会匹配。)
找寻服务接口
通过J2EE,你通常与客户端开发者同在一个企业工作,则你可发Email给他接口,或者你可以在内部的知识库中公布。无论如何,重要的是,就像RMI和Jini,客户端必须在编译时有此接口。
同样,对stub类 ,你可以选择提前给客户端或者用动态代码下载。但是这种情况下动态代码下载依赖于你特定的J2EE服务器,也许不可行,所以你也许会被困于自行将stub类传递给客户端的情况。大多数J2EE服务器为你部署bean准备了小而精的客户JAR。
找到服务
警告:我们在这节扩展了鸡尾酒会知识的限制,所以这段人们就会说:”哇噢,在你写到这之前我都跟得上你。。。”这不是我的错,是我们的。所以这里如果你想掠过,你可以直接跳入Web服务,别人也不会知&进入讨论组讨论。
(出处:http://www.cncms.com)
更多精彩
赞助商链接