前置准备

jdk: 1.8

springboot: 2.3.2.RELEASE

cxf: 3.5.7

下载

选择3.5版本,3.6及以上的版本不支持jdk8

下载地址: https://cxf.apache.org/download.html

代码生成

先将 cxfbin 目录保存到环境变量,否则 wsdl2java 命令需要使用全路径。

编写wsdl文件

下面是 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://cxf.tg.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="MyHelloWorldService" targetNamespace="http://cxf.tg.com/">
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://cxf.tg.com/" elementFormDefault="unqualified" targetNamespace="http://cxf.tg.com/" version="1.0">

<xs:element name="getPerson" type="tns:getPerson"/>

<xs:element name="getPersonResponse" type="tns:getPersonResponse"/>

<xs:element name="sayHello" type="tns:sayHello"/>

<xs:element name="sayHelloResponse" type="tns:sayHelloResponse"/>

<xs:complexType name="sayHello">
<xs:sequence>
<xs:element minOccurs="0" name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="sayHelloResponse">
<xs:sequence>
<xs:element minOccurs="0" name="returnHello" type="xs:string"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="getPerson">
<xs:sequence>
<xs:element minOccurs="0" name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="getPersonResponse">
<xs:sequence>
<xs:element minOccurs="0" name="returnPerson" type="tns:person"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="person">
<xs:sequence>
<xs:element name="age" type="xs:int"/>
<xs:element minOccurs="0" name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>

</xs:schema>
</wsdl:types>
<wsdl:message name="sayHelloResponse">
<wsdl:part element="tns:sayHelloResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="getPersonResponse">
<wsdl:part element="tns:getPersonResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="sayHello">
<wsdl:part element="tns:sayHello" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="getPerson">
<wsdl:part element="tns:getPerson" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="HelloWorldService">
<wsdl:operation name="sayHello">
<wsdl:input message="tns:sayHello" name="sayHello">
</wsdl:input>
<wsdl:output message="tns:sayHelloResponse" name="sayHelloResponse">
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="getPerson">
<wsdl:input message="tns:getPerson" name="getPerson">
</wsdl:input>
<wsdl:output message="tns:getPersonResponse" name="getPersonResponse">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="MyHelloWorldServiceSoapBinding" type="tns:HelloWorldService">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="sayHello">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="sayHello">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="sayHelloResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="getPerson">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="getPerson">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="getPersonResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="MyHelloWorldService">
<wsdl:port binding="tns:MyHelloWorldServiceSoapBinding" name="HelloWorldServicePort">
<soap:address location="http://localhost/helloWorld"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

集成到springboot

引入依赖

<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-spring-boot-starter-jaxws</artifactId>
<version>3.5.7</version>
</dependency>

编写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://cxf.tg.com/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="MyHelloWorldService" targetNamespace="http://cxf.tg.com/">
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://cxf.tg.com/" elementFormDefault="unqualified" targetNamespace="http://cxf.tg.com/" version="1.0">

<xs:element name="getPerson" type="tns:getPerson"/>

<xs:element name="getPersonResponse" type="tns:getPersonResponse"/>

<xs:element name="sayHello" type="tns:sayHello"/>

<xs:element name="sayHelloResponse" type="tns:sayHelloResponse"/>

<xs:complexType name="sayHello">
<xs:sequence>
<xs:element minOccurs="0" name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="sayHelloResponse">
<xs:sequence>
<xs:element minOccurs="0" name="returnHello" type="xs:string"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="getPerson">
<xs:sequence>
<xs:element minOccurs="0" name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="getPersonResponse">
<xs:sequence>
<xs:element minOccurs="0" name="returnPerson" type="tns:person"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="person">
<xs:sequence>
<xs:element name="age" type="xs:int"/>
<xs:element minOccurs="0" name="name" type="xs:string"/>
</xs:sequence>
</xs:complexType>

</xs:schema>
</wsdl:types>
<wsdl:message name="sayHelloResponse">
<wsdl:part element="tns:sayHelloResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="getPersonResponse">
<wsdl:part element="tns:getPersonResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="sayHello">
<wsdl:part element="tns:sayHello" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="getPerson">
<wsdl:part element="tns:getPerson" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="HelloWorldService">
<wsdl:operation name="sayHello">
<wsdl:input message="tns:sayHello" name="sayHello">
</wsdl:input>
<wsdl:output message="tns:sayHelloResponse" name="sayHelloResponse">
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="getPerson">
<wsdl:input message="tns:getPerson" name="getPerson">
</wsdl:input>
<wsdl:output message="tns:getPersonResponse" name="getPersonResponse">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="MyHelloWorldServiceSoapBinding" type="tns:HelloWorldService">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="sayHello">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="sayHello">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="sayHelloResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="getPerson">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="getPerson">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="getPersonResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="MyHelloWorldService">
<wsdl:port binding="tns:MyHelloWorldServiceSoapBinding" name="HelloWorldServicePort">
<soap:address location="http://localhost/helloWorld"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

生成代码

wsdl2java -encoding utf-8 -p com.github.weichun97.cxf -d D:\work\study\java\test2\webservice-test\src\main\java -impl D:\work\study\java\test2\webservice-test\src\main\resources\HelloWorldService.wsdl
  • -encoding: 代码编码集
  • -p: 指定其wsdl的命名空间,也就是要生成代码的包名:
  • -d: 指定要产生代码所在目录
  • -impl: 生成服务端代码

