ITPub博客

首页 > Linux操作系统 > Linux操作系统 > SOA 核心技术及应用,第 7 章

SOA 核心技术及应用,第 7 章

原创 Linux操作系统 作者:isoa 时间:2009-05-07 14:40:24 0 删除 编辑

7.1 SCA 与 Spring

在 Java 企业应用 EJB 技术的反面,在 Java 企业应用的开源社区,Spring 可谓如日中天,“今天,你 Spring 了吗”的问候语一度流行。当 SCA 遇到 Spring,是在春天绽放,还是被春风融化,沉醉不知归路?在回答这个问题之前,需要对 Spring 有一个基本的认识。

7.1.1 Spring 的新主张

Rod Johnson 是 Spring 的缔造者。在建立 Spring 之前,Rod 就是世界上顶尖的 Java、J2EE 开发专家之一。在 2003 年 2 月,Rod 出版了极其热卖的一本书《J2EE 设计开发编程指南》(Expert One-on-One J2EE Design and Development),其中的示例代码,形成了一个开源的项目,名字叫 Spring。这是 2003 年春天的事。

Rod 是 JCP 的一员,是 Servlet2.4 和 JDO2.0 规范制定专家组的成员。长期从事咨询行业的经验,使得他能够经常从客户的角度而不是一个技术人员的角度去看待问题,更驱使他在深入研究 J2EE 后,对 J2EE 架构进行批判。

在《J2EE 设计开发编程指南》里,Rod 指出传统的 J2EE 架构过于臃肿,低效率,给关注它的人们带来的优点和益处很有限。

Rod 不是光说不做的人。在指出传统 J2EE 的诸多不变之后,Rod 给出他理想中的 EJB 应该是什么样子,并给出示例的代码。这些代码利用开源迅速成长,形成了现在的 Spring 项目。因此 Spring 就是 Rod 的主张。

Spring 认为传统的 J2EE 是侵入式的,代码之间耦合严重。重耦合带来两个严重的问题:

(1)可重用性差;

(2)不容易实施单元测试。

EJB 被设计为一个中间件,中间件主要目的就是为了被重用。一个可重用性差的中间件,其存在的合理性都有可能受到质疑。

而且,随着软件的规模变得越来越大,不容易实施单元测试的产品生命力就弱。在没有单元测试保障的前提下,几乎没有程序员有能力和胆量往一个大规模的项目里添加新的代码;即使斗胆添加了新的代码,也不能很快知道这些新的代码是否功能正确。新代码是否正确这个问题会留给系统测试人员、甚至用户来回答。等程序员得到相应反馈,太长的时间已经过去,根本没办法随需应变。那么,怎么办?

Spring 认为应该解耦。解耦在软件界是永恒的话题。解耦永远正确,关键是如何解耦。为此 Rod 还找到一个解耦的利器:控制反转模式(Inversion of Control,IoC)或叫依赖注入(Dependency Injection)模式。

什么是控制反转?Martin Fowler 在 2003 年在一篇文章中给出了清晰明了的阐述。Rod Johnson 也给出了容易理解的解释。控制反转的概念经常表述为好莱坞原则:“别给我打电话,我会打给你的”。这是经理人经常说的一句话。演员需要演出机会,导演需要演员。当导演需要演员时可以通过经理人找到演员。利用经理人,导演和演员解除了耦合。就编程而言,控制反转将控制创建组件的职责搬进了框架,并把它从组件代码中脱离开来。比如组件 A 需要组件 B,组件 A 可以告诉框架它需要组件 B,框架直接把创建好的组件 B 返还给组件 A,不用组件 A 去操心如何创建组件 B 的细节。组件 A 和组件 B 的依赖是在运行时注入的,因此 Martin Fowler 认为,“控制反转”这个名字太泛了,常常让人有些迷惑,而依赖注入(Dependency Injection)更合适些。

