转载自:http://fengshen-xia.iteye.com/blog/293745
翻译自<<Service Oriented Architecture with Java>>(使用Java开发面向服务的架构)一书之第二章
本章我们将详细讲述SOA的实现,并开始我们的Web服务实践之旅。在本章中,我们将会看到,为什么在企业交互的环境中,XML是消息交换的正确选择。接下来我们开始定义一个示例的Web服务,采用自顶向下的方法来开发我们第一个Web服务。然后我们再采用颇为通用的两种传输协议改进我们的程序。
通过对Web服务两种风格(RPC和Document)的比较,我们可以看到采用Document风格的Web服务更加完善,并被广泛使用。在本章的最后,我们将简单介绍一下最流行的几种Web服务实现框架。
SOA的实施方法
实施SOA的第一步其实很简单,即:确定您的应用所包含的业务功能。下面我们就仔细分析这句话的含义:
(1) 确定:就是要找到软件组成的独立模块,这些模块是自包含的,并且从功能上说,它们是不可分割的(具有功能上的原子性)。这就要求我们在设计时,要把您的问题域按照软件调用规范切分成良好定义的模块,同时也要确定这些模块的边界。对业务模块调用者而言,他们要按照契约式软件规范来正确调用这些模块。在软件设计时,请牢记一条,在多个环境(项目)中都能使用的一个模块就是软件开发中的金块。从某种意义上说,确定出软件中的业务功能,就是一种更高层次的抽象,它将我们以前对“接口”的抽象提高到业务层面上。
(2) 业务功能:这个词是指在SOA实践中,我们将专注于业务层(MVC概念中的M),而不是表示层和控制层(MVC模式中的V和C)。我们在这里只讨论服务,而良好设计的服务应该和表示层相互独立,服务对表现它们的表示层一无所知。
(3) 应用所包含的:应用程序可能包含许多软件层,但我们这里的应用程序强调的是多个而非仅仅一个应用程序。这是SOA实施方法的一个“大跃进”,它超越了我们现在正在进行的项目,我们只需做少量的工作,就可以让SOA中的业务组件超越单一项目,它们可以非常容易地使用在以后的应用程序中。
现在,假定我们按照上面的设计方法设计出了我们所需的服务,下一步我们该做什么呢,我们又如何实现它们呢?
我们不妨先举一个服务的例子,这个例子将返回所有的客户列表,它不需要输入参数,并返回一串对象列表。
服务的消费者(例如用户界面)如何能调用到该服务,并得到它所请求的对象列表呢?这可以通过多种方式实现,下面是其中最为流行的几种:
(1) 使用本地调用:就Java而言,本地调用可以通过RMI(远程方法调用)、Sockets、Servlets或JMS来实现;
(2) 使用分布式对象交换中间层:例如服务消费者可使用CORBA和DCOM来调用服务;
(3) 使用基于文本的交换协议来调用服务:服务消费者可以发送基于文本流的请求,然后可获得包含数据的应答文本。这是Web服务实现的基础。
第一种方法非常直接,当它有一些缺陷,它必须依赖于一种语言,服务及其消费者都必须使用同一种语言,比如Java、.Net等,并且,服务及其消费者交换对象的版本还必须相同;否则,对象传输就会失败。
分布式对象传输在相当长的时间内非常成功,尤其是CORBA的跨平台特性为后来的可互操作性提供了奠定了良好的基础。
第三种基于文本交换的实现方法表明,在客户端和服务器端都需要进行序列化和反序列化操作。但客户端发送请求时,需要将对象序列化成文本格式,然后发送;但服务器收到请求后,它需要将文本经过序列化转换成对象。同样的过程也发送在服务应答的流程中。序列化和反序列化貌似增加了对象交换的复杂性,其实不然,请想想这样做的优势:软件能完全独立于技术,实现了软件之间的松散耦合。
将数据嵌入到文本中最自然的方法莫过于通过XML来实现。
XML的优点和不足
XML语言由W3C于1998年为了数据交换的目的而设计的。对着时间的推移,该语言的魅力也开始显现。XML的主要优点有以下四个方面:
具有结构化特性
是可移动的
具可扩展性
具文本格式
XML的不足
以树型结构组织的XML语言优势可能有一些缺点。例如,XML在表示共享的对象应用方面有些不足,所以人们通常争论,XML是否是表示一个任意对象的最佳选择。想想我们前面举的例子,假设您在伦敦有许多客户,使用XML表示这些客户列表时,就会产生数据冗余。这是因为在客户这个XML实体中,其城市属性值都相同。这是人们所不能接受的。其实,这个例子恰恰反证了人们对于XML的误用。在上例中,城市这个属性应该算作一个实体而非属性。这个问题的更好的解决方法是,向关系数据库学习,把重复的数据搬移到主要对象之外,在主要对象中只应用这些重复对象的ID号即可。
客户端可以使用有状态方法(Stateful Approach),首先得到城市这个实体的列表。当它调用getAllCustomers服务时,服务器返回的客户列表的城市属性只需ID即可,而不需完整的城市名,返回的示例客户列表如清单1所示:
程序清单1—有状态方法(Stateful Approach)
<Customers>
<customer>
<id>4</id>
<name>Smith Ltd</name>
<location>
<address>39, Kensington Rd.</address>
<city>LND</city>
</location>
</customer>
<customer>
<id>7</id>
<name> Merkx & Co.</name>
<location>
<address>39, Venice Blvd.</address>
<city>LAX</city>
</location>
</customer> <id>7</id>
...
</Customers>
而且,我们还可以采用无状态方法(Stateless Approach),实现自包含的服务,即在我们的XML应答中,嵌入所需要的所有数据对象。示例代码如清单2所示:
程序清单2—无状态方法(Stateless Approach)
<Entireresponse>
<cities>
<city>
<id>LND</id>
<name>London</name>
<country>UK</country>
</city>
<city>
<id>LAX</id>
<name>Los Angeles</name>
<country>USA</country>
</city>
</cities>
<customers>
<customer>
<id>4</id>
...
<location>
...
<city>LND</city>
</location>
</customer>
<customer>
<id>7</id>
...
<location>
...
<city>LAX</city>
</location>
</customer>
...
</customers>
</Entireresponse>