使用CXF集成Web Service

Web service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序, 用于开发分布式的互操作的应用程序。

添加依赖

<dependency>
  <groupId>org.apache.cxfgroupId>
  <artifactId>cxf-spring-boot-starter-jaxwsartifactId>
  <version>3.2.4version>
dependency>


<dependency>
  <groupId>org.apache.cxfgroupId>
  <artifactId>cxf-spring-boot-starter-jaxwsartifactId>
  <version>3.2.4version>
dependency>

创建服务端接口

package com.example.demo.webservice;

import com.example.demo.base.BaseResult;
import com.example.demo.dto.UserDTO;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

/**
 * ---------------------------
 * User Web Service服务端接口 (UserWebService)
 * ---------------------------
 * @author:XiaoYang
 * @date:2020-09-15 13:00:00
 * ---------------------------
 */
@WebService
public interface UserWebService {

    /**
     * 测试传字符串
     * @param name
     * @return
     */
    @WebMethod(operationName="sayHello")
    String sayHello(@WebParam(name = "name", targetNamespace = "http://webservice.demo.example.com/") String name);

    /**
     * 测试传对象类型
     * @param userDTO
     * @return BaseResult是封装的返回结果对象
     */
    @WebMethod(operationName="operateUser")
    BaseResult operateUser(@WebParam(name = "userDTO", targetNamespace = "http://webservice.demo.example.com/") UserDTO userDTO);

}

package com.example.demo.webservice;

import com.example.demo.base.BaseResult;
import com.example.demo.dto.UserDTO;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

/**
 * ---------------------------
 * User Web Service服务端接口 (UserWebService)
 * ---------------------------
 * @author:XiaoYang
 * @date:2020-09-15 13:00:00
 * ---------------------------
 */
@WebService
public interface UserWebService {

    /**
     * 测试传字符串
     * @param name
     * @return
     */
    @WebMethod(operationName="sayHello")
    String sayHello(@WebParam(name = "name", targetNamespace = "http://webservice.demo.example.com/") String name);

    /**
     * 测试传对象类型
     * @param userDTO
     * @return BaseResult是封装的返回结果对象
     */
    @WebMethod(operationName="operateUser")
    BaseResult operateUser(@WebParam(name = "userDTO", targetNamespace = "http://webservice.demo.example.com/") UserDTO userDTO);

}

创建服务端接口实现类

package com.example.demo.webservice.impl;

import com.example.demo.base.BaseResult;
import com.example.demo.dto.UserDTO;
import com.example.demo.webservice.UserWebService;
import org.springframework.context.annotation.Configuration;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

/**
 * ---------------------------
 * User Web Service服务端接口实现类 (UserWebServiceImpl)
 * ---------------------------
 * @author:XiaoYang
 * @date:2020-09-15 13:00:00
 * ---------------------------
 */
@WebService (
    serviceName = "webService", // 暴露的服务名称
    targetNamespace = "http://webservice.demo.example.com/", // wsdl命名空间,一般是接口的包名倒序
    endpointInterface = "com.example.demo.webservice.UserWebService" // 指定发布webservcie的接口类
)
@Configuration
public class UserWebServiceImpl implements UserWebService {

    @Override
    @WebMethod(operationName="sayHello")
    public String sayHello(@WebParam(name="name", targetNamespace = "http://webservice.demo.example.com/") String name) {
        System.out.println("WebService sayHello " + name);
        return "sayHello " + name;
    }

    @Override
    @WebMethod(operationName="operateUser")
    public BaseResult operateUser(@WebParam(name="userDTO", targetNamespace = "http://webservice.demo.example.com/") UserDTO userDTO) {
        if (null == userDTO) {
            return BaseResult.error("参数为空!");
        }
        // TODO 逻辑代码
        System.out.println(userDTO.getUserName());
        return BaseResult.ok("操作成功!");
    }

}

package com.example.demo.webservice.impl;

import com.example.demo.base.BaseResult;
import com.example.demo.dto.UserDTO;
import com.example.demo.webservice.UserWebService;
import org.springframework.context.annotation.Configuration;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;

/**
 * ---------------------------
 * User Web Service服务端接口实现类 (UserWebServiceImpl)
 * ---------------------------
 * @author:XiaoYang
 * @date:2020-09-15 13:00:00
 * ---------------------------
 */
@WebService (
    serviceName = "webService", // 暴露的服务名称
    targetNamespace = "http://webservice.demo.example.com/", // wsdl命名空间,一般是接口的包名倒序
    endpointInterface = "com.example.demo.webservice.UserWebService" // 指定发布webservcie的接口类
)
@Configuration
public class UserWebServiceImpl implements UserWebService {

    @Override
    @WebMethod(operationName="sayHello")
    public String sayHello(@WebParam(name="name", targetNamespace = "http://webservice.demo.example.com/") String name) {
        System.out.println("WebService sayHello " + name);
        return "sayHello " + name;
    }

