Apache CXF学习 - SOAP Handler的使用

简介:

引入:

我们已经用前2篇的文章用2种方法创建了web service的endpoint并且对外提供服务。现在我们假设要对来往的消息进行处理,而这些处理应该和我们业务代码正交(也就是AOP,比如我们要吧消息写入日志,或者区分是去web service站点的请求消息还是从web service站点返回的相应消息),为了满足这个需求,我们可以利用SOAPHandler来完成。


代码实践:

简单来说,SOAPHandler就是用来对于SOAP消息进行处理的类,为了实现AOP,我们有两种方式来实现对SOAP消息的处理。

一种是实现SOAPHandler<SOAPMessageContext>接口,它会对于整个SOAP消息进行处理。

一种是实现LogicalHandler<LogicalMessageContext>接口,它会对消息的payload 进行处理。

我们就分别写2个处理器,来演示这2种用法。


服务器端:

首先,我们开发一个LogHandler,它会拦截交互的SOAP消息并且打印出消息内容,我们让其采用第一种方式,实现SOAPHandler<SOAPMessageContext>接口:

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
/**
  * SOAP Handler可以用来对SOAP消息进行访问。
  * 有2种Handler,一种是访问整个消息的, 一种是只访问SOAP消息payload的
  * 这里演示的是第一种,它必须实现SOAPHandler<SOAPMessageContext>接口
  */
package  com.charles.cxfstudy.server.handlers;
import  java.util.Set;
import  javax.xml.namespace.QName;
import  javax.xml.soap.SOAPMessage;
import  javax.xml.ws.handler.MessageContext;
import  javax.xml.ws.handler.soap.SOAPHandler;
import  javax.xml.ws.handler.soap.SOAPMessageContext;
/**
  * 演示访问整个SOAP消息的Handler的用法
  * @author charles.wang
  *
  */
public  class  LogHandler  implements  SOAPHandler<SOAPMessageContext>{
     /**
      * 如何去处理SOAP消息的逻辑。
      * 这里会先打印当前调用的方法,然后从消息上下文中取出消息,然后写到标准输出流
      */
     public  boolean  handleMessage(SOAPMessageContext context) {
         System.out.println( "LogHandler->handleMessage(context) method invoked" );
         SOAPMessage message = context.getMessage();
         try {
             message.writeTo(System.out);
             System.out.println();
         } catch (Exception ex){
             System.err.print( "Exception occured when handling message" );
         }
         return  true ;
     }
     /**
      * 如何去处理错误的SOAP消息
      * 这里会先打印当前调用的方法,然后从消息上下文中取出消息,然后写到标准输出流
      */
     public  boolean  handleFault(SOAPMessageContext context) {
         System.out.println( "LogHandler->handleFault(context) method invoked" );
         SOAPMessage message = context.getMessage();
         try {
             message.writeTo(System.out);
             System.out.println();
         } catch (Exception ex){
             System.err.print( "Exception occured when handling fault message" );
         }
         return  true ;
     }
     /**
      * 这里没有资源清理的需求,所以我们只打印动作到控制台
      */
     public  void  close(MessageContext context) {
         System.out.println( "LogHandler->close(context) method invoked" );
                                                                                                                                                                                                                                                                                                                                                                                    
     }
     public  Set<QName> getHeaders() {
         return  null ;
     }
                                                                                                                                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                                                                                                
}


然后我们再开发AddCustomizedPartHandler,这个Handler会判断这个消息是入站(往web service Endpoint发送的请求消息)消息还是出站(从web service Endpoint返回的消息)消息然后打印出结果,我们采用第二种方式,让其实现 LogicalHandler<LogicalMessageContext>接口:

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
/**
  * SOAP Handler可以用来对SOAP消息进行访问。
  * 有2种Handler,一种是访问整个消息的, 一种是只访问SOAP消息payload的
  * 这里演示的是第二种,它必须实现LogicalHandler<LogicalMessageContext>接口
  */
package  com.charles.cxfstudy.server.handlers;
import  javax.xml.ws.handler.LogicalHandler;
import  javax.xml.ws.handler.LogicalMessageContext;
import  javax.xml.ws.handler.MessageContext;
/**
  * 演示访问SOAP消息的payload的Handler的用法
  * @author charles.wang
  *
  */
public  class  AddCustomizedPartHandler  implements  LogicalHandler<LogicalMessageContext> {
     /**
      * 如何去处理SOAP消息的逻辑,它会去判断这是入站还是出站消息
      */
     public  boolean  handleMessage(LogicalMessageContext context) {
         System.out.println( "AddCustomizedPartHandler->handleMessage(context) invoked" );
                                                                                                                                                                                                                                                                                                                       
         //先判断消息来源是入站还是出站的
         //(入站表示是发送到web service站点的消息,出站表示是从web service站点返回的消息)
         boolean  outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
                                                                                                                                                                                                                                                                                                                       
         //如果是出站消息
         if (outbound){
             System.out.println( "This is an outbound message" );    
         } else {
             System.out.println( "This is an inbound message" );
         }
                                                                                                                                                                                                                                                                                                                           
                                                                                                                                                                                                                                                                                                                       
         return  true ;
     }
     public  boolean  handleFault(LogicalMessageContext context) {
         System.out.println( "AddCustomizedPartHandler->handleFault(context) invoked" );
         return  true ;
     }
     public  void  close(MessageContext context) {
         System.out.println( "AddCustomizedPartHandler->close(context) invoked" );
                                                                                                                                                                                                                                                                                                                       
     }
                                                                                                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                                                                                                   
}



