Apache CXF 与 WSDL 优先


你开发的 CXF-POJO 应用程序导致客户端和服务器之间的耦合非常紧密。直接访问服务接口也可能构成严重的安全威胁。因此,通常需要客户端和服务器之间的解耦,这是通过使用 WSDL(Web 服务描述语言)来实现的。

我们在基于 XML 的 WSDL 文档中编写 Web 服务接口。我们将使用一个工具将此 WSDL 映射到 Apache CXF 接口,然后由我们的客户端和服务器应用程序实现和使用。为了提供解耦,从 WSDL 开始是一种首选方式。为此,你首先需要学习一门新语言——WSDL。编写 WSDL 需要谨慎的方法,如果你在开始工作之前能够对此有所了解,那就更好了。

在本课中,我们将从在 WSDL 文档中定义 Web 服务接口开始。我们将从 WSDL 开始学习如何使用 CXF 创建服务器和客户端应用程序。我们将保持应用程序简单,以保持专注于 CXF 的使用。创建服务器应用程序后,我们将使用内置的 CXF 类将其发布到所需的 URL。

首先,让我们描述一下我们将要使用的 WSDL。

HelloWorld 的 WSDL


我们将要实现的 web 服务将有一个名为 问候 接受一个 string 参数保存用户名,并在向用户名附加问候消息后向调用者返回字符串消息。完整的wsdl如下图:

// 你好.wsdl
<?xml version = "1.0" encoding = "UTF-8"?>
<wsdl:definitions xmlns:soap = "http:// schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tns = "http:// helloworld.newbiego.com/"
    xmlns:wsdl = "http:// schemas.xmlsoap.org/wsdl/"
    xmlns:xsd = "http:// www.w3.org/2001/XMLSchema"
    name = "HelloWorld"
    targetNamespace = "http:// helloworld.newbiego.com/">
    <wsdl:types>
        <xsd:schema attributeFormDefault = "unqualified"
            elementFormDefault = "qualified"
            targetNamespace = "http:// helloworld.newbiego.com/">
            <xsd:element name = "greetings" type = "tns:greetings"/>
            <xsd:complexType name = "greetings">
                <xsd:sequence>
                    <xsd:element minOccurs = "0" name = "arg0" type = "xsd:string"/>
                </xsd:sequence>
            </xsd:complexType>
            <xsd:element name = "greetingsResponse"
            type = "tns:greetingsResponse"/>
            <xsd:complexType name = "greetingsResponse">
                <xsd:sequence>
                    <xsd:element minOccurs = "0" name = "return" type = "xsd:string"/>
                </xsd:sequence>
            </xsd:complexType>
        </xsd:schema>
    </wsdl:types>
    <wsdl:message name = "greetings">
        <wsdl:part element = "tns:greetings" name = "parameters"> </wsdl:part>
    </wsdl:message>
    <wsdl:message name = "greetingsResponse">
        <wsdl:part element = "tns:greetingsResponse" name = "parameters"> </wsdl:part>
    </wsdl:message>
    <wsdl:portType name = "HelloWorldPortType">
        <wsdl:operation name = "greetings">
            <wsdl:input message = "tns:greetings" name = "greetings">  </wsdl:input>
            <wsdl:output message = "tns:greetingsResponse" name = "greetingsResponse">
            </wsdl:output>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name = "HelloWorldSoapBinding" type = "tns:HelloWorldPortType">
        <soap:binding style = "document"
        transport = "http:// schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name = "greetings">
            <soap:operation soapAction = "" style = "document"/>
            <wsdl:input name = "greetings"></wsdl:input>
            <wsdl:output name = "greetingsResponse">
                <soap:body use = "literal"/>
            </wsdl:output>
            </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name = "HelloWorldService">
        <wsdl:port binding = "tns:HelloWorldSoapBinding" name = "HelloWorldPort">
            <soap:address location = "http:// localhost:9090/HelloServerPort"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

请注意,编写语法正确的 wsdl 一直是开发人员面临的挑战;有许多工具和在线编辑器可用于创建 wsdl。这些编辑器会询问你要实现的消息名称以及你希望在消息中传递的参数以及你希望客户端应用程序接收的返回消息的类型。如果你了解 wsdl 语法,你可以手动编写整个文档的代码或使用其中一个编辑器来创建你自己的。

在上面的 wsdl 中,我们定义了一个名为 问候 .消息被传递到调用的服务 HelloWorldService 运行在 http://localhost:9090/HelloServerPort。

有了这个,我们现在将继续进行服务器开发。在开发服务器之前,我们需要为我们的 Web 服务生成 Apache CXF 接口。这是从给定的 wsdl 中完成的。为此,你可以使用一个名为 wsdl2java .

