前置准备
jdk: 1.8
springboot: 2.3.2.RELEASE
cxf: 3.5.7
下载
选择3.5版本,3.6及以上的版本不支持jdk8
下载地址: https://cxf.apache.org/download.html
代码生成
先将 cxf 的 bin 目录保存到环境变量,否则 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/,如果想要自定义,修改 springboot 的 application.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,浏览器显示如下内容
你好,张三
可能出现的问题
- 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集成springboot:
https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-65/lab-65-cxf-ws-demo