CXF方式搭建本地webservice服务和soap方式调用踩坑

、导入pom依赖
  
            org.apache.cxf
            cxf-spring-boot-starter-jaxws
            3.4.0
 

说明:本文章记录的是基于springboot搭建的cxf服务端和soap客户端调用

、创建webservice服务端接口
package cn.lyn.webservice.serviceone;

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

/**
 * webService服务端接口
 * @author LengYouNuan
 * @create 2021-05-23 下午5:47
 */
@WebService
//@XmlType(name = "TypeName")
//@BindingType(value = SOAPBinding.SOAP12HTTP_MTOM_BINDING)
public interface WebServiceInterface {

    @WebMethod
    //WebResult注解指定返回值 不使用该注解可能导致获取不到返回值
    @WebResult(name="sayHelloResponse",targetNamespace = "http://serviceone.webservice.lyn.cn/")
    public String sayHello(@WebParam(name="name",targetNamespace = "http://serviceone.webservice.lyn.cn/")String name);
}

说明:目前我使用到了四个注解,分别是
@WebService:表明该接口是一个webservice服务接口,注解里的属性值可以设置生成的wsdl文档的命名空间等信息,此处没有遇到相关错误,因此没有进行设置,不过建议还是设置上。
@WebMethod:表明该方法是服务要发布的方法,对应属性也可以设置上
@WebResult:设置方法返回值,此处最开始没有设置导致获取不到返回值,因此务必设置上
@WebParam:设置参数信息,指定参数名字和参数所属命名空间,必须设置,不然可能导致参数不能传递进来

、创建实现类 实现具体的业务
package cn.lyn.webservice.serviceone.impl;

import cn.lyn.webservice.serviceone.WebServiceInterface;

import javax.jws.WebService;

/**
 * @author LengYouNuan
 * @create 2021-05-23 下午7:17
 */
@WebService
public class WebServiceImpl implements WebServiceInterface {
    public String sayHello(String name) {
        System.out.println(name+" say hello");
        if (name !=null)
            return name+" say hello";
        return "通了  但是参数没有传递过来";
    }
}

说明:实现类上需要贴上注解,方法不需要

、自定义cxf拦截器

说在前头:这里我只是实验了拦截器的定义,没有什么具体的业务操作,想学习的同学请自行研究

package cn.lyn.webservice.interceptor;

import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.List;

/**
 * @author LengYouNuan
 * @create 2021-05-24 下午3:05
 */
public class CxfInterceptor extends AbstractPhaseInterceptor{

    private static final Logger log = LogManager.getLogger(CxfInterceptor.class);

    public CxfInterceptor(String phase) {
        super(phase);
    }

    public CxfInterceptor(){
        super(Phase.PRE_PROTOCOL);
    }

    @Override
    public void handleMessage(SoapMessage message) throws Fault {

        System.out.println("=======================================");
        List
headers = message.getHeaders(); log.info(headers); } }
、配置cxf信息
package cn.lyn.webservice.config;

import cn.lyn.webservice.interceptor.CxfInterceptor;
import cn.lyn.webservice.serviceone.WebServiceInterface;
import cn.lyn.webservice.serviceone.impl.WebServiceImpl;
import org.apache.cxf.Bus;
import org.apache.cxf.bus.spring.SpringBus;
import org.apache.cxf.endpoint.Server;
import org.apache.cxf.jaxws.JaxWsServerFactoryBean;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author LengYouNuan
 * @create 2021-05-24 下午2:19
 */
@Configuration
public class CxfConfig implements WebMvcConfigurer {

    /**
     * 此方法被注释后:wsdl访问地址为http://127.0.0.1:8080/services/user?wsdl
     * 去掉注释后:wsdl访问地址为:http://127.0.0.1:8080/soap/user?wsdl
     */
    @Bean
    public ServletRegistrationBean creatDispatcherServlet() {
        return new ServletRegistrationBean(new CXFServlet(), "/soap/*");
    }

    /**
     * 非必要项
     * 用于打印cxf日志信息
     */
    @Bean(name = Bus.DEFAULT_BUS_ID)
    public SpringBus springBus() {
        SpringBus springBus = new SpringBus();

        //将cxf拦截器添加到bus里
        CxfInterceptor cxfInterceptor = new CxfInterceptor();
        springBus.getInInterceptors().add(cxfInterceptor);
        return springBus;
    }

    @Bean
    public WebServiceInterface getWebSer() {
        return new WebServiceImpl();
    }

    /**
     * 发布endpoint
     */
    @Bean
    public Server endpoint() {
        //EndpointImpl endpoint = new EndpointImpl(springBus(),getWebSer());
        //发布地址
        //endpoint.publish("/sayHello");
        //return endpoint;

        /**
         * ===上边是java原生方式发布服务;下边是cxf方式发布服务  发布之后生成的wsdl文档信息有很明显的区别
         *因为我是采用soap方式作为客户端 所以只能用cxf方式发布服务
         * 如果客户端是采用http请求方式,则对发布方式没有严格要求
         *
         */
        //以下发布方式会在wsdl文档中生成对应方法和参数的标签信息
        WebServiceImpl hw = new WebServiceImpl();
        JaxWsServerFactoryBean jwsFactory = new JaxWsServerFactoryBean();
        jwsFactory.setAddress("sayHello");   //指定WebService的发布地址
        jwsFactory.setServiceClass(WebServiceInterface.class);//WebService对应的类型
        jwsFactory.setServiceBean(hw);//WebService对应的实现对象

        return jwsFactory.create();
    }

