使用 Maven 和重用 类 生成 WS 客户端

WS Client generation using maven and reusing classes

我的应用程序分为两层,其中后端层公开了一堆 Web 服务,而表示层使用这些服务来获取业务数据。

为了使我的构造尽可能自动化,我将所有生成的 WSDL 和相关的 XSD 文件打包成一个 ZIP 依赖​​项(使用 Maven 汇编程序插件),它在安装时上传到我的 nexus相.

然后在 UI POM 上我将这个 ZIP 文件(使用 maven 依赖插件)解压到本地目录 (src/main/resources/wsdl),然后使用 CXF-CODEGEN 插件生成客户端。

问题是——许多这些服务共享公共模型实体,例如很多方法使用 Catalogue class,这是我用于对象的实体ID 和文本值。

使用 CXF 生成的代码,对于我拥有的每个 Web 服务,我最终得到不同的 Catalogue class(具有不同的包名称),因此在我的代码中失去了 OO 多态性功能,因为它们都是class实践中的不同。

我听说您可以通过某种方式先将 XSD classes 编译成名为 "episodes" 的东西,然后将这些剧集文件提供给 CXF 以便告诉它,从而避免这个问题利用它们而不是生成 classes.

我尝试添加一个额外的编译步骤,其中我 运行 maven-jaxb2-plugin 从 XSD 生成 classes,但我得到了一个 org.xml.sax.SAXParseException; (...) 'catalogueDTO' is already defined 异常,因为此 class 在 XSD 个文件中重复出现。

任何人都可以指出正确的方向吗?

当您的 Web 服务中有一些基础 classes 将跨服务和操作使用时,最好在单个 XSD 中声明这些 classes,然后在您的项目中包含并使用此 XSD。

以下示例将向您展示如何操作。我将此示例基于所提供的信息,因此它可能不完全符合您的需要,但会向您展示概念。

首先让我们创建一个 Catalog.xsd 文件并使用以下代码在该文件中声明我们的目录对象:

<?xml version="1.0" encoding="utf-8" ?>

<xs:schema xmlns="http://www.yourcompany.com/Services_V1/CommonType"
       elementFormDefault="qualified"
       targetNamespace="http://www.yourcompany.com/Services_V1/CommonType"
       xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="CatalogueID_Type">
    <xs:annotation>
        <xs:documentation>The Catalogue ID is an integer value that is used to uniquely identify the catalog item on the database.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="xs:int" />
</xs:simpleType>
<xs:simpleType name="CatalogueValue_Type">
    <xs:annotation>
        <xs:documentation>The catalog is a string value that will contain the information describing the catalog key.</xs:documentation>
    </xs:annotation>
    <xs:restriction base="xs:string" />
</xs:simpleType>
<xs:complexType name="Catalogue_Type">
    <xs:sequence>
        <xs:element ref="CatalogID"
                    minOccurs="1"
                    maxOccurs="1" />
        <xs:element ref="CatalogueValue"
                    minOccurs="0"
                    maxOccurs="1" />
    </xs:sequence>
</xs:complexType>
<xs:element name="CatalogID"
            type="CatalogueID_Type" />
<xs:element name="CatalogueValue"
            type="CatalogueValue_Type" />
<xs:element name="Catalogue"
            type="Catalogue_Type" />

或者,如果您喜欢视觉表示:

需要注意的是目录 class/object 有一个命名空间 http://www.yourcompany.com/Services_V1/CommonType 我们将在我们的 WSDL 中再次使用这个命名空间。该对象现在将由 BathroomCatalogue 服务和 KicthenCatalogue 服务这两个服务使用。为简单起见,我在名为 GetCatalogueItem 的服务中只有一个操作。对于这些服务中的每一项,我都将包含 catalog.xsd 文件并重复使用此目录对象。

这是浴室服务的 WSDL:

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions name="BathroomCatalogueSRV"
              targetNamespace="http://www.yourcompany.com/Services_V1/BathroomCatalogueService"
              xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
              xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
              xmlns:tns="http://www.yourcompany.com/Services_V1/BathroomCatalogueService"
              xmlns:xs="http://www.w3.org/2001/XMLSchema"
              xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
              xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
              xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
              xmlns:catalogue="http://www.yourcompany.com/Services_V1/CommonType">
<wsdl:types>
    <xs:schema elementFormDefault="qualified"
               targetNamespace="http://example.com/">
        <xs:import schemaLocation="Catalog.xsd"
                   namespace="http://www.yourcompany.com/Services_V1/CommonType" />

    </xs:schema>
</wsdl:types>
<wsdl:message name="GetCatalogueItemReq">
    <wsdl:part name="GetCatalogueItemReq"
               element="catalogue:Catalogue" />
</wsdl:message>
<wsdl:message name="GetCatalogueItemRs">
    <wsdl:part name="GetCatalogueItemRs"
               element="catalogue:Catalogue" />
</wsdl:message>
<wsdl:portType name="BathroomCataloguePortType">
    <wsdl:operation name="GetCatalogueItem">
        <wsdl:input message="tns:GetCatalogueItemReq" />
        <wsdl:output message="tns:GetCatalogueItemRs" />
    </wsdl:operation>
</wsdl:portType>
<wsdl:binding name="BathroomCatalogueBinding"
              type="tns:BathroomCataloguePortType">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
                  style="document" />
    <wsdl:operation name="GetCatalogueItem">
        <wsdl:input />
        <wsdl:output />
    </wsdl:operation>
