CXF 与 WSDL
您开发的 CXF-POJO 应用程序导致客户端和服务器之间的耦合非常紧密。直接访问服务接口也会造成严重的安全威胁。因此,通常需要客户端和服务器之间的解耦,这是通过使用 WSDL(Web 服务描述语言)来实现的。
我们在基于 XML 的 WSDL 文档中编写 Web 服务接口。我们将使用一个工具将此 WSDL 映射到 Apache CXF 接口,然后由我们的客户端和服务器应用程序实现和使用。为了提供解耦,从 WSDL 开始是首选方式。为此,您首先需要学习一门新语言——WSDL。编写 WSDL 需要谨慎的方法,如果您能在开始工作之前对此有所了解会更好。
在本课中,我们将首先在 WSDL 文档中定义 Web 服务接口。我们将学习如何使用 CXF 从 WSDL 开始创建服务器和客户端应用程序。我们将保持应用程序的简单性,以保持专注于 CXF 的使用。创建服务器应用程序后,我们将使用内置的 CXF 类将其发布到所需的 URL。
首先,让我们描述一下我们将要使用的 WSDL。
HelloWorld 的 WSDL
我们将要实现的网络服务将有一个名为
greetings 的网络方法,它接受一个包含用户名的
string 参数,并在之后向调用者返回一个字符串消息将问候消息附加到用户名。完整的 wsdl 如下所示-
//Hello.wsdl
<?xml version = "1.0" encoding = "UTF-8"?>
<wsdl:definitions xmlns:soap = "http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns = "http://helloworld.lidihuo.com/"
xmlns:wsdl = "http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd = "http://www.w3.org/2001/XMLSchema"
name = "HelloWorld"
targetNamespace = "http://helloworld.lidihuo.com/">
<wsdl:types>
<xsd:schema attributeFormdefault = "unqualified"
elementFormdefault = "qualified"
targetNamespace = "http://helloworld.lidihuo.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 中,我们定义了一个名为
greetings 的消息。消息被传送到名为
HelloWorldService 的服务,该服务在
http://localhost:9090/HelloServerPort 上运行。
有了这个,我们现在将进行服务器开发。在开发服务器之前,我们需要为我们的 Web 服务生成 Apache CXF 接口。这是从给定的 wsdl 完成的。为此,您可以使用一个名为
的工具wsdl2java.
wsdl2java 插件
由于我们将使用 maven 来构建项目,因此您需要将以下插件添加到
pom.xml 文件中。
<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 文件的位置指定为
src/main/resources/Hello.wsdl。您必须确保为项目创建适当的目录结构,并将前面显示的
hello.wsdl 文件添加到指定文件夹。
wsdl2java 插件将编译此 wsdl 并在预定义文件夹中创建 Apache CXF 类。此处显示了完整的项目结构,供您随时参考。
现在,您已准备好使用
wsdl2java 生成的类创建服务器。 wsdl2java 创建的类如下图所示-
生成的服务接口
在生成的类列表中,您一定已经注意到其中一个是 Apache CXF 接口-这是
HelloWorldPortType.java。在您的代码编辑器中检查此文件。此处显示文件内容供您随时参考-
//HelloWorldPortType.java
package com.lidihuo.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.lidihuo.com/",
name = "HelloWorldPortType")
@XmlSeeAlso({ObjectFactory.class})
public interface HelloWorldPortType {
@WebMethod
@RequestWrapper(localName = "greetings", targetNamespace =
"http://helloworld.lidihuo.com/", className =
"com.lidihuo.helloworld.Greetings")
@ResponseWrapper(localName = "greetingsResponse", targetNamespace =
"http://helloworld.lidihuo.com/", className =
"com.lidihuo.helloworld.GreetingsResponse")
@WebResult(name = "return", targetNamespace =
"http://helloworld.lidihuo.com/")
public java.lang.String greetings(
@WebParam(name = "arg0", targetNamespace =
"http://helloworld.lidihuo.com/")
java.lang.String arg0
);
}
请注意,该接口包含一个名为
greetings 的方法。这是我们 wsdl 中的一种消息类型。
wsdl2java 工具已将此方法添加到生成的接口中。现在,您可以理解,无论您在 wsdl 中写入什么消息,都会在接口中生成相应的方法。
现在,您的任务是实现与您在 wsdl 中定义的各种消息相对应的所有这些方法。请注意,在前面的 Apache CXF-First 示例中,我们从 Web 服务的 Apache CXF 接口开始。在本例中,Apache CXF 接口是从 wsdl 创建的。
实现服务接口
服务接口的实现是微不足道的。完整的实现显示在下面的列表中-
//HelloWorldImpl.java
package com.lidihuo.helloworld;
public class HelloWorldImpl implements HelloWorldPortType {
@Override
public String greetings(String name) {
return ("hi " + name);
}
}
该代码实现了名为
greetings 的唯一接口方法。该方法接受一个
string 类型的参数,在它前面添加一个"hi"消息并将结果字符串返回给调用者。
接下来,我们将编写服务器应用程序。
开发服务器
开发服务器应用程序再次变得微不足道。在这里,我们将使用 CXF 提供的
Endpoint 类来发布我们的服务。这是在以下两行代码中完成的-
HelloWorldPortType implementor = new HelloWorldImpl();
Endpoint.publish("http://localhost:9090/HelloServerPort",
implementor,
new LoggingFeature());
首先,我们创建服务实现类的对象-
HelloWorldImpl。然后,我们将此引用作为第二个参数传递给
publish 方法。第一个参数是发布服务的地址-客户端将使用此 URL 访问服务。此处提供了服务器应用程序的整个源代码-
//Server.java
package com.lidihuo.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.lidihuo.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 类的完全限定名称是在配置中指定的。此外,dependency 标签指定我们将使用嵌入式 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>
在部署应用程序之前,您需要向项目中添加另外两个文件。这些显示在下面的屏幕截图中-
这些文件是定义
CXFServlet 映射的 CXF 标准文件。
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>
在
cxf-servlet.xml 中,您声明服务端点的属性。这显示在下面的代码片段中-
<beans ...>
<jaxws:endpoint xmlns:helloworld = "http://lidihuo.com/"
id="helloHTTP"
address = "http://localhost:9090/HelloServerPort"
serviceName = "helloworld:HelloServiceService"
endpointName = "helloworld:HelloServicePort">
</jaxws:endpoint>
</beans>
在这里,我们定义了服务端点的 id、服务可用的地址、服务名称和端点名称。现在,您了解了 CXF servlet 如何路由和处理您的服务。
最终的 pom.xml
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://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lidihuo</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.lidihuo.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.lidihuo.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 服务
现在,您已准备好运行网络应用程序。在命令窗口中,使用以下命令运行构建脚本。
这将从您的 wsdl 生成适当的 Apache CXF 类,编译您的 Apache CXF 类,在嵌入式码头服务器上部署服务器并运行您的应用程序。
您将在控制台上看到以下消息-
INFO: Setting the server's publish address to be
http://localhost:9090/HelloServerPort
Server ready...
和以前一样,您可以通过在浏览器中打开服务器 URL 来测试服务器。
由于我们没有指定任何操作,所以我们的应用程序只向浏览器返回一条错误消息。现在,尝试将
?wsdl 添加到您的 URL,您将看到以下输出-
所以我们的服务器应用程序按预期运行。您可以使用前面描述的
Postman 之类的 SOAP 客户端来进一步测试您的服务。
本教程的下一部分是编写一个使用我们服务的客户端。
开发客户端
在 CXF 应用程序中编写客户端与编写服务器一样重要。这是客户端的完整代码,基本上只包含三行,其余行只是将服务信息打印给用户。
//Client.java
package com.lidihuo.helloworld;
public class Client {
public static void main(String[] args) throws Exception {
//Create the service client with its default 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,通过调用
getHelloWorldPort 方法获取它的端口,然后传递我们的
greetings 消息到它。运行客户端,您将看到以下输出-
service: {http://helloworld.lidihuo.com/}HelloWorldService
wsdl location: file:/Users/drsarang/Desktop/tutorialpoint/cxf-
wsdl/src/main/resources/Hello.wsdl
hi drsarang
到目前为止,您已经学习了如何在 Apache CXF-First 和 WSDL-First 架构中使用 CXF。在 Apache CXF-First 方法中,您使用来自 CXF 库的带有
ServerFactoryBean 类的 POJO 来创建服务器。要创建客户端,您使用了 CXF 库中的
ClientProxyFactoryBean 类。在 WSDL-First 方法中,您使用
Endpoint 类在所需的 URL 和指定的实现者处发布服务。您现在可以扩展这些技术以集成不同的协议和传输。