第 3 章 开发 JAX-WS Web 服务


基于 XML 的 Web 服务的 Java API(JAX-WS)定义 WSDL 和 Java 之间的映射,以及用于访问 Web 服务并发布它们的类。JBossWS 实施最新的 JAX-WS 规范,用户可以参考该规范来了解与供应商无关的 Web 服务使用需求。JAX-WS 的 Jakarta EE 是 Jakarta XML Web 服务规范 2.3 规范

3.1. Using JAX-WS Tools

以下 JAX-WS 命令行工具包含在 JBoss EAP 分发中:这些工具可用于服务器和 客户端开发

表 3.1. JAX-WS Command-Line Tools
命令描述

wsprovide

生成 JAX-WS 可移植工件,并提供抽象合同。用于底层开发.

wsconsume

使用抽象合同(WSDL 和架构文件),并为服务器和客户端生成工件。用于自上而下和客户端开发.

有关使用这些工具的更多详细信息,请参阅 JAX-WS 工具

3.1.1. 服务器侧开发策略

在服务器端开发 Web 服务端点时,您可以选择从 Java 代码(称为底部开发)或 WSDL 开始,后者定义您的服务,称为自上而下开发。如果这是一种新服务,即没有现有合同,则下向上的方法是最快的路线;您只需向类添加一些注释即可启动和运行服务。但是,如果您在开发服务时已定义了合同,使用自上而下的方法会更加简单,因为该工具可以为您生成注释的代码。

底部向上使用案例:

  • 将现有的 EJB3 bean 公开为 Web 服务.
  • 提供新的服务,并且您希望为您生成合同。

上下使用案例:

  • 替换现有 Web 服务的实施,您不能破坏与旧客户端的兼容性。
  • 公开符合第三方指定合同的服务,例如供应商重新调用您已定义的协议。
  • 创建遵循预先开发的 XML 架构和 WSDL 的服务。
使用 wsprovide 的底部策略

底层策略涉及为您的服务开发 Java 代码,然后使用 JAX-WS 注释为其标注。这些注解可用于自定义为您的服务生成的合同。基于 XML 的 Web 服务的 Jakarta EE 是 Jakarta XML Web 服务规范 2.3。例如,您可以更改操作名称来映射到您喜欢的任何内容。但是,所有注释都具有明智的默认值,因此只需要 @WebService 注释。

这就像创建单个类一样简单:

package echo;

@javax.jws.WebService
public class Echo {

   public String echo(String input) {
      return input;
   }
}

可以使用此类构建部署,这是在 JBossWS 上部署的唯一 Java 代码。在部署时,会为您生成 WSDL 和所有其他 Java 构件,称为 wrapper 类

wsprovide 工具的主要用途是生成可移植的 JAX-WS 构件。此外,它也可用于为您的服务提供 WSDL 文件。这可以通过使用 -w 选项调用 wsprovide 来获取:

$ javac -d . Echo.java
$ EAP_HOME/bin/wsprovide.sh --classpath=. -w echo.Echo

检查 WSDL 会显示一个名为 EchoService 的服务

<wsdl:service name="EchoService">
  <wsdl:port name="EchoPort" binding="tns:EchoServiceSoapBinding">
    <soap:address location="http://localhost:9090/EchoPort"/>
  </wsdl:port>
</wsdl:service>

如预期的那样,该服务会定义一个操作,echo:

<wsdl:portType name="Echo">
  <wsdl:operation name="echo">
    <wsdl:input name="echo" message="tns:echo">
    </wsdl:input>
    <wsdl:output name="echoResponse" message="tns:echoResponse">
    </wsdl:output>
  </wsdl:operation>
</wsdl:portType>

在部署时,您不需要运行此工具。您只需要为服务生成可移植工件或抽象合同即可。

可在简单的 web.xml 文件中为部署创建 POJO 端点:

<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
  version="2.4">

  <servlet>
    <servlet-name>Echo</servlet-name>
    <servlet-class>echo.Echo</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>Echo</servlet-name>
    <url-pattern>/Echo</url-pattern>
  </servlet-mapping>
</web-app>

web.xml 和单个 Java 类现在可以用于创建 WAR:

$ mkdir -p WEB-INF/classes
$ cp -rp echo WEB-INF/classes/
$ cp web.xml WEB-INF
$ jar cvf echo.war WEB-INF
added manifest
adding: WEB-INF/(in = 0) (out= 0)(stored 0%)
adding: WEB-INF/classes/(in = 0) (out= 0)(stored 0%)
adding: WEB-INF/classes/echo/(in = 0) (out= 0)(stored 0%)
adding: WEB-INF/classes/echo/Echo.class(in = 340) (out= 247)(deflated 27%)
adding: WEB-INF/web.xml(in = 576) (out= 271)(deflated 52%)

然后可以将 WAR 部署到 JBoss EAP。这会在内部调用 wsprovide,这将生成 WSDL。如果部署成功,并且您正在使用默认设置,它应在管理控制台中可用。

注意

对于可移植的 JAX-WS 部署,可以添加之前生成的打包程序类到部署中。

使用 wssume 的自顶策略

自上而下的开发策略从该服务的抽象合同开始,其中包括 WSDL 文件和零个或多个架构文件。然后,使用 wssume 工具来消耗此合同,生成注解的 Java 类,以及定义它的可选来源。

注意

ws Consume 可能会在 Unix 系统中出现符号链接问题。

