【Web Service】

一、基础

webservice即web服务,它是一种跨编程语言和跨操作系统平台的远程调用技术。

1.1 WS协议

JAVA 中共有三种WebService 规范,分别是JAX-WS(JAX-RPC)、JAXM&SAAJ、JAX-RS

  • JAX-WS(Java API For XML-WebService),JDK1.6 自带的版本为JAX-WS2.1,其底层支 持为JAXB。JAX-WS(JSR 224)规范的API 位于javax.xml.ws.*包,其中大部分都是注解,提供API操作Web 服务(通常在客户端使用的较多,由于客户端可以借助SDK 生成,因此这个包中的API我们较少会直接使用)。
  • JAXM&SAAJ:JAXM(JAVA API For XML Message)主要定义了包含了发送和接收消息所需的API,相当于Web 服务的服务器端,其API位于javax.messaging包,它是JAVA EE的可选包,因此需要单独下载。SAAJ(SOAP With Attachment API For Java,JSR 67)是与JAXM 搭配使用的API,为构建SOAP包和解析SOAP包提供了重要的支持,支持附件传输,它在服务器端、客户端都需要使用。这里还要提到的是SAAJ 规范,其API 位于javax.xml.soap包。
  • JAX-RS:JAX-RS是JAVA针对REST(RepresentationState Transfer)风格制定的一套Web服务规范,由于推出的较晚(效率高),该规范(JSR 311,目前JAX-RS 的版本为1.0)并未随JDK1.6 一起发行,你需要到JCP上单独下载JAX-RS规范的接口,其API 位于javax.ws.rs.*包。这里的JAX-WS和JAX-RS规范我们采用Apache CXF作为实现,CXF是Objectweb Celtix 和Codehaus XFire 合并而成。CXF 的核心是org.apache.cxf.Bus(总线),类似于Spring的ApplicationContext,Bus由BusFactory创建,默认是SpringBusFactory类,可见默认CXF是依赖于Spring的,Bus都有一个ID,默认的BUS的ID是cxf。你要注意的是Apache CXF2.2 的发行包中的jar你如果直接全部放到lib目录,那么你必须使用JDK1.6,否则会报JAX-WS版本不一致的问题。对于JAXM&SAAJ规范我们采用JDK中自带的默认实现。

1.2 SOAP 协议

SOAP即简单对象访问协议(Simple Object Access Protocol),它是用于交换 XML(标准通用标记语言下的一个子集)编码信息的轻量级协议。它有三个主要方面: XML-envelope为描述信息内容和如何处理内容定义了框架,将程序对象编码成为XML对象的规则,执行远程过程调用(RPC)的约定。SOAP可以运行在任何其他传输协议上。SOAP是基于JAX-WS协议下的通信协议;SOAP = 在HTTP的基础上+XML数据。SOAP的组成如下:

  • Envelope – 必须的部分。以XML的根元素出现。
  • Headers – 可选的。
  • Body – 必须的。在body部分,包含要执行的服务器的方法和发送到服务器的数据。

SOAP 1.1和 SOAP 1.2的区别

  • 数据格式不同:content-type不同
    SOAP1.1:text/xml;charset=utf-8
    SOAP1.2:application/soap+xml;charset=utf-8
  • 命名空间不同:
    SOAP1.1:http://schemas.xmlsoap.org/soap/envelope
    SOAP1.2:http://www.w3.org/2003/05/soap-envelope

1.3 WSDL

wsdl说明书是对SOAP协议的描述,通过wsdl说明书,就可以描述webservice服务端对外发布的服务;wsdl说明书是一个基于xml文件,通过xml语言描述整个服务;在wsdl说明中,描述了:对外发布的服务名称(类)接口方法名称(方法)接口参数(方法参数)服务返回的数据类型(方法返回值)。

1.4 UDDI

Web服务提供商又如何将自己开发的Web服务公布到因特网上,这就需要使用到UDDI了,UDDI的话,是一个跨产业,跨平台的开放性架构,可以帮助 Web 服务提供商在互联网上发布 Web 服务的信息。
UDDI 是一种目录服务,企业可以通过 UDDI 来注册和搜索Web服务。 简单来说的话,UDDI 就是一个目录,只不过在这个目录中存放的是一些关于Web服务的信息而已。并且UDDI通过SOAP进行通讯,构建于 . Net 之上。 UDDI 即 Universal Description,Discovery andIntegration,也就是通用的描述,发现以及整合。 UDDI的目的是为电子商务建立标准;UDDI是一套基于Web的、分布式的、为WebService提供的、信息注册中心的实现标准规范,同时也包含一组使企业能将自身提供的Web Service注册,以使别的企业能够发现的访问协议的实现标准。

【Web Service】_第1张图片
ws调用流程
  • 1.Client在UDDI Registry上可以查到所需Web Server A服务。
  • 2.UDDI Registry返回所需的服务Web Server A信息。
  • 3.Client 去 Web Server A询问确切的调用方法。
  • 4.Web Server A看到Client 提出的“确切方法查询”之后,返回给它一个 WSDL。
  • 5.Client根据WSDL将请求封装成为HTTP请求 , 发给 Web Server A。这些封装方式采用的是标准的SOAP方式 , 实质是满足HTTP协议的一些SOAP的报文消息。
  • 6.Web Server A回应的也是HTTP协议的SOAP包。这样双方的请求 - 响应完全畅通。

