第 5 章 Quarkus CXF 用户指南


本章提供有关 Quarkus CXF 用法和配置的信息。

5.1. 用户指南

此用户指南解释了 Quarkus CXF 的典型用例。

您可能想要从以下一些主题开始:

5.1.1. 创建新项目

本指南介绍了如何为托管 CXF 客户端或服务器或两者的 Quarkus 应用程序设置新项目。

5.1.1.1. 先决条件

请参阅 Quarkus 入门指南 的先决条件部分

除此之外,您可能需要

  • 安装了 native-image 命令以及 GRAALVM_HOME 环境变量集的 GraalVM。请参阅 Quarkus 文档中的 构建原生可执行文件 部分。
  • 如果您在 Linux 上,如 docker 的容器运行时也足以满足原生模式。如果选择这个选项,则使用 -Pnative -Dquarkus.native.container-build=true 而不是 -Pnative

5.1.1.2. 创建一个项目

可以使用 code.quarkus.redhat.com 生成新项目框架。

  • 您可以在此处选择您要使用的扩展。
  • 对于简单的 Hello world Web 服务或客户端,quarkus-cxf 扩展就足够了。
  • 单击 blue Generate your application 按钮,以下载基本的框架项目。
  • 解包 zip 文件,并将项目 导入到您首选的 IDE 中。

5.1.1.3. Quarkus 平台

Quarkus CXF 是 Quarkus Platform 版本 3.1.0.Final 的一部分。

Quarkus 平台聚合了由各种独立项目生成的 Quarkus 扩展,如 Quarkus Core、Quarkus CXF、Apache Camel、Qpid JMS、Debezium 等。

其主要目标是:

5.1.1.4. 依赖项管理

我们建议使用 Quarkus Platform BOM 来管理 Quarkus CXF 依赖项。这是您获得的内容,当您使用 code.quarkus.redhat.com 或其他 Quarkus 开发工具 时,如 Quarkus CLI。

