二、Webservice基于Java的多种调用方式

前言

前不久,为客户开发的一个身份验证功能开发要用到Webservice,我是第一次接触Webservice的开发。

刚开始用CXF的方式去调用Webservice,成功了,但是部署到测试环境报错了,原因是CXF依赖与JDK的一个tool.jar,而测试环境使用的是JRE环境(JRE环境没有tool.jar这个jar包),因而CXF调用Webservice的方式行不通。于是,我又寻找了其它调用Webservice的方式进行尝试,AXIS、AXIS2,HttpClient,这三种方式都试过了,也踩了不少坑,最后使用HttpClient的方式解决了问题。

在此,我需要将这个踩坑过程记录,加深Webservice调用方式的知识点。

这次Webservice身份验证功能接口我一共使用了四种方式去调用,它们分别是:

(1)CXF方式。

(2)AXIS方式。

(3)AXIS2方式

(4)Http Client方式。

下面,对这四种调用方式进行介绍。可能某些方面介绍的不太齐全,但希望能大家能有所帮助。

首先看一下Webservice服务端的代码,这是一个非常简单的Webservice接口,方便理解Webservice调用的交互流程。

Webservice服务端代码

package com.chenlw.webservice;

import javax.jws.WebService;
import javax.xml.ws.Endpoint;


/**
 * Title: ServiceHello
 * Description: 基于jdk1.6以上的javax.jws 发布webservice接口
 *
 * @author panchengming
 * @WebService - 它是一个注解,用在类上指定将此类发布成一个ws。
 * Endpoint – 此类为端点服务类,它的方法publish用于将一个已经添加了@WebService注解
 * 对象绑定到一个地址的端口上。
 * Version:1.0.0
 */
@WebService
public class MyWebServiceTester {

    public static String JWS_SERVER_URL = "http://192.168.174.1:9090/MyWebService/testWebService";


    public static void main(String[] args) {
        // 通过EndPoint(端点服务)发布一个WebService
        // 发布成功后 在浏览器输入 http://192.168.174.1:9090/MyWebService/testWebService?wsdl
        Endpoint.publish(JWS_SERVER_URL, new MyWebServiceTester());
        System.out.println("发布成功!");

    }


    /**
     * 供客户端调用方法  该方法是非静态的,会被发布
     *
     * @param param 传入参数
     * @return String 返回结果
     */
    public String testWebService(String param) {
        return "Webservice接口返回数据:" + param;
    }


}

代码运行后,在浏览器输入http://192.168.174.1:9090/MyWebService/testWebService?wsdl可以如下内容(这是WSDL,即基于XML语言的Web服务描述语言)。

二、Webservice基于Java的多种调用方式_第1张图片

1 CXF方式调用Webservice

1.1 CXF简介

Apache CXF 是一个开源的 Services 框架,CXF 帮助您利用 Frontend 编程 API 来构建和开发 Services ,像 JAX-WS 。这些 Services 可以支持多种协议,比如:SOAP、XML/HTTP、RESTful HTTP 或者 CORBA ,并且可以在多种传输协议上运行,比如:HTTP、JMS 或者 JBI,CXF 大大简化了 Services 的创建,同时它继承了 XFire 传统,一样可以天然地和 Spring 进行无缝集成。【来源于:百度百科CXF】

1.2 CXF调用Webservice

测试代码:

package com.chenlw.webservice;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;

/**
 * @author chenlw 2019/10/02
 */
public class CxfClientTester {

    public static final String SERVICE_URL = "http://192.168.174.1:9090/MyWebService/testWebService?wsdl";
    public static final String METHOD_NAME = "testWebService";


    public static void main(String[] args) {
        try {
            test1();
        } catch (Exception e) {
            System.out.println("ERROR:" + e.getMessage());
        }
    }

    public static void test1() throws Exception {
        JaxWsDynamicClientFactory clientFactory = JaxWsDynamicClientFactory.newInstance();
        Client client = clientFactory.createClient(SERVICE_URL);
        Object[] result = client.invoke(METHOD_NAME, "hello");
        System.out.println(result[0]);
    }

}

运行结果:

二、Webservice基于Java的多种调用方式_第2张图片

如果调用webservice接口需要添加身份验证参数怎么办呢?请看1.3章节,在HTTP请求头可以添加身份验证的参数。

