.NET/Java 互操作性:使用服务接口和 DTO 架构模式






4.73/5 (8投票s)
本文将向您展示如何使用服务接口和数据传输对象 (DTO) 架构模式,在 .NET 和 Java 之间构建一个简单的跨平台互操作解决方案
引言
运行在 Java/Linux 平台和 .NET/Windows 上的自定义开发分布式系统之间的互操作性至今仍是一个挑战。基于 RPC 的解决方案,如“COM-CORBA 桥接”,可以解决这个问题,但会引入脆弱耦合的重大缺陷。一些基于 Web 服务、数据库集成和远程桥接的专有和开源解决方案也试图解决这个问题。然而,这些解决方案通常包含深层且(在某些情况下是昂贵的)软件堆栈,引入了偶然的复杂性、大量资源占用、陡峭的学习曲线和性能下降。
在本文中,我将提出一个跨平台互操作的架构解决方案。该解决方案在架构上重要的方面是显露、定义和管理跨两个平台的数据结构和交换。该架构基于服务接口和数据传输对象 (DTO) 模式。
参考实现演示了如何使用此架构来集成两个业务系统:“履约中心”运行在 Java/Linux 层,“CustomerOrderProcessing
”运行在 .NET/Windows 上。
背景
服务接口
服务接口实现了消费者和提供者之间的契约。该契约允许它们即使在不同系统上也能交换信息。服务接口负责执行此通信所需的所有实现细节。
数据传输对象
数据传输对象是一种分发模式,您可以在其中创建可序列化对象以在两个系统之间传输状态。DTO 没有行为,其序列化/反序列化由汇编器对象处理。
XSD
XML Schema 或 XSD 提供构造,以平台/语言无关的格式表达数据类型。例如,定义客户地址信息的 XSD 数据类型将如下所示
<xs:complexType name="Address">
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="zip" type="xs:integer" />
<xs:element name="city" type="xs:string" />
<xs:element name="country" type="xs:string" />
</xs:sequence>
</xs:complexType>
XXsd2Code
XXsd2Code 是一个开源代码生成器,可从 XSD 生成可序列化的 Java、C#、C++ 和 C++/CLI 类。
问题
“履约中心”应用程序是仓库管理系统的一部分,用 Java 编写并运行在 RHEL 上。“CustomerOrderProcessing
”是 POS 系统的一部分,用 .NET 编写并运行在 Microsoft Windows 2008 服务器上。
用例
CustomerOrderProcessing
系统处理的任何新订单都需要发送到“履约中心”。一旦履约系统发货,它就会反过来通知“CustomerOrderProcessing
”。
解决方案大纲
- 基于 DTO 模式,创建一个表示两个系统之间数据交换结构的模式。这就是接口契约。
- 将此契约建模为 XSD。
- 使用
XXsd2Code
在 C# 和 Java 中生成平台特定契约代码。 - 使用 TCP/IP 作为传输方式为“履约中心”应用程序创建服务接口。
- 为“
CustomerOrderProcessing
”创建一个连接到此服务接口的服务网关。
结构(类图) design.pdf
行为(序列图) design.pdf
解决方案实现
服务合同
- 定义契约
使用任何 XSD 编辑器,创建以下数据契约。我使用 Microsoft VS 2005 创建了这个 XSD。
- 代码生成 C# 类
假设 XXsd2Code.exe 与 .xsd 文件位于同一文件夹中,执行以下命令
XXsd2Code.exe .\/.\/C#
这将为 xsd 契约生成 C# 绑定代码
namespace CustomerOrderAndFulfillment { { namespace Contract [Serializable] public class FulfillmentRecord : ICloneable { public string FulfillmentRecordID; public bool IsBackOrder; public FuzzyCondition StorageTemperature; ... } [Serializable] public class CustomerOrder : ICloneable { public string OrderNumber; public CreditRating Rating; public Address AddressInfo; public List<OrderItem> Orders; public CreditCardDetails CcInfo; ... } }
- 代码生成 Java 类
执行以下命令:
XXsd2Code.exe .\/.\/Java
这将为 xsd 契约生成 Java 绑定代码
package CustomerOrderAndFulfillment.Contract; public class FulfillmentRecord implements Cloneable { public String FulfillmentRecordID; public boolean IsBackOrder; public FuzzyCondition StorageTemperature; ... } package CustomerOrderAndFulfillment.Contract; public class CustomerOrder implements Cloneable { public String OrderNumber; public CreditRating Rating; public Address AddressInfo; public java.util.List<OrderItem> Orders; public CreditCardDetails CcInfo; ... }
传输
- .NET TCP/IP 客户端
使用 TcpClient 和 NetworkStream 编写一个简单的 TCP/IP 客户端
TcpClient client = new TcpClient(server,port); NetworkStream stream = client.GetStream(); //Send Message Byte[] data = System.Text.Encoding.UTF8.GetBytes(message); stream.Write(data, 0, data.Length) //Receive response – blocking call byte[] buffer = new byte[1024]; stream.Read(buffer, 0, buffer.Length); return buffer;
- Java TCP/IP 服务器
使用 ServerSocket、BufferedReader 和 DataOutputStream 在端口 1111 上编写一个简单的 TCP/IP 守护程序。
ServerSocket welcomeSocket = new ServerSocket(1111); Socket connectionSocket = welcomeSocket.accept(); BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream())); DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream()); //get request message String request = inFromClient.readLine(); //Send response outToClient.writeBytes(response);
数据传输对象
- 编写一个汇编器类,使用 XMLSerializer 序列化和反序列化 .NET 服务接口契约类。
Serialize
方法将CustomerOrder
对象转换为字节流,以便将其发送到FulfillmentCenter
服务器。static string SerializeCustomerOrder(CustomerOrder order) { XmlSerializer serializer = new XmlSerializer(typeof(CustomerOrder)); using (MemoryStream memoryStream = new MemoryStream()) { serializer.Serialize(memoryStream, order); return Encoding.UTF8.GetString(memoryStream.ToArray()); } }
DeSerialize
将来自FulfillmentCenter
服务器的响应转换为强类型FulfillmentRecord
对象static FulfillmentRecord DeSerializeFulfillmentRecord(byte[] stream) { XmlSerializer serializer = new XmlSerializer(typeof(FulfillmentRecord)); using (MemoryStream memoryStream = new MemoryStream(stream)) { return serializer.Deserialize(memoryStream) as FulfillmentRecord; } }
- 编写一个汇编器类,使用 xstream 序列化和反序列化 Java 服务接口契约类。
DeSerialize
将来自CustomerOrderProcessing
的请求转换为强类型CustomerOrder
对象。static CustomerOrder DeSerializeCustomerOrder(String request) { XStream serializer = new XStream(); RegisterXstreamConverters(serializer); return (CustomerOrder) serializer.fromXML(request); }
Serialize
将FulfillmentRecord
对象转换为字节流,以便将其发送回CustomerOrderProcessing
系统。static String SerializeFulfillmentRecord(FulfillmentRecord fulfillmentRecord) { XStream serializer = new XStream(); RegisterXstreamConverters(serializer); return serializer.toXML(fulfillmentRecord); }
XMLSerializer 和 xstream XML 兼容性。
为确保 xstream 生成与 XMLSerializer 兼容的 XML,请使用以下代码
xs.aliasType("CustomerOrder", CustomerOrder.class); xs.aliasType("OrderItem", OrderItem.class); xs.aliasType("FulfillmentRecord", FulfillmentRecord.class); xs.aliasType("Address", Address.class);
XStream 枚举序列化
为了强制 xstream
正确序列化 enum
,编写一个 Enum
转换器
public class EnumSingleValueConverter< T extends Enum<T> > extends
EnumConverter implements SingleValueConverter
{
private Class<T> enumType;
public static <V extends Enum<V>> SingleValueConverter create(Class<V>
enumClass) { return new EnumSingleValueConverter<V>(enumClass); }
private EnumSingleValueConverter(Class<T> newEnumType)
{ this.enumType = newEnumType; }
public boolean canConvert(Class type) { return type == enumType; }
public Object fromString(String str) {
Object r = null;
Method m = enumType.getMethod("fromValue", String.class);
r = m.invoke(null, str);
}
public String toString(Object obj) { return obj.toString(); }
}
使用参考实现
下载并解压参考实现,然后按照以下步骤操作
- 运行 GenerateCrossPlatformDataBindings.bat。
- 打开 .\CustomerOrderProcessing-DotNetClient\CustomerOrderProcessing.sln 并编译它。
- 使用 Eclipse 或 Netbeans 打开 .\FulfillmentCenterSimple-JavaTcpIpServer。
- 导出可运行的 jar 文件并将其命名为 FulfillmentCenterApplication.jar。
- 使用此命令运行 jar 文件
java -jar FulfillmentCenterApplication.jar
- 现在使用此命令运行
CustomerOrderProcessing
CustomerOrderProcessing.exe 127.0.0.1 1111
注意:如果您修改了 xsd(接口契约),请确保再次运行 GenerateCrossPlatformDataBindings.bat。
集成测试
我使用 Eclipse Ganymede 创建了一个可运行的 jar 文件。为了测试我的“履约中心”服务器应用程序,我使用在 Windows 7 主机上作为 VirtualBox
客户机运行的 Ubuntu 10.10。确保为 VM 添加一个桥接网络适配器,以便 Windows .NET 应用程序可以访问在 Ubuntu 客户机上运行的 Java 服务器。现在运行 Java/Linux “履约中心”服务器应用程序。
现在运行“CustomerOrderProcessing
”.NET 应用程序。确保您指向 Linux 虚拟机的 IPAddress
。
结论
本文描述的架构解决方案使您能够
历史
- 2011年2月7日:首次发布