使用底部示例中的 WSDL 文件,可以生成遵循此服务的新 Java 实施。k 选项 传递到 ws Consume 以保留 生成的 Java 源文件,而不是仅提供 Java 类:

$ EAP_HOME/bin/wsconsume.sh -k EchoService.wsdl

下表显示了每个生成的文件的用途:

表 3.2. 生成的文件
File用途

Echo.java

服务端点接口

EchoResponse.java

用于响应消息的 wrapper bean

EchoService.java

仅 JAX-WS 客户端使用

Echo_Type.java

用于请求消息的 wrapper bean

ObjectFactory.java

JAXB XML Registry

package-info.java

JAXB 软件包注释的拥有者

检查服务端点接口发现的注释比底部上例中手动编写的类中更明确,但它们评估到同一合同。

@WebService(targetNamespace = "http://echo/", name = "Echo")
@XmlSeeAlso({ObjectFactory.class})
public interface Echo {

    @WebMethod
    @RequestWrapper(localName = "echo", targetNamespace = "http://echo/", className = "echo.Echo_Type")
    @ResponseWrapper(localName = "echoResponse", targetNamespace = "http://echo/", className = "echo.EchoResponse")
    @WebResult(name = "return", targetNamespace = "")
    public java.lang.String echo(
        @WebParam(name = "arg0", targetNamespace = "")
        java.lang.String arg0
    );
}

除了打包之外缺少的唯一部分是实施类,现在可以使用上述界面编写。

package echo;

@javax.jws.WebService(endpointInterface="echo.Echo")
public class EchoImpl implements Echo {
   public String echo(String arg0) {
      return arg0;
   }
}

3.1.2. 客户端开发策略

在详细讲解客户端之前,务必要了解 Web 服务的核心分离概念。Web 服务不是最适合内部 RPC 服务,即使这些服务可以以这种方式使用。有更好的技术可以做到这一点,如 CORBA 和 RMI。Web 服务专为可互操作的粗粒度交流而设计。不期望或保证参与 Web 服务交互的各方将位于任何特定的位置,在任何特定的操作系统上运行,或者使用任何特定编程语言编写。因此,明确分隔客户端和服务器实施非常重要。他们唯一应当具有的共性是抽象合同定义。如果出于任何原因,您的软件不遵循此主体,那么您不应使用 Web 服务。由于上述原因,建议使用自上而下的方法开发客户端,即使客户端在同一服务器上运行也是如此。

使用 wssume 的自顶策略

本节重复了服务器端上下一节的流程,但它使用了已部署的 WSDL。这是为 soap :address (如下所示)检索正确的值,该值在部署时计算。如有必要,可在 WSDL 中手动编辑这个值,但您必须小心提供正确的路径。

示例 :已部署 WSDL 中的 soap:address

<wsdl:service name="EchoService">
  <wsdl:port name="EchoPort" binding="tns:EchoServiceSoapBinding">
    <soap:address location="http://localhost.localdomain:8080/echo/Echo"/>
  </wsdl:port>
</wsdl:service>

使用 ws Consume 为部署的 WSDL 生成 Java 类。

$ EAP_HOME/bin/wsconsume.sh -k http://localhost:8080/echo/Echo?wsdl

注意 EchoService.java 类如何存储从中获取 WSDL 的位置。

@WebServiceClient(name = "EchoService",
                  wsdlLocation = "http://localhost:8080/echo/Echo?wsdl",
                  targetNamespace = "http://echo/")
public class EchoService extends Service {

    public final static URL WSDL_LOCATION;

    public final static QName SERVICE = new QName("http://echo/", "EchoService");
    public final static QName EchoPort = new QName("http://echo/", "EchoPort");

    ...

    @WebEndpoint(name = "EchoPort")
    public Echo getEchoPort() {
        return super.getPort(EchoPort, Echo.class);
    }

    @WebEndpoint(name = "EchoPort")
    public Echo getEchoPort(WebServiceFeature... features) {
        return super.getPort(EchoPort, Echo.class, features);
    }
}

如您所见,这一生成的类扩展了 JAX-WS( javax.xml.ws.Service )中的主要客户端入口点。虽然您可以直接使用 服务,但这要简单得多,因为它能为您提供配置信息。注意 getEchoPort() 方法,它会返回我们服务端点接口的实例。然后,可以通过对返回的接口调用方法来调用任何 Web 服务操作。

重要

不要引用生产应用中的远程 WSDL URL。这会导致每次实例化 Service 对象时网络 I/O。相反,请使用已保存的本地副本上的 工具,或者使用构造器的 URL 版本来提供新的 WSDL 位置。

编写并编译客户端:

import echo.*;

public class EchoClient {

   public static void main(String args[]) {

      if (args.length != 1) {
          System.err.println("usage: EchoClient <message>");
          System.exit(1);
      }

      EchoService service = new EchoService();
      Echo echo = service.getEchoPort();
      System.out.println("Server said: " + echo.echo(args0));
   }
}

您可以通过设置 ENDPOINT_ADDRESS_PROPERTY,在运行时更改操作的端点地址,如下所示:

EchoService service = new EchoService();
Echo echo = service.getEchoPort();

/* Set NEW Endpoint Location */
String endpointURL = "http://NEW_ENDPOINT_URL";
BindingProvider bp = (BindingProvider)echo;
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointURL);

System.out.println("Server said: " + echo.echo(args0));
Red Hat logoGithubRedditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

© 2024 Red Hat, Inc.