组件和组件通过依赖注入解耦之后,单元测试会变得容易。一个组件的测试并不依赖于它所依赖的组件的完成。在一个组件所依赖的组件没完工的情况下,可以写 Mock Objects,在测试时动态注入,以完成真正意义上的单元测试。于是测试开发的迭代周期变短,节奏加快;代码的瑕疵迅速被发现;产品质量得到保障。

组件间的解耦还使得代码更容易重用。即使组件 A 依赖于组件 B,在依赖注入的框架下,也只是接口的依赖,组件 A 对组件 B 的实现一无所知;因此组件 B 的实现可以随意替换而组件 A 的代码无须更改。

实现了依赖注入模式之后 Spring 就成了“好莱坞的经理人”。于是她开始到处找“导演”和“演员”。

春风唤醒了冬眠的熊。当 Spring 遇到 Hibernate,Spring 发现,Hibernate 出于不得已,把他的会话工厂(Session Factory)绑到了 JNDI 上。Spring 很轻松地将 Hibernate 从 JNDI 上面解下;并将 Hibernate 的会话处理拿出来共享。于是 Hibernate 这只冬眠的熊开始发威了,不仅在中小企业应用中迅速风靡,而且在 EJB3 推出之际,成功取代 EJB2 的实体 Bean,成为 EJB3 里新一代的 O/R Mapping 组件。

Spring 是让已有的开源技术更加易用,因此除非不得已,其并不直接和其他的开源项目竞争。Spring 的位置是传统 EJB 反叛者技术们的框架。因此这种策略至少在斗争哲学上,是很好的策略。

在 2004 年,Rod Johnson 又写的一本更畅销的书《J2EE without EJB》。封面和上一本书是一样的。

他还成立了一家咨询公司,叫 Interface21。Rod Johnson 把 Spring 的主要目的总结为两条,使 J2EE 易用和促进良好的编程习惯。其中第一条通过依赖注入来实现,而第二条,通过面向接口编程和 Interface21 公司的日常业务来贯彻。当然也可以理解为一条,因为面向接口编程可以由依赖注入很自然就能得到的结果,而 Interface21 也是 Spring 背后的公司。

7.1.2 从 Spring 到 Tuscany

可以说,Spring 是面向接口编程的。

SCA 也可以说是面向接口编程的;但更精确的描述是,SCA 是面向服务编程的。

接口可以用来描述服务提供的内容,但不能描述服务提供的方式。一个服务,必须提供以下两个信息:

(1)服务的内容,有哪些操作,这些操作需要什么参数,有什么返回值,有什么异常等;

(2)服务的提供方式,SCA 服务、Web 服务、SessionBean 还是 JMS 等。

服务的内容可以由接口来描述。在 SCA 里,服务提供的方式用绑定来描述。如果不明确指出服务提供的方式,SCA 默认是 SCA 绑定。因此在 SCA 组件之间是面向服务的调用,只是在使用默认的 SCA 绑定的时候,看起来和面向接口编程一样。从这个层面看,面向接口编程是面向服务编程的最简形式。

Spring 利用依赖注入,提供一个轻量级的框架,整齐划一地使用现有技术,使现有开源 Java 技术更加容易使用。在使用其他技术这一点上,SCA 和 Spring 很类似。在 SCA 的 Java 实现里,Tuscany 也利用了依赖注入,提供了一个架构,使 SCA 可以用统一的方式,也就是 SCA 的方式,来使用其他技术。但 SCA 不仅限于此。

SCA 不仅使其他技术一致而简单地被使用(绑定用在引用里),还提供一致而简单的方式来构建其他技术(绑定用在服务里),以使其他现有的技术来使用 SCA。从这个角度讲,较之 Spring,SCA 是更为温和的技术规范;它的出现不是为了取代什么,更是为了增进和谐。

当然 SCA 是一个规范,其实现之一 Tuscany 就不仅限于 Java,到目前为止,还有相应 C++ 和 PHP 的实现。

SCA 利用其他技术还有另一种方式:实现。其他的技术可以以实现的身份进入 SCA。比如 SCA 遇到 Spring,就将其以实现的身份纳入了 SCA 的体系。

