Java EE 应用程序之间的 Web 服务通信

Posted

技术标签:

【中文标题】Java EE 应用程序之间的 Web 服务通信【英文标题】:Webservice communication between Java EE applications 【发布时间】:2013-03-27 15:42:33 【问题描述】:

我在两个独立的应用服务器上有两个 Java EE 应用程序。其中一个已经包含一个工作的 EJB。我希望能够通过使用 JAX-WS Web 服务从其他应用程序与这个 EJB 进行通信(通信必须在不同的应用程序服务器和不同的服务器版本之间工作,所以远程 EJB 调用是没有选择的)。将服务器 api 暴露给客户端应用程序是没有问题的。 服务端很清楚,添加@Webservice注解似乎效果很好。但我想知道构建客户端的最佳方法是什么:我真的不想从 wsdl 生成客户端存根(在我的例子中,它本身是由容器从 ejb 代码生成的)并打包所有这些生成的类进入客户的耳朵 - 但这似乎是我可以使用 @WebServiceRef 注释的唯一方法。

规范不推荐使用 javax.xml.ws.Service 的静态方法(例如 service=Service.create() 和 service.getPort())自己制作动态代理的替代方法和“容器提供者不需要支持使用这些方法创建的托管服务实例”。 但这正是某事。我想使用的:

有没有办法在我的代码中注入动态代理,由应用服务器管理?或者是使用生成的客户端存根类完成托管 Web 服务客户端实例的唯一方法?

【问题讨论】:

如果您尝试与两个 EAR 中的两个 EJB 通信,通过将 bean 公开为 MessageDriven bean 来使用 JMS 提供程序进行通信不是更好吗?它将为您节省很多必须设置 Web 服务的麻烦,并且有效负载也会更少。 为什么几乎每个人都讨厌拥有 WSDL 文档,但仍然需要 SEI 接口和其他类? 【参考方案1】:

阅读 JAX-WS 2.2 规范,第 4 章:客户端 API。

1.静态客户端生成

确实是使用 JAX-WS 最简单的方法。从 Web 服务的角度来看,WSDL 是接口和连接属性。即使您选择不实际使用它,您仍然需要从逻辑意义上了解它才能进行有意义的 SOAP 调用。

来自 JAX-WS 规范的注释:使用 SOAP 1.1/HTTP 绑定的端点必须 在以?WSDL?wsdl 为后缀的发布地址上以WSDL 1.1 文档的形式提供其合约

2。动态客户端编程

有没有办法在我的代码中注入动态代理,由应用服务器管理?

此方法涉及针对 JAX-WS API 的动态编程,以使用或不使用 WSDL 连接到 Web 服务。没有办法随便“注入”一个动态代理。您需要使用 SEI 的端口 URL 构建和配置一个。 WSDL 文档是存储此类配置信息的标准位置,尽管可以避免它并以编程方式插入信息。

2A) 使用 WSDL 进行动态编程:

 javax.xml.ws.Service service = Service.create(
     new URL("http://example.org/stocks.wsdl"),
     new QName("http://example.org/stocks", "StockQuoteService"));
 com.example.StockQuoteProvider proxy = service.getPort(portName,
 com.example.StockQuoteProvider.class)
 javax.xml.ws.BindingProvider bp = (javax.xml.ws.BindingProvider)proxy;
 Map<String,Object> context = bp.getRequestContext();
 context.setProperty("javax.xml.ws.session.maintain", Boolean.TRUE);
 proxy.getLastTradePrice("ACME");

优于 (1): 可以在应用部署后动态动态更改 WSDL 文档,提供此类更改不会影响到客户端的 java 接口。

即对您几乎没有好处。您的 WSDL 是静态的。虽然您可以将您的客户端指向&lt;service endpoint URL&gt;?wsdl 以动态查找,但这意味着您需要手动配置&lt;service endpoint URL&gt; 并且在不影响您的客户端逻辑的情况下在SEI/WSDL 中几乎没有其他可以更改的内容。

2B) 没有 WSDL 的动态编程:

String endpointUrl = ...;           
QName serviceName = new QName("http://example.org/wssample/echo/", "EchoService");
QName portName = new QName("http://example.org/wssample/echo/", "EchoServicePort");

/** Create a service and add at least one port to it. **/ 
Service service = Service.create(serviceName);
service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING, endpointUrl);

/** Create a Dispatch instance from a service.**/ 
Dispatch<SOAPMessage> dispatch = service.createDispatch(portName, 
SOAPMessage.class, Service.Mode.MESSAGE);

/** Create SOAPMessage request. **/
// compose a request message
MessageFactory mf = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);

// Create a message.  This example works with the SOAPPART.
SOAPMessage request = mf.createMessage();
SOAPPart part = request.getSOAPPart();

// Obtain the SOAPEnvelope and header and body elements.
SOAPEnvelope env = part.getEnvelope();
SOAPHeader header = env.getHeader();
SOAPBody body = env.getBody();

// Construct the message payload.
SOAPElement operation = body.addChildElement("invoke", "ns1",
"http://com/ibm/was/wssample/echo/");
SOAPElement value = operation.addChildElement("arg0");
value.addTextNode("ping");
request.saveChanges();

/** Invoke the service endpoint. **/
SOAPMessage response = dispatch.invoke(request);

优势(不是真的):最终可以让它执行与上述相同的行为。

缺点:编程复杂。非标准配置(WSDL 之外)。需要避免硬编码设置。易受界面变化影响。在服务器和客户端之间手动同步设置 - 容易忽略某些东西,调试起来非常困难。

答案:

返回 (1)。从 WSDL 生成客户端存根。将其用作接口契约——它应该设计得好而不是改变。

然后花时间解决实际问题... ;) ;)

【讨论】:

好吧,谢谢,我差点忘了这个问题 :) 事实上你是完全正确的,我当时完全按照你在 (1) 中描述的方式做的,这意味着我创建了客户端存根类并使用它们。不过,我使用 String JNDI 查找在运行时(通过 BindingProvider)配置具体的 web 服务位置,并在我的应用程序服务器配置中注册地址。无论如何,感谢您提供详细的答案。

以上是关于Java EE 应用程序之间的 Web 服务通信的主要内容,如果未能解决你的问题,请参考以下文章

java EE是啥?

EJB3 实例之间的通信(Java EE bean 间通信)可能吗?

如何在 Tomcat 中为 Java EE 应用程序实现套接字

SSH:Struts + Spring + Hibernate 轻量级Java EE企业框架

WEB UI 前端和 C++ 后端之间的通信?

各种容器与服务器的区别与联系:Servlet容器WEB容器Java EE容器应用服务器WEB服务器Java EE服务器