1.5 REST

REST(英文:Representational State Transfer,简称REST)描述了一个架构样式的网络系统,比如web应用程序。它首次出现在2000年Roy Fielding的博士论文中,他是HTTP规范的主要编写者之一。在目前主流的三种Web服务交互方案中,REST相比于SOAP(Simple Object Access protocol,简单对象访问协议)以及XML-RPC更加简单明了,无论是对URL的处理还是对Payload的编码,REST都倾向于用更加简单轻量的方法设计和实现。值得注意的是REST并没有一个明确的标准,而更像是一种设计的风格。
GET用来获取资源, POST用来新建资源, PUT用来更新资源, DELETE用来删除资源。 访问服务器资源,通过不同的http请求方式,服务器就知道对CRUD的哪个操作!JAX-RS 发布服务就是使用RESTFUL风格。

二、 非框架的JAX-WS实现

2.1 服务端(JDK):

服务端通常使用CXF等框架发布,使用JDK的方式比较少。
关于发布WebService主要就是通过javax.xml.ws.Endpoint类提供的静态方法publish进行发布,如果是普通的java项目,那么可以专门写一个类用于发布WebService,如果是Web项目,那么可以使用ServletContextListener或者Servlet进行发布,框架与WEB项目集成较好。

1.定义接口和实现

package ws;
import javax.jws.WebMethod;
import javax.jws.WebService;

@WebService
public interface WebServiceI {
    @WebMethod
    String sayHello(String name);
}
package ws;
import javax.jws.WebService;
@WebService
public class WebServiceImpl implements WebServiceI {
    @Override
    public String sayHello(String name) {
        System.out.println("WebService sayHello "+name);
        return "sayHello "+name;
    }
}

2.发布服务

package ws.test;
import ws.WebServiceImpl;
import javax.xml.ws.Endpoint;
public class WebServicePublish {
    public static void main(String[] args) {
        String address = "http://127.0.0.1:8989/myservice";
        Endpoint.publish(address , new WebServiceImpl());
        System.out.println("发布webservice成功!");
    }
}

2.2 客户端:

客户端只要符合SOAP协议即可;通常使用httpclient即可。

第一种方式:使用 JDK 的工具生成服务端代码,在本地调用即可。仅支持SOAP1.1。

wsimport -s . -p com.ws.transHello http://127.0.0.1:8081/hello?wsdl

该种方式,会分两次调用服务端,第一次是GET请求,获取WSDL文件说明书;第二次是POST请求,发送客户端请求。
第二和第三种方式,是直接发送请求。前提已知WSDL。
第二种方式:HttpURLConnection

package ws;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class WebServiceClient {
    public static void main(String[] args)  throws Exception{
        String WSDL = "http://192.168.202.61:8989/myservice";
        String result = callWebService(WSDL);
        System.out.println("出参:"+result);
    }
    private static String callWebService(String wsdl)  throws Exception{
        System.setProperty("sun.net.client.defaultConnectTimeout","20000");
        System.setProperty("sun.net.client.defaultReadTimeout","20000");
        //设置代理后,该请求就能被Fiddle抓取到
        System.setProperty("http.proxyHost", "localhost");
        System.setProperty("http.proxyPort", "8888");
        System.setProperty("https.proxyHost", "localhost");
        System.setProperty("https.proxyPort", "8888");
        // URL连接
        URL url = new URL(wsdl);
        HttpURLConnection conn = (HttpURLConnection)url.openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Length", String.valueOf(actionBySOAP().getBytes().length));
        conn.setRequestProperty("Content-Type","text/xml; charset=UTF-8");
        conn.setDoOutput(true);
        conn.setDoInput(true);
        conn.setConnectTimeout(20000);
        // 请求内容
        OutputStream output = conn.getOutputStream();
        output.write(actionBySOAP().getBytes());
        output.flush();
        output.close();
        System.out.println("入参:"+actionBySOAP());
        // 返回内容
        InputStreamReader isr = new InputStreamReader(conn.getInputStream());
        BufferedReader br = new BufferedReader(isr);
        StringBuilder sb = new StringBuilder();
        String str = null;
        while((str = br.readLine())!=null){
            sb.append(str + "\n");
        }
        br.close();
        isr.close();
        return sb.toString();
    }
 
    private static String actionBySOAP(){
        StringBuilder sb = new StringBuilder();
        sb.append("");
        sb.append("");
        sb.append("");
        sb.append("");
        sb.append("123");
        sb.append("");
        sb.append("");
        sb.append("");
        return sb.toString();
    }
}

以上方式是SOAP 1.1

第三种方式:HttpClient。建议采用soap1.1方式调用,经测试使用soap1.1方式能调用soap1.1和soap1.2的服务端。

import java.nio.charset.Charset;
import org.apache.http.HttpEntity;
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.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
 
public class HttpClientCallSoapUtil {
    static int socketTimeout = 30000;// 请求超时时间
    static int connectTimeout = 30000;// 传输超时时间
    static Logger logger = Logger.getLogger(HttpClientCallSoapUtil.class);
 