7.1.3 在 SCA 里使用 Spring



Spring 通过实现的方式融入 SCA 的体系。下面的例子还是计算器。这次把计算器用 Spring 实现。以下是 Spring 的应用上下文定义(calculator.xml):


 


实现和接口重用了以前的。于是这个 Spring 上下文就生成了。

把计算器组件修改一下:


	
 	
	
 
 	
	 	
 	
 	
 


其中 location 指向 Spring 应用程序上下文。下面是一个简单的测试:

public class CalculatorTest {
	private SCADomain domain;

	@Before
	public void startSCADomain() {
		domain = SCADomain.newInstance("calculator.composite");
	}
	@Test
	public void testAddService(){
		AddService addService = domain.getService(AddService.class, 
		"CalculatorComponent/AddService");
		assertEquals(2.0, addService.add(1, 1));		
	}
	@After
	public void stopSCADomain() {
		domain.close();
	}
}

由这个例子还可以看出 Spring 里的一些概念是和 SCA 里的概念有对应的:

(1)Spring 里应用程序上下文对应 SCA 里的 Composite;

(2)Spring 里的 Bean 对应 SCA 里的组件;

(3)Spring 里的 Bean 的接口对应 SCA 里的服务;

(4)Spring 里的实现类对应 SCA 里的实现类。

这基本解决了如何在 SCA 里使用 Spring 的问题。那下一个问题是,如何在 Spring 里使用 SCA?

为解决这个问题,SCA 给出 Spring 扩展标记,使在 Spring 里可以显示声明服务()、引用()和属性()。这三个标记定义在:

xmlns:sca="http://www.springframework.org/schema/sca"

其中 用来 Spring 向 SCA 提供服务, 用在 Spring 里引用 SCA 的服务。引用和属性还是在运行时动态注入,其行为和 SCA 里的引用与属性没什么两样,因此不再举例。

7.2 SCA 与 OSGi

在 Java 技术社区的里面,汹涌澎湃着一股暖流。这股暖流企图在 Java 技术的能力范围之内,解决 Java 技术自身的一些问题,以提高 Java 技术的现有能力,扩大 Java 技术的能力范围;之后在 Java 技术新的能力范围之内,再解决新的问题,进一步提高 Java 技术的能力,进一步扩大 Java 技术本身的能力范围;如此往复。这个流派就是 OSGi——利用温和的改良方式推进 Java 技术的革命性变革,并取得了巨大的成就。

OSGi 以前的全名是 Open Services Gateway initiative,现在这个全名已废弃。OSGi 联合会不再为这个看似缩写的名字作出进一步解释。

7.2.1 OSGi 的成功

Java 作为一个面向对象的语言,违反了结构化设计的神圣准则:低耦合、高一致性。面向对象编程忙着在实例变量里隐藏数据对象,为此还写 get 和 set 方法,但忘记了整个的类结构都暴露给了其他的类。比如类 A 和类 B,如果在类 A 里有一行 new B(),那类 A 就整个依赖类 B 了,没有类 B,类 A 就运行不了。类 B 再怎么隐藏自己的数据对象也没用,因为类 B 已经整个地暴露在类 A 面前了。

这种强耦合导致包依赖问题。Java 开源项目的各个库之间,依赖关系非常严重,以至于专门成立了一个叫 Maven 的项目,来解决 Java 的包依赖。下面是一个基于 Tuscany SCA 的类似 HelloWorld 的程序,其定义在 Maven pom.xml 里的依赖关系:

tuscany-host-webapp
tuscany-implementation-java-runtime
tuscany-binding-ws-axis2
tuscany-http-tomcat

只依赖于 4 个包并不算多,但这 4 个包又依赖于另外 64 个包,由于太多,不一一列出。这 64 个包里,一旦哪个包的一个类发生变化,就会导致 Tuscany 不工作。而这 64 个包是由很多不同的团队维护,类变化是很正常的事。这不是 Tuscany 的问题,这是面向对象开发的通病。

