Apache CXF 学习-使用Dispatch/Provider来直接处理SOAP消息

简介:

引入:

前面的例子中我们都是采用了SEI/SIB的方式来发送接收消息,其实我们客户端的代码是直接采用的传统的API调用,比如:

1
service.calcSum(a, b)

然后,在CXF框架中,它会把这些API调用的方式通过JAXB转为SOAP格式的消息,然后返回SOAP格式的消息也通过JAXB转回真正的返回值。所以这里的弊端是,虽然真正在网络上传输的是SOAP消息,但是我们却依然用传统的调用方式操作,显的多此一举。如果一个对象很大,那么将其通过JAXB转为SOAP消息则会花费一定的时间。那么有没有办法可以让我们客户端和服务器端都直接对SOAP消息操作呢?这就需要我们这里讨论的Dispatch/Provider技术。


实践:

Dispatch/Provider总是成对用的,客户端一般会构造一个SOAP消息,然后把它Dispatch到服务器的Endpoint之上,这就是Dispatch.而服务器端会给出如何对约定的SOAP消息格式进行处理并且构造返回消息的代码,这就叫Provider。 从对于消息的处理方式上看, 有直接处理整个消息的,对应就是Service.Mode.MESSAGE,也有只处理消息Payload的,对应就是Service.Mode.PAYLOAD,我们这里只演示Service.Mode.MESSAGE,另外一个和这个用法类似。


服务器端代码:

还是从服务器端开始,首先我们定义一个消息处理类CalcPlusServiceProvider,它可以处理整个SOAP请求消息并且构造返回SOAP消息,我们让其逻辑为只对请求的SOAP消息中的2个参数做加法运算,然后运算结果封装在返回SOAP消息中,并且代码中会分别把请求消息和响应消息打印到服务器的控制台上。代码如下:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package  com.charles.cxfstudy.provider;
import  java.io.IOException;
import  javax.xml.namespace.QName;
import  javax.xml.soap.MessageFactory;
import  javax.xml.soap.SOAPBody;
import  javax.xml.soap.SOAPElement;
import  javax.xml.soap.SOAPException;
import  javax.xml.soap.SOAPMessage;
import  javax.xml.transform.dom.DOMSource;
import  javax.xml.ws.Provider;
import  javax.xml.ws.Service;
import  javax.xml.ws.ServiceMode;
import  javax.xml.ws.WebServiceProvider;
import  org.w3c.dom.Node;
/**
  * 这个加法运算类用于演示基于Message模式的Provider,它会把消息作为整体来处理
  * @author Administrator
  *
  */
@WebServiceProvider ()
@ServiceMode (value=Service.Mode.MESSAGE)
public  class  CalcPlusServiceProvider  implements  Provider<DOMSource> {
     /**
      * 这个方法用于定义如何处理DOMSource的XML消息的逻辑,并且构造响应消息
      */
     public  DOMSource invoke(DOMSource request) {
         try {
         //先构造 一个SOAPMessage,用于放入请求的SOAP消息
         MessageFactory factory = MessageFactory.newInstance();
         SOAPMessage soapRequestMsg = factory.createMessage();
         //注意,因为我们的代码是吧消息作为整体处理,所以放入的是soapPart,而不是soapBody
         soapRequestMsg.getSOAPPart().setContent(request);
                                                                                                                                                                                                                                                                                                                                                                                     
         //打印到客户端请求来的消息到控制台
         System.out.println( "从客户端请求来的消息为:" );
         soapRequestMsg.writeTo(System.out);
         System.out.println();
                                                                                                                                                                                                                                                                                                                                                                                     
         //现在我们从请求消息中分离出我们所要的信息
         SOAPBody soapBody = soapRequestMsg.getSOAPBody();
                                                                                                                                                                                                                                                                                                                                                                                     
         Node calcSumNode = soapBody.getFirstChild();
                                                                                                                                                                                                                                                                                                                                                                                     
         //获得要做加法运算的数
         Node aNode = calcSumNode.getChildNodes().item( 0 );
         int  a = Integer.parseInt(aNode.getTextContent());
         Node bNode = calcSumNode.getChildNodes().item( 1 );
         int  b = Integer.parseInt(bNode.getTextContent());
                                                                                                                                                                                                                                                                                                                                                                                         
         //计算加法
         String sum = String.valueOf(a + b);
                                                                                                                                                                                                                                                                                                                                                                                     
         //封装结果到响应对象中
         SOAPMessage soapResponseMsg = factory.createMessage();
                                                                                                                                                                                                                                                                                                                                                                                     
         //构造<calcSumResponse>元素,它的namespace为"http://services.server.cxfstudy.charles.com",注意这个元素在SOAPMessage的<soap:Body>部分
         QName calcSumResponseQName =  new  QName( "http://services.server.cxfstudy.charles.com" , "calcSumResponse" );
         SOAPElement calcSumResponseEle = soapResponseMsg.getSOAPBody().addChildElement(calcSumResponseQName);
         calcSumResponseEle.addChildElement( "sum" ).addTextNode(sum);
                                                                                                                                                                                                                                                                                                                                                                                     
         //打印即将返回到客户端的响应消息到控制台
         System.out.println( "要发送到客户端的消息为:" );
         soapResponseMsg.writeTo(System.out);
         System.out.println();
                                                                                                                                                                                                                                                                                                                                                                                     
         //把SOAPMessage转为DOMSource类型
         DOMSource response =  new  DOMSource(soapResponseMsg.getSOAPPart());
         return  response;
                                                                                                                                                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                                                                                     
         } catch (SOAPException ex){
             ex.printStackTrace();
             return  null ;
         } catch (IOException ex){
             ex.printStackTrace();
             return  null ;
         }
                                                                                                                                                                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                                                                                                     
     }
}