    @Bean
    public CxfInterceptor getCxfInterceptor() {
        return new CxfInterceptor();
    }
}

此时启动boot项目就可以访问到wsdl文档

、客户端编写
package cn.lyn.webservice.client;

import com.sun.xml.internal.ws.client.BindingProviderProperties;
import com.sun.xml.internal.ws.developer.JAXWSProperties;
import org.w3c.dom.Document;

import javax.xml.namespace.QName;
import javax.xml.soap.*;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

/**
 * soap方式调用webservice方式客户端
 * @author LengYouNuan
 * @create 2021-05-31 下午2:35
 */
public class SoapClient {
    String nameSpace = ""; //wsdl的命名空间
    String wsdlUrl = ""; //wsdl文档地址
    String serviceName = ""; //服务的名字
    String portName = ""; 
    String responseName = ""; //@WebResult:注解上的name值
    String elementName = ""; //默认是要访问的方法名 如果@WebMethod属性name有值 则是该值,实际还是以wsdl文档为主
    int timeout = 20000;

    /**
     *
     * @param nameSpace
     * @param wsdlUrl
     * @param serviceName
     * @param portName
     * @param element
     * @param responseName
     */

    public SoapClient(String nameSpace, String wsdlUrl,
                            String serviceName, String portName, String element,
                            String responseName) {
        this.nameSpace = nameSpace;
        this.wsdlUrl = wsdlUrl;
        this.serviceName = serviceName;
        this.portName = portName;
        this.elementName = element;
        this.responseName = responseName;
    }

    /**
     *
     * @param nameSpace
     * @param wsdlUrl
     * @param serviceName
     * @param portName
     * @param element
     * @param responseName
     * @param timeOut
     *            毫秒
     */

    public SoapClient(String nameSpace, String wsdlUrl,
                            String serviceName, String portName, String element,
                            String responseName, int timeOut) {
        this.nameSpace = nameSpace;
        this.wsdlUrl = wsdlUrl;
        this.serviceName = serviceName;
        this.portName = portName;
        this.elementName = element;
        this.responseName = responseName;
        this.timeout = timeOut;
    }

    public String sendMessage(HashMap inMsg) throws Exception {
        // 创建URL对象
        URL url = null;
        try {
            url = new URL(wsdlUrl);
        } catch (Exception e) {
            e.printStackTrace();
            return "创建URL对象异常";
        }
        // 创建服务(Service)
        QName sname = new QName(nameSpace, serviceName);
        Service service = Service.create(url, sname);

        // 创建Dispatch对象
        Dispatch dispatch = null;
        try {
            dispatch = service.createDispatch(new QName(nameSpace, portName),
                    SOAPMessage.class, Service.Mode.MESSAGE);
        } catch (Exception e) {
            e.printStackTrace();
            return "创建Dispatch对象异常";
        }

        // 创建SOAPMessage
        try {
            SOAPMessage msg = MessageFactory.newInstance(
                    SOAPConstants.SOAP_1_1_PROTOCOL).createMessage();
            msg.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "UTF-8");

            SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();


            // 创建SOAPHeader(不是必需)
            // SOAPHeader header = envelope.getHeader();
            // if (header == null)
            // header = envelope.addHeader();
            // QName hname = new QName(nameSpace, "username", "nn");
            // header.addHeaderElement(hname).setValue("huoyangege");

            // 创建SOAPBody
            SOAPBody body = envelope.getBody();
            QName ename = new QName(nameSpace, elementName, "q0");
            SOAPBodyElement ele = body.addBodyElement(ename);
            // 增加Body元素和值
            for (Map.Entry entry : inMsg.entrySet()) {
                ele.addChildElement(new QName(nameSpace, entry.getKey()))
                        .setValue(entry.getValue());
            }

            // 超时设置
            dispatch.getRequestContext().put(
                    BindingProviderProperties.CONNECT_TIMEOUT, timeout);
            dispatch.getRequestContext().put(JAXWSProperties.REQUEST_TIMEOUT,
                    timeout);

            // 通过Dispatch传递消息,会返回响应消息
            SOAPMessage response = dispatch.invoke(msg);

            // 响应消息处理,将响应的消息转换为doc对象
            Document doc = response.getSOAPPart().getEnvelope().getBody()
                    .extractContentAsDocument();
            String ret = doc.getElementsByTagName(responseName).item(0)
                    .getTextContent();
            return ret;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    public static void main(String[] args) throws Exception {
        SoapClient soapClient=new SoapClient("http://serviceone.webservice.lyn.cn/","http://127.0.0" +
                ".1:8080/soap/sayHello?wsdl","WebServiceInterfaceService","WebServiceInterfacePort","sayHello",
                "sayHelloResponse",
                2000);
        //封装请求参数
        HashMap msg=new HashMap<>();
        msg.put("name","啊哈");
        String s = soapClient.sendMessage(msg);
        System.out.println(s);
    }
}

总结、其实我踩的最大的坑主要有两部分

第一个就是请求参数无法传递进去,解决办法是设置@WebParam注解的targetNamespace属性值
这个主要就是报非法属性异常

Unmarshalling Error: 意外的元素 (uri:"http://service
s.bingosoft.net/", local:"arg1")。所需元素为<{}arg5>,<{}arg4>,<{}arg3>,<{}arg2>,
<{}arg1>,<{}arg0>

第二个就是无法获取到返回值,解决办法就是需要添加@WebResult注解并设置对应的属性值
这个主要就是在客户端获取返回值哪里报空指针异常

你可能感兴趣的:(CXF方式搭建本地webservice服务和soap方式调用踩坑)