Java 发布的时候给出三个发行版:JavaEE、JavaSE 和 JavaME,分别对应企业应用、桌面应用和嵌入式应用。Sun 企图通过提供不同的 Java 版本来满足不同的需要,而这实际上还是违反了神圣原则的第二条,高一致性原则;这个分堆降低了一致性。

OSGi 秉承松耦合髙一致性的原则来解决 Java 的重用问题。OSGi 提供了一个模块框架,这个框架可以管理模块,可以动态加载模块;模块之间通过服务接口协作。模块在 OSGi 里被叫做 Bundle。OSGi 使用微内核 + 系统 Bundle+ 应用 Bundle 设计。微内核保证了髙一致性,基于 Bundle 的设计保证了松耦合。

OSGi 主要做三件事:

(1)Bundle 管理;

(2)Bundle 的动态加载;

(3)保证 Bundle 间使用服务机制来交互。

OSGi 最初是为嵌入式设备设计的。嵌入式设备对模块的重用要求很高。跑在豪华型轿车里的模块可以不加修改地跑在经济型轿车里。这样可以节约很多开发成本。OSGi 用模块框架成功地保证了这一点。BMW 汽车的应用控制系统采用 OSGi 作为其底层架构。

真正使 OSGi 引起大众关注的是 Eclipse。Eclipse 3.0 平台是 OSGi 的一种实现(就像 Emacs 是 lisp 语言的解释执行器一样),每一个 Eclipse 插件,实际上在内部都使用了 OSGi。

Eclipse 的插件体系结构,源自于 Visual Age for Java 的血统,在整个业界非常有名,被认为是非常成功的一种设计。但 Eclipse 在 3.0 版本时,项目组却做了一个重大决定,就是抛弃 Eclipse 自己以前的插件体系架构,转为直接使用 OSGi 作为其插件体系结构。这是为什么呢?

Eclipse 的插件体系结构和 OSGi 的思想非常类似,都强调微核、系统插件、应用插件的概念。Eclipse 的插件体系比 OSGi 在插件或 Bundle 管理上稍差一筹。随着 Eclipse 插件的增多,Eclipse 的启动速度越来越慢。Eclipse 期望 OSGi 能解决它的性能问题。在插件的动态加载上,Eclipse 插件体系也落下风。在 Eclipse 3.0 之前,安装完插件 Eclipse 都要重启插件才能生效。Eclipse 还想得到 OSGi 的插件动态加载的好处。关于插件之间的交互方面,Eclipse 和 OSGi 有不同。Eclipse 的插件使用扩展、扩展点来交互;而 OSGi 使用服务注册、寻找、绑定来交互。这时候 OSGi 的微内核 + 系统 Bundle+ 应用 Bundle 的好处显现了。Eclipse 成功地基于 OSGi 开发出扩展和扩展点的 Bundle;基于 OSGi 自身满足了自己的需求。

Eclipse 采用 OSGi 作为其插件体系结构的成功是很明显的,在 Eclipse 3.1 版本以后可以明显地感觉到启动速度的提升,同时也使得可以在运行时对插件进行管理,更明显地提升使插件的开发更加规范,从而可以使用很多已有的 OSGi 插件。Eclipse 也向 OSGi 社区贡献了自己的 Bundle。

Eclipse 的 Bundle 随即被用到了 IBM WAS6.1 里。一切都是顺理成章的事。IBM WAS6.1 由此基于 OSGi。Eclipse 的成功转型为 WAS 6.1 的转型扫清了主要障碍。

7.2.2 OSGi 与 SCA 的异同

OSGi 关注软件系统内部的模块化。OSGi 认为,软件系统是由一个一个的 Bundle 组成的;这些 Bundle 的重新组合可以形成新的软件系统。模块化是为了 Bundle 的重用。Bundle 的动态加载使运行时替换 Bundle 成为可能。Bundle 间通过服务交互使 Bundle 之间的耦合很松。

