吐槽
我不喜欢 webService ,因为它相对于 http 协议类型的接口来说太麻烦了。即便它拥有很多种类的代码生成器。
但我们又不得不学会使用它,因为有很多老的系统依旧在使用它。需求中有时又有数据必须和他们对接。
官方文档:https://cxf.apache.org/docs/springboot.html
官方 springboot 代码示例:https://github.com/apache/cxf/blob/master/distribution/src/main/release/samples/jaxws_spring_boot/README
依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.1.8.RELEASE</version > <relativePath /> </parent > <dependency > <groupId > org.apache.cxf</groupId > <artifactId > cxf-spring-boot-starter-jaxws</artifactId > <version > 3.4.2</version > <exclusions > <exclusion > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-logging</artifactId > </exclusion > </exclusions > </dependency > <dependency > <groupId > io.micrometer</groupId > <artifactId > micrometer-core</artifactId > </dependency >
服务端 Hello.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import javax.jws.WebMethod;import javax.jws.WebParam;import javax.jws.WebResult;import javax.jws.WebService;import javax.xml.ws.RequestWrapper;import javax.xml.ws.ResponseWrapper;@WebService(targetNamespace = "http://platform.wzy.com/", name = "Hello") public interface Hello { @WebResult(name = "return", targetNamespace = "") @RequestWrapper(localName = "sayHello", targetNamespace = "http://platform.wzy.com/", className = "com.wzy.platform.service.SayHello") @WebMethod(action = "urn:SayHello") @ResponseWrapper(localName = "sayHelloResponse", targetNamespace = "http://platform.wzy.com/", className = "com.wzy.platform.service.SayHelloResponse") String sayHello (@WebParam(name = "myname", targetNamespace = "") String myname) ; }
**HelloPortImpl.java**
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import com.wzy.platform.service.Hello;import java.util.logging.Logger;@javax .jws.WebService(serviceName = "HelloService" , portName = "HelloPort" , targetNamespace = "http://platform.wzy.com/" , endpointInterface = "com.wzy.platform.service.Hello" ) public class HelloPortImpl implements Hello { private static final Logger LOG = Logger.getLogger(HelloPortImpl.class.getName()); @Override public java.lang.String sayHello (java.lang.String myname) { LOG.info("Executing operation sayHello" + myname); try { return "Hello, Welcome to CXF Spring boot " + myname + "!!!" ; } catch (java.lang.Exception ex) { ex.printStackTrace(); throw new RuntimeException (ex); } } }
CxfConfig.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import com.wzy.platform.service.impl.HelloPortImpl;import org.apache.cxf.Bus;import org.apache.cxf.jaxws.EndpointImpl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import javax.xml.ws.Endpoint;@Configuration public class CxfConfig { @Autowired private Bus bus; @Bean public Endpoint endpoint () { EndpointImpl endpoint = new EndpointImpl (bus, new HelloPortImpl (), null , null , null ); endpoint.publish("/Hello" ); return endpoint; } }
application.properties
启动项目:访问地址 http://localhost:8081/wsTest/Hello?wsdl
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 import org.apache.cxf.staxutils.StaxUtils;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import javax.xml.namespace.QName;import javax.xml.transform.Source;import javax.xml.transform.stream.StreamSource;import javax.xml.ws.Dispatch;import javax.xml.ws.Service;import java.io.StringReader;import java.net.URL;public class SpringbootBaseApplicationTests {public static void main (String[] args) { String address = "http://localhost:8081/wsTest/Hello" ; String request = "<q0:sayHello xmlns:q0=\"http://platform.wzy.com/\"><myname>Elan</myname></q0:sayHello>" ; StreamSource source = new StreamSource (new StringReader (request)); Service service = Service.create(new URL (address + "?wsdl" ), new QName ("http://platform.wzy.com/" , "HelloService" )); Dispatch<Source> disp = service.createDispatch(new QName ("http://platform.wzy.com/" , "HelloPort" ), Source.class, Service.Mode.PAYLOAD); Source result = disp.invoke(source); System.out.println("===================================result====" ); String resultAsString = StaxUtils.toString(result); System.out.println(resultAsString); } }
执行结果:
方式二:交给 spring 管理
将上边的 接口 类复制到项目(新的项目)中
resources -> config 建立一个 cxf-client.xml 文件 内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws ="http://cxf.apache.org/jaxws" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/jaxws" > <jaxws:client id ="helloClient" serviceClass = "com.example.springbootbase.service.Hello" address = "http://localhost:8081/wsTest/Hello" > </jaxws:client > </beans >
建立一个配置类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.ImportResource;@Configuration @ImportResource("classpath:config/cxf-client.xml") public class CxfClient {}
控制器中注入 helloClient
1 2 3 4 5 6 7 @Autowired Hello hello; @GetMapping("/cxf") public String wxClientTest (String p) { return hello.sayHello(p); }
启动项目访问 接口响应如下:
关于报错 1 2 3 4 5 6 com.sun.xml.bind.v2.util.StackRecorder: null at com.sun.xml.bind.v2.schemagen.XmlSchemaGenerator.write(XmlSchemaGenerator.java:425) [jaxb-runtime-2.3.3.jar:2.3.3] at com.sun.xml.bind.v2.runtime.JAXBContextImpl.generateSchema(JAXBContextImpl.java:799) [jaxb-runtime-2.3.3.jar:2.3.3] at org.apache.cxf.common.jaxb.JAXBUtils.generateJaxbSchemas(JAXBUtils.java:810) [cxf-core-3.4.2.jar:3.4.2] at org.apache.cxf.jaxb.JAXBDataBinding.generateJaxbSchemas(JAXBDataBinding.java:468) [cxf-rt-databinding-jaxb-3.4.2.jar:3.4.2] at org.apache.cxf.jaxb.JAXBDataBinding.initialize(JAXBDataBinding.java:385) [cxf-rt-databinding-jaxb-3.4.2.jar:3.4.2]
直接无视 ,还没找具体什么原因,但不影响使用
补充 在实际开发中 服务端的代码(接口或实现类)并没有配置的注解 例如 targetNamespace :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @WebService(name = "hello-test") public interface HelloEndpoint { @WebMethod @WebResult() String sayHellottt () ; @WebMethod WsResult testResult0 (@WebParam(name = "id") String id ) ; SysUser testUser () ; }
启动服务后项目默认会把包名设定为 命名空间
这时 客户端 如果复制过来的接口类在新项目中的包路径名不一致可能就调不通了。
那么在客户端这边 我们就要手动 配置下命名空间就可以了。
此外: 服务端 如果返回复杂对象。那么该对象也得复制到 客户端。(有的做法是把所依赖的对象都打成一个 jar 包,服务端客户端都引用这个 jar )
参考 https://cxf.apache.org/docs/springboot.html
https://github.com/apache/cxf/blob/master/distribution/src/main/release/samples/jaxws_spring_boot/README
https://janus.blog.csdn.net/article/details/78430076
http://www.xwood.net/_site_domain_/_root/5870/5874/t_c265946.html