wsdl2java 插件


由于我们将使用 maven 来构建项目,因此你需要将以下插件添加到 pom.xml file.

<plugins>
    <plugin>
        <groupId>org.apache.cxf</groupId>
        <artifactId>cxf-codegen-plugin</artifactId>
        <version>3.3.0</version>
        <executions>
            <execution>
                <id>generate-sources</id>
                <phase>generate-sources</phase>
                <configuration>
                    <wsdlOptions>
                        <wsdlOption>
                            <wsdl>src/main/resources/hello.wsdl</wsdl>
                            <faultSerialVersionUID> 1 </faultSerialVersionUID>
                        </wsdlOption>
                    </wsdlOptions>
                </configuration>
                <goals>
                    <goal>wsdl2java</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
</plugins>

请注意,我们指定的位置 wsdl file as src/main/resources/Hello.wsdl .你必须确保为你的项目创建适当的目录结构并添加前面显示的 你好.wsdl 文件到指定的文件夹。

The wsdl2java 插件将编译这个 wsdl 并在预定义的文件夹中创建 Apache CXF 类。此处显示了完整的项目结构,供你随时参考。

WSDL2Apache CXF Predefined Folder

现在,你已准备好使用 wsdl2java 生成的类。 wsdl2java创建的类如下图所示:

WSDL2Apache CXF generated classes

生成的服务接口


在生成的类列表中,你一定注意到其中之一是 Apache CXF 接口 - 这是 HelloWorldPortType.java .在你的代码编辑器中检查此文件。文件内容如下所示,供你参考:

// HelloWorldPortType.java
package com.newbiego.helloworld;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
/**
* This class was generated by Apache CXF 3.3.0
* 2019-02-11T12:05:55.220+05:30
* Generated source version: 3.3.0
*
*/

@WebService(targetNamespace = "http:// helloworld.newbiego.com/",
    name = "HelloWorldPortType")
@XmlSeeAlso({ObjectFactory.class})
public interface HelloWorldPortType {
    @WebMethod
    @RequestWrapper(localName = "greetings", targetNamespace =
        "http:// helloworld.newbiego.com/", className =
        "com.newbiego.helloworld.Greetings")
        @ResponseWrapper(localName = "greetingsResponse", targetNamespace =
            "http:// helloworld.newbiego.com/", className =
            "com.newbiego.helloworld.GreetingsResponse")
    @WebResult(name = "return", targetNamespace =
        "http:// helloworld.newbiego.com/")
    public java.lang.String greetings(
        @WebParam(name = "arg0", targetNamespace =
        "http:// helloworld.newbiego.com/")
        java.lang.String arg0
    );
}

请注意,该接口包含一个名为 问候 .这是我们 wsdl 中的消息类型。这 wsdl2java 工具已将此方法添加到生成的界面中。现在,你可以理解,无论你在 wsdl 中编写什么消息,都会在接口中生成相应的方法。

现在,你的任务是实现与你在 wsdl 中定义的各种消息相对应的所有这些方法。请注意,在前面的 Apache CXF-First 示例中,我们从 Web 服务的 Apache CXF 接口开始。在这种情况下,Apache CXF 接口是从 wsdl 创建的。

实现服务接口


服务接口的实现是微不足道的。完整的实现如下表所示:

// HelloWorldImpl.java
package com.newbiego.helloworld;
public class HelloWorldImpl implements HelloWorldPortType {
    @Override
    public String greetings(String name) {
        return ("hi " + name);
    }
}

该代码实现了唯一的接口方法,称为 问候 .该方法采用一个参数 string 类型,为其添加“hi”消息并将结果字符串返回给调用者。

接下来,我们将编写服务器应用程序。

开发服务器


开发服务器应用程序再次变得微不足道。在这里,我们将使用提供的 CXF Endpoint 类来发布我们的服务。这是在以下两行代码中完成的:

HelloWorldPortType implementor = new HelloWorldImpl();
    Endpoint.publish("http:// localhost:9090/HelloServerPort",
        implementor,
        new LoggingFeature());

首先,我们创建一个服务实现类的对象—— HelloWorldImpl .然后,我们将此引用作为第二个参数传递给 publish 方法。第一个参数是服务发布到的地址——客户端将使用这个 URL 来访问服务。服务器应用程序的完整源码在这里给出:

// 服务器.java
package com.newbiego.helloworld;
import javax.xml.ws.Endpoint;
import org.apache.cxf.ext.logging.LoggingFeature;
public class Server {
    public static void main(String[] args) throws Exception {
        HelloWorldPortType implementor = new HelloWorldImpl();
        Endpoint.publish("http:// localhost:9090/HelloServerPort",
            implementor,
            new LoggingFeature());
        System.out.println("Server ready...");
        Thread.sleep(5 * 60 * 1000);
        System.out.println("Server exiting");
        System.exit(0);
    }
}