SCA 关注服务的使用和实现。通过一致的方式使用服务,也通过一致的方式实现服务。SCA 将服务和服务的实现解耦,并将服务的接口和服务的类型解耦。

“服务”在 OSGi 和 SCA 里都出现但意义不同。OSGi 里的服务是从技术上讲的服务。SCA 里的服务是从业务上讲的服务。技术上的服务和业务上的服务概念一致但粒度不同。OSGi 的服务是细粒度的,而 SCA 的服务是粗粒度的。

因此从理论上讲,SCA 的实现,至少是 Java 实现,应该基于 OSGi。业务随需应变的根基是技术上的可重用。如果 SCA 的实现一旦基于 OSGi,就等于插上了可重用翅膀。

7.2.3 SCA 和 OSGi 的关系

总结起来,OSGi 可以以下列 3 种方式进入 SCA:

1.OSGi 绑定(binding.osgi)

像对待 Web 服务、会话 Bean 一样,将 OSGi 看做和 SCA 对等的技术,实现两种技术的互访问。

2.OSGi 作为 SCA 的一个实现(implementation.osgi)

把 OSGi 作为 SCA 的一个实现,像 Spring、BPEL 进入 SCA 的方式一样。

3.OSGi Host

用 OSGi 来实现 SCA,SCA 作为 OSGi 的一套 Bundle 来运行。