为了让这个服务类生效,我们配置到beans.xml中(参见http://supercharles888.blog.51cto.com/609344/1361334)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<? xml  version = "1.0"  encoding = "UTF-8" ?>
< beans  xmlns = "http://www.springframework.org/schema/beans"
        xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
        xmlns:jaxws = "http://cxf.apache.org/jaxws"
        xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<!-- 导入cxf中的spring的一些配置文件,他们都在cxf-<version>.jar文件中 -->
< import  resource = "classpath:META-INF/cxf/cxf.xml"  />
< import  resource = "classpath:META-INF/cxf/cxf-extension-soap.xml"  />
< import  resource = "classpath:META-INF/cxf/cxf-servlet.xml"  />
< jaxws:endpoint
id = "calcPlusService"
implementor = "com.charles.cxfstudy.provider.CalcPlusServiceProvider"
address = "/calcPlus"  />
</ beans >


打包并部署应用到服务器上,就可以使用了,最终的wsdl文件如下:

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
< wsdl:definitions  xmlns:xsd = "http://www.w3.org/2001/XMLSchema"  xmlns:wsdl = "http://schemas.xmlsoap.org/wsdl/"  xmlns:tns = "http://provider.cxfstudy.charles.com/"  xmlns:soap = "http://schemas.xmlsoap.org/wsdl/soap/"  xmlns:ns1 = "http://schemas.xmlsoap.org/soap/http"  name = "CalcPlusServiceProviderService"  targetNamespace = "http://provider.cxfstudy.charles.com/" >
< wsdl:types >
< xsd:schema  xmlns:xsd = "http://www.w3.org/2001/XMLSchema"  xmlns:tns = "http://provider.cxfstudy.charles.com/"  attributeFormDefault = "unqualified"  elementFormDefault = "qualified"  targetNamespace = "http://provider.cxfstudy.charles.com/" >
< xsd:element  name = "invoke"  nillable = "true"  type = "xsd:anyType" />
< xsd:element  name = "invokeResponse"  nillable = "true"  type = "xsd:anyType" />
</ xsd:schema >
</ wsdl:types >
< wsdl:message  name = "invokeResponse" >
< wsdl:part  element = "tns:invokeResponse"  name = "invokeResponse" ></ wsdl:part >
</ wsdl:message >
< wsdl:message  name = "invoke" >
< wsdl:part  element = "tns:invoke"  name = "invoke" ></ wsdl:part >
</ wsdl:message >
< wsdl:portType  name = "CalcPlusServiceProvider" >
< wsdl:operation  name = "invoke" >
< wsdl:input  message = "tns:invoke"  name = "invoke" ></ wsdl:input >
< wsdl:output  message = "tns:invokeResponse"  name = "invokeResponse" ></ wsdl:output >
</ wsdl:operation >
</ wsdl:portType >
< wsdl:binding  name = "CalcPlusServiceProviderServiceSoapBinding"  type = "tns:CalcPlusServiceProvider" >
< soap:binding  style = "document"  transport = "http://schemas.xmlsoap.org/soap/http" />
< wsdl:operation  name = "invoke" >
< soap:operation  soapAction = ""  style = "document" />
< wsdl:input  name = "invoke" >
< soap:body  use = "literal" />
</ wsdl:input >
< wsdl:output  name = "invokeResponse" >
< soap:body  use = "literal" />
</ wsdl:output >
</ wsdl:operation >
</ wsdl:binding >
< wsdl:service  name = "CalcPlusServiceProviderService" >
< wsdl:port  binding = "tns:CalcPlusServiceProviderServiceSoapBinding"  name = "CalcPlusServiceProviderPort" >
< soap:address  location = "http://localhost:8080/cxf_jaxws_provider/services/calcPlus" />
</ wsdl:port >
</ wsdl:service >
</ wsdl:definitions >



客户端代码:

现在我们来构造客户端,因为我们的目的是使用直接构造并且发送SOAP消息的方式而不是类似SEI调用的方式来发送消息,所以我们先定义工具类,内含一个工具方法可以发送SOAP消息并且获得从服务器端的返回消息:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package  com.charles.cxfstudy.dispatcher;
import  java.net.MalformedURLException;
import  java.net.URL;
import  javax.xml.namespace.QName;
import  javax.xml.soap.MessageFactory;
import  javax.xml.soap.SOAPException;
import  javax.xml.soap.SOAPMessage;
import  javax.xml.transform.dom.DOMSource;
import  javax.xml.ws.Dispatch;
import  javax.xml.ws.Service;
import  javax.xml.ws.Service.Mode;
/**
  * 工具类用于发送和接收消息
  * @author Administrator
  *
  */
public  class  DispatcherUtil {
                                                                                                                                                                                                                                                               
     /**
      * 把指定的SOAP消息发送到指定的endpoint上,并且给出返回的SOAP消息
      * @param wsdlURLString
      * @param serviceQName
      * @param serviceProviderServiceName
      * @param serviceProviderPortName
      * @param soapRequest
      * @param factory
      * @return
      * @throws MalformedURLException
      * @throws SOAPException
      */
     public  static  SOAPMessage sendMessage (
             String wsdlURLString, String serviceQName,String serviceProviderServiceName,String serviceProviderPortName,SOAPMessage soapRequest,MessageFactory factory)  throws  MalformedURLException,SOAPException{
         //把SOAPMessage转为Source类型
         DOMSource requestMsg =   new  DOMSource(soapRequest.getSOAPPart());
                                                                                                                                                                                                                                                                   
         URL wsdlURL =  new  URL(wsdlURLString);
                                                                                                                                                                                                                                                                   
         //构造一个Service对象
         QName serviceProvider =  new  QName(serviceQName,serviceProviderServiceName);
         QName portName        =  new  QName(serviceQName,serviceProviderPortName);
         Service service  = Service.create(wsdlURL, serviceProvider);
                                                                                                                                                                                                                                                                   
         //利用Service对象来发送(Dispatch) Source类型的SOAPMessage到指定的Port上
         Dispatch<DOMSource> domMsg  = service.createDispatch(portName, DOMSource. class , Mode.MESSAGE);
         //获得响应消息
         DOMSource respMsg = domMsg.invoke(requestMsg);
         SOAPMessage soapResponse = factory.createMessage();
         soapResponse.getSOAPPart().setContent(respMsg);
                                                                                                                                                                                                                                                                   
         return  soapResponse;
                                                                                                                                                                                                                                                                                   
     }
}


注意:我非常喜欢这种方式,因为它最直接了,发送什么消息就构造什么消息,然后直接调用API,而无需用wsimport工具去操作WSDL文件去生成N多桩文件了


然后我们的测试类的方法就是构造SOAP消息,然后调用工具方法来发送SOAP消息并且获取返回消息,并且分别打印到客户端的控制台上:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
  * 这里用于演示如何用Dispatch来发送一个SOAP消息到指定的Provider
  * @author Administrator
  *
  */
public  class  MainTest {
                                                                                                                                                                      
     public  static  SOAPMessage buildMessageForAdd(MessageFactory factory)  throws  SOAPException{
                                                                                                                                                                          
         SOAPMessage soapRequest = factory.createMessage();
                                                                                                                                                                          
         //构造<calcSum>元素,它的namespace为"http://services.server.cxfstudy.charles.com",注意这个元素在SOAPMessage的<soap:Body>部分
         QName calcSumQName =  new  QName( "http://services.server.cxfstudy.charles.com" , "calcSum" );
         SOAPElement calcSumEle = soapRequest.getSOAPBody().addChildElement(calcSumQName);
         //在<calcSum>元素中添加2个子元素,一个为<a>3</a>,一个为<b>5</b>
         calcSumEle.addChildElement( "a" ).addTextNode( "3" );
         calcSumEle.addChildElement( "b" ).addTextNode( "5" );
                                                                                                                                                                          
         return  soapRequest;
                                                                                                                                                                          
                                                                                                                                                                          
     }
                                                                                                                                                                      
                                                                                                                                                                      
                                                                                                                                                                      
     public  static  void  main(String [] args)  throws  Exception {
                                                                                                                                                                          
         String wsdlURLStringForCalcPlus =  "http://localhost:8080/cxf_jaxws_provider/services/calcPlus?wsdl" ;
         String serviceQName            =   "http://provider.cxfstudy.charles.com/" ;
         String serviceProviderStringForCalcPlus =  "CalcPlusServiceProviderService" ;
         String servicePortStringForCalcPlus =  "CalcPlusServiceProviderPort" ;
                                                                                                                                                                          
         //构造要发送的Soap消息内容并且转为Source类型
         //从MessageFactory 构造一个要发送的Soap消息
         MessageFactory factory = MessageFactory.newInstance();
                                                                                                                                                                          
         SOAPMessage soapRequest= buildMessageForAdd(factory);
         System.out.println( "发送的消息为:" );
         soapRequest.writeTo(System.out);
         System.out.println();
                                                                                                                                                                          
                                                                                                                                                                          
                                                                                                                                                                          
         SOAPMessage soapResponse =DispatcherUtil.sendMessage(
                 wsdlURLStringForCalcPlus,serviceQName,serviceProviderStringForCalcPlus,servicePortStringForCalcPlus,soapRequest, factory);
         System.out.println( "响应的消息为:" );
         soapResponse.writeTo(System.out);
                                                                                                                                                                          
                                                                                                                                                                          
                                                                                                                                                                          
         }
}


从上看出,我们构造了一个消息,其包含2个数,一个是3,一个是5,我们期望通过web service计算加法后返回8。


看客户端控制台:

wKioL1MIRYfBSWbAAAMEJv3S7UA774.jpg


看服务器端的控制台:

wKioL1MIRZaiB109AAGcxW2ncwQ991.jpg


显然,和我们设想的一样,所有现在的处理都是和最终消息打交道,并且web服务也正确的做了加法运算,所以我们代码是完全正确的。


附加说明:

本例演示了如何用Dispatch/Provider发送和处理Service模式是MESSAGE的消息,如果要处理Service模式是PAYLOAD的消息,则应该如下:

1
2
3
@WebServiceProvider ()
@ServiceMode (value=Service.Mode.PAYLOAD)
public  class  CalcMinusServiceProvider  implements  Provider<DOMSource> {




本文转自 charles_wang888 51CTO博客,原文链接:http://blog.51cto.com/supercharles888/1362037,如需转载请自行联系原作者
目录
相关文章
|
7月前
|
SQL 运维 API
Apache Flink 学习教程----持续更新
Apache Flink 学习教程----持续更新
292 0
|
7月前
|
分布式计算 Java 大数据
IO流【Java对象的序列化和反序列化、File类在IO中的作用、装饰器模式构建IO流体系、Apache commons-io工具包的使用】(四)-全面详解(学习总结---从入门到深化)
IO流【Java对象的序列化和反序列化、File类在IO中的作用、装饰器模式构建IO流体系、Apache commons-io工具包的使用】(四)-全面详解(学习总结---从入门到深化)
100 0
|
7月前
|
存储 NoSQL 数据处理
Apache Paimon流式湖仓学习交流群成立
Apache Paimon流式湖仓学习交流群成立
502 59
|
7月前
|
Java API Apache
Apache CXF生成WebService的客户端
Apache CXF生成WebService的客户端
244 0
|
安全 API Apache
Apache服务深入学习篇(详细介绍)
Apache服务深入学习篇(详细介绍)
1125 0
Apache服务深入学习篇(详细介绍)
|
缓存 网络协议 关系型数据库
学习http+Apache
学习http+Apache
147 0
|
存储 运维 供应链
为什么要学习 Apache Flink| 学习笔记
快速学习为什么要学习 Apache Flink。
为什么要学习 Apache Flink| 学习笔记
|
云安全 安全 druid
Apache Log4j2 远程代码执行漏洞学习
通过Apache Log4j2远程代码执行漏洞学习jndi等知识
|
XML Java API
彻底了解|利用Apache CXF框架开发WebService
前言WebService是为了支持网络的机器间操作交互而设计用来开发分布式的交互操作的应用程序组件,通常被定义为一组模块化的API,他们可以通过网络进行调用,来执行远程系统的请求服务,而...
515 0
|
消息中间件 存储 缓存
重磅消息!弃用 Java 8、Apache Kafka 3.0 发布!
 什么是Kafka? Apache Kafka是分布式发布订阅消息传递系统和强大的队列,可以处理大量数据,并使您能够将消息从一个端点传递到另一个终端。Kafka适用于离线和在线消息消费。Kafka消息被保留在磁盘上,并在集群内复制以防止数据丢失。Kafka建立在ZooKeeper同步服务之上。它与Apache Storm和Spark完美结合,实时流式传输数据分析。 作者:zhulin1028 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

推荐镜像

更多