    @Override
    @WebMethod(operationName="operateUser")
    public BaseResult operateUser(@WebParam(name="userDTO", targetNamespace = "http://webservice.demo.example.com/") UserDTO userDTO) {
        if (null == userDTO) {
            return BaseResult.error("参数为空!");
        }
        // TODO 逻辑代码
        System.out.println(userDTO.getUserName());
        return BaseResult.ok("操作成功!");
    }

}

创建CXF配置类

package com.example.demo.config;

import com.example.demo.webservice.UserWebService;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.xml.ws.Endpoint;

/**
 * ---------------------------
 * CXF配置类 (CxfConfig)
 * ---------------------------
 * @author:XiaoYang
 * @date:2020-09-15 13:00:00
 * ---------------------------
 */
@Configuration
public class CxfWebServiceConfig {

    @Autowired
    private UserWebService userWebService;

    @Bean("cxfServletRegistration")
    public ServletRegistrationBean dispatcherServlet() {
        // 注册servlet 拦截/ws 开头的请求 如果不设置 默认为:/services/*
        return new ServletRegistrationBean(new CXFServlet(), "/ws/*");
    }

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

    /**
     * 发布endpoint
     * 地址:http://IP地址:端口号/ws/webService?wsdl
     * @return
     */
    @Bean
    public Endpoint endpoint() {
        EndpointImpl endpoint = new EndpointImpl(springBus(), userWebService);
        // 发布的地址
        endpoint.publish("/webService");
        return endpoint;
    }

}

package com.example.demo.config;

import com.example.demo.webservice.UserWebService;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.jaxws.EndpointImpl;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.xml.ws.Endpoint;

/**
 * ---------------------------
 * CXF配置类 (CxfConfig)
 * ---------------------------
 * @author:XiaoYang
 * @date:2020-09-15 13:00:00
 * ---------------------------
 */
@Configuration
public class CxfWebServiceConfig {

    @Autowired
    private UserWebService userWebService;

    @Bean("cxfServletRegistration")
    public ServletRegistrationBean dispatcherServlet() {
        // 注册servlet 拦截/ws 开头的请求 如果不设置 默认为:/services/*
        return new ServletRegistrationBean(new CXFServlet(), "/ws/*");
    }

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

    /**
     * 发布endpoint
     * 地址:http://IP地址:端口号/ws/webService?wsdl
     * @return
     */
    @Bean
    public Endpoint endpoint() {
        EndpointImpl endpoint = new EndpointImpl(springBus(), userWebService);
        // 发布的地址
        endpoint.publish("/webService");
        return endpoint;
    }

}

启动SpringBoot服务

浏览器访问:http://127.0.0.1:8080/ws?wsdl即可查看整体信息。

wmware esxi 添加主机 esxi对接whmcs_wmware esxi 添加主机

浏览器访问:http://127.0.0.1:8080/ws/webService?wsdl即可查看某个Service详情信息。

wmware esxi 添加主机 esxi对接whmcs_cxf webservice开发_02

客户端调用

Java中调用

package com.example.demo.webservice;

import com.example.demo.base.BaseResult;
import com.example.demo.dto.UserDTO;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;

/**
 * ---------------------------
 * UserWebService测试类 (UserWebServiceTest)
 *  该类提供两种不同的方式来调用webservice服务:
 *  1:代理工厂方式
 *  2:动态调用webservice
 * ---------------------------
 * @author XiaoYang
 * @date 2020-09-15 13:30:00
 * ---------------------------
 */
public class UserWebServiceTest {

    /**
     * 接口地址
     */
    private static final String ADDRESS = "http://127.0.0.1:8080/ws/webService?wsdl";

    public static void main(String[] args) {
        testSayHello();
        testOperateUser();
    }