这三种方式中,第三种方式即 OSGi Host 的方式,是较为理想的。作为一个组件编程模型,SCA 的核心部分可以使用 OSGi 实现;扩展部分使用 SCA 自身的组件编程模型实现自实现。SCA 的另一个开源实现,Newton(http://newton.codecauldron.org/),就使用 OSGi 来构建 SCA,采用的是第三种方式。Newton 的 SCA 实现证实了第三种方式的可行性,其实现过程中积累的经验教训也可以为将来的实现者所借鉴。

SCA 和 OSGi 中的服务的粒度并不同。SCA 中的服务粒度较粗,OSGi 中的服务粒度较细。OSGi 更适合软件内部的模块化。将 SCA 与 OSGi 看做对等的技术实现互访问不是很可取。因此第一种方式只是作为一个参考。

第二种方式对 Tuscany 的 SCA 实现而言是比较现实的方式,实现起来也较容易。当下 Tuscany SCA 的 Java 实现使用第二种方式来欢迎 OSGi,即以“implementation.osgi”的方式将 OSGi 引入 SCA。Tuscany 目前采用微内核加可扩展插件的架构,这些可扩展插件是由 SCA 组件组成。从这一点上可以讲 Tuscany 是自实现的。这和 Eclispe 的情形很像。就当前 Tuscany 的实现来讲,其与其他技术耦合过重的迹象已经显现。因此如果 Tuscany 的 SCA Java 实现也会以 Bundle 的方式来融入 OSGi,可能会部分地解决这个问题。

7.3 SCA 与 SDO

就像 Web 服务与 SOAP 的关系一样,SCA 还有一个亲密伙伴——SDO(Service Data Objects,服务数据对象)。作为伙伴,两者互补,相互协作。SCA 关注服务的整合,在服务整合里,数据的整合是关键,这正是 SDO 所关注的。

7.3.1 SDO 简介

SDO 的目的是把开发人员从处理数据的底层技术中解放出来,从而更关注业务逻辑。放在 SOA 的大框架下,SCA 关注服务,不同的服务用一致的方式来使用,并可以用一致的方式来构建;BPEL 关注流程,把各个服务按需要串起来;SDO 关注数据,数据整合在 SDO 之下,这样 SDO 数据就可以像血液一样,在 BPEL 流程里无阻碍地流动。

Tuscany 里有 SDO 的一个 Java 参考实现。如果要使用,可以在 pom.xml 里加入如下依赖:


 org.apache.tuscany.sdo
 tuscany-sdo-impl
 1.0-incubating
 runtime


SDO 推荐使用 XSD 来定义数据对象,比如用 XSD 定义一个订单(Order)如下:



 
 	
 	 
	 
	 
	 
	 
 	
 


这个简单的业务对象描述了一个订单:包括客户需要的产品、数量以及发货地址等。这些描述跟技术无关,而是面向业务的。

使用 XSD 描述数据对象有很多好处。其中之一是可以重用已有的工业标准。在 XML 的应用过程中,工业的各个领域用 XSD 设计了大量的、极其精细的数据描述。这是一笔财富。因此采用 XSD 来描述数据对象很自然地继承了这笔财富。

其次 XSD 图形界面的编辑器并不缺乏。这些编辑器可以继续使用。比如本例使用 Eclipse 里的 XML Schema Editor 看起来是这样的:



借助于编辑器,业务人员设计这些数据对象会更在行,而且要比技术人员设计的更贴近业务和客户的需求。

Tuscany 里 SDO 的使用并不复杂。下面是一个简单的例子,展示如何使用;同时也是一个单元测试,以表明单元测试即使对数据对象的设计来讲,也是必要的。

@Test
public void testOrder(){
	List list = XSDHelper.INSTANCE.define(
	ClassLoader.getSystemResourceAsStream("Order.xsd"), 
	null);
	assertNotNull(list);
	assertEquals(1, list.size());
	DataGraph dataGraph = SDOUtil.createDataGraph();
	assertNotNull(dataGraph);
	DataObject rder = dataGraph.createRootObject("
	 http://samples. hex/Order", "Order");
	assertNotNull(order);
	order.setString("name", "whoami");
}

最后一行的赋值表明了一个技术人员对实际业务知识的缺乏:他甚至不知如何给一个订单一个合适的名字,因此键入了一个 UNIX 命令权且测试。如果一个真正的业务人员,估计会这么写:

order.setString("name", "TP-1248-9987");

数据对象可以序列化为 XML。这又得到了 XML 跨平台、跨语言特性的好处。

更详尽的关于 SDO 的介绍参见第 8 至第 13 章。

7.3.2 SCA 里使用 SDO

SDO 和 SCA 几乎同时出生。之后 SDO 很快地从 IBM 释出,进入公众视野,并形成一个规范,并且 Eclipse 很快在 EMF 里部分实现了 SDO 规范。在 Tuscany 的 SDO 实现也是围绕 EMF 的 SDO 实现展开。SCA 没那么快进入公众视野。SCA 的最初实现在 IBM 内部完成。IBM 确认 SCA 的方案可行之后,才将其公布。

在 SCA 里使用 SDO 并没有什么特别。接口的定义使用 DataObject 作为参数和返回值。

import commonj.sdo.DataObject;

public interface OrderService {
	public DataObject createOrder();
	public void updateOrder(DataObject order);
}

Java 的接口的定义很自然。参数类型和返回值类型选用 commonj.sdo.DataObject 就是了。

在 WSDL 接口的定义里,在定义 WSDL 类型的时候把 XSD 里定义的类型用上就可以了。



 



其他的部分保持不变。这也是 SDO 使用 XSD 作为定义而得来的好处。

7.3.3 数据整合和服务整合的目的

为什么用 SDO 进行数据整合?可以回答是为了 SCA 更好地进行服务的整合。那服务整合的目的又何在?当然不能回答是为了数据整合了。

服务整合的目的是为了流程的整合。真正的商业流程是一系列的、各种类型的服务的调用。业务逻辑也反映在商业流程中。当一系列服务汇入流程的时候,应该尽可能地把技术细节屏蔽掉,不能让技术细节干扰了业务逻辑的编写。

业务和技术分离不能只是一句口号。业务和技术的分离同样需要技术的保障。这个技术的保障就是 SOA 系列技术。可以讲 SOA 技术的目标之一就是保证业务和技术的分离。


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/14780828/viewspace-594430/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2008-07-07

  • 博文量
    251
  • 访问量
    299138