    /**
     * 使用SOAP1.1发送消息
     * 
     * @param postUrl
     * @param soapXml
     * @param soapAction
     * @return
     */
    public static String doPostSoap1_1(String postUrl, String soapXml,String soapAction) {
        String retStr = "";
        // 创建HttpClientBuilder
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        // HttpClient
        CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
        HttpPost httpPost = new HttpPost(postUrl);
                //  设置请求和传输超时时间
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
        httpPost.setConfig(requestConfig);
        try {
            httpPost.setHeader("Content-Type", "text/xml;charset=UTF-8");
            httpPost.setHeader("SOAPAction", soapAction);
            StringEntity data = new StringEntity(soapXml,Charset.forName("UTF-8"));
            httpPost.setEntity(data);
            CloseableHttpResponse response = closeableHttpClient.execute(httpPost);
            HttpEntity httpEntity = response.getEntity();
            if (httpEntity != null) {
                // 打印响应内容
                retStr = EntityUtils.toString(httpEntity, "UTF-8");
                logger.info("response:" + retStr);
            }
            // 释放资源
            closeableHttpClient.close();
        } catch (Exception e) {
            logger.error("exception in doPostSoap1_1", e);
        }
        return retStr;
    }
 
    /**
     * 使用SOAP1.2发送消息
     * 
     * @param postUrl
     * @param soapXml
     * @param soapAction
     * @return
     */
    public static String doPostSoap1_2(String postUrl, String soapXml,
            String soapAction) {
        String retStr = "";
        // 创建HttpClientBuilder
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        // HttpClient
        CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
        HttpPost httpPost = new HttpPost(postUrl);
                // 设置请求和传输超时时间
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
        httpPost.setConfig(requestConfig);
        try {
            httpPost.setHeader("Content-Type",
                    "application/soap+xml;charset=UTF-8");
            httpPost.setHeader("SOAPAction", soapAction);
            StringEntity data = new StringEntity(soapXml,Charset.forName("UTF-8"));
            httpPost.setEntity(data);
            CloseableHttpResponse response = closeableHttpClient.execute(httpPost);
            HttpEntity httpEntity = response.getEntity();
            if (httpEntity != null) {
                // 打印响应内容
                retStr = EntityUtils.toString(httpEntity, "UTF-8");
                logger.info("response:" + retStr);
            }
            // 释放资源
            closeableHttpClient.close();
        } catch (Exception e) {
            logger.error("exception in doPostSoap1_2", e);
        }
        return retStr;
    }
 
    public static void main(String[] args) {
        String orderSoapXml = ""
                + ""
                + "   "
                + "   "
                + "      "
                + "         "
                + "            ?"
                + "            ?"
                + "            ?"
                + "         " + "      "
                + "   " + "";
        String querySoapXml = ""
                + ""
                + "   "
                + "   "
                + "      "
                + "         "
                + "            ?"
                + "            ?"
                + "            ?"
                + "         " + "      "
                + "   " + "";
        String postUrl = "http://localhost:8080/services/WebServiceFromB";
        //采用SOAP1.1调用服务端,这种方式能调用服务端为soap1.1和soap1.2的服务
        doPostSoap1_1(postUrl, orderSoapXml, "");
        doPostSoap1_1(postUrl, querySoapXml, "");
 
        //采用SOAP1.2调用服务端,这种方式只能调用服务端为soap1.2的服务
        //doPostSoap1_2(postUrl, orderSoapXml, "order");
        //doPostSoap1_2(postUrl, querySoapXml, "query");
    }
}

第四种方式:使用可视化工具,例如SOAPUI和POSTMAN。
备注:Web Service是HTTP+XML,底层还是HTTP协议,不管使用哪种客户端(SOAPUI/POSTMAN/JAVA)所以可以使用Fiddle抓包工具,抓取到请求和响应。前提是客户端设置的代理与Fiddle一样,系统默认的代理:localhost:8888。
第五、六、七、八种是JDK自带的不需要引入其他框架。
第五种方式:Payload

/**
 * dispatch Payload方式调用WebService
 * @param portName 端口名称
 * @param param 参数
 */
