说起web service最近几年restful大行其道,大有取代传统soap web service的趋势,
但是一些特有或相对老旧的系统依然使用了传统的soap web service,例如银行、航空公司的机票查询接口等。
本篇主要讲解spring boot整合cxf发布webservice服务和spring boot整合cxf客户端调用webservice服务。
maven依赖 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 36 37 <dependencies >     <dependency >          <groupId > org.springframework.boot</groupId >          <artifactId > spring-boot-starter-web</artifactId >          <exclusions >              <exclusion >                  <groupId > org.springframework.boot</groupId >                  <artifactId > spring-boot-starter-tomcat</artifactId >              </exclusion >          </exclusions >      </dependency >      <dependency >          <groupId > org.springframework.boot</groupId >          <artifactId > spring-boot-starter-jetty</artifactId >      </dependency >           <dependency >          <groupId > org.apache.cxf</groupId >          <artifactId > cxf-spring-boot-starter-jaxws</artifactId >          <version > 3.2.4</version >      </dependency >           <dependency >          <groupId > org.springframework.boot</groupId >          <artifactId > spring-boot-starter-test</artifactId >          <scope > test</scope >      </dependency >      <dependency >          <groupId > org.hamcrest</groupId >          <artifactId > hamcrest-all</artifactId >          <version > 1.3</version >          <scope > test</scope >      </dependency >  </dependencies > 
服务接口 先定义一个简单的用户类User:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package  com.xncoding.webservice.model;public  class  User  {    private  Long id;     private  String name;     private  Integer age;     public  User ()  {     }     public  User (Long id, String name, Integer age)  {         this .id = id;         this .name = name;         this .age = age;     }      } 
注意,上面的包名是下面接口定义的targetNamespace的倒序。
创建一个简单的服务接口,定义了两个方法,一个返回字符串,一个返回一个实体类User:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package  com.xncoding.webservice.service;import  com.xncoding.webservice.model.User;import  javax.jws.WebMethod;import  javax.jws.WebParam;import  javax.jws.WebService;@WebService(name = "CommonService", // 暴露服务名称         targetNamespace = "http://model.webservice.xncoding.com/"// 命名空间,一般是接口的包名倒序 ) public  interface  ICommonService  {    @WebMethod      public  String sayHello (@WebParam(name = "userName")  String name) ;     @WebMethod      public  User getUser (@WebParam(name = "userName")  String name) ; } 
接口实现 接下来实现这个接口,编写相应的业务逻辑:
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 package  com.xncoding.webservice.service.impl;import  com.xncoding.webservice.model.User;import  com.xncoding.webservice.service.ICommonService;import  org.springframework.stereotype.Component;import  javax.jws.WebService;@WebService(serviceName = "CommonService", // 与接口中指定的name一致         targetNamespace = "http://model.webservice.xncoding.com/", // 与接口中的命名空间一致,一般是接口的包名倒         endpointInterface = "com.xncoding.webservice.service.ICommonService"// 接口地址 ) @Component public  class  CommonServiceImpl  implements  ICommonService  {    @Override      public  String sayHello (String name)  {         return  "Hello ,"  + name;     }     @Override      public  User getUser (String name)  {         return  new  User (1000L , name, 23 );     } } 
配置类 接下来编写cxf的配置类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Configuration public  class  CxfConfig  {    @Autowired      private  Bus bus;     @Autowired      ICommonService commonService;          @Bean      public  Endpoint endpoint ()  {         EndpointImpl  endpoint  =  new  EndpointImpl (bus, commonService);         endpoint.publish("/CommonService" );         return  endpoint;     } } 
这里把Commonservice接口发布在了路径/services/CommonService
下,wsdl文档路径为http://localhost:{port}/services/CommonService?wsdl
如果你想自定义wsdl的访问url,那么可以在application.yml中自定义:
客户端访问 直接客户端去访问有两种方式
代理类工厂的方式 这种方式需要拿到对方的接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Test public  void  cl1 ()  {    try  {                           JaxWsProxyFactoryBean  jaxWsProxyFactoryBean  =  new  JaxWsProxyFactoryBean ();                  jaxWsProxyFactoryBean.setAddress(wsdlAddress);                  jaxWsProxyFactoryBean.setServiceClass(ICommonService.class);                  ICommonService  cs  =  (ICommonService) jaxWsProxyFactoryBean.create();                  String  userName  =  "Leftso" ;                  String  result  =  cs.sayHello(userName);         System.out.println("返回结果:"  + result);     } catch  (Exception e) {         e.printStackTrace();     } } 
动态调用方式 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 @Test public  void  cl2 ()  {         JaxWsDynamicClientFactory  dcf  =  JaxWsDynamicClientFactory.newInstance();     Client  client  =  dcf.createClient(wsdlAddress);               Object[] objects;     try  {                  objects = client.invoke("sayHello" , "Leftso" );         System.out.println("返回类型:"  + objects[0 ].getClass());         System.out.println("返回数据:"  + objects[0 ]);     } catch  (java.lang.Exception e) {         e.printStackTrace();     } } @Test public  void  cl3 ()  {         JaxWsDynamicClientFactory  dcf  =  JaxWsDynamicClientFactory.newInstance();     Client  client  =  dcf.createClient(wsdlAddress);     Object[] objects;     try  {                  objects = client.invoke("getUser" , "张三" );         System.out.println("返回类型:"  + objects[0 ].getClass());         System.out.println("返回数据:"  + objects[0 ]);         User  user  =  (User) objects[0 ];         System.out.println("返回对象User.name="  + user.getName());     } catch  (java.lang.Exception e) {         e.printStackTrace();     } } 
注意到如果方法返回对象的话,那么对象所在的包一定是targetNamespace的倒序。
生成客户端代码 这是推荐做法,就是根据wsdl的访问路径来生成客户端代码。这里有两种生成方式
Apache的wsdl2java工具 1 wsdl2java -autoNameResolution http://xxx?wsdl 
JDK自带的工具(推荐) JDK自己内置了一个wsimport工具,这个是推荐的生成方式。
1 wsimport -encoding utf-8 -p com.xncoding.webservice.client -keep http://xxx?wsdl -s d:/ws -B-XautoNameResolution 
其中:
1 2 3 4 5 6 7 8 -encoding :指定编码格式(此处是utf-8的指定格式) -keep:是否生成Java源文件 -s:指定.java文件的输出目录 -d:指定.class文件的输出目录 -p:定义生成类的包名,不定义的话有默认包名 -verbose:在控制台显示输出信息 -b:指定jaxws/jaxb绑定文件或额外的schemas -extension:使用扩展来支持SOAP1.2 
上面的命令会在d:/ws目录下面生成相应的客户端代码。
客户端使用例子:
1 2 3 CommonService_Service  c  =  new  CommonService_Service ();com.xncoding.webservice.client.User  user  =  c.getCommonServiceImplPort().getUser("Tom" ); assertThat(user.getName(), is("Tom" )); 
GitHub源码 springboot-cxf