    /**
     * 1.静态调用,代理类工厂的方式,需要拿到对方的接口地址
     * @return
     */
    public static void testOperateUser() {
        try {
            // 创建Web Service客户端代理工厂
            JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
            // 设置代理地址
            jaxWsProxyFactoryBean.setAddress(ADDRESS);
            // 设置接口类型
            jaxWsProxyFactoryBean.setServiceClass(UserWebService.class);
            // 创建一个代理接口实现
            UserWebService us = (UserWebService) jaxWsProxyFactoryBean.create();
            // 数据准备
            UserDTO userDTO = new UserDTO();
            userDTO.setUserName("张三");
            // 调用代理接口的方法调用并返回结果
            BaseResult result = us.operateUser(userDTO);
            System.out.println("状态码:" + result.getCode());
            System.out.println("提示信息:" + result.getMsg());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 2:动态调用
     * @return
     */
    public static void testSayHello() {
        // 创建动态客户端
        JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
        Client client = dcf.createClient(ADDRESS);
        // 需要密码的情况需要加上用户名和密码
        // client.getOutInterceptors().add(new ClientLoginInterceptor(USER_NAME, PASS_WORD));
        Object[] objects = new Object[0];
        try {
            // invoke("方法名",参数1,参数2,参数3....);
            objects = client.invoke("sayHello", "admin");
            System.out.println("返回的数据:" + objects[0]);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

package com.example.demo.webservice;

import com.example.demo.base.BaseResult;
import com.example.demo.dto.UserDTO;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;

/**
 * ---------------------------
 * UserWebService测试类 (UserWebServiceTest)
 *  该类提供两种不同的方式来调用webservice服务:
 *  1:代理工厂方式
 *  2:动态调用webservice
 * ---------------------------
 * @author XiaoYang
 * @date 2020-09-15 13:30:00
 * ---------------------------
 */
public class UserWebServiceTest {

    /**
     * 接口地址
     */
    private static final String ADDRESS = "http://127.0.0.1:8080/ws/webService?wsdl";

    public static void main(String[] args) {
        testSayHello();
        testOperateUser();
    }

    /**
     * 1.静态调用,代理类工厂的方式,需要拿到对方的接口地址
     * @return
     */
    public static void testOperateUser() {
        try {
            // 创建Web Service客户端代理工厂
            JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();
            // 设置代理地址
            jaxWsProxyFactoryBean.setAddress(ADDRESS);
            // 设置接口类型
            jaxWsProxyFactoryBean.setServiceClass(UserWebService.class);
            // 创建一个代理接口实现
            UserWebService us = (UserWebService) jaxWsProxyFactoryBean.create();
            // 数据准备
            UserDTO userDTO = new UserDTO();
            userDTO.setUserName("张三");
            // 调用代理接口的方法调用并返回结果
            BaseResult result = us.operateUser(userDTO);
            System.out.println("状态码:" + result.getCode());
            System.out.println("提示信息:" + result.getMsg());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 2:动态调用
     * @return
     */
    public static void testSayHello() {
        // 创建动态客户端
        JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance();
        Client client = dcf.createClient(ADDRESS);
        // 需要密码的情况需要加上用户名和密码
        // client.getOutInterceptors().add(new ClientLoginInterceptor(USER_NAME, PASS_WORD));
        Object[] objects = new Object[0];
        try {
            // invoke("方法名",参数1,参数2,参数3....);
            objects = client.invoke("sayHello", "admin");
            System.out.println("返回的数据:" + objects[0]);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

运行后,控制台可以找到输出如下:

返回的数据:sayHello admin

状态码:00000
提示信息:操作成功!

返回的数据:sayHello admin

状态码:00000
提示信息:操作成功!

Postmen中调用

地址栏输入Web Service 接口地址,选择post方式,Headers中设置Content-Type为text/xml;charset=utf-8

wmware esxi 添加主机 esxi对接whmcs_axis调用webservice接口_03

在Body中发送数据进行测试,选择raw 和 XML,其中xmlns="http://webservice.demo.example.com/" 对应Service中targetNamespace的值。

「接口:sayHello」

<?xml  version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    
    <sayHello xmlns="http://webservice.demo.example.com/">
        
        <name>张三name>
  sayHello>
  soap:Body>
soap:Envelope>

<?xml  version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    
    <sayHello xmlns="http://webservice.demo.example.com/">
        
        <name>张三name>
  sayHello>
  soap:Body>
soap:Envelope>

返回结果:

wmware esxi 添加主机 esxi对接whmcs_cxf webservice开发_04

「接口:operateUser」

<?xml  version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    
    <operateUser xmlns="http://webservice.demo.example.com/">
        
        <userDTO>
            <id>123id>
            <userName>张三userName>
        userDTO>
  operateUser>
  soap:Body>
soap:Envelope>

<?xml  version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    
    <operateUser xmlns="http://webservice.demo.example.com/">
        
        <userDTO>
            <id>123id>
            <userName>张三userName>
        userDTO>
  operateUser>
  soap:Body>
soap:Envelope>

返回结果:

wmware esxi 添加主机 esxi对接whmcs_cxf webservice开发_05

遇到的问题

Java中调用报:

javax.xml.ws.soap.SOAPFaultException: Error reading XMLStreamReader: Unexpected character '{' (code 123) in prolog; expected '
 at [row,col {unknown-source}]: [1,1]
 at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:161)
 at com.sun.proxy.$Proxy37.sendWorkingHoursOrder(Unknown Source)
javax.xml.ws.soap.SOAPFaultException: Error reading XMLStreamReader: Unexpected character '{' (code 123) in prolog; expected '
 at [row,col {unknown-source}]: [1,1]
 at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:161)
 at com.sun.proxy.$Proxy37.sendWorkingHoursOrder(Unknown Source)

出现问题的原因:是因为Spring Boot整合了在Shiro,这个Web Service路径没有添加到可以过滤的路径上面去。

解决:在Shiro配置文件中加上这个过滤路径。

// 过滤掉WebService的接口
filterMap.put("/ws/**","anon");

// 过滤掉WebService的接口
filterMap.put("/ws/**","anon");