<project ...>
  ...
  <properties>
    ...
    <quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
    <quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
    <quarkus.platform.version><!-- Check the latest https://repo1.maven.org/maven2/io/quarkus/platform/quarkus-cxf-bom/ --></quarkus.platform.version>
  </properties>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>${quarkus.platform.group-id}</groupId>
        <artifactId>${quarkus.platform.artifact-id}</artifactId>
        <version>${quarkus.platform.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
      <dependency>
        <groupId>${quarkus.platform.group-id}</groupId>
        <artifactId>quarkus-cxf-bom</artifactId>
        <version>${quarkus.platform.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
...
Copy to Clipboard Toggle word wrap

您应该始终谨慎将同一版本的 io.quarkus.platform:quarkus-bomio.quarkus.platform:quarkus-cxf-bom 导入到项目中。这是获取 Quarkus、CXF、Pa Quarkus CXF 及其所有传输依赖项的最可靠方法。

5.1.1.5. 进入下一个位置

我们建议使用以下任何章节:

5.1.2. Quarkus 上的第一个 SOAP Web 服务

在本指南中,我们将说明如何创建 Quarkus 应用程序公开简单的 SOAP Web 服务。

首先创建项目

在继续此处前,请按照 项目创建 指南操作。

5.1.2.1. 您好好!Web 服务

pom.xml 已就绪,您可以添加一个简单的 Hello world!src/main/java 中的 Web 服务.

代码示例

本节中使用的代码片段示例来自 Quarkus CXF 源树中的服务器 集成测试

首先添加服务接口:

HelloService.java

package io.quarkiverse.cxf.it.server;

import jakarta.jws.WebMethod;
import jakarta.jws.WebService;

/**
 * The simplest Hello service.
 */
@WebService(name = "HelloService", serviceName = "HelloService")
public interface HelloService {

    @WebMethod
    String hello(String text);

}
Copy to Clipboard Toggle word wrap

然后实现:

HelloServiceImpl.java

package io.quarkiverse.cxf.it.server;

import jakarta.jws.WebMethod;
import jakarta.jws.WebService;

/**
 * The simplest Hello service implementation.
 */
@WebService(serviceName = "HelloService")
public class HelloServiceImpl implements HelloService {

    @WebMethod
    @Override
    public String hello(String text) {
        return "Hello " + text + "!";
    }

}
Copy to Clipboard Toggle word wrap

要使在特定路径下公开的实现,您需要将以下配置添加到 application.properties 中:

# The context path under which all services will be available
quarkus.cxf.path = /soap

# Publish "HelloService" under the context path /${quarkus.cxf.path}/hello
quarkus.cxf.endpoint."/hello".implementor = io.quarkiverse.cxf.it.server.HelloServiceImpl
quarkus.cxf.endpoint."/hello".features = org.apache.cxf.ext.logging.LoggingFeature
Copy to Clipboard Toggle word wrap
提示

所有配置属性都记录在 配置属性参考 中。

提示

检查 服务端点和路径 章节,以了解在特定路径下公开服务端点的替代方法。

使用这些文件,您可以在 dev 模式中启动 Quarkus:

$ mvn quarkus:dev
Copy to Clipboard Toggle word wrap

这将编译项目,并在后台启动应用程序。

您可以使用 curl 或其它 SOAP 客户端测试服务。

首先,让我们在 http://localhost:8080/soap/hello?wsdl 下查看自动生成的 WSDL:

$ curl http://localhost:8080/soap/hello?wsdl
<?xml version='1.0' encoding='UTF-8'?>
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://server.it.cxf.quarkiverse.io/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
    name="HelloService" targetNamespace="http://server.it.cxf.quarkiverse.io/">
  <wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://server.it.cxf.quarkiverse.io/" attributeFormDefault="unqualified" elementFormDefault="unqualified" targetNamespace="http://server.it.cxf.quarkiverse.io/">
  <xsd:element name="hello" type="tns:hello"/>
  <xsd:complexType name="hello">
    <xsd:sequence>
      <xsd:element minOccurs="0" name="arg0" type="xsd:string"/>
    </xsd:sequence>
  </xsd:complexType>
  <xsd:element name="helloResponse" type="tns:helloResponse"/>
  <xsd:complexType name="helloResponse">
    <xsd:sequence>
      <xsd:element minOccurs="0" name="return" type="xsd:string"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>
  </wsdl:types>
  <wsdl:message name="helloResponse">
    <wsdl:part element="tns:helloResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="hello">
    <wsdl:part element="tns:hello" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:portType name="HelloService">
    <wsdl:operation name="hello">
      <wsdl:input message="tns:hello" name="hello">
    </wsdl:input>
      <wsdl:output message="tns:helloResponse" name="helloResponse">
    </wsdl:output>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="HelloServiceSoapBinding" type="tns:HelloService">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="hello">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="hello">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="helloResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="HelloService">
    <wsdl:port binding="tns:HelloServiceSoapBinding" name="HelloServicePort">
      <soap:address location="http://localhost:8080/soap/hello"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>
Copy to Clipboard Toggle word wrap

其次,让我们向服务发送 SOAP 请求:

$ curl -v -X POST -H "Content-Type: text/xml;charset=UTF-8" \
    -d \
      '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
        <soap:Body><ns2:hello xmlns:ns2="http://server.it.cxf.quarkiverse.io/"><arg0>World</arg0></ns2:hello></soap:Body>
       </soap:Envelope>' \
    http://localhost:8080/soap/hello
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ns1:helloResponse xmlns:ns1="http://server.it.cxf.quarkiverse.io/">
      <return>Hello World!</return>
    </ns1:helloResponse>
  </soap:Body>
</soap:Envelope>
Copy to Clipboard Toggle word wrap

您可以在 SOAP 响应中看到预期的 & lt;return>Hello World!</return >。

5.1.2.2. 在 dev 模式运行时添加日志记录功能

有时,可以方便检查由服务器或客户端接收或发送的 SOAP 消息。这可以通过将 quarkus-cxf-rt-features-logging 扩展添加到 pom.xml 来轻松实现。

提示

尝试在 Quarkus dev 模式运行时执行此操作。您应该会在源树中保存更改时重新编译并重新部署应用程序。

将其添加到 pom.xml

<dependency>
    <groupId>io.quarkiverse.cxf</groupId>
    <artifactId>quarkus-cxf-rt-features-logging</artifactId>
</dependency>
Copy to Clipboard Toggle word wrap

application.properties中启用 SOAP 有效负载日志记录

quarkus.cxf.endpoint."/hello".features=org.apache.cxf.ext.logging.LoggingFeature
Copy to Clipboard Toggle word wrap

在发送一个新的 SOAP 请求并在应用程序控制台中看到一些 SOAP 有效负载后:

2023-01-11 22:12:21,315 INFO  [org.apa.cxf.ser.Hel.REQ_IN] (vert.x-worker-thread-0) REQ_IN
    Address: http://localhost:8080/soap/hello
    HttpMethod: POST
    Content-Type: text/xml;charset=UTF-8
    ExchangeId: af10747a-8477-4c17-bf5f-2a4a3a95d61c
    ServiceName: HelloService
    PortName: HelloServicePort
    PortTypeName: HelloService
    Headers: {Accept=*/*, User-Agent=curl/7.79.1, content-type=text/xml;charset=UTF-8, Host=localhost:8080, Content-Length=203, x-quarkus-hot-deployment-done=true}
    Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body><ns2:hello xmlns:ns2="http://server.it.cxf.quarkiverse.io/"><arg0>World</arg0></ns2:hello></soap:Body>
</soap:Envelope>


2023-01-11 22:12:21,327 INFO  [org.apa.cxf.ser.Hel.RESP_OUT] (vert.x-worker-thread-0) RESP_OUT
    Address: http://localhost:8080/soap/hello
    Content-Type: text/xml
    ResponseCode: 200
    ExchangeId: af10747a-8477-4c17-bf5f-2a4a3a95d61c
    ServiceName: HelloService
    PortName: HelloServicePort
    PortTypeName: HelloService
    Headers: {}
    Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns1:helloResponse xmlns:ns1="http://server.it.cxf.quarkiverse.io/"><return>Hello World!</return></ns1:helloResponse></soap:Body></soap:Envelope>
Copy to Clipboard Toggle word wrap

5.1.2.3. 进一步的步骤

您可能希望继续打包应用程序,以便在 JVM 上运行或原生运行

5.1.3. Quarkus 上的第一个 SOAP 客户端

在本指南中,我们解释了如何创建充当远程 Web 服务客户端的简单 Quarkus 应用程序。

首先创建项目

在继续此处前,请按照 项目创建 指南操作。

5.1.3.1. 用于测试的远程 Web 服务

首先,我们需要一些远程 Web 服务来连接。我们可以使用一个在容器中运行的简单 计算器 Web 服务 来实现这一目的。

$ docker run -p 8082:8080 quay.io/l2x6/calculator-ws:1.0
Copy to Clipboard Toggle word wrap

容器启动并运行后,我们可以检查其 WSDL

$ curl -s http://localhost:8082/calculator-ws/CalculatorService?wsdl
<?xml version="1.0" ?>
<wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="CalculatorService" targetNamespace="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator">

  ...

  <wsdl:binding name="CalculatorServiceSoapBinding" type="tns:CalculatorService">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"></soap:binding>
    <wsdl:operation name="add">
      <soap:operation soapAction="" style="document"></soap:operation>
      <wsdl:input name="add">
        <soap:body use="literal"></soap:body>
      </wsdl:input>
      <wsdl:output name="addResponse">
        <soap:body use="literal"></soap:body>
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="subtract">
      <soap:operation soapAction="" style="document"></soap:operation>
      <wsdl:input name="subtract">
        <soap:body use="literal"></soap:body>
      </wsdl:input>
      <wsdl:output name="subtractResponse">
        <soap:body use="literal"></soap:body>
      </wsdl:output>
    </wsdl:operation>

    ...

  </wsdl:binding>

  ...

</wsdl:definitions>
Copy to Clipboard Toggle word wrap

正如您在 WSDL 中看到的那样,该服务提供一些基本的算术操作,如添加、减去 等。

我们使用 curl 测试它:

$ curl -s \
    -X POST \
    -H "Content-Type: text/xml;charset=UTF-8" \
    -d \
        '<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
            <Body>
                <add xmlns="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator">
                    <arg0 xmlns="">7</arg0> 
1

                    <arg1 xmlns="">4</arg1>
                </add>
            </Body>
        </Envelope>' \
    http://localhost:8082/calculator-ws/CalculatorService
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ns2:addResponse xmlns:ns2="http://www.jboss.org/eap/quickstarts/wscalculator/Calculator">
      <return>11</return> 
2

    </ns2:addResponse>
  </soap:Body>
</soap:Envelope>
Copy to Clipboard Toggle word wrap
1
添加 74的请求
2
11 - 返回操作的值

5.1.3.2. SOAP 客户端

现在,让我们来看看如何在 Quarkus 应用程序中获取客户端。

首先,我们需要服务端点接口(SEI)以及它所需的所有所有其他模型类。

获取它们的方法有几种:

  • 手动写入
  • 使用 Java 编写 Web Sevice 项目复制
  • 具有包含模型类的 Maven 工件,可能由 Service 项目提供
  • 从 WSDL 生成模型类

最后一个选项往往是客户端应用程序最简单且最灵活的选项。

提示

如果要使用这种方法,请首先遵循 从 WSDL 部分中生成 Java,然后继续后续步骤。

5.1.3.3. 使用 SEI 作为客户端

在我们的情形中,Service Endpoint Interface (SEI)是 org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService

在 Quarkus 中,我们可以通过 CDI 获取其实例。

为了便于测试,我们将将其嵌套在 REST 服务中:

CxfClientResource.java

package io.quarkiverse.cxf.client.it;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;

import org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService;

import io.quarkiverse.cxf.annotation.CXFClient;

@Path("/cxf/calculator-client")
public class CxfClientRestResource {

    @CXFClient("myCalculator") 
1

    CalculatorService myCalculator;

    @GET
    @Path("/add")
    @Produces(MediaType.TEXT_PLAIN)
    public int add(@QueryParam("a") int a, @QueryParam("b") int b) {
        return myCalculator.add(a, b); 
2

    }

}
Copy to Clipboard Toggle word wrap

1
让 CDI 容器注入客户端的实例。@CXFClient ("myCalculator") 实际上等同于 @Inject @CXFClient ("myCalculator")
2
调用 add 操作,从而调用远程 Web 服务

除了上述之外,我们是否需要使用此功能?- 除了上述之外,我们需要向 application.properties 中的 CXF Quarkus 扩展告知其他内容:

application.properties

cxf.it.calculator.baseUri=http://localhost:8082
quarkus.cxf.client.myCalculator.wsdl = ${cxf.it.calculator.baseUri}/calculator-ws/CalculatorService?wsdl
quarkus.cxf.client.myCalculator.client-endpoint-url = ${cxf.it.calculator.baseUri}/calculator-ws/CalculatorService
quarkus.cxf.client.myCalculator.service-interface = org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService
Copy to Clipboard Toggle word wrap

提示

所有客户端配置属性都记录在 配置属性参考 中。

对于以上所有文件,我们应当能够在 Quarkus dev 模式中启动应用程序

$ mvn quarkus:dev
...
INFO  [io.quarkus] (Quarkus Main Thread) ... Listening on: http://localhost:8080
Copy to Clipboard Toggle word wrap

并通过向它发送一些请求来测试它:

$ curl -s 'http://localhost:8080/cxf/calculator-client/add?a=5&b=6'
11
Copy to Clipboard Toggle word wrap

其中 11 是添加 56 的正确结果。

5.1.3.4. 进一步的步骤

您可能需要继续

5.1.4. 配置

Quarkus CXF 会公开大量配置选项。每个扩展都会在其 参考页面底部 记录了其选项。

配置选项可以在 application.properties 文件中设置,或者通过环境变量设置 - 请参阅 Quarkus 配置参考

5.1.4.1. Bean 引用

Quarkus CXF 的多个配置选项允许引用 Quarkus CDI 容器中出现的 Bean。功能和拦截器 是它们的典型示例。

可以通过两种方式在配置中设置 bean 引用:根据类型或 bean 名称。

5.1.4.1.1. 根据类型进行 bean 引用

下面是一个示例:

application.properties

# bean reference by type
quarkus.cxf.endpoint."/hello".features = org.apache.cxf.ext.logging.LoggingFeature
Copy to Clipboard Toggle word wrap

当使用按类型名称的引用时,解析进行如下:

  • 按照类型在 Quarkus CDI 容器中查找 bean。
  • 如果 bean 可用,则会使用它。
  • 如果多个 Bean 分配给给定类型,则抛出异常。
  • 如果没有匹配的 bean 可用,则会加载类并执行尝试以使用其默认构造器实例化它。
5.1.4.1.2. Bean 名称引用

下面是一个示例:

application.properties

# bean reference by bean name
quarkus.cxf.endpoint."/fruit".features = #myCustomLoggingFeature
Copy to Clipboard Toggle word wrap

使用 bean 名称的引用时,记者会按名称查找 Quarkus CDI 容器。名为 myCustomLoggingFeature 的命名 Bean 可以定义如下:

import org.apache.cxf.ext.logging.LoggingFeature;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;

class Producers {

    @Produces
    @ApplicationScoped
    @Named("myCustomLoggingFeature")
    LoggingFeature myCustomLoggingFeature() {
        LoggingFeature loggingFeature = new LoggingFeature();
        loggingFeature.setPrettyLogging(true);
        return loggingFeature;
    }
}
Copy to Clipboard Toggle word wrap

5.1.5. 用于在 JVM 或原生运行的软件包

在本章中,我们解释了如何打包 Quarkus CXF 应用程序,以便在 JVM 上运行或原生运行它。

5.1.5.1. JVM 模式

在 SOAP 客户端和 SOAP 服务的入门指南中,我们只在 Quarkus dev mode: Quarkus 工具中工作,在后台监视工作区的更改,根据需要重新编译并重新载入应用程序。

我们使用开发完成后,如何在 JVM 上运行应用程序?

首先,我们需要使用 Maven 打包它:

$ mvn package
Copy to Clipboard Toggle word wrap

在 JVM 上运行应用程序所需的库可在 target/quarkus-app 目录中找到:

$ ls -lh target/quarkus-app
drwxr-xr-x. 2 ppalaga ppalaga 4.0K Jan 12 22:29 app
drwxr-xr-x. 4 ppalaga ppalaga 4.0K Jan 12 22:29 lib
drwxr-xr-x. 2 ppalaga ppalaga 4.0K Jan 12 22:29 quarkus
-rw-r-r--. 1 ppalaga ppalaga 6.1K Jan 12 22:29 quarkus-app-dependencies.txt
-rw-r-r--. 1 ppalaga ppalaga  678 Jan 12 22:29 quarkus-run.jar
Copy to Clipboard Toggle word wrap

我们可以按如下所示启动应用程序:

$ java -jar target/quarkus-app/quarkus-run.jar
Copy to Clipboard Toggle word wrap

您可以使用 curl 发送一些 SOAP 请求,以确保应用程序正常工作。

5.1.5.2. 原生模式

Quarkus 为构建 GraalVM 原生镜像和 Quarkus CXF 完全遵循的类支持。

镜像

GraalVM 原生镜像是平台特定的可执行文件,您可以在没有 JVM 的情况下直接运行。与在 JVM 模式下运行相同的应用相比,它们启动速度更快,且内存更少。

code.quarkus.redhat.com 生成的 pom.xml 文件包含构建 原生 镜像所需的原生配置集:

<profile>
  <id>native</id>
  <activation>
    <property>
      <name>native</name>
    </property>
  </activation>
  <properties>
    <skipITs>false</skipITs>
    <quarkus.package.type>native</quarkus.package.type>
  </properties>
</profile>
Copy to Clipboard Toggle word wrap

此外,如 第 5.1.1 节 “创建新项目” 部分所述,您需要 GraalVM native-image 工具。

您应该在本地安装它,并有正确设置 GRAALVM_HOME 环境变量,或者需要生成 Linux 原生的可执行文件 swig-mvapich,您可以使用 docker

使用本地安装 GraalVM

# Make sure $GRAALVM_HOME is set properly
$ echo $GRAALVM_HOME
/home/{user}/.sdkman/candidates/java/{major}.{minor}.r{java-version}-grl

# Produce the native executable
mvn package -Pnative
Copy to Clipboard Toggle word wrap

提示

Quarkus 对 GraalVM 版本非常自选。使用本地安装时,请务必使用 Quarkus 首选版本。为此,您可以打开 pom.xml 中导入的 quarkus-bom,并在其中搜索 graalvm。如果使用 Docker,Quarkus 会为您拉取正确的版本。

使用 docker

# Produce the native executable
mvn package -Pnative -Dquarkus.native.container-build=true
Copy to Clipboard Toggle word wrap

对于简单的应用程序,这可能需要一分钟的时间。

构建完成后,原生可执行文件应 在目标目录中 可用:

$ ls -l target
...
-rwxr-xr-x. 1 ppalaga ppalaga  71M Jan 11 22:42 quarkus-cxf-integration-test-server-1.8.0-SNAPSHOT-runner
...
Copy to Clipboard Toggle word wrap

正如您所见,它的大小只能为 71 MB,并且是可执行的。

您可以按照以下方式运行它:

$ target/*-runner
...
INFO  [io.quarkus] (main) quarkus-cxf-integration-test-server 1.8.0-SNAPSHOT native (powered by Quarkus
2.15.2.Final) started in 0.042s. Listening on: http://0.0.0.0:8080
...
Copy to Clipboard Toggle word wrap

同样,您可以使用 curl 发送一些 SOAP 请求,以确保原生可执行文件正常工作。

不要忘记比较内存用量、第一次请求和其他性能指标与您前面使用的堆栈进行比较,并共享结果!

5.1.5.3. 原生镜像:附加资源

您还可以参考以下链接,其中包含有关如何处理原生镜像的提示。

5.1.5.4. 创建容器镜像

请参阅 Quarkus 容器镜像 指南。

5.1.6. 日志记录

有关 Quarkus 上日志记录的基本信息,请参阅 Quarkus Logging 指南,例如

5.1.6.1. 有效负载日志记录

history

从 Quarkus CXF 2.6.0 开始,有效负载日志记录功能可以通过 io.quarkiverse.cxf:quarkus-cxf 扩展提供。在 2.6.0 之前,它可以通过单独的扩展 io.quarkiverse.cxf:quarkus-cxf-rt-features-logging 提供,它将在以后的版本中删除。

有效负载日志记录功能主要通过 org.apache.cxf.ext.logging.LoggingFeature 类实现。

您可以通过几种方法在客户端或服务端点上设置该功能。

5.1.6.2. 通过配置属性配置有效负载日志记录

5.1.6.2.1. 全局设置

自 Quarkus CXF 2.6.0 开始存在全局日志记录选项。它们需要使用 quarkus.cxf.logging.enabled-for 启用。有四个可能的值:

  • none (默认)- 没有为客户端和服务端点启用全局日志记录功能
  • 客户端 - 为应用程序中的所有客户端启用全局日志记录功能
  • 服务 - 为应用程序中的所有服务端点启用全局日志记录功能
  • 两者都 为应用程序中的所有客户端和服务端点启用全局日志记录功能

全局设置可以在 客户端或 服务端点级别上覆盖。

application.properties

# Global settings
quarkus.cxf.logging.enabled-for = both
quarkus.cxf.logging.pretty = true
Copy to Clipboard Toggle word wrap

所有日志记录配置选项都列在 quarkus-cxf 参考页面中。

提示

本页中提到的所有日志记录属性都是 运行时 配置选项。因此,您可以在启动应用程序时传递它们,而无需重新构建它。它可以通过在命令行中传递系统属性(例如,-Dquarkus.cxf.logging.enabled-for=both)或设置环境变量(例如,导出 QUARKUS_CXF_LOGGING_ENABLED_FOR=both)。

5.1.6.2.2. 每个客户端和每个服务端点设置

从 Quarkus CXF 2.5.0 开始,可以通过在 application.properties 中设置适当的选项来声明性地配置并附加到客户端或服务端点:

application.properties

# For a service:
quarkus.cxf.endpoint."/hello".logging.enabled = true
quarkus.cxf.endpoint."/hello".logging.pretty = true
# For a client:
quarkus.cxf.client.hello.logging.enabled = true
quarkus.cxf.client.hello.logging.pretty = true
Copy to Clipboard Toggle word wrap

所有日志记录配置选项都记录在 quarkus-cxf 参考页面中:

要使用默认设置附加实例,您可以执行以下操作之一:

  1. application.properties 中:

    # For a service:
    quarkus.cxf.endpoint."/hello".features = org.apache.cxf.ext.logging.LoggingFeature
    # For a client:
    quarkus.cxf.client."myClient".features = org.apache.cxf.ext.logging.LoggingFeature
    Copy to Clipboard Toggle word wrap
    提示

    用户指南 的第一 SOAP Web 服务 章节中有一个示例。

    或者

  2. 使用 CXF 的 @Features 注释:

    @org.apache.cxf.feature.Features (features = {"org.apache.cxf.ext.logging.LoggingFeature"})
    @WebService(endpointInterface = "org.acme.SayHi", targetNamespace = "uri:org.acme")
    public class SayHiImplementation implements SayHi {
       public long sayHi(long arg) {
           return arg;
       }
       //...
    }
    Copy to Clipboard Toggle word wrap
5.1.6.3.1. 生成自定义 LoggingFeature bean

如果您需要一些自定义逻辑来设置 LoggingFeature,您可以生成名为 LoggingFeature bean :

import org.apache.cxf.ext.logging.LoggingFeature;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;

class Producers {

    @Produces
    @ApplicationScoped
    @Named("limitedLoggingFeature") // "limitedLoggingFeature" is redundant if the name of the method is the same
    LoggingFeature limitedLoggingFeature() {
        LoggingFeature loggingFeature = new LoggingFeature();
        loggingFeature.setPrettyLogging(true);
        loggingFeature.setLimit(1024);
        return loggingFeature;
    }
}
Copy to Clipboard Toggle word wrap

然后,您可以使用其名称以 prefixed in application.properties 来引用它:

# For a service:
quarkus.cxf.endpoint."/hello".features = #limitedLoggingFeature
# For a client:
quarkus.cxf.client.hello.features = #limitedLoggingFeature
Copy to Clipboard Toggle word wrap

5.1.7. 使用 JAXB 复杂的 SOAP 有效负载

我们为 Quarkus SOAP 客户端和 SOAP 服务入门指南 处理仅有原语参数和返回值的服务,如整数和字符串。让我们来看一下传递和接收更复杂的对象。

例如,让我们创建用于管理有问题的应用程序。

注意

本节中使用的代码片段示例来自 Quarkus CXF 源树中的服务器 集成测试

由于我们的混淆的表示应该比较复杂,因此让我们将其作为具有几个属性的 Java Bean 进行建模:

package io.quarkiverse.cxf.it.server;

import java.util.Objects;

import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlType;

@XmlType(name = "Fruit")
@XmlRootElement
public class Fruit {

    private String name;

    private String description;

    public Fruit() {
    }

    public Fruit(String name, String description) {
        this.name = name;
        this.description = description;
    }

    public String getName() {
        return name;
    }

    @XmlElement
    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    @XmlElement
    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Fruit)) {
            return false;
        }

        Fruit other = (Fruit) obj;

        return Objects.equals(other.getName(), this.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(this.getName());
    }
}
Copy to Clipboard Toggle word wrap

您可能已经注意到,我们使用了一些 JAXB 注释,如 @XmlElement@XmlRootElement@XmlType。这是为了控制 bean 从和 XML 序列化和反序列化。

5.1.7.1. 自动注册以反应

JAXB 是一个基于反射的序列化框架。当学习 GraalVM 原生镜像时,您通常会听到的第一件事是在构建时注册类、字段和方法来反映。使用普通 GraalVM,您必须手动通过 reflection-config.json 来执行此操作。至少,对于您编写的类,至少要注意。不要使用 Quarkus。quarkus-jaxb 扩展(其 quarkus-cxf 依赖)能够扫描应用的类路径以获取带有 JAXB 注解的类类路径,并自动注册它们,以自动反映。

因此,在 Quarkus 上使用复杂有效负载与库存 CXF 不同。JAXB serialization 和 deserialization 将开箱即用,无需任何额外的配置。

5.1.7.2. SEI 和实现

用于管理模糊的服务端点接口(SEI)可能类似如下:

package io.quarkiverse.cxf.it.server;

import java.util.Set;

import jakarta.jws.WebMethod;
import jakarta.jws.WebService;

@WebService
public interface FruitService {

    @WebMethod
    Set<Fruit> list();

    @WebMethod
    Set<Fruit> add(Fruit fruit);

    @WebMethod
    Set<Fruit> delete(Fruit fruit);
}
Copy to Clipboard Toggle word wrap

我们可以根据需要实施 SEI:

package io.quarkiverse.cxf.it.server;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

import jakarta.jws.WebService;

@WebService(serviceName = "FruitService")
public class FruitServiceImpl implements FruitService {

    private Set<Fruit> fruits = Collections.synchronizedSet(new LinkedHashSet<>());

    public FruitServiceImpl() {
        fruits.add(new Fruit("Apple", "Winter fruit"));
        fruits.add(new Fruit("Pineapple", "Tropical fruit"));
    }

    @Override
    public Set<Fruit> list() {
        return fruits;
    }

    @Override
    public Set<Fruit> add(Fruit fruit) {
        fruits.add(fruit);
        return fruits;
    }

    @Override
    public Set<Fruit> delete(Fruit fruit) {
        fruits.remove(fruit);
        return fruits;
    }
}
Copy to Clipboard Toggle word wrap

5.1.7.3. application.properties

实现非常简单,您只需要使用 application.properties 定义端点。

quarkus.cxf.endpoint."/fruits".implementor = io.quarkiverse.cxf.it.server.FruitServiceImpl
quarkus.cxf.endpoint."/fruits".features = org.apache.cxf.ext.logging.LoggingFeature
Copy to Clipboard Toggle word wrap

5.1.7.4. 使用 Quarkus dev 模式和 curl测试

使用上述文件,您可以在 dev 模式中启动 Quarkus 工具:

$ mvn quarkus:dev
...
INFO  [io.quarkus] (Quarkus Main Thread) ... Listening on: http://localhost:8080
Copy to Clipboard Toggle word wrap

然后,通过调用其 list 操作来检查该服务是否正常工作:

$ curl -v -X POST -H "Content-Type: text/xml;charset=UTF-8" \
    -d \
      '<soapenv:Envelope
      xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
      xmlns:ns1="http://server.it.cxf.quarkiverse.io/">
        <soapenv:Body>
            <ns1:list/>
        </soapenv:Body>
    </soapenv:Envelope>' \
    http://localhost:8080/soap/fruits
...
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ns1:listResponse xmlns:ns1="http://server.it.cxf.quarkiverse.io/">
      <return xmlns:ns2="http://server.it.cxf.quarkiverse.io/">
        <description>Winter fruit</description>
        <name>Apple</name>
      </return>
      <return xmlns:ns2="http://server.it.cxf.quarkiverse.io/">
        <description>Tropical fruit</description>
        <name>Pineapple</name>
      </return>
    </ns1:listResponse>
  </soap:Body>
</soap:Envelope>
Copy to Clipboard Toggle word wrap

正如您所见,端点返回了默认情况下提供的两个 fruits ApplePineapple

现在,让我们再添加另一个问题,表示一个 Orange

$ curl -v -X POST -H "Content-Type: text/xml;charset=UTF-8" \
    -d \
     '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
        <soap:Body>
          <ns2:add xmlns:ns2="http://server.it.cxf.quarkiverse.io/">
            <arg0>
              <description>Mediterranean fruit</description>
              <name>Orange</name>
            </arg0>
          </ns2:add>
       </soap:Body></soap:Envelope>' \
    http://localhost:8080/soap/fruits
...
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <ns1:addResponse xmlns:ns1="http://server.it.cxf.quarkiverse.io/">
      <return xmlns:ns2="http://server.it.cxf.quarkiverse.io/">
        <description>Winter fruit</description>
        <name>Apple</name>
      </return>
      <return xmlns:ns2="http://server.it.cxf.quarkiverse.io/">
        <description>Tropical fruit</description>
        <name>Pineapple</name>
      </return>
      <return xmlns:ns2="http://server.it.cxf.quarkiverse.io/">
        <description>Mediterranean fruit</description>
        <name>Orange</name>
      </return>
    </ns1:addResponse>
  </soap:Body>
</soap:Envelope>
Copy to Clipboard Toggle word wrap

我们可以看到 Orange 已如预期在返回的列表中添加。

5.1.7.5. 进一步的步骤

您可能希望继续打包应用程序,以便在 JVM 上运行或原生运行

5.1.8. 合同第一和代码第一种方法

Quarkus CXF 完全支持第一个和代码第一个开发模式。

5.1.8.1. 合同第一个客户端

WSDL 描述 SOAP 服务。它是定义操作的合同,其参数和返回值等。WSDL 足以生成完整客户端的代码。CXF 为其提供 wsdl2java 工具。

Quarkus CXF 将 wsdl2java 嵌套在 quarkus-cxf 扩展中,因此您不需要直接使用它。

按照用户指南 的 WSDL 部分中生成模型类,以了解更多有关如何使用它的详细信息。

您可能还想检查 CXF 开发一个 Consumer 作为一般简介。

5.1.8.2. 合同第一个服务

在实施服务时,来自 WSDL 的 Java 代码 也可能会很方便。wsdl2java 可以为您生成模型类(带有 JAXB 注释)和服务接口(带有 JAX-WS 注释)。然后,您的任务是为这些接口提供实施。

您可能需要检查 CXF 文档中的 WSDL 第一服务开发 部分,以更好地了解底层概念。

5.1.8.3. 代码第一个服务

您提出的另一个有效选项是使用 JAX-WS 和 JAXB 在 Java 中编写您的服务。然后,您有两个选择如何获取 WSDL 合同:

  1. 启动服务并将客户端指向 http://your-host/your-service?wsdl
  2. 在构建时从 Java 类生成 WSDL 文档
提示

如需更多信息,请参阅 CXF 文档的 Code first development 部分。

5.1.8.4. 从 WSDL 生成模型类

在 Quarkus 代码生成阶段,quarkus-cxf 扩展支持从 WSDL 生成 Java 类。

代码示例

本节中显示的代码片段来自 Quarkus CXF 的源树中的客户端 集成测试。您可能需要将其作为可执行示例进行检查。

您需要设置几个内容才能使 CXF 代码生成正常工作:

  • 在项目中具有 io.quarkiverse.cxf:quarkus-cxf 依赖项
  • 对于 Maven 项目,generate-code 目标需要在 quarkus-maven-plugin 配置中存在:

    pom.xml

                <plugin>
                    <groupId>io.quarkus</groupId>
                    <artifactId>quarkus-maven-plugin</artifactId>
                    <executions>
                        <execution>
                            <goals>
                                <goal>build</goal>
                                <goal>generate-code</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
    Copy to Clipboard Toggle word wrap

  • 对于 Gradle 项目,不需要额外的 io.quarkus 插件配置
  • 将您的 WSDL 文件置于 src/main/resourcessrc/test/resources 或其中的任何子目录下。
  • 您的 WSDL 文件名必须以 .wsdl结尾
  • quarkus.cxf.codegen.wsdl2java.includes配置属性设置为 与您要处理的 WSDL 文件匹配的模式。如果要处理 src/main/resources/wsdlsrc/test/resources/wsdl 下的所有 WSDL 文件,请按如下所示设置:

    application.properties

    quarkus.cxf.codegen.wsdl2java.includes = wsdl/*.wsdl
    Copy to Clipboard Toggle word wrap

这将在 target/generated-sources/wsdl2javatarget/generated-test-sources/wsdl2java 目录中生成 Java 类。它们由编译器插件自动选择。因此,我们可以自由地引用应用程序或测试代码。

注意

请注意,quarkus-cxf 代码生成使用来自 CXF 的 wsdl2Java 工具(位于 hood 下)。wsdl2Java 通过包含和排除选择的每个 WSDL 文件单独调用。

通过 quarkus.cxf.codegen. wsdl2java.additional-params 配置参数可以将自定义参数传递给 wsdl2java。

如果需要为每个 WSDL 文件使用不同的 additional-params,您可能需要为它们分别定义一个单独的命名参数。下面是一个示例:

application.properties

# Parameters for foo.wsdl
quarkus.cxf.codegen.wsdl2java.foo-params.includes = wsdl/foo.wsdl
quarkus.cxf.codegen.wsdl2java.foo-params.wsdl-location = wsdl/foo.wsdl
# Parameters for bar.wsdl
quarkus.cxf.codegen.wsdl2java.bar-params.includes = wsdl/bar.wsdl
quarkus.cxf.codegen.wsdl2java.bar-params.wsdl-location = wsdl/bar.wsdl
quarkus.cxf.codegen.wsdl2java.bar-params.xjc = ts
Copy to Clipboard Toggle word wrap

提示

io.quarkiverse.cxf:quarkus-cxf-xjc-plugins 依赖项添加到项目,以便可以使用 -xjc-Xbg,-xjc-Xdv,-xjc-Xjavadoc,-xjc-Xproperty-listener,-xjc-Xts-xjc-Xwsdlextension wsdl2java.

5.1.8.4.1. 非 ASCII Characters

有时,由于代码中包含非 ASCII 字符,因此 wsdl2java 自动生成的 Java 类可能无法与 GraalVM 完全兼容。在原生镜像构建过程中可能会出现与以下类似的例外:

[quarkus-dalkia-ticket-loader-1.0.0-SNAPSHOT-runner:26]      compile: 161 459,15 ms,  8,54 GB
[quarkus-dalkia-ticket-loader-1.0.0-SNAPSHOT-runner:26]        image: 158 272,73 ms,  8,43 GB
[quarkus-dalkia-ticket-loader-1.0.0-SNAPSHOT-runner:26]        write:     205,82 ms,  8,43 GB
Fatal error:com.oracle.svm.core.util.VMError$HostedError: java.lang.RuntimeException: oops : expected ASCII string! com.oracle.svm.reflect.OperationOrderStatusType_CRÉÉ_f151156b0d42ecdbdfb919501d8a86dda8733012_1456.hashCode
    at com.oracle.svm.core.util.VMError.shouldNotReachHere(VMError.java:72)
Copy to Clipboard Toggle word wrap

以下是 Java 类中自动生成的非 ASCII 字符示例:

@XmlType(name = "OperationOrderStatusType")
@XmlEnum
public enum OperationOrderStatusType {

    @XmlEnumValue("Cr\u00e9\u00e9")
    CRÉÉ("Cr\u00e9\u00e9"),
    @XmlEnumValue("A communiquer")
    A_COMMUNIQUER("A communiquer"),
    @XmlEnumValue("En attente de r\u00e9ponse")
    EN_ATTENTE_DE_RÉPONSE("En attente de r\u00e9ponse"),
    @XmlEnumValue("Attribu\u00e9")
    ATTRIBUÉ("Attribu\u00e9"),
    @XmlEnumValue("Clotur\u00e9")
    CLOTURÉ("Clotur\u00e9"),
    @XmlEnumValue("Annul\u00e9")
    ANNULÉ("Annul\u00e9");
    private final String value;

    OperationOrderStatusType(String v) {
        value = v;
    }

    public String value() {
        return value;
    }

    public static OperationOrderStatusType fromValue(String v) {
        for (OperationOrderStatusType c: OperationOrderStatusType.values()) {
            if (c.value.equals(v)) {
                return c;
            }
        }
        throw new IllegalArgumentException(v);
    }
}
Copy to Clipboard Toggle word wrap

\u 开头的任何内容都将是一个问题。因此,需要以下重构:

@XmlType(name = "OperationOrderStatusType")
@XmlEnum
public enum OperationOrderStatusType {

    @XmlEnumValue("Créé")
    CREE("Créé"),
    @XmlEnumValue("A communiquer")
    A_COMMUNIQUER("A communiquer"),
    @XmlEnumValue("En attente de réponse")
    EN_ATTENTE_DE_REPONSE("En attente de réponse"),
    @XmlEnumValue("Attribué")
    ATTRIBUE("Attribué"),
    @XmlEnumValue("Cloturé")
    CLOTURE("Cloturé"),
    @XmlEnumValue("Annulé")
    ANNULE("Annulé");
    private final String value;

    OperationOrderStatusType(String v) {
        value = v;
    }

    public String value() {
        return value;
    }

    public static OperationOrderStatusType fromValue(String v) {
        for (OperationOrderStatusType c: OperationOrderStatusType.values()) {
            if (c.value.equals(v)) {
                return c;
            }
        }
        throw new IllegalArgumentException(v);
    }
}
Copy to Clipboard Toggle word wrap

5.1.8.5. 从 Java 生成 WSDL 文档

如果您的服务位于 http://your-host/your-service?wsdl 的 WSDL 服务不足,因为您想要将其分发为 Maven 构件,那么您可以在构建时使用 java2ws 生成 WSDL 文档。

您不需要直接调用 CXF 提供的 java2ws 工具,您不必使用 cxf-java2ws-plugin

quarkus-cxf wraps java2ws,您可以在 application.properties 中配置它作为应用程序的任何其他方面。

下面是一个示例:

注意

本节中使用的代码片段示例来自 Quarkus CXF 源树中的服务器 集成测试

application.properties

quarkus.cxf.java2ws.includes = io.quarkiverse.cxf.it.server.HelloServiceImpl,io.quarkiverse.cxf.it.server.FaultyHelloServiceImpl
quarkus.cxf.java2ws.wsdl-name-template = %TARGET_DIR%/Java2wsTest/%SIMPLE_CLASS_NAME%-from-java2ws.wsdl
Copy to Clipboard Toggle word wrap

在这里,我们已指示 java2ws 为两个服务类生成 WSDLs,即 HelloServiceImplFaultyHelloServiceImpl

注解

服务类必须使用 jakarta.xml.ws.WebService 注解,以进行 java2ws 处理。

这两个生成的 WSDL 文档将分别存储为 target/Java2wsTest/FaultyHelloServiceImpl-from-java2ws.wsdltarget/Java2wsTest/HelloServiceImpl-from-java2ws.wsdl

注意

与在 Quarkus 源生成阶段执行的 wsdl2java 不同,java2ws 是编译后发生的 Quarkus 扩展的一部分。java2ws 的输入是,所有 Java 类文件之后。因此,您不需要将 generate-code 添加到 java2wsquarkus-maven-plugin 中。

5.1.8.5.1. 另请参阅

5.1.9. CXF Interceptors 和 features, JAX-WS Handlers

查看以下章节以了解定制 SOAP 请求和响应处理的各种方法:

5.1.9.1. CXF Interceptors 和功能

CXF 拦截器CXF 功能 可以使用注解或 application.properties 配置添加到您的客户端或服务器。

虽然 CXF 提供了多个开箱即用的嵌入式拦截器和功能,但您也可以集成自定义开发的实现。

注解可用于服务接口或实施类。

@org.apache.cxf.feature.Features (features = {"org.apache.cxf.ext.logging.LoggingFeature"})
@org.apache.cxf.interceptor.InInterceptors (interceptors = {"org.acme.Test1Interceptor" })
@org.apache.cxf.interceptor.InFaultInterceptors (interceptors = {"org.acme.Test2Interceptor" })
@org.apache.cxf.interceptor.OutInterceptors (interceptors = {"org.acme.Test1Interceptor" })
@org.apache.cxf.interceptor.InFaultInterceptors (interceptors = {"org.acme.Test2Interceptor","org.acme.Test3Intercetpor" })
@WebService(endpointInterface = "org.acme.SayHi", targetNamespace = "uri:org.acme")
public class SayHiImplementation implements SayHi {
   public long sayHi(long arg) {
       return arg;
   }
   //...
}
Copy to Clipboard Toggle word wrap

您还可以在 application.properties 文件中定义配置。

quarkus.cxf.endpoint."/greeting-service".features=org.apache.cxf.ext.logging.LoggingFeature
quarkus.cxf.endpoint."/greeting-service".in-interceptors=org.acme.Test1Interceptor
quarkus.cxf.endpoint."/greeting-service".out-interceptors=org.acme.Test1Interceptor
quarkus.cxf.endpoint."/greeting-service".in-fault-interceptors=org.acme.Test2Interceptor,org.acme.Test3Intercetpor
quarkus.cxf.endpoint."/greeting-service".out-fault-interceptors=org.acme.Test1Intercetpor
Copy to Clipboard Toggle word wrap
类加载

功能和拦截器类都会首先通过 CDI 加载。它们可以通过完全限定类名称或 bean 名称来引用。

如果没有 CDI Bean 可用,则会调用没有参数的构造器来实例化每个类。

5.1.9.2. JAX-WS 处理程序

作为 @HandlerChain 注释的替代选择,JAX-WS 处理程序 可以通过 application.properties 添加至您的客户端或服务器:

application.properties

# A web service endpoint with multiple Handler classes
quarkus.cxf.endpoint."/greeting-service".handlers=org.acme.MySOAPHandler,org.acme.AnotherSOAPHandler

# A web service client with a single Handler
quarkus.cxf.client."greeting-client".handlers=org.acme.MySOAPHandler
Copy to Clipboard Toggle word wrap

其中 MySOAPHandler 可能类似如下:

import jakarta.xml.ws.handler.soap.SOAPHandler;
import jakarta.xml.ws.handler.soap.SOAPMessageContext;

public class MySOAPHandler implements SOAPHandler<SOAPMessageContext> {

    public boolean handleMessage(SOAPMessageContext messageContext) {
        SOAPMessage msg = messageContext.getMessage();
        return true;
    }
    // other methods
}
Copy to Clipboard Toggle word wrap
类加载

SOAPHandler 类首先通过 CDI 加载。

如果没有 CDI Bean 可用,则会调用没有参数的构造器来实例化每个类。

5.1.10. 高级服务主题

有关实现 SOAP 服务端点的更多详细信息,请查看以下章节:

5.1.10.1. 服务端点和路径

让我们来说明如何在特定 URL 路径下公开服务端点。

5.1.10.1.1. 通过 application.properties设置端点路径

First SOAP Web 服务 章节中,我们解释了如何使用 application.properties 来公开服务:

application.properties

# The context path under which all services will be available
quarkus.cxf.path = /soap

# Publish "HelloService" under the context path /${quarkus.cxf.path}/hello
quarkus.cxf.endpoint."/hello".implementor = io.quarkiverse.cxf.it.server.HelloServiceImpl
quarkus.cxf.endpoint."/hello".features = org.apache.cxf.ext.logging.LoggingFeature
Copy to Clipboard Toggle word wrap

有了此设置,可以在 http://localhost:8080/soap/hello 下访问 io.quarkiverse.cxf.it.server.HelloServiceImpl

这是自 Quarkus CXF 非常开始工作的传统方法。

5.1.10.1.2. 使用 @CXFEndpoint 注释设置端点路径

自 Quarkus CXF 3.11.0 起,有新的方法在特定路径下公开端点 :@io.quarkiverse.cxf.annotation.CXFEndpoint 注解。该路径通过其非可选 属性值 进行设置,它相对于 quarkus.cxf.path,就像通过 application.properties 执行时一样。

让我们来看一个示例。

注意

本节中显示的代码片段示例来自 Quarkus CXF 的源树中的客户端和服务器 集成测试。您可能需要将其用作可运行的示例。

PathAnnotationHelloServiceImpl.java

package io.quarkiverse.cxf.it.annotation.cxfendpoint;

import jakarta.jws.WebService;

import io.quarkiverse.cxf.annotation.CXFEndpoint;
import io.quarkiverse.cxf.it.HelloService;

@CXFEndpoint("/path-annotation") 
1

@WebService(serviceName = "HelloService", targetNamespace = HelloService.NS)
public class PathAnnotationHelloServiceImpl implements HelloService {
    @Override
    public String hello(String person) {
        return "Hello " + person + " from PathAnnotationHelloServiceImpl!";
    }
}
Copy to Clipboard Toggle word wrap

1
如果 application.properties 中的 quarkus.cxf.path 的值为 /soap,则该服务可在 http://localhost:8080/soap/path-annotation 下访问。

MyServiceImpl 类型的 @CxfEndpoint ("/my-path") 注释等同于 application.properties 中的 quarkus.cxf.endpoint."/my-path".implementor = org.acme.MyServiceImpl 行。因此,仅使用其中之一就足够了。

/my-path 端点的 application.properties 中设置的其他选项,将只与 @CXFEndpoint ("/my-path") 合并。

@CXFEndpoint 注释也可用于生成者方法。这特别适用于测试客户端,因为返回的实现可以是模拟的。

下面是一个示例:

MockedEndpointTest.java

package io.quarkiverse.cxf.it.annotation.cxfendpoint;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import io.quarkiverse.cxf.annotation.CXFClient;
import io.quarkiverse.cxf.annotation.CXFEndpoint;
import io.quarkiverse.cxf.it.HelloService;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
public class MockedEndpointTest {

    @CXFEndpoint("/helloMock") 
1

    HelloService helloMockService() {
        final HelloService result = Mockito.mock(HelloService.class);
        Mockito.when(result.hello("Mock")).thenReturn("Hello Mock!");
        return result;
    }

    @CXFClient("helloMock") 
2

    HelloService helloMockClient;

    @Test
    void helloMock() {
        Assertions.assertThat(helloMockClient.hello("Mock")).isEqualTo("Hello Mock!"); 
3

    }

}
Copy to Clipboard Toggle word wrap

1
在这里,我们对返回 HelloService 接口的模拟的方法使用 @CXFEndpoint 注释。不需要 @jakarta.enterprise.inject.Produces 注解,因为 Quarkus CXF 声明 @CXFEndpoint 作为定义注解的 bean。
2
客户端在 application.properties 中配置以连接到 http://localhost:8080/soap/helloMock
3
断言可确保服务实施可以按预期工作。

5.1.10.2. JAX-WS 提供商

JAX-WS 提供程序 受到全面支持,可以按照如下所示实施。

根据以下示例 供应商 实现:

import jakarta.xml.transform.stream.StreamSource;
import jakarta.xml.ws.BindingType;
import jakarta.xml.ws.Provider;
import jakarta.xml.ws.Service;
import jakarta.xml.ws.ServiceMode;
import jakarta.xml.ws.WebServiceProvider;
import java.io.StringReader;

@WebServiceProvider
@ServiceMode(value = Service.Mode.PAYLOAD)
public class StreamSourcePayloadProvider implements Provider<StreamSource> {

    public StreamSourcePayloadProvider() {
    }

    public StreamSource invoke(StreamSource request) {
        String payload = StaxUtils.toString(request);

        // Do some interesting things ...

        StreamSource response = new StreamSource(new StringReader(payload));
        return response;
    }
}
Copy to Clipboard Toggle word wrap

application.properties 可以配置如下。

# A web service endpoint with the Provider implementation class
quarkus.cxf.endpoint."/stream-source".implementor=org.acme.StreamSourcePayloadProvider
Copy to Clipboard Toggle word wrap
类加载

供应商 类首先通过 CDI 加载。

如果没有 CDI Bean 可用,则会调用没有参数的构造器来实例化每个类。

5.1.10.3. REST 和 SOAP 端点

有时,使用 Quarkus CXF 扩展的同一项目中可能需要 REST 端点。REST 端点路径必须与 SOAP 端点路径不同(为了避免在两个协议之间请求转发冲突)。

例如,如果在 WSDL 中声明了 WeatherWebService 接口,您可以先创建 org.acme.cxf.WeatherWebServiceImpl 类,如下所示:

package org.acme.cxf;

import ...

@Slf4j
@WebService(endpointInterface = "org.acme.cxf.WeatherWebService")
public class WeatherWebServiceImpl implements WeatherWebService {

    @Inject
    BackEndWeatherService backEndWeatherService;

    private Map<String, DailyTemperature> dailyTempByZipCode = Collections.synchronizedMap(new LinkedHashMap<>());

    public WeatherWebServiceImpl() {
        this.dailyTempByZipCode.addAll(
                this.backEndWeatherService.getDailyForecast(Instant.now()));
    }

    @Override
    public DailyTemperature estimationTemperatures(String zipCode) {
        log.info("Daily estimation temperatures forecast called with '{}' zip code paramter", zipCode);
        return this.dailyTempByZipCode.get(zipCode);
    }
}
Copy to Clipboard Toggle word wrap

之后,您需要为 CXF Web 服务指定根上下文,如 配置文档 中所示,以根据其根上下文路径分割 REST (例如 RESTEasy)和 SOAP 路由。

CXF 的 SOAP 属性:

quarkus.cxf.path=/soap
quarkus.cxf.endpoint."/weather".implementor=org.acme.cxf.WeatherWebServiceImpl
Copy to Clipboard Toggle word wrap

现在,请想象以下 RESTEasy 端点:

package org.acme.reasteasy;

import ...

@Slf4j
@Path("/healthcheck")
public class HealthCheckResource {

    @Inject
    BackEndWeatherService backEndWeatherService;

    @GET
    public Response doHealthCheck() {
        if(this.backEndWeatherService.isAvailable()) {
            return Response.ok().build();
        } else {
            return Response.status(Response.Status.SERVICE_UNAVAILABLE);
        }
    }
}
Copy to Clipboard Toggle word wrap

您可以通过配置 REASTEasy 路径来分隔 REST 端点:

quarkus.resteasy.path=/rest
Copy to Clipboard Toggle word wrap

现在,您应该能够向在单个项目中部署的 REST 和 SOAP 端点发送请求,该端点位于:

5.1.10.4. 在反向代理后运行

SOAP 请求旨在向 Quarkus 上运行的服务进行路由,该代理可以生成额外标头(如 X-Forwarded-Host),以保留来自面向客户端的代理服务器的信息,并在涉及时更改或丢失。在这些情况下,可将 Quarkus 配置为自动更新协议、主机、端口和 URI 等信息,反映这些标头中的值。

提示

如需更多详细信息,请参阅 Quarkus HTTP 参考

对各种 X-Forwarded 标头的 quarkus CXF 支持可以在 Quarkus 配置中正常工作。

重要

激活此功能会使服务器暴露几个安全问题(例如,信息欺骗)。只有在反向代理后运行时,请考虑将其激活。

以下是相关的 Quarkus 属性及其对 Quarkus CXF 的影响:

  • quarkus.http.proxy.proxy-address-forwarding - 主开关来启用请求目的地部分的重写。

    • 如果启用,在整个 CXF 服务器堆栈中,请求字段的重写将有效。
    • 如果启用,则使用通过 X-Forwarded-ProtoX-Forwarded-Port 标头传递的值来设置协议部分,以及 jakarta.servlet.http.HttpServletRequest.getRequestURL () 返回的 URL 的端口部分。
    • 如果启用,通过 X-Forwarded-For 传递的值将由 jakarta.servlet.ServletRequest.getRemoteAddr () 返回。
  • quarkus.http.proxy.enable-forwarded-host - 启用 jakarta.servlet.http.HttpServletRequest.getRequestURL () 返回的 URL 部分的重写。实际主机名从通过 quarkus.http.proxy.forwarded-host-header 配置的标头获取(默认为 X-Forwarded-Host)。
  • quarkus.http.proxy.enable-forwarded-prefix - 启用 jakarta.servlet.http.HttpServletRequest.getRequestURL () 返回的 URL 的路径部分的重写,以及 jakarta.servlet.http.HttpServletRequest.getRequestURI () 返回的 URI 部分。实际路径前缀从通过 quarkus.http.proxy.forwarded-prefix-header 配置的标头获取(默认为 X-Forwarded-Prefix)。

以下是复制到 application.properties 的最常见片段:

quarkus.http.proxy.proxy-address-forwarding = true
quarkus.http.proxy.enable-forwarded-host = true
quarkus.http.proxy.enable-forwarded-prefix = true
Copy to Clipboard Toggle word wrap

这些设置的可观察影响之一是更改在 http://localhost:8080/services/my-service?wsdl 上提供的 WSDL 中的位置值。例如,如果请求包含以下标头

X-Forwarded-Proto: https
X-Forwarded-Host: api.example.com
X-Forwarded-Port: 443
X-Forwarded-Prefix: /my-prefix
Copy to Clipboard Toggle word wrap

然后 http://localhost:8080/services/my-service?wsdl 上提供的 WSDL 将包含 以下位置

...
<soap:address location="https://api.example.com:443/my-prefix/services/my-service"/>
...
Copy to Clipboard Toggle word wrap

5.1.11. 高级 SOAP 客户端主题

有关实现 SOAP 客户端的更多详细信息,请查看以下章节:

5.1.11.1. client-endpoint-url 默认值

如果省略 application.properties 中的 client-endpoint-url 属性,则 CXF Quarkus 扩展将假定该服务在 http://localhost:8080/{service-path} 中发布,其中 {service-path} 派生自

  • 配置属性 quarkus.cxf.path (如果指定); 和
  • SEI 的类名称(小写)

给定 quarkus.cxf.path = /wsCalculatorService 的默认有效 client-endpoint-urlhttp://localhost:8080/ws/org.jboss.eap.quickstarts.wscalculator.calculator.calculatorservice

如果没有指定 quarkus.cxf.pathclient-endpoint-url 将只是 http://localhost:8080/org.jboss.eap.quickstarts.wscalculator.calculator.calculatorservice

5.1.11.2. 配置多个客户端

在上例中,我们只配置了一个名为 myCalculator 的单个客户端。当然,您可以使用多个标识符配置指向不同 URL 和/或实施不同 SEI 的多个客户端:

application.properties

cxf.it.calculator.baseUri = http://localhost:8082
quarkus.cxf.client.myCalculator.wsdl = ${cxf.it.calculator.baseUri}/calculator-ws/CalculatorService?wsdl
quarkus.cxf.client.myCalculator.client-endpoint-url = ${cxf.it.calculator.baseUri}/calculator-ws/CalculatorService
quarkus.cxf.client.myCalculator.service-interface = org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService

# another client
quarkus.cxf.client.anotherCalculator.wsdl = https://acme.com/ws/WeatherService?wsdl
quarkus.cxf.client.anotherCalculator.client-endpoint-url = https://acme.com/ws/WeatherService
quarkus.cxf.client.anotherCalculator.service-interface = org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService
Copy to Clipboard Toggle word wrap

5.1.11.3. 通过 @CXFClient注入的客户端的 CDI 范围

quarkus CXF 在默认的 @Dependent 范围中生成通过 @io.quarkiverse.cxf.annotation.CXFClient 注入的所有客户端。因此,注入的实例的实际范围取决于注入客户端的 bean 的 CDI 范围。

因此,如果客户端注入 @ApplicationScoped bean,则客户端实例也会变为 @ApplicationScoped。如果客户端注入 @RequestScoped bean,则客户端实例也会变为 @RequestScoped

如果您需要在应用程序启动后 动态配置客户端,则此行为会变得方便。

5.1.11.4. 启动时编程客户端配置

要在应用程序启动时配置所有客户端,您可以实施 HTTPConduitConfigurer,并使用 StartupEvent observer 方法在 CXF 总线上设置它。

在以下示例中,我们配置 HTTPClientPolicy 的一些方面。可以利用同样的方法来自定义授权策略 ,Proxy AuthorizationPolicy ,甚至您的客户端的 TLSClientParameters

import io.quarkus.runtime.StartupEvent;
import jakarta.enterprise.event.Observes;
import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transport.http.HTTPConduitConfigurer;
...

void onStart(@Observes StartupEvent ev) {

     HTTPConduitConfigurer httpConduitConfigurer = new HTTPConduitConfigurer() {
         public void configure(String name, String address, HTTPConduit conduit) {
             conduit.getClient().setAllowChunking(false);
             conduit.getClient().setAutoRedirect(true);
         }
     };

     final Bus bus = BusFactory.getDefaultBus();
     bus.setExtension(httpConduitConfigurer, HTTPConduitConfigurer.class);
}
Copy to Clipboard Toggle word wrap

5.1.11.5. 动态客户端配置

有时,您需要在应用 启动后重新配置客户端,甚至在 每个请求之前重新配置客户端。例如,这可能是每个请求发送到不同的远程 URL 的情况。

CXF 提供了一个 API,用于设置远程端点的 URL。但是,在可能从其他线程访问的客户端实例上使用该 API 可能会导致竞争条件。

5.1.11.5.1. 防止对 CXF 客户端的并发访问

如果您的客户端用作外部请求的一部分,您可以将客户端注入到 @RequestScoped bean 中。然后,每个请求都由一个全新的客户端实例提供,您可以安全地进行配置。

例如,如果您的客户端从 REST-handler 方法调用,或从提供外部请求的 @WebMethod 调用时,此解决方案适用。

package io.quarkiverse.cxf.client.it;

import java.util.Map;

import jakarta.enterprise.context.RequestScoped;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.xml.ws.BindingProvider;

import org.jboss.eap.quickstarts.wscalculator.calculator.CalculatorService;

import io.quarkiverse.cxf.annotation.CXFClient;

/*
 * The @RequestScoped annotation causes that the REST resource is instantiated
 * anew for every call of the add() method. Therefore also a new client instance
 * is injected into the calculator field for every request served by add().
 */
@RequestScoped
@Path("/cxf/dynamic-client")
public class DynamicClientConfigRestResource {

    @CXFClient("requestScopedVertxHttpClient")
    CalculatorService calculator;

    @GET
    @Path("/add")
    @Produces(MediaType.TEXT_PLAIN)
    public int add(@QueryParam("a") int a, @QueryParam("b") int b, @QueryParam("baseUri") String baseUri) {
        Map<String, Object> ctx = ((BindingProvider) calculator).getRequestContext();
        /* We are setting the remote URL safely, because the client is associated exclusively with the current request */
        ctx.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, baseUri + "/calculator-ws/CalculatorService");
        return calculator.add(a, b);
    }

}
Copy to Clipboard Toggle word wrap

5.1.11.6. 纯客户端应用程序

Quarkus batch (如定期调度)或命令行应用程序,在没有 HTTP 服务器的情况下可以这样做。使用以下属性以防止在启动时启动 HTTP 服务器:

quarkus.http.host-enabled = false
Copy to Clipboard Toggle word wrap

5.1.11.7. 防止资源泄漏

CXF 客户端代理实现 java.io.Closeable。因此,在客户端不再需要释放所有关联的系统资源 (如线程)后,调用(关闭) proxy)。close () 非常重要。

quarkus CXF 在被 CDI 容器处理后立即自动关闭通过 @io.quarkiverse.cxf.annotation.CXFClient 注入的客户端。

对于手动创建的客户端代理,您需要调用 (关闭) proxy).close ()

import java.net.URL;
import javax.xml.namespace.QName;
import jakarta.xml.ws.Service;
import java.io.Closeable;

final URL serviceUrl = new URL("http://localhost/myService?wsdl");
final QName qName = new QName("http://acme.org/myNamespace", "MyService");
final Service service = jakarta.xml.ws.Service.create(serviceUrl, qName);
final MyService proxy = service.getPort(MyService.class);

try {
    proxy.doSomething();
} finally {
    ((Closeable) proxy).close();
}
Copy to Clipboard Toggle word wrap

5.1.12. Camel Integration

Camel Quarkus 支持 CXF 版本 2.12.0。在 hood 下,实施基于 Quarkus CXF。因此,Camel Quarkus 也提供 Quarkus CXF 中的所有功能。

详情请参考 Camel Quarkus CXF SOAP 扩展文档。

5.1.13. 例子

代码库的 integration-tests 文件夹 提供了各种示例,演示了如何广泛使用此扩展。

Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

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

让开源更具包容性

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

關於紅帽

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

Theme

© 2026 Red Hat
返回顶部