为了让这2个handler生效,我们必须写一个配置文件,来配置处理器链,这里我们自定义某个handler_chains.xml(可以是任意名字),并且在其中配置了2个处理器,定义他们的顺序和实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
<? xml  version = "1.0"  encoding = "UTF-8" ?>
< handler-chains  xmlns = "http://java.sun.com/xml/ns/javaee" >
    < handler-chain >
     < handler >
         < handler-name >AddCustomizedPartHandler</ handler-name >
         < handler-class >com.charles.cxfstudy.server.handlers.AddCustomizedPartHandler</ handler-class >
     </ handler >
     < handler >
         < handler-name >LogHandler</ handler-name >
         < handler-class >com.charles.cxfstudy.server.handlers.LogHandler</ handler-class >
     </ handler >
    </ handler-chain >
</ handler-chains >

从这里看出,判断入站出站的处理器在前,打印日志的处理器在后。


为了让我们的Handler链生效到我们的web service的Endpoint,我们必须在服务接口或者服务实现类上用@HandlerChain注解来配置这个Handler链定义文件。这里,我们为前面开发的加法运算的web服务激活处理器,所以在CalcServiceImpl上我们采用了这个注解:

1
2
3
4
5
@WebService (endpointInterface= "com.charles.cxfstudy.server.services.ICalcService" )
//这里展示如何用@HandlerChain来声明一组Handler,他们会对指定的web service使用的SOAP消息进行处理,类似AOP
@HandlerChain (file= "/handler_chains.xml" )
public  class  CalcServiceImpl  implements  ICalcService {
...


这时候,打包应用并且部署在服务器上,我们服务器端的代码就完成了。


客户端:

为了演示客户端和服务器端的交互是否会自动触发Handler的执行,我们写一个客户端,首先我们用JDK的wsimport工具来从给定的wsdl文件生成一组客户端的java文件:

wKioL1MHOImTLy65AACb6YSQJ_0800.jpg


这样它会在我们给定目录用给定的包名生成一组文件:

wKioL1MHOMGA0a1EAAJ6thTnO-E053.jpg

我们把这些文件复制到我们客户端的项目应用中,然后编写测试方法如下:

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
/**
  * 客户端测试代码
  */
package  com.charles.cxfclient;
import  org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
/**
  * @author charles.wang
  *
  */
public  class  MainTest {
                                                                                                                                                              
     public  static  void  main(String [] args){
                                                                                                                                                              
     JaxWsProxyFactoryBean factory =  new  JaxWsProxyFactoryBean();
     factory.setServiceClass(ICalcService. class );
     factory.setAddress( "http://localhost:8080/cxf_jaxws_server/services/calc" );
                                                                                                                                                              
     //调用业务方法
     ICalcService service = (ICalcService) factory.create();
     int  a =  3 ,b= 5 ;
     System.out.println( "调用CalcService进行加法计算,2个运算子分别是:" +a+ "," +b);
     System.out.println( "加法运算结果为:"  + service.calcSum(a, b));
                                                                                                                                                              
     }
}


执行,它显然会打印出执行结果,不过这个执行的结果运算是通过 web service的调用完成的。

wKiom1MHOXGSfcIXAAEq-ZRBv2s284.jpg


我们来看服务器的日志,如下图:

wKiom1MHOaDyH1EJAAJjuSCTL6E927.jpg

显然,这两个Handler都被正确的调用了(可以比较我们的Handler的代码),比如AddCustomizedPartHandler会正确的识别这是inbound 还是outbound消息,比如LogHandler能吧inbound消息(就是图片中内含<a>3</a><b>5</b>的)和outbound消息(就是图片中内涵<return>8</return>的)都打印出来,所以证明我们开发的Handler是正确的。


当然了,Handler还可以做很多更高级别的功能,比如检验cookie,持久化消息,添加自定义头等,有待我们发觉,但是代码的结构大体和我们例子相似。





本文转自 charles_wang888 51CTO博客,原文链接:http://blog.51cto.com/supercharles888/1361866,如需转载请自行联系原作者
目录
相关文章
|
6月前
|
SQL 运维 API
Apache Flink 学习教程----持续更新
Apache Flink 学习教程----持续更新
291 0
|
6月前
|
Java
SpringBoot启动报错:org.apache.catalina.LifecycleException: Protocol handler start failed
SpringBoot启动报错:org.apache.catalina.LifecycleException: Protocol handler start failed
98 0
|
6月前
|
分布式计算 Java 大数据
IO流【Java对象的序列化和反序列化、File类在IO中的作用、装饰器模式构建IO流体系、Apache commons-io工具包的使用】(四)-全面详解(学习总结---从入门到深化)
IO流【Java对象的序列化和反序列化、File类在IO中的作用、装饰器模式构建IO流体系、Apache commons-io工具包的使用】(四)-全面详解(学习总结---从入门到深化)
98 0
|
6月前
|
存储 NoSQL 数据处理
Apache Paimon流式湖仓学习交流群成立
Apache Paimon流式湖仓学习交流群成立
490 59
|
6月前
org.apache.catalina.LifecycleException: Protocol handler start failed
org.apache.catalina.LifecycleException: Protocol handler start failed
72 0
|
6月前
|
Java API Apache
Apache CXF生成WebService的客户端
Apache CXF生成WebService的客户端
226 0
|
安全 API Apache
Apache服务深入学习篇(详细介绍)
Apache服务深入学习篇(详细介绍)
1099 0
Apache服务深入学习篇(详细介绍)
|
缓存 网络协议 关系型数据库
学习http+Apache
学习http+Apache
145 0
|
存储 运维 供应链
为什么要学习 Apache Flink| 学习笔记
快速学习为什么要学习 Apache Flink。
为什么要学习 Apache Flink| 学习笔记
|
云安全 安全 druid
Apache Log4j2 远程代码执行漏洞学习
通过Apache Log4j2远程代码执行漏洞学习jndi等知识

推荐镜像

更多