public static void dispatchPayload(String portName, String param) {
    try {
        StringBuffer source = new StringBuffer();
        source.append("");
        source.append("").append(param).append("");
        source.append("");
        StreamSource xmlSource = new StreamSource(new StringReader(source.toString()));
        
        URL wsdlURL = new URL(url);
        QName serviceQName = new QName(targetNamespace, "TraditionalSimplifiedWebService");
        Service service = Service.create(wsdlURL, serviceQName);
        QName portQName = new QName(targetNamespace, portName);
        Dispatch dispatch = service.createDispatch(portQName, Source.class, Service.Mode.PAYLOAD);
        
        //.NET的服务端Soap1.1需要,不加会报错误:服务器未能识别 HTTP 头 SOAPAction 的值
        Map requestContext = dispatch.getRequestContext();
        requestContext.put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE);
        requestContext.put(BindingProvider.SOAPACTION_URI_PROPERTY, "http://webxml.com.cn/toTraditionalChinese");
        
        Source orderSource = dispatch.invoke(xmlSource);
        StreamResult result = new StreamResult(new ByteArrayOutputStream());
        Transformer trans = TransformerFactory.newInstance().newTransformer();
        trans.transform(orderSource, result);
        ByteArrayOutputStream baos = (ByteArrayOutputStream) result.getOutputStream();
        String responseContent = new String(baos.toByteArray());
        System.out.println(responseContent);

        Reader file = new StringReader(responseContent);
        SAXReader reader = new SAXReader();
        Document dc = reader.read(file);
        Element root = dc.getRootElement();
        String r = root.elementText("toTraditionalChineseResult").trim();
        System.out.println(r);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

第六种:Message方式

package com.inspur.ws;

import java.io.ByteArrayOutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.net.URL;
import java.util.Map;

import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * JAX-WS Dispatch方式调用WebService样例
 * @author wuyy
 *
 */
public class JaxWsDispatch {
    private static String url = "http://www.webxml.com.cn/WebServices/TraditionalSimplifiedWebService.asmx?wsdl";
    private static String targetNamespace = "http://webxml.com.cn/";
    /**
     * dispatch Payload方式调用WebService
     * @param portName 端口名称
     * @param param 参数
     */
    public static void dispatchPayload(String portName, String param) {
        try {
            StringBuffer source = new StringBuffer();
            source.append("");
            source.append("").append(param).append("");
            source.append("");
            StreamSource xmlSource = new StreamSource(new StringReader(source.toString()));
            
            URL wsdlURL = new URL(url);
            QName serviceQName = new QName(targetNamespace, "TraditionalSimplifiedWebService");
            Service service = Service.create(wsdlURL, serviceQName);
            QName portQName = new QName(targetNamespace, portName);
            Dispatch dispatch = service.createDispatch(portQName, Source.class, Service.Mode.PAYLOAD);
            
            //.NET的服务端Soap1.1需要,不加会报错误:服务器未能识别 HTTP 头 SOAPAction 的值
            Map requestContext = dispatch.getRequestContext();
            requestContext.put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE);
            requestContext.put(BindingProvider.SOAPACTION_URI_PROPERTY, "http://webxml.com.cn/toTraditionalChinese");
            
            Source orderSource = dispatch.invoke(xmlSource);
            StreamResult result = new StreamResult(new ByteArrayOutputStream());
            Transformer trans = TransformerFactory.newInstance().newTransformer();
            trans.transform(orderSource, result);
            ByteArrayOutputStream baos = (ByteArrayOutputStream) result.getOutputStream();
            String responseContent = new String(baos.toByteArray());
            System.out.println(responseContent);

            Reader file = new StringReader(responseContent);
            SAXReader reader = new SAXReader();
            Document dc = reader.read(file);
            Element root = dc.getRootElement();
            String r = root.elementText("toTraditionalChineseResult").trim();
            System.out.println(r);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * dispatch Payload方式调用WebService
     * @param soapNamespace soap消息整个消息体的命名空间,Soap1.1和Soap1.2不一样
     * @param portName 端口名称
     * @param param 参数
     */
    public static void dispatchMessage(String soapNamespace, String portName, String param) {
        try {
            StringBuffer source = new StringBuffer();
            source.append("");
            source.append("");
            source.append("");
            source.append("");
            source.append("").append(param).append("");
            source.append("");
            source.append("");
            source.append("");
            StreamSource xmlSource = new StreamSource(new StringReader(source.toString()));
            
            URL wsdlURL = new URL(url);
            QName serviceQName = new QName(targetNamespace, "TraditionalSimplifiedWebService");
            Service service = Service.create(wsdlURL, serviceQName);
            QName portQName = new QName(targetNamespace, portName);
            Dispatch dispatch = service.createDispatch(portQName, Source.class, Service.Mode.MESSAGE);
            
            //.NET的服务端Soap1.1需要,不加会报错误:服务器未能识别 HTTP 头 SOAPAction 的值
            Map requestContext = dispatch.getRequestContext();
            requestContext.put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE);
            requestContext.put(BindingProvider.SOAPACTION_URI_PROPERTY, "http://webxml.com.cn/toTraditionalChinese");
            
            Source orderSource = dispatch.invoke(xmlSource);
            StreamResult result = new StreamResult(new ByteArrayOutputStream());
            Transformer trans = TransformerFactory.newInstance().newTransformer();
            trans.transform(orderSource, result);
            ByteArrayOutputStream baos = (ByteArrayOutputStream) result.getOutputStream();
            String responseContent = new String(baos.toByteArray());
            System.out.println(responseContent);

            Reader file = new StringReader(responseContent);
            SAXReader reader = new SAXReader();
            Document dc = reader.read(file);
            //节点名称为toTraditionalChineseResult 命名空间为http://webxml.com.cn/
            String r = dc.selectSingleNode("//*[local-name()='toTraditionalChineseResult' and namespace-uri()='http://webxml.com.cn/']").getText().trim();
            System.out.println(r);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        //Soap1.1对应的portName为TraditionalSimplifiedWebServiceSoap,Soap1.2对应的portName为TraditionalSimplifiedWebServiceSoap12
        dispatchPayload("TraditionalSimplifiedWebServiceSoap", "小学");
        dispatchPayload("TraditionalSimplifiedWebServiceSoap12", "大学");
        
        //Soap1.1对应的soapNamespace为http://schemas.xmlsoap.org/soap/envelope/,Soap1.1对应的soapNamespace为http://www.w3.org/2003/05/soap-envelope
        dispatchMessage("http://schemas.xmlsoap.org/soap/envelope/", "TraditionalSimplifiedWebServiceSoap", "小学");
        dispatchMessage("http://www.w3.org/2003/05/soap-envelope", "TraditionalSimplifiedWebServiceSoap12", "大学");
    }

}

第七种:Proxy方式

package com.inspur.ws;

import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import com.inspur.zsyw.ws.ITestService;
/**
 * JAX-WS Proxy调用 ,需把接口类拷贝到客户端
 *
 */
public class JaxWsProxy {
    private static String url = "http://10.40.103.48:9006/zsywservice/TestService?wsdl";
    private static String targetNamespace = "http://ws.zsyw.inspur.com/";
    
    public static void proxy(String param) {
        try {
            QName qname = new QName(targetNamespace, "TestService");
            Service service = Service.create(new URL(url), qname);
            ITestService testService = service.getPort(ITestService.class);
            System.out.println(testService.hello(param));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        proxy("大学");
    }
}

第八种:RPC方式

package com.inspur.ws;

import java.net.URL;

import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;

import com.inspur.zsyw.ws.ITestService;

/**
 * JAX-WS RPC调用 ,需把接口类拷贝到客户端,接口类需继承java.rmi.Remote接口
 *
 */
public class JaxWsRpc {
    private static String url = "http://10.40.103.48:9006/zsywservice/TestService?wsdl";
    private static String targetNamespace = "http://ws.zsyw.inspur.com/";
    
    public static void rpc(String param) {
        try {
            ServiceFactory serviceFactory = ServiceFactory.newInstance();
            Service service = serviceFactory.createService(new URL(url), new QName(targetNamespace, "TestService"));
            ITestService testService = (ITestService) service.getPort(ITestService.class);
            String result = testService.hello(param);
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        rpc("大学");
    }
}

三、ApacheCXF框架实现JAX-WS

Apache CXF = Celtix + XFire,ApacheCXF的前身叫Apache CeltiXfire,现在已经正式更名为 Apache CXF了,以下简称为CXF。CXF继承了Celtix和XFire两大开源项目的精华,提供了对JAX-WS全面的支持,并且提供了多种Binding DataBinding、Transport以及各种Format的支持,并且可以根据实际项目的需要,采用代码优先 (Code First)或者WSDL优先(WSDL First)来轻松地实现Web Services的发布和使用。目前它仍只是 Apache 的一个孵化项目。
Apache CXF是一个开源的Services框架,CXF帮助您利用Frontend编程API来构建和开发Services,像JAX-WS 。这些Services可以支持多种协议,比如:SOAP、XML/HTTP、RESTfulHTTP或者CORBA ,并且可以在多种传输协议上运行,比如:HTTP、JMS或者JBI,CXF大大简化了Services的创建,同时它继承了XFire传统,一样可以天然地和Spring进行无缝集成。

3.1 JAVA项目的代码实现

pom.xml



    4.0.0
    com.itheima
    01_jaxws_server
    pom
    1.0-SNAPSHOT
    
        ../03_jaxws_spring_server
    
    01_jaxws_server
    
        
        
            org.apache.cxf
            cxf-rt-frontend-jaxws
            3.0.1
        
        
        
            org.apache.cxf
            cxf-rt-transports-http-jetty
            3.0.1
        
        
     
           org.slf4j
           slf4j-simple
           1.7.12
     
        
            junit
            junit
            4.10
            test
        
    
    
        
            
                
                    org.apache.maven.plugins
                    maven-compiler-plugin
                    3.2
                    
                        1.8
                        1.8
                        UTF-8
                        true
                    
                
            
        
    

log4j.properties:

# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE            debug   info   warn error fatal
log4j.rootCategory=info, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

服务端:

//对外发布服务的接口
@WebService
public interface HelloService {
   public String sayHello(String name);
}
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello(String name) {
        return name + ",Welcome to Itheima!";
    }
}

自定义拦截器:
拦截器详解:CXF拦截器介绍及自定义拦截器实现 - IT从业者互助会 (wawazhua.cn)

public class MyInterceptor extends AbstractPhaseInterceptor {
    public MyInterceptor() {
        super(Phase.PRE_INVOKE);  // 在调用方法之前调用自定拦截器    
    }
    @SuppressWarnings("null")
    public void handleMessage(SoapMessage message) throws Fault {
        List
headers=message.getHeaders(); if(headers==null && headers.size()==0){ throw new Fault(new IllegalArgumentException("没有Header,拦截器实施拦截")); } Header firstHeader=headers.get(0); Element ele=(Element) firstHeader.getObject(); NodeList uList=ele.getElementsByTagName("userName"); NodeList pList=ele.getElementsByTagName("password"); if(uList.getLength()!=1){ throw new Fault(new IllegalArgumentException("用户名格式不对")); } if(pList.getLength()!=1){ throw new Fault(new IllegalArgumentException("密码格式不对")); } String userName=uList.item(0).getTextContent(); String password=pList.item(0).getTextContent(); if(!userName.equals("java1234")||!password.equals("123456")){ throw new Fault(new IllegalArgumentException("用户名或者密码错误!")); } } }

发布服务:

public class Server {
    public static void main(String[] args) {
        //  发布服务的工厂
        JaxWsServerFactoryBean factory = new JaxWsServerFactoryBean();
        //  设置服务地址
        factory.setAddress("http://localhost:8000/ws/hello");
        //  设置服务类
        factory.setServiceBean(new HelloServiceImpl());
        //  添加日志输入、输出拦截器,观察soap请求、soap响应内容
       //factoryBean.getInInterceptors().add(new MyInterceptor()); //自定义拦截器
        factory.getInInterceptors().add(new LoggingInInterceptor());
        factory.getOutInterceptors().add(new LoggingOutInterceptor());
        //  发布服务
        factory.create();
        System.out.println("发布服务成功,端口8000.....");
    }
}

客户端:服务端的接口客户端也是需要的,正常使用工具即可生成。

【Web Service】_第2张图片
CXF 获取客户端代码

自定义拦截器:

public class AddHeaderInterceptor extends AbstractPhaseInterceptor {
    private String userName;
    private String password;
    public AddHeaderInterceptor(String userName,String password) {
        super(Phase.PREPARE_SEND); // 准备发送SOAP消息的时候调用拦截器
        this.userName=userName;
        this.password=password;
    }
    public void handleMessage(SoapMessage message) throws Fault {
        List
headerList=message.getHeaders(); Document doc=DOMUtils.createDocument(); Element ele=doc.createElement("authHeader"); Element uElement=doc.createElement("userName"); uElement.setTextContent(userName); Element pElement=doc.createElement("password"); pElement.setTextContent(password); ele.appendChild(uElement); ele.appendChild(pElement); headerList.add(new Header(new QName("java1234"),ele)); } }

客户端:

public class Client {
    public static void main(String[] args) {
        //  服务接口访问地址:http://localhost:8000/ws/hello
        // 创建cxf代理工厂
        JaxWsProxyFactoryBean factory = new  JaxWsProxyFactoryBean();
        //  设置远程访问服务端地址
        factory.setAddress("http://localhost:8000/ws/hello");
        //  设置接口类型
        factory.setServiceClass(HelloService.class);
        //  对接口生成代理对象
        HelloService helloService = factory.create(HelloService.class);
        //  代理对象对象  class com.sun.proxy.$Proxy34       [Java代理: 1. 静态代理;  2.动态代理(jdk接口代理、cglib子类代理)]    $CGLIB123
        System.out.println(helloService.getClass());
        // 客户端添加拦截器
       //client.getOutInterceptors().add(new AddHeaderInterceptor("java1234","123")); // 添加自定义拦截器
       //org.apache.cxf.endpoint.Client client=ClientProxy.getClient(helloService );
       //client.getInInterceptors().add(new LoggingInInterceptor()); // 添加In拦截器 日志拦截器
       //client.getOutInterceptors().add(new LoggingOutInterceptor()); // 添加Out拦截器 日志拦截器

        // 远程访问服务端方法  
        //复杂类型的传递,CXF需要利用@XmlJavaAdapter适配器转换。
        //复杂类型的传递,CXF需要利用@XmlJavaAdapter适配器转换。
        //复杂类型的传递,CXF需要利用@XmlJavaAdapter适配器转换。
        String content = helloService.sayHello("Jet");
        System.out.println(content);
    }
}
【Web Service】_第3张图片
SOAP通信内容

3.2 Web项目的CXF代码实现

1.省略配置pox.xml
2.定义接口服务

@WebService(name="MobileAddress"
            ,serviceName="MobileAddressService"
            ,portName="MobileAddressPort"
            ,targetNamespace="http://ws.esb.com/"
           )
@BindingType(javax.xml.ws.soap.SOAPBinding.SOAP12HTTP_BINDING)
public interface MobileAddress {
    @WebMethod(operationName="getAddressbyMobileNo")
    public @WebResult(name="address")String getAddressByMobile(@WebParam(name="mobileNo")String mobile);
}

3.接口实现类

package com.esb.ws.server;
public class MobileAddressImpl implements MobileAddress {
    @Override
    public String getAddressByMobile(String mobile) {
        return "电话号码"+mobile+"属于上海电信";
    }
}

4.Web.xml



  WebService_CXF_Web
  
      cxf
     
      org.apache.cxf.transport.servlet.CXFServlet
  
  
      cxf
      /ws/*
  

5.cxf-servlet.xml文件



    
    
        
            
        
    
    

6.启动TomCat。

3.3 Web项目的CXF与Spring集成

pox.xml



    4.0.0

    com.itheima
    03_jaxws_spring_server
    1.0-SNAPSHOT
    war
    03_jaxws_spring_server Maven Webapp
    
        
        
            org.apache.cxf
            cxf-rt-frontend-jaxws
            3.0.1
        
        
            junit
            junit
            4.12
        
        
            org.springframework
            spring-context
            4.2.4.RELEASE
        
        
            org.springframework
            spring-web
            4.2.4.RELEASE
        
        
            org.springframework
            spring-test
            4.2.4.RELEASE
        
    
    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.2
                
                    1.8
                    1.8
                    UTF-8
                    true
                
            
            
            
                org.apache.tomcat.maven
                tomcat7-maven-plugin
                2.2
                
                    
                    8080
                    
                    /
                
            
        
    

web.xml:



  Archetype Created Web Application
  
  
    cxfservlet
    org.apache.cxf.transport.servlet.CXFServlet
  
  
  
  
    cxfservlet
    /ws/*
  
  
  
    contextConfigLocation
    classpath:applicationContext.xml
  
  
    org.springframework.web.context.ContextLoaderListener
  

applicationContext.xml:



        
        
   
     
    


服务端:

@WebService
public interface HelloService {
    public String sayHello(String name);
}
public class HelloServiceImpl implements HelloService {
    public String sayHello(String name) {
        return name + ",Welcome to Itheima!";
    }
}

客户端:服务端的接口客户端也是需要的,正常使用工具即可生成。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class Client {
    // 注入对象
    @Resource
    private HelloService helloService;
    @Test
    public void remote(){
        // 查看接口的代理对象类型
        // class com.sun.proxy.$Proxy45
        System.out.println(helloService.getClass());
        // 远程访问服务端方法
        System.out.println(helloService.sayHello("Jerry"));
    }
}

applicationContext.xml:



        
    

四、 ApacheCXF框架实现JAX-RS

基于restful风格的webservice,请求使用的是http协议,可以传递xml/json数据

4.1 代码实现

服务端:
pom.xml:




    4.0.0
    com.itheima
    05_jaxrs_server
    pom
    1.0-SNAPSHOT
    
        ../07_jaxrs_spring_server
    
    05_jaxrs_server
    
        
            org.apache.cxf
            cxf-rt-frontend-jaxrs
            3.0.1
        
        
            org.apache.cxf
            cxf-rt-transports-http-jetty
            3.0.1
        
        
            org.slf4j
            slf4j-log4j12
            1.7.12
        
        
            org.apache.cxf
            cxf-rt-rs-client
            3.0.1
        
        
            org.apache.cxf
            cxf-rt-rs-extension-providers
            3.0.1
        
        
            org.codehaus.jettison
            jettison
            1.3.7
        
        
            junit
            junit
            4.10
            test
        
    
    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.2
                
                    1.8
                    1.8
                    UTF-8
                    true
                
            
        
    

实体类:

@XmlRootElement(name = "Car")
public class Car {
    private Integer id;
    private String carName;
    private Double price;
    //省略get/set
}
/**
 *  基于restful风格的webservice,客户端与服务端之间通讯可以传递xml数据、json数据
 *  @XmlRootElement 指定对象序列化为xml或json数据时根节点的名称
 *  xml:
 *       
 *           
 *           
 *           
 *       
 *   json:
 *   {"User":  {"id":100,  "username":"jack","city":"广州" }}
 *
 */
@XmlRootElement(name = "User")
public class User {
    private Integer id;
    private String username;
    private String city;
    private List cars = new ArrayList();
}

接口:

//  访问当前服务接口对应的路径
@Path("/userService")
@Produces("*/*")  // 服务器支持的返回的数据格式类型
public interface IUserService {

    // 表示处理的请求的类型,post 对应的是insert新增操作
    @POST
    // 访问当前服务接口方法对应的路径。 【.../userService/user】
    @Path("/user")
    //  服务器支持的请求的数据格式类型
    @Consumes({ "application/xml", "application/json" })
    public void saveUser(User user);

    // 表示处理的请求的类型,put 对应的是update修改操作
    @PUT
    @Path("/user")
    @Consumes({ "application/xml", "application/json" })
    public void updateUser(User user);

    // 表示处理的请求的类型,get 对应的是查询修改操作
    @GET
    @Path("/user")
    // 服务器支持的返回的数据格式类型
    @Produces({ "application/xml", "application/json" })
    public List findAllUsers();

    @GET
    @Path("/user/{id}")
    @Consumes("application/xml")
    @Produces({ "application/xml", "application/json" })
    public User finUserById(@PathParam("id") Integer id);

    // 表示处理的请求的类型,delete 对应的是删除操作
    @DELETE
    @Path("/user/{id}")
    @Consumes({"application/xml", "application/json"})
    public void deleteUser(@PathParam("id") Integer id);
}

接口实现类:

public class UserServiceImpl implements IUserService {

    public void saveUser(User user) {
        System.out.println("save user:" + user);
    }

    public void updateUser(User user) {
        System.out.println("update user:" + user);
    }

    public List findAllUsers() {
        List users = new ArrayList();
        User user1 = new User();
        user1.setId(1);
        user1.setUsername("小明");
        user1.setCity("北京");

        List carList1 = new ArrayList();
        Car car1 = new Car();
        car1.setId(101);
        car1.setCarName("保时捷");
        car1.setPrice(1000000d);
        carList1.add(car1);
        Car car2 = new Car();
        car2.setId(102);
        car2.setCarName("宝马");
        car2.setPrice(400000d);
        carList1.add(car2);
        user1.setCars(carList1);

        users.add(user1);

        User user2 = new User();
        user2.setId(2);
        user2.setUsername("小丽");
        user2.setCity("上海");
        users.add(user2);

        return users;
    }

    public User finUserById(Integer id) {
        if (id == 1) {
            User user1 = new User();
            user1.setId(1);
            user1.setUsername("小明");
            user1.setCity("北京");
            return user1;
        }
        return null;
    }

    public void deleteUser(Integer id) {
        System.out.println("delete user id :" + id);
    }
}

发布服务

public class Server {
    public static void main(String[] args) {
        // 创建发布服务的工厂
        JAXRSServerFactoryBean factory = new JAXRSServerFactoryBean();
        // 设置服务地址
        factory.setAddress("http://localhost:8001/ws/");
        // 设置服务类
        factory.setServiceBean(new UserServiceImpl());
        // 添加日志输入输出拦截器,需要log4j配置文件
        factory.getInInterceptors().add(new LoggingInInterceptor());
        factory.getOutInterceptors().add(new LoggingOutInterceptor());
        // 发布服务
        factory.create();
        System.out.println("发布服务成功,端口8001");
    }
}

客户端:

public class Client {
    @Test
    public void testSave(){
        User user = new User();
        user.setId(100);
        user.setUsername("Jerry");
        user.setCity("gz");
        // 通过WebClient对象远程调用服务端
        WebClient
                    .create("http://localhost:8001/ws/userService/user")
                    .type(MediaType.APPLICATION_JSON)  // 指定请求的数据格式为json
                    .post(user);
    }
    @Test
    public void testGet(){
        // 查询一个
        User user =
            WebClient
                    .create("http://localhost:8001/ws/userService/user/1")
                   .accept(MediaType.APPLICATION_JSON)
                    .get(User.class);
        System.out.println(user);
    }
}

4.2 Spring集成

服务端:
web.xml:



    Archetype Created Web Application
    
        cxfservlet
        org.apache.cxf.transport.servlet.CXFServlet
    
    
        cxfservlet
        /ws/*
    
    
    
        contextConfigLocation
        classpath:applicationContext.xml
    
    
        org.springframework.web.context.ContextLoaderListener
    

pom.xml:



    4.0.0
    com.itheima
    07_jaxrs_spring_server
    1.0-SNAPSHOT
    
        ../08_jaxrs_spring_client
    
    pom

    07_jaxrs_spring_server Maven Webapp
    
        
        
            org.apache.cxf
            cxf-rt-frontend-jaxrs
            3.0.1
        
        
        
            org.slf4j
            slf4j-log4j12
            1.7.12
        
        
        
            org.apache.cxf
            cxf-rt-rs-client
            3.0.1
        
        
        
            org.apache.cxf
            cxf-rt-rs-extension-providers
            3.0.1
        
        
        
            org.codehaus.jettison
            jettison
            1.3.7
        
        
        
            org.springframework
            spring-context
            4.2.4.RELEASE
        
        
        
            org.springframework
            spring-web
            4.2.4.RELEASE
        
        
        
            org.springframework
            spring-test
            4.2.4.RELEASE
        
        
        
            junit
            junit
            4.12
        
    
    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.2
                
                    1.8
                    1.8
                    UTF-8
                    true
                
            
            
            
                org.apache.tomcat.maven
                tomcat7-maven-plugin
                2.2
                
                    
                    8080
                    
                    /
                
            
        
    

applicationContext.xml:



        
        
                
                    
                
        

沿用上面的User和Car实体类
IUserService:

//  访问当前服务接口对应的路径
@Path("/userService")
@Produces("*/*")  // 服务器支持的返回的数据格式类型
public interface IUserService {
    // 表示处理的请求的类型,post 对应的是insert新增操作
    @POST
    // 访问当前服务接口方法对应的路径。 【.../userService/user】
    @Path("/user")
    //  服务器支持的请求的数据格式类型
    @Consumes({ "application/xml", "application/json" })
    public void saveUser(User user);

    // 表示处理的请求的类型,put 对应的是update修改操作
    @PUT
    @Path("/user")
    @Consumes({ "application/xml", "application/json" })
    public void updateUser(User user);

    // 表示处理的请求的类型,get 对应的是查询修改操作
    @GET
    @Path("/user")
    // 服务器支持的返回的数据格式类型
    @Produces({ "application/xml", "application/json" })
    public List findAllUsers();

    @GET
    @Path("/user/{id}")
    @Consumes("application/xml")
    @Produces({ "application/xml", "application/json" })
    public User finUserById(@PathParam("id") Integer id);

    // 表示处理的请求的类型,delete 对应的是删除操作
    @DELETE
    @Path("/user/{id}")
    @Consumes({"application/xml", "application/json"})
    public void deleteUser(@PathParam("id") Integer id);
}

UserServiceImpl :

public class UserServiceImpl implements IUserService {
    public void saveUser(User user) {
        System.out.println("save user:" + user);
    }
    public void updateUser(User user) {
        System.out.println("update user:" + user);
    }
    public List findAllUsers() {
        List users = new ArrayList();
        User user1 = new User();
        user1.setId(1);
        user1.setUsername("小明");
        user1.setCity("北京");

        List carList1 = new ArrayList();
        Car car1 = new Car();
        car1.setId(101);
        car1.setCarName("保时捷");
        car1.setPrice(1000000d);
        carList1.add(car1);
        Car car2 = new Car();
        car2.setId(102);
        car2.setCarName("宝马");
        car2.setPrice(400000d);
        carList1.add(car2);
        user1.setCars(carList1);

        users.add(user1);

        User user2 = new User();
        user2.setId(2);
        user2.setUsername("小丽");
        user2.setCity("上海");
        users.add(user2);

        return users;
    }
    public User finUserById(Integer id) {
        if (id == 1) {
            User user1 = new User();
            user1.setId(1);
            user1.setUsername("小明");
            user1.setCity("北京");
            return user1;
        }
        return null;
    }
    public void deleteUser(Integer id) {
        System.out.println("delete user id :" + id);
    }
}

客户端:

public class Client {
    @Test
    public void testSave(){
        User user = new User();
        user.setId(100);
        user.setUsername("Jerry");
        user.setCity("gz");
        // 通过WebClient对象远程调用服务端
        WebClient
                    .create("http://localhost:8080/ws/userService/userService/user")
                    .type(MediaType.APPLICATION_JSON)  // 指定请求的数据格式为json
                    .post(user);
    }
    @Test
    public void testGet(){
        // 查询一个
        User user =
            WebClient
                    .create("http://localhost:8080/ws/userService/userService/user/1")
                   .accept(MediaType.APPLICATION_JSON)
                    .get(User.class);
        System.out.println(user);
    }
}

你可能感兴趣的:(【Web Service】)