1.3 CXF调用Webservice(增加自定义的HTTP请求头)

测试代码:

package com.chenlw.webservice;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;

import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @author chenlw 2019/10/02
 */
public class CxfClientTester {

    public static final String SERVICE_URL = "http://192.168.174.1:9090/MyWebService/testWebService?wsdl";
    public static final String METHOD_NAME = "testWebService";


    public static void main(String[] args) {
        try {
            // test1();
            test2();
        } catch (Exception e) {
            System.out.println("ERROR:" + e.getMessage());
        }
    }

    /**
     * 调用Webservice
     *
     * @throws Exception 异常
     */
    public static void test1() throws Exception {
        JaxWsDynamicClientFactory clientFactory = JaxWsDynamicClientFactory.newInstance();
        Client client = clientFactory.createClient(SERVICE_URL);
        Object[] result = client.invoke(METHOD_NAME, "hello");
        System.out.println(result[0]);
    }

    /**
     * 调用Webservice
     * 添加HTTP请求头
     *
     * @throws Exception
     */
    public static void test2() throws Exception {
        JaxWsDynamicClientFactory clientFactory = JaxWsDynamicClientFactory.newInstance();
        Client client = clientFactory.createClient(SERVICE_URL);

        // 添加HTTP请求头
        HttpHeaderInterceptor httpHeaderInterceptor = new HttpHeaderInterceptor();
        httpHeaderInterceptor.setTicket("ticket");
        client.getOutInterceptors().add(httpHeaderInterceptor);

        Object[] result = client.invoke(METHOD_NAME, "hello");
        System.out.println(result[0]);
    }


    public static class HttpHeaderInterceptor extends AbstractPhaseInterceptor {

        private String ticket;


        public HttpHeaderInterceptor() {
            super(Phase.POST_PROTOCOL);
        }

        @Override
        public void handleMessage(Message message) throws Fault {
            // 添加HTTP请求头
            Map headers = (Map) message.get(Message.PROTOCOL_HEADERS);
            try {
                System.out.println("ticket: " + ticket);
                headers.put("ticket", Collections.singletonList(ticket));
            } catch (Exception ce) {
                throw new Fault(ce);
            }
        }

        public String getTicket() {
            return ticket;
        }

        public void setTicket(String ticket) {
            this.ticket = ticket;
        }
    }

}

运行结果:

二、Webservice基于Java的多种调用方式_第3张图片

2 AXIS方式调用Webservice

2.1 AXIS简介

axis全称Apache Extensible Interaction System 即阿帕奇可扩展交互系统。Axis本质上就是一个SOAP引擎,提供创建服务器端、客户端和网关SOAP操作的基本框架。【来源于:百度百科】

 

2.2 AXIS调用Webservice

测试代码:

package com.chenlw.webservice.axis;

import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.encoding.XMLType;

import javax.xml.namespace.QName;
import javax.xml.rpc.ParameterMode;

/**
 * @author chenlw
 * @date 2019/10/02
 */
public class AxisClientTester2 {

    public static final String SERVICE_URL = "http://192.168.174.1:9090/MyWebService/testWebService?wsdl";
    public static final String TARGET_NAMESPACE = "http://webservice.chenlw.com/";
    public static final String METHOD_NAME = "testWebService";

    public static void main(String[] args) {
        try {
            testAxis1();
        } catch (Exception e) {
            System.out.println("错误:" + e.getMessage());
        }
    }