</wsdl:binding>
<wsdl:service name="BathroomCatalogueService">
    <wsdl:port name="BathroomCataloguePort"
               binding="tns:BathroomCatalogueBinding">
        <soap:address location="http://www.yourcompany.com/Services_V1/BathroomCatalogueService" />
    </wsdl:port>
</wsdl:service>

或者,如果您想要视觉表示:

Kitchen WSDL 如下所示:

<?xml version="1.0" encoding="utf-8"?>

<wsdl:definitions name="KitchenCatalogueSRV"
              targetNamespace="http://www.yourcompany.com/Services_V1/KitchenCatalogueService"
              xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
              xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
              xmlns:tns="http://www.yourcompany.com/Services_V1/KitchenCatalogueService"
              xmlns:xs="http://www.w3.org/2001/XMLSchema"
              xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
              xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
              xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
              xmlns:catalogue="http://www.yourcompany.com/Services_V1/CommonType">
<wsdl:types>
    <xs:schema elementFormDefault="qualified"
               targetNamespace="http://example.com/">
        <xs:import schemaLocation="Catalog.xsd"
                   namespace="http://www.yourcompany.com/Services_V1/CommonType" />

    </xs:schema>
</wsdl:types>
<wsdl:message name="GetCatalogueItemReq">
    <wsdl:part name="GetCatalogueItemReq"
               element="catalogue:Catalogue" />
</wsdl:message>
<wsdl:message name="GetCatalogueItemRs">
    <wsdl:part name="GetCatalogueItemRs"
               element="catalogue:Catalogue" />
</wsdl:message>
<wsdl:portType name="KitchenCataloguePortType">
    <wsdl:operation name="GetCatalogueItem">
        <wsdl:input message="tns:GetCatalogueItemReq" />
        <wsdl:output message="tns:GetCatalogueItemRs" />
    </wsdl:operation>
</wsdl:portType>
<wsdl:binding name="KitchenCatalogueBinding"
              type="tns:KitchenCataloguePortType">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
                  style="document" />
    <wsdl:operation name="GetCatalogueItem">
        <wsdl:input />
        <wsdl:output />
    </wsdl:operation>
</wsdl:binding>
<wsdl:service name="KitchenCatalogueService">
    <wsdl:port name="KitchenCataloguePort"
               binding="tns:KitchenCatalogueBinding">
        <soap:address location="http://www.yourcompany.com/Services_V1/KitchenCatalogueService" />
    </wsdl:port>
</wsdl:service>

再一次在视觉上:

在这两个 WSDL 文件中,我都包含了 catalog.xsd 文件。您可以在以下代码行中看到这一点:

<xs:import schemaLocation="Catalog.xsd" 
 namespace="http://www.yourcompany.com/Services_V1/CommonType" />

现在,当我将这些 WSDL 和 XSD 与 cxf 一起使用时,两个服务将只使用一个目录 object/class。我很快做了一个shell项目,生成了如下结构:

请注意,我声明的 CatalogueType 现在与我声明它的命名空间位于同一个包中。

查看服务时 classes 浴室和厨房服务都将使用此服务 class。在厨房服务 class 它使用 com.yourcompany.services_v1.commontype.CatalogueType.

@WebService(targetNamespace = "http://www.yourcompany.com/Services_V1/KitchenCatalogueService", name = "KitchenCataloguePortType")
@XmlSeeAlso({com.yourcompany.services_v1.commontype.ObjectFactory.class})
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface KitchenCataloguePortType {

@WebResult(name = "Catalogue", targetNamespace = "http://www.yourcompany.com/Services_V1/CommonType", partName = "GetCatalogueItemRs")
@WebMethod(operationName = "GetCatalogueItem")
public com.yourcompany.services_v1.commontype.CatalogueType getCatalogueItem(
    @WebParam(partName = "GetCatalogueItemReq", name = "Catalogue", targetNamespace = "http://www.yourcompany.com/Services_V1/CommonType")
    com.yourcompany.services_v1.commontype.CatalogueType getCatalogueItemReq
);
}

而在厨房服务中 class 它使用 com.yourcompany.services_v1.commontype.CatalogueType

@WebService(targetNamespace = "http://www.yourcompany.com/Services_V1/BathroomCatalogueService", name = "BathroomCataloguePortType")
@XmlSeeAlso({com.yourcompany.services_v1.commontype.ObjectFactory.class})
@SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public interface BathroomCataloguePortType {

@WebResult(name = "Catalogue", targetNamespace = "http://www.yourcompany.com/Services_V1/CommonType", partName = "GetCatalogueItemRs")
@WebMethod(operationName = "GetCatalogueItem")
public com.yourcompany.services_v1.commontype.CatalogueType getCatalogueItem(
    @WebParam(partName = "GetCatalogueItemReq", name = "Catalogue", targetNamespace = "http://www.yourcompany.com/Services_V1/CommonType")
    com.yourcompany.services_v1.commontype.CatalogueType getCatalogueItemReq
);
}

提醒您切勿编辑这些 classes,因为它们是由 CXF 派生和生成的,您的更改将会丢失。因此,通过优先使用 WSDL 方法,您必须确保正确构建 XSD 文件和 WSDL。

让我知道这是否有意义。