要构建此服务器类,你需要在你的 pom.xml .如下图所示:

<profile>
    <id>server</id>
    <build>
        <defaultGoal>test</defaultGoal>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.6.0</version>
                <executions>
                    <execution>
                        <phase>test</phase>
                        <goals>
                            <goal>java</goal>
                        </goals>
                        <configuration>
                            <mainClass>
                                com.newbiego.helloworld.Server
                            </mainClass>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http-jetty</artifactId>
            <version>3.3.0</version>
        </dependency>
    </dependencies>
</profile>

请注意, Server 类在配置中指定。此外,依赖标签指定我们将使用嵌入式 jetty Web 服务器来部署我们的服务器应用程序。

部署服务器


最后,要部署服务器应用程序,你需要在 pom.xml 中再进行一次修改,以将你的应用程序设置为 Web 应用程序。你需要添加到你的代码 pom.xml 下面给出:

<defaultGoal>install</defaultGoal>
<pluginManagement>
    <plugins>
        <plugin>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.2.2</version>
            <configuration>
                <webXml>src/main/webapp/WEB-INF/web.xml</webXml>
                <webResources>
                    <resource>
                        <directory>src/main/resources</directory>
                        <targetPath>WEB-INF</targetPath>
                        <includes>
                            <include>*.wsdl</include>
                        </includes>
                    </resource>
                </webResources>
            </configuration>
        </plugin>
    </plugins>
</pluginManagement>

在部署应用程序之前,你需要向项目中添加另外两个文件。这些显示在下面的屏幕截图中:

Before Deploy WSDL Application

这些文件是定义映射的 CXF 标准文件 CXFServlet .内的代码 web.xml 文件显示在这里供你快速参考:

// cxf-servlet.xml
<web-app xmlns = "http:// java.sun.com/xml/ns/javaee"
    xmlns:xsi = "http:// www.w3.org/2001/XMLSchema-instance" version="2.5"
    xsi:schemaLocation = "http:// java.sun.com/xml/ns/javaee
    http:// java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <display-name>cxf</display-name>
    <servlet>
        <description>Apache CXF Endpoint</description>
        <display-name>cxf</display-name>
        <servlet-name>cxf</servlet-name>
        <servlet-class>
            org.apache.cxf.transport.servlet.CXFServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>cxf</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>60</session-timeout>
    </session-config>
</web-app>

In the cxf-servlet.xml 你声明服务端点的属性。这显示在下面的代码片段中:

<beans ...>
    <jaxws:endpoint xmlns:helloworld = "http:// newbiego.com/"
        id="helloHTTP"
        address = "http:// 本地主机:9090/HelloServerPort"
        serviceName = "helloworld:HelloServiceService"
        endpointName = "helloworld:HelloServicePort">
    </jaxws:endpoint>
</beans>

在这里,我们定义了服务端点的 id、服务可用的地址、服务名称和端点名称。现在,你了解了 CXF servlet 如何路由和处理你的服务。

最后的 pom.xml


The pom.xml 包括更多的依赖项。我们没有描述所有的依赖关系,而是在下面包含了 pom.xml 的最终版本:

<?xml version="1.0" encoding = "UTF-8"?>
<project xmlns = "http:// maven.apache.org/POM/4.0.0"
    xmlns:xsi = "http:// www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation = "http:// maven.apache.org/POM/4.0.0 http:
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.newbiego</groupId>
    <artifactId>cxf-wsdl</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    <build>
        <defaultGoal>install</defaultGoal>
        <pluginManagement>
            <plugins>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.2.2</version>
                    <configuration>
                        <webXml>src/main/webapp/WEB-INF/web.xml</webXml>
                        <webResources>
                            <resource>
                                <directory>src/main/resources</directory>
                                <targetPath>WEB-INF</targetPath>
                                <includes>
                                    <include>*.wsdl</include>
                                </includes>
                            </resource>
                        </webResources>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.cxf</groupId>
                <artifactId>cxf-codegen-plugin</artifactId>
                <version>3.3.0</version>
                <executions>
                    <execution>
                        <id>generate-sources</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <wsdlOptions>
                                <wsdlOption>
                                    <wsdl>src/main/resources/Hello.wsdl</wsdl>
                                    <faultSerialVersionUID>1</faultSerialVersionUID>
                                </wsdlOption>
                            </wsdlOptions>
                        </configuration>
                        <goals>
                            <goal>wsdl2java</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <profiles>
        <profile>
            <id>server</id>
            <build>
                <defaultGoal>test</defaultGoal>
                <plugins>
                    <plugin>
                        <groupId>org.codehaus.mojo</groupId>
                        <artifactId>exec-maven-plugin</artifactId>
                        <version>1.6.0</version>
                        <executions>
                            <execution>
                                <phase>test</phase>
                                <goals>
                                    <goal>java</goal>
                                </goals>
                                <configuration>
                                    <mainClass>
                                        com.newbiego.helloworld.Server
                                    </mainClass>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
            <dependencies>
                <dependency>
                    <groupId>org.apache.cxf</groupId>
                    <artifactId>cxf-rt-transports-http-jetty</artifactId>
                    <version>3.3.0</version>
                </dependency>
            </dependencies>
        </profile>
        <profile>
            <id>client</id>
            <build>
                <defaultGoal>test</defaultGoal>
                <plugins>
                    <plugin>
                        <groupId>org.codehaus.mojo</groupId>
                        <artifactId>exec-maven-plugin</artifactId>
                        <executions>
                            <execution>
                                <phase>test</phase>
                                <goals>
                                    <goal>java</goal>
                                </goals>
                                <configuration>
                                    <mainClass>
                                        com.newbiego.helloworld.Client
                                    </mainClass>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
    <dependencies>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>3.3.0</version>
        </dependency>
     
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>3.3.0</version>
        </dependency>
      
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-management</artifactId>
            <version>3.3.0</version>
        </dependency>
      
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-features-metrics</artifactId>
            <version>3.3.0</version>
        </dependency>
      
        <dependency>
            <groupId>org.apache.cxf.xjc-utils</groupId>
            <artifactId>cxf-xjc-runtime</artifactId>
            <version>3.3.0</version>
        </dependency>
      
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-features-logging</artifactId>
            <version>3.3.0</version>
        </dependency>
     
     <dependency>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.6.0</version>
        </dependency>
      
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.8.0-beta2</version>
        </dependency>
      
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http-jetty</artifactId>
            <version>3.3.0</version>
        </dependency>
    </dependencies>
</project>

请注意,它还包括用于构建客户端的配置文件,我们将在后面的部分中很快学习。

运行 HelloWorld 服务


现在,你已准备好运行 Web 应用程序。在命令窗口中,使用以下命令运行构建脚本。

mvn clean install

这将从你的 wsdl 生成适当的 Apache CXF 类,编译你的 Apache CXF 类,在嵌入式码头服务器上部署服务器并运行你的应用程序。

你将在控制台上看到以下消息:

INFO: Setting the server's publish address to be 
http:// 本地主机:9090/HelloServerPort
Server ready...

和以前一样,你可以通过在浏览器中打开服务器 URL 来测试服务器。

Opening Server URL

由于我们没有指定任何操作,因此我们的应用程序只会向浏览器返回一条错误消息。现在,尝试添加 ?wsdl 到你的 URL,你将看到以下输出:

WSDL 输出

所以我们的服务器应用程序按预期运行。你可以使用 SOAP 客户端,例如 Postman 前面描述的以进一步测试你的服务。

本教程的下一部分是编写一个使用我们服务的客户端。

开发客户


在 CXF 应用程序中编写客户端与编写服务器一样重要。这是客户端的完整代码,基本上只包含三行,其余行只是将服务信息打印给用户。

// 客户端.java
package com.newbiego.helloworld;
public class Client {
    public static void main(String[] args) throws Exception {
        // 使用其默认 wsdlurl 创建服务客户端
        HelloWorldService helloServiceService = new HelloWorldService();
        System.out.println("service: " +
            helloServiceService.getServiceName());
        System.out.println("wsdl location: " +
            helloServiceService.getWSDLDocumentLocation());
        HelloWorldPortType helloService =
            helloServiceService.getHelloWorldPort();
        System.out.println(helloService.greetings
        (System.getProperty("user.name")));
    }
}

在这里,我们简单地创建一个我们的服务实例 HelloWorldService , 通过调用获取它的端口 获取HelloWorldPort 方法,然后通过我们的 问候 给它留言。运行客户端,你会看到如下输出:

service: {http:// helloworld.newbiego.com/}HelloWorldService
wsdl location: file:/Users/drsarang/Desktop/tutorialpoint/cxf-
wsdl/src/main/resources/Hello.wsdl
hi drsarang

到目前为止,你已经了解了如何将 CXF 与 Apache CXF-First 和 WSDL-First 架构一起使用。在 Apache CXF-First 方法中,你使用了 POJO 服务器工厂Bean CXF 库中的类来创建服务器。创建你使用的客户端 ClientProxyFactoryBean 来自 CXF 库的类。在 WSDL-First 方法中,你使用了 Endpoint 类以在所需的 URL 和指定的实现者处发布服务。你现在可以扩展这些技术以集成不同的协议和传输。