    /**
     * 通过axis方式调用webservice接口
     */
    public static void testAxis1() {
        try {

            // 创建一个服务(service)调用(call)
            Service service = new Service();
            // 通过service创建call对象
            Call call = (Call) service.createCall();

            // 设置service所在URL
            call.setTargetEndpointAddress(new java.net.URL(SERVICE_URL));
            // 设置方法名,需要设定TARGET_NAMESPACE,否则可能会报错的。
            // call.setOperationName(METHOD_NAME);
            call.setOperationName(new QName(TARGET_NAMESPACE, METHOD_NAME));
            // call.setUseSOAPAction(true);

            // 变量最好只是用String类型,其他类型会报错
            // 入参,在设定参数时,不使用服务端定义的参数名,而是arg0~argN来定义,也不需制定namespaceURI
            call.addParameter("arg0", XMLType.SOAP_STRING, ParameterMode.IN);
            // 设置参数名 state  第二个参数表示String类型,第三个参数表示入参
            // call.addParameter("param", org.apache.axis.encoding.XMLType.XSD_STRING, javax.xml.rpc.ParameterMode.IN);
            // 设置返回类型
            call.setReturnType(org.apache.axis.encoding.XMLType.XSD_STRING);

            // 此处为数组,有几个变量传几个变量
            Object jsonString = call.invoke(new Object[]{"param"});
            // 输出SOAP请求报文
            System.out.println("--SOAP Request: " + call.getMessageContext().getRequestMessage().getSOAPPartAsString());
            // 输出SOAP返回报文
            System.out.println("--SOAP Response: " + call.getResponseMessage().getSOAPPartAsString());
            // 输出返回信息
            System.out.println("result===" + jsonString.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

运行结果:

二、Webservice基于Java的多种调用方式_第4张图片

问题记录:

添加请求参数如果使用call.addParameter("param", org.apache.axis.encoding.XMLType.XSD_STRING, javax.xml.rpc.ParameterMode.IN);

返回结果表明服务器端接收到的参数为null。

解决办法:

在设定参数时,不使用服务端定义的参数名,而是arg0~argN来定义,也不需制定namespaceURI

。就是这句代码:call.addParameter("arg0", XMLType.SOAP_STRING, ParameterMode.IN);

3 AXIS2方式调用Webservice

3.1 AXIS2简介

Axis2是下一代 Apache Axis。Axis2 虽然由 Axis 1.x 处理程序模型提供支持,但它具有更强的灵活性并可扩展到新的体系结构。Axis2 基于新的体系结构进行了全新编写,而且没有采用 Axis 1.x 的常用代码。支持开发 Axis2 的动力是探寻模块化更强、灵活性更高和更有效的体系结构,这种体系结构可以很容易地插入到其他相关 Web 服务标准和协议(如 WS-Security、WS-ReliableMessaging 等)的实现中。

Apache Axis2 是Axis的后续版本,是新一代的SOAP引擎。【来源于:百度百科】

3.2 AXIS2调用Webservice

测试代码:

package com.chenlw.webservice.axis2;


import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.client.Options;
import org.apache.axis2.context.NamedValue;
import org.apache.axis2.rpc.client.RPCServiceClient;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.params.HttpMethodParams;

import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.List;

/**
 * AXIS2调用Webservice
 *
 * @author chenlw
 * @date 2019/10/02
 */
public class Axis2Tester1 {


    public static final String targetEndpoint = "http://192.168.174.1:9090/MyWebService/testWebService?wsdl";
    public static final String targetNamespace = "http://webservice.chenlw.com/";

    public static final String METHOD = "testWebService";


    public static void main(String[] args) {
        try {
            test1();
        } catch (Exception e) {
            System.out.println("异常:" + e.getMessage());
        }
    }

    public static void test1() throws Exception {
        //RPCServiceClient是RPC方式调用
        RPCServiceClient client = new RPCServiceClient();
        Options options = client.getOptions();
        //设置调用WebService的URL
        EndpointReference epf = new EndpointReference(targetEndpoint);
        options.setTo(epf);
        // 添加HTTP请求头
        List headerList = new ArrayList<>();
        headerList.add(new NamedValue("ticket", "ticket"));
        options.setProperty(HTTPConstants.HTTP_HEADERS, headerList);
        // 设置超时
        options.setProperty(HTTPConstants.CONNECTION_TIMEOUT, 10 * 1000);
        // 取消重复请求
        options.setProperty(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(0, false));

        QName qname = new QName(targetNamespace, METHOD);
        //指定调用的方法和传递参数数据,及设置返回值的类型
        Object[] result = client.invokeBlocking(qname, new Object[]{"param"}, new Class[]{String.class});
        System.out.println(result[0]);
    }


}

运行结果:

二、Webservice基于Java的多种调用方式_第5张图片

4 Http Client方式调用Webservice

4.1 Http Client方式

Http Client方式调用Webservice,其实是根据Webservice的交互流程来实现的。关键点在于把SOAP请求作为HTTP请求的正文,并且增加HTTP请求头,然后获取HTTP响应正文,解析结果。

测试代码中使用了org.apache.httpcomponents的jar包。

 

4.2 Http Client调用Webservice

测试代码:

package com.chenlw.webservice.httpclient;

import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;

import java.nio.charset.StandardCharsets;

/**
 * @author chenlw
 * @date 2019/09/27
 */
public class WebserviceHttpClientTester2 {

    public static final String SERVICE_URL = "http://192.168.174.1:9090/MyWebService/testWebService?wsdl";

    /**
     * Webservice连接超时时间
     */
    public static final int CLIENT_CONNECT_TIMEOUT = 10 * 1000;

    /**
     * 直接用soapui中请求的数据
     */
    public static final String requestXml = "\n" +
            "   \n" +
            "   \n" +
            "      \n" +
            "         \n" +
            "         %s\n" +
            "      \n" +
            "   \n" +
            "";


    public static void main(String[] args) {
        try {
            testHttpClient();
        } catch (Exception e) {
            System.out.println("错误:" + e.getMessage());
        }
    }


    public static void testHttpClient() throws Exception {
        String param = "param";
        String ticket = "ticket";
        String requestData = String.format(requestXml, param);
        String soapRes = callWebserviceUsingHttpClient(SERVICE_URL, requestData, ticket);
        System.out.println("result:" + soapRes);
    }

    /**
     * Http Client 调用Webservice接口
     *
     * @param wsdl        Webservice接口
     * @param requestData 请求数据
     * @param ticket      ticket
     * @return 结果
     */
    public static String callWebserviceUsingHttpClient(String wsdl, String requestData, String ticket) throws Exception {
        String soapResponse = null;
        HttpClientBuilder builder = HttpClientBuilder.create();
        CloseableHttpClient client = builder.build();
        try {
            HttpPost httpPost = new HttpPost(wsdl);
            // 添加HTTP请求头
            httpPost.setHeader("ticket", ticket);

            // 设置连接参数
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(CLIENT_CONNECT_TIMEOUT)
                    .setConnectionRequestTimeout(CLIENT_CONNECT_TIMEOUT)
                    .setSocketTimeout(CLIENT_CONNECT_TIMEOUT)
                    .build();
            httpPost.setConfig(requestConfig);

            // 请求正文
            StringEntity stringEntity = new StringEntity(requestData, StandardCharsets.UTF_8.displayName());
            //stringEntity.setContentType(ContentType.APPLICATION_XML.toString());
            stringEntity.setContentType("text/xml; charset=utf-8");
            httpPost.setEntity(stringEntity);
            // 发送请求
            CloseableHttpResponse response = client.execute(httpPost);
            int responseStatusCode = response.getStatusLine().getStatusCode();
            if (responseStatusCode != HttpStatus.SC_OK) {
                System.out.println("Webservice接口未能正确处理请求,HTTP响应状态码为:" + responseStatusCode);
            }

            HttpEntity entity = response.getEntity();
            if (entity != null) {
                soapResponse = EntityUtils.toString(entity, StandardCharsets.UTF_8.displayName());
            }
            if (response != null) {
                response.close();
            }
        } catch (Exception e) {
            System.out.println("调用Webservice出错:" + e.getMessage());
            throw e;
        } finally {
            if (client != null) {
                client.close();
            }
        }
        return soapResponse;
    }


}

运行结果:

二、Webservice基于Java的多种调用方式_第6张图片

 

5 多种调用方式优缺点比较

5.1 CXF方式

优点:API使用比较方便,不用写很多代码。

缺点:依赖于JDK的tool.jar包,如果运行环境是JRE则无法使用。

 

5.2 AXIS方式

优点:API使用比较方便,不用写很多代码。

缺点:添加HTTP请求头比较麻烦;配置项多。

 

5.3 AXIS2方式

优点:API使用比较方便,不用写很多代码;配置项方便;

缺点:依赖JAR包多,有可能跟Web项目的jar包有冲突。

 

5.4 Http Client方式

优点:接近于一种“原生代码”的方式调用Webservice,无需依赖过多的jar包,灵活性和扩展性较高,效率比框架实现快一些。

缺点:需要事先知道SOAP请求报文的格式(可用soapUI工具获取)。

 

以上就是我在项目实践过程中总结出来的优缺点,可能不够全面。仁者见仁智者见智吧。个人推荐调用Webservice的方式顺序如下:

(1)Http Client模式

(2)CXF方式

(3)AXIS2方式

(4)AXIS方式

 

你可能感兴趣的:(Java-Web,Webservice,CXF,AXIS2,AXIS,Http,Client)