编写服务端

编写服务端配置文件

创建 CxfServerConfig 配置类

import com.github.weichun97.cxf.HelloWorldService;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import javax.xml.ws.Endpoint;

/**
* @author chun
* @date 2023/11/9 16:08
*/
@Configuration
public class CxfServerConfig {

/**
* 指定beanName,防止和客户端注入的bean冲突
*/
@Resource(name = "serverHelloWorldService")
private HelloWorldService helloWorldService;

@Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}

/**
* 服务发布
* @return
*/
@Bean
public Endpoint helloWorldServiceEndpoint() {
Endpoint endpoint = Endpoint.create(helloWorldService);
endpoint.publish("/helloWorld");//发布地址
return endpoint;
}
}

修改 webservice 实现类

添加 @Service 注解, 并实现对应的方法

/**
* Please modify this class to meet your needs
* This class is not complete
*/

package com.github.weichun97.cxf;

import org.springframework.stereotype.Service;

import java.util.logging.Logger;
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.5.6
* 2023-11-10T10:26:20.574+08:00
* Generated source version: 3.5.6
*
* 指定beanName,防止和客户端的bean冲突
*/
@Service("serverHelloWorldService")
@javax.jws.WebService(
serviceName = "MyHelloWorldService",
portName = "HelloWorldServicePort",
targetNamespace = "http://cxf.tg.com/",
wsdlLocation = "file:/D:/work/study/java/test2/webservice-test/src/main/resources/HelloWorldService.wsdl",
endpointInterface = "com.github.weichun97.cxf.HelloWorldService")
public class HelloWorldServicePortImpl implements HelloWorldService {

private static final Logger LOG = Logger.getLogger(HelloWorldServicePortImpl.class.getName());

/* (non-Javadoc)
* @see com.github.weichun97.cxf.HelloWorldService#sayHello(java.lang.String name)*
*/
public java.lang.String sayHello(java.lang.String name) {
LOG.info("Executing operation sayHello");
System.out.println(name);
java.lang.String _return = "你好," + name;
return _return;
}

/* (non-Javadoc)
* @see com.github.weichun97.cxf.HelloWorldService#getPerson(java.lang.String name)*
*/
public com.github.weichun97.cxf.Person getPerson(java.lang.String name) {
LOG.info("Executing operation getPerson");
System.out.println(name);
com.github.weichun97.cxf.Person _return = null;
return _return;
}

}

配置 webservice url 访问前缀

cxf默认的访问前缀是 /services/,如果想要自定义,修改 springbootapplication.yml 文件, 添加 cxf.path 配置

server:
port: 8080
cxf:
path: /ws/

验证

访问 http://localhost:8080/ws/helloWorld?WSDL 查看是否能有正常访问

编写客户端

配置

application.yml 自定义要访问的 wsdl 服务 url

itran:
# 配置客户端要访问的wsdl服务端
webservice:
helloWorld: http://localhost:8080/ws/helloWorld

**创建 **CxfClientConfig 配置类

import com.github.weichun97.cxf.HelloWorldService;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;

/**
* @author chun
* @date 2023/11/9 16:08
*/
@Configuration
public class CxfClientConfig {

@Value("${itran.webservice.helloWorld}")
private String helloWorldServiceUrl;

@Resource
private SpringBus bus;

@Bean("clientHelloWorldService")
public HelloWorldService helloWorldService() {
JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
// 设置 UserService 接口
jaxWsProxyFactoryBean.setServiceClass(HelloWorldService.class);
// 设置 Web Services 地址
jaxWsProxyFactoryBean.setAddress(helloWorldServiceUrl);
// 使用已有的bus, 防止覆盖服务端的bus
jaxWsProxyFactoryBean.setBus(bus);
// 创建
return (HelloWorldService) jaxWsProxyFactoryBean.create();
}
}

编写测试类

import com.github.weichun97.cxf.HelloWorldService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
* @author chun
* @date 2023/11/7 11:32
*/
@RestController
@RequestMapping
public class TestController {

/**
* 指定客户端bean,防止和服务端的冲突
*/
@Resource(name = "clientHelloWorldService")
private HelloWorldService helloWorldService;

@GetMapping("test")
public String test(){
return helloWorldService.sayHello("张三");
}
}

测试

访问 http://localhost:8080/test,浏览器显示如下内容

你好,张三

可能出现的问题

  1. springboot注入客户端service后,wsdl链接无法访问,日志提示如下错误:
Can't find the request for http://localhost:8080/ws/ucms's Observer

原因:当cxf服务端和客户端同时部署在一个项目时,客户端会生成新的 SpringBus 覆盖掉服务端的。导致服务端错误。

解决方案:在客户端直接注入服务端的SpringBus并使用。

总结

本文介绍了怎么使用cxf自动生成代码,并集成springboot项目。示例代码请访问 https://github.com/weichun97/test2/tree/master/webservice-test

参考链接

cxf生成代码:https://tanggao1314.github.io/2019/11/02/WebService%E5%AD%A6%E4%B9%A0%E7%B3%BB%E5%88%97%E4%B9%8BCXF%E5%BC%80%E5%8F%91WebService/

cxf集成springboot:

https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-65/lab-65-cxf-ws-demo