使用 XML 实现 REST 式的 SOA
什么是 SOA?
如果公司有大量应用程序,这些程序供不同部门的承担不同责任的职员使用,那么就适合使用面向服务体系结构(Service Oriented Architecture,SOA)。这些应用程序可以共享功能,但是功能的组合、用户界面细节和易用性需求是不同的。与许多企业体系结构一样,SOA 也采用一个多层模型,但是它不只如此。在服务器中,功能分散在单独的服务上。一个客户机可以使用其中的一个或多个服务,而一个服务也可以由许多客户机使用。由此形成了一个松散耦合的体系结构,这大大提高了现有软件的可重用性。
常用的重型实现
常用缩写词
- API:应用程序编程接口(Application program interface)
- IT:信息技术(Information technology)
- XML:可扩展标记语言(Extensible Markup Language)
SOA 尤其适合大公司,大公司往往有数百个应用程序,应用程序之间缺少良好的集成,所以公司需要清理 IT 基础结构。SOA 是一种已经证明有效的实践,对于大型环境尤其有效。采用 SOA 的公司可以把遗留的应用程序转换为服务,并把服务集成为现代应用程序的后端。可以使用中间件技术对服务进行组合,并对服务中的特定功能进行访问控制。因为在大型环境中对 SOA 的需求最为强烈,所以中间件技术的厂商通常把产品的重点放在大型和重型解决方案上。
SOA 和轻量型技术
SOA 背后的思想对于小公司同样是有价值的。重型解决方案的设置成本和所需的人员技能可能使小公司不敢尝试 SOA — 但这是不应该的。暂且不要考虑重型实现,我们先来考察一下 SOA 的基本概念:
- 从现有的或新的应用程序中提取出服务
- 把服务集中起来,供许多客户机使用
并没有什么因素妨碍我们用轻量型技术实现这些思想。您可以先建立一个小型 SOA 并逐渐扩展它。如果您的公司以后发展成大型跨国公司,随时可以迁移到重型技术。
什么是 REST?
SOA 通常是用 SOAP 协议实现的,服务由一个 WSDL(Web Services Description Language, Web 服务描述语言)文档来描述。尽管有许多开发工具大大简化了对 SOAP 和 WSDL 的处理过程,但是我仍然把它们看作重型技术,因为如果不使用这些工具,SOAP 和 WSDL 是很难处理的。
也可以通过超文本传输协议(HTTP)发送简单的消息来实现 SOA。这基本上就是 REST 式 Web 服务 (RESTful Web services) 的工作方式。Representational State Transfer(简称 REST,中文翻译“具象状态传输”。REST 这个名称是由 Roy Fielding 首创的)并不是一个协议或技术;它是一种体系结构风格。REST 是 SOAP 的轻量型替代品,它是面向资源的,而不是面向操作的。它常常被归结为远程过程使用 HTTP 调用 GET
、POST
、PUT
和 DELETE
语句。我认为,这只是第二个重要的步骤。
第一个(也是最重要的)步骤是把所有资源建模为 URL 形式。URL 容易记忆,同时能够访问无数 Web 页面。至少,如果建模方式适当的话,很容易记住 URL(比如 http://www.ibm.com/developerworks/xml/)。如果过分重视 GET
、POST
、PUT
和 DELETE
,就可能产生不容易记忆的 URL,比如 http://www.longfakeurl.com/pol_srdm/70612/9,3993.32?id=78688&lang=cz&st=idx。
在实践中,使用 HTTP 的方法可以进一步限制为 GET
和 POST
两种方法,因为大多数浏览器对它们的支持很完善。可以对 http://domain.com/myresources/new 执行 POST
,以替代对 http://domain.com/myresources 执行 PUT
;对 http://domain.com/myresources/oldresource/delete 执行 POST
,以替代对 http://domain.com/myresources/oldresource 执行DELETE
。
REST 式的设计过程
在设计 REST 式 Web 服务时,可以采用以下四个步骤:
- 决定资源及其描述性 URL。
- 为每个 URL 上的通信选择一种数据格式。
- 指定每个资源上的方法。
- 指定返回的数据和状态码。
以下是具体的设计过程。假设您是一家航空公司的开发人员。公司有用于预订航班的软件,还有处理付款(现金和信用卡)的组件。它使用软件跟踪包裹、执行内部资源规划和执行许多其他任务。
假设机场登记处的职员使用一个客户机应用程序,这个程序访问包裹跟踪服务,还使用一个服务为乘客分配座位。处理包裹的地勤人员只需要包裹跟踪服务,不需要其他服务。他们的客户机只允许他们确认已经登记的包裹是否到达了。不允许他们登记新的包裹。
在这个示例中,我们将设计包裹跟踪服务。首先,决定资源:旅行者、航班和包裹(注意,在出现 {id}
的任何地方,都可以填写任意数字):
http://luggagetracking.airlinecompany.com/bags/{id} http://luggagetracking.airlinecompany.com/flights/{id} http://luggagetracking.airlinecompany.com/travellers/{id} |
为每个资源选择一种数据格式:
包裹:
<bag id="{id}"> <traveller id="{traveller-id}"/> <flight id="{flight-id}" /> <status>{current-status: departure/plane/arrival}</status> </bag> |
航班:
<flight id="{id}"> <travellers> <traveller id="{traveller-id-0}" /> <traveller id="{traveller-id-1}" /> <traveller id="{traveller-id-2}" /> </travellers> <bags> <bag id="{bag-id-0}" /> <bag id="{bag-id-1}" /> <bag id="{bag-id-2}" /> </bags> </flight> |
乘客:
<traveller id="{id}"> <flight id="{flight-id}" /> <bags> <bag id="{bag-id-0}" /> <bag id="{bag-id-1}" /> <bag id="{bag-id-2}" /> </bags> </traveller> |
显然,这个模型过于简单了。对于当前的示例,只需要支持两个方法,因此这个模型已经足够了。登记处应该能够为乘客登记新包裹。在把包裹装进飞机时,地勤人员应该能够修改包裹的状态:
- 对 http://luggagetrackingairlinecompany.com/travellers/{id}/newbag 执行
POST
,返回一个<bag>
XML 结构。 - 对 http://luggagetracking.airlinecompany.com/bags/{id}/status/{newstatus} 执行
POST
,返回修改后的 XML 结构。
使用标准的 HTTP 状态作为状态码。成功的操作都会返回 200。如果系统无法根据资源的 ID 找到它,就会返回 404。系统故障导致的任何错误都会返回 500。
代码示例:URL 映射
可以使用多种方式把 URL 映射到实现方法。比较先进的方法可能更灵活,应该用在比较大的应用程序中。这个小示例使用最简单的方法:正则表达式。下面是 BagServlet 上的 post 方法示例,它把 URL 参数传递给底层 servlet。可以在本文的下载文件中找到完整的 servlet 代码。注意,这里没有实现实际的底层服务。 以下是该示例:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Pattern pattern = Pattern.compile("^/?.*?/bags/(.*)/status/(.*)$"); Matcher matcher = pattern.matcher(request.getRequestURI()); if(matcher.matches()) { String bagId = matcher.group(1); String newStatus = matcher.group(2); bagService.changeBagStatus(bagId, newStatus); } } |
在调用这个 URL 时,如果成功,就会隐式地返回状态码 200。更有意义的是,代码返回 XML 结构。这个示例使用 XStream API 把 Java™ 对象转换成 XML 结构。这个 API 需要的配置非常少,而且主要根据类中的字段名选择元素名。
这个示例代码使用下面这些简单的类:
航班:
package eu.adraandejonge.restfulsoa; public class Flight { String id; public Flight(String id) { super(); this.id = id; } } |
乘客:
package eu.adraandejonge.restfulsoa; public class Traveller { private String id; public Traveller(String id) { super(); this.id = id; } } |
包裹:
package eu.adraandejonge.restfulsoa; public class Bag { private String id; private Flight flight; private Traveller traveller; private String status; public Bag(String id, Flight flight, Traveller traveller, String status) { super(); this.id = id; this.flight = flight; this.traveller = traveller; this.status = status; } } |
假设底层的 BagService 返回一个包裹,包裹的航班 ID 是 1,乘客 ID 是 1,状态是 new。请考虑下面的 GET
实现:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Pattern pattern = Pattern.compile("^/?.*?/bags/(.*)$"); Matcher matcher = pattern.matcher(request.getRequestURI()); if (matcher.matches()) { String bagId = matcher.group(1); Bag bag = bagService.retrieveBag(bagId); XStream xstream = new XStream(); xstream.alias("bag", Bag.class); xstream.alias("traveller", Traveller.class); xstream.alias("flight", Flight.class); xstream.useAttributeFor(Bag.class, "id"); xstream.useAttributeFor(Traveller.class, "id"); xstream.useAttributeFor(Flight.class, "id"); String xml = xstream.toXML(bag); response.getWriter().write(xml); } } |
在查询这个 URL 时,它会返回以下信息:
<bag id="1"> <flight id="1"/> <traveller id="1"/> <status>new</status> </bag> |
还能做什么?
我选择这些示例代码是为了说明,不需要很多底层通信,URL 也能够实现很多功能。对于其他服务,可能需要处理上传给 REST 服务的 XML 结构。XStream 也可以帮助完成这个任务。例如,要想对包裹的 XML 结构进行去序列化,应该调用:
Bag bag = (Bag) xstream.fromXML(xml); |
客户机上的应用程序
到目前为止,本文已经讨论了服务器端的实现。客户端上的代码非常相似。客户机可以共享数据类 Flight
、Traveller
和 Bag
,并使用 XStream API 对 XML 进行序列化和去序列化。客户机上惟一的新部分是连接 URL 并读取内容或发送内容。通过使用 Java 类库提供的 URL 连接,很容易完成这个任务:
String xml = "<newinput>input</newinput>"; URL url = new URL("http://luggagetracking.airlinecompany.com/bags/1/newmethod"); URLConnection connection = url.openConnection(); // set POST connection.setDoOutput(true); Writer output = new OutputStreamWriter(connectiongetOutputStream()); output.write(xml); output.close(); // display result BufferedReader input = new BufferedReader( new InputStreamReader(connection.getInputStream())); String decodedString; while ((decodedString = input.readLine()) != null) { System.out.println(decodedString); } input.close(); |
与 Ruby on Rails 等技术的互操作性
尽管 REST 并没有明确的规范来规定如何实现它,但是对 REST 的开箱即用支持越来越多了。因此,虽然没有需要遵循的标准,但是您需要遵守一些约定。例如,Ruby on Rails 提供 ActiveResource。如果遵守 Rails 对 URL 和输出格式的约定,就很容易用最小的开销把 Rails Web 客户机连接到 Java REST 式 Web 服务。
可伸缩性和向重型 SOA 的迁移
随着应用程序环境的增长,很可能会对越来越多的 REST 实现细节进行抽象。当增长和抽象发展到一定程度之后,从轻量型技术迁移到重型的 SOA 技术可能会节省成本。这需要把服务背后的实际业务逻辑提取出来,并重新包装在新环境中的一个 SOAP 包中,这个过程应该不是太难。
寻找应用 REST 式 SOA 的机会
航空公司只是本文使用的一个示例。实际的航空公司规模都比较大,它们应该直接使用重型技术。如果您为小公司工作,可能需要发挥想像力,寻找到在实践中应用 SOA 和 REST 原则的最佳方式。花些时间考虑这个问题,这会带来长远的回报!
使用 XML 实现 REST 式的 SOA的更多相关文章
- java JAXB + STAX(是一种针对XML的流式拉分析API)读取xml
JDK1.5需要添加jar包,1.6以后就不需要了<dependency> <groupId>stax</groupId> <artifactId>st ...
- 【Java】Java XML 技术专题
XML 基础教程 XML 和 Java 技术 Java XML文档模型 JAXP(Java API for XML Parsing) StAX(Streaming API for XML) XJ(XM ...
- SOA (面向服务的架构)
面向服务的体系结构,是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和契约联系起来.接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台.操作系统和编程语 ...
- 4、C#进阶:MD5加密、进程、线程、GDI+、XML、委托
MD5加密 将字符串进行加密,无法解密.网上的解密方式也都是在库里找,找不到也没有. 1 protected void Page_Load(object sender, EventArgs e) 2 ...
- Linq世界走一走(LINQ TO XML)
前言:Linq to xml是一种使用XML的新方法.从本质上来说,它采用了多种当前使用的XML处理技术,如DOM和XPath,并直接在.NET Framework内将它们组合为一个单一的编程接口.L ...
- 进程、线程、GDI+、XML、委托
进制 表示某一位置上的数运算时是逢X进一位.二进制就是逢二进一, 十进制是逢十进一,十六进制是逢十六进一,以此类推. so:二进制001010101只有0和1计算机中的数据都是二进制表示,四进制以0. ...
- 浅析深究什么是SOA?(转)
http://blog.vsharing.com/fengjicheng/A1059842.html 阅读提示: 本文探讨SOA概念背后的核心内涵,如何将SOA落地的实务方法. 金蝶中间件作为全球领先 ...
- 浅析深究什么是SOA?
浅析深究什么是SOA? http://blog.vsharing.com/fengjicheng/A1059842.html 金蝶中间件有限公司总经理 奉继承 博士 阅读提示: 本文探讨SOA概念背后 ...
- 笔记:Jersey REST 传输格式-XML
XML类型是使用最广泛的数据类型,Jersey 对XML类型的数据处理,支持Java领域的两大标准,即JAXP(Java API for XML Processing,JSR-206)和JAXB(Ja ...
随机推荐
- Xamarin.Android MVP模式
一.简介 随着UI创建技术的功能日益增强,UI层也履行着越来越多的职责.为了更好地细分视图(View)与模型(Model)的功能,让View专注于处理数 据的可视化以及与用户的交互,同时让Model只 ...
- JavaPersistenceWithHibernate第二版笔记-第五章-Mapping value types-004嵌套组件的注解AttributeOverrides
一.数据库 二.代码 1. package org.jpwh.model.advanced; import javax.persistence.AttributeOverride; import ja ...
- 关于java中split的使用
之前在http://shukuiyan.iteye.com/blog/507915文中已经叙述过这个问题,但是最近一次笔试中居然有碰到了这个知识点,而且还做错了,囧!学艺不精啊.题目大概是这样的: ) ...
- Struts2笔记——result结果类型
result > 每个 action方法都将返回一个 String 类型的值,Struts 将根据这个值来决定响应什么结果. > 每个 Action声明都必须包含有数量足够多的 resul ...
- Echarts - js-20160611
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...
- Linux平台Cpu使用率的计算
proc文件系统 /proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间.它以文件系统的方式为内核与进程提供通信的接口.用户和应用程序可以通过/proc得到系统的信息,并可以改变内核的 ...
- 着色器(Shader)
着色器(Shader) 顶点着色器(Vertex shader) 片段着色器(Fragment shader) 几何着色器(Geometry Shader) 提供通用计算能力的着色器(Compute ...
- installation and configuration of OpenCV4Android SDK
http://docs.opencv.org/doc/tutorials/introduction/android_binary_package/O4A_SDK.html#running-opencv ...
- USACO Section 3.1: Contact
算法简单,写起来遇到些小问题 /* ID: yingzho1 LANG: C++ TASK: contact */ #include <iostream> #include <fst ...
- YTU 2618: B 求类中数据成员的最大值-类模板
2618: B 求类中数据成员的最大值-类模板 时间限制: 1 Sec 内存限制: 128 MB 提交: 430 解决: 300 题目描述 声明一个类模板,类模板中有三个相同类型的数据成员,有一函 ...