在webservice的客户端和服务端的请求和响应的过程中,能动态的操作请求和响应的数据,能够拦截请求和响应的数据并进行响应的操作,设计了cxf的拦截器,注意JDK中支持webservice,但不支持拦截器,所以开发强大的webservice通信,推荐cxf框架。
拦截器分类:
1. 按所处的位置分:服务器端拦截器,客户端拦截器
2. 按消息的方向分:入拦截器,出拦截器
3. 按定义者分:系统拦截器,自定义拦截器
如果在客户端设置了出拦截器,客户端在发送请求之前先被拦截器执行,然后发送请求;如果服务器端设置了入拦截器,服务器端在接收客户端的请求之前,先被拦截器执行,然后执行服务器端的服务。
下面以系统拦截器和自定义拦截器为例,分别在客户端和服务器端定义入拦截器和出拦截器。
一、系统拦截器
以日志拦截器为例,如果在服务端的入方向和出方向都添加日志拦截器,则在服务器端接收响应和返回给客户端结果时都会打印出日志信息。
<一>服务器端
1、 创建服务器端响应客户端的服务接口
package com.lzj.webservice.ws;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
public interface Webservice {
@WebMethod
public String sayHello(String name);
}
2、 创建服务器端接口的实现
package com.lzj.webservice.ws;
import javax.jws.WebService;
@WebService
public class WebserviceImpl implements Webservice {
@Override
public String sayHello(String name) {
System.out.println("hello:" + name);
return "hello " + name;
}
}
3、 把cxf框架下lib包下的jar包导入服务器端工程中,并创建测试程序,用于发布服务器端的服务,在发布服务时,设置系统自带的日志入拦截器LoggingInInterceptor 和出拦截器LoggingOutInterceptor。
package com.lzj.webservice.ws.test;
import java.util.List;
import javax.xml.ws.Endpoint;
import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws22.EndpointImpl;
import org.apache.cxf.message.Message;
import com.lzj.webservice.ws.WebserviceImpl;
public class WebservicePublish {
public static void main(String[] args) {
String address = "http://localhost:8989/Webservice-cxf-server1/webservice";
Endpoint endpoint = Endpoint.publish(address, new WebserviceImpl());
EndpointImpl endpointImpl = (EndpointImpl)endpoint;
//添加服务器端的入日志拦截器
List<Interceptor<? extends Message>> inInterceptors = endpointImpl.getInInterceptors();
inInterceptors.add(new LoggingInInterceptor());
//添加服务器端的出日志拦截器
List<Interceptor<? extends Message>> outInterceptors = endpointImpl.getOutInterceptors();
outInterceptors.add(new LoggingOutInterceptor());
System.out.println("发布webservice成功");
}
}
<二>客户端
1、 生成客户端代码
在cmd中通过wadl2java工具生成客户端代码,首先进入客户端工程的src目录下,用如下命令生成代码
wsdl2java http://localhost:8989/Webservice-cxf-server1/webservice?wsdl
生成代码如下:
2、把cxf框架下lib包中的jar包导入到客户端工程中,并创建测试程序,用于请求服务器端服务,并设置系统自带的出拦截器LoggingOutInterceptor。
package com.lzj.webservice.ws.client;
import java.util.List;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.message.Message;
import com.lzj.webservice.ws.Webservice;
import com.lzj.webservice.ws.WebserviceImplService;
public class WsClient {
public static void main(String[] args) {
WebserviceImplService factory = new WebserviceImplService();
Webservice ws = factory.getWebserviceImplPort();
//获取发送请求的客户端对象
Client client = ClientProxy.getClient(ws);
//添加客户端的出拦截器
List<Interceptor<? extends Message>> outInterceptors = client.getOutInterceptors();
outInterceptors.add(new LoggingOutInterceptor());
String result = ws.sayHello("lzj");
System.out.println("result : " + result);
}
}
<服务器端客户端的运行结果>
首先运行服务器端程序,然后再运行客户端程序
客户端中输出如下内容:
ID: 1
Address: http://localhost:8989/Webservice-cxf-server1/webservice
Encoding: UTF-8
Content-Type: text/xml
Headers: {Accept=[*/*], SOAPAction=[""]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHello xmlns:ns2="http://ws.webservice.lzj.com/"><arg0>lzj</arg0></ns2:sayHello></soap:Body></soap:Envelope>
--------------------------------------
result : hello lzj
从输出中的日志可以看出,如果客户端设置了日志出拦截器,当客户端向服务器端发送请求时,日志出拦截器拦截到了请求,并把请求打印出来了,包括请求的地址、请求头、请求体等,然后输出的服务器端返回的数据。如果不定义拦截器就不会打印请求信息。
服务器端输出日志如下:
发布webservice成功
二月 21, 2018 10:35:31 下午 org.apache.cxf.services.WebserviceImplService.WebserviceImplPort.Webservice
信息: Inbound Message
----------------------------
ID: 1
Address: http://localhost:8989/Webservice-cxf-server1/webservice?wsdl
Encoding: UTF-8
Http-Method: GET
Content-Type: text/xml
Headers: {Accept=[*/*], Cache-Control=[no-cache], connection=[keep-alive], content-type=[text/xml], Host=[localhost:8989], Pragma=[no-cache], User-Agent=[Apache CXF 2.5.9]}
--------------------------------------
二月 21, 2018 10:35:32 下午 org.apache.cxf.services.WebserviceImplService.WebserviceImplPort.Webservice
信息: Inbound Message
----------------------------
ID: 2
Address: http://localhost:8989/Webservice-cxf-server1/webservice
Encoding: UTF-8
Http-Method: POST
Content-Type: text/xml; charset=UTF-8
Headers: {Accept=[*/*], Cache-Control=[no-cache], connection=[keep-alive], Content-Length=[196], content-type=[text/xml; charset=UTF-8], Host=[localhost:8989], Pragma=[no-cache], SOAPAction=[""], User-Agent=[Apache CXF 2.5.9]}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHello xmlns:ns2="http://ws.webservice.lzj.com/"><arg0>lzj</arg0></ns2:sayHello></soap:Body></soap:Envelope>
--------------------------------------
hello:lzj
二月 21, 2018 10:35:32 下午 org.apache.cxf.services.WebserviceImplService.WebserviceImplPort.Webservice
信息: Outbound Message
---------------------------
ID: 2
Encoding: UTF-8
Content-Type: text/xml
Headers: {}
Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHelloResponse xmlns:ns2="http://ws.webservice.lzj.com/"><return>hello lzj</return></ns2:sayHelloResponse></soap:Body></soap:Envelope>
--------------------------------------
从日志中可以看出,先执行的日志入拦截器,然后执行的服务器端响应服务,最后执行的日志出拦截器。
服务器端工程目录:
客户端工程目录:
二、自定义拦截器
使用自定义拦截器实现用户名与密码的校验。服务器端设置入拦截器,客户端设置出拦截器。
<一>服务器端
1、 定义响应客户端的服务接口
package com.lzj.webservice.ws;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
public interface Webservice {
@WebMethod
public String sayHello(String name);
}
2、 创建服务接口的实现类
package com.lzj.webservice.ws;
import javax.jws.WebService;
@WebService
public class WebserviceImpl implements Webservice {
@Override
public String sayHello(String name) {
System.out.println("hello:" + name);
return "hello " + name;
}
}
3、 把cxf框架中lib目录下的jar包导入到服务器端工程中,并创建检查用户姓名和密码的入拦截器。
package com.lzj.webservice.ws.interceptor;
import javax.xml.namespace.QName;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.w3c.dom.Element;
/*
自定义的拦截器都要继承AbstractPhaseInterceptor;
SoapMessage表示拦截的信息类型,因为webservice传输soap类型的消息;
Phase是一个abstract类,定义了在哪个阶段进行拦截消息
*/
public class CheckUserInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
/*在准备协议阶段拦截请求*/
private static String phase = Phase.PRE_PROTOCOL;
public CheckUserInterceptor() {
super(phase);
}
/*如果不设置拦截器,客户端准备发送请求的协议是下面的形式
<Body>
<sayHello>
<arg0>lzj</arg0>
<sayHello>
</Body>
现在设置拦截器,在发送请求之前,加上了用户的姓名和密码,则在准备协议时准备成了下面的形式
<Envelope>
<head>
<!--头中可以添加多个内容,本例中只添加一个-->
<customer>
<name>lzj</name>
<password>123456</password>
</customer>
<customer2>
<name>zhangsan</name>
<password>123456</password>
</customer2>
<head>
<Body>
<sayHello>
<arg0>lzj</arg0>
<sayHello>
</Body>
</Envelope>
在服务器端应拦截上面xml中的head部分
*/
@Override
public void handleMessage(SoapMessage message) throws Fault {
/*现在准备拦截如下内容
<customer>
<name>lzj</name>
<password>123456</password>
</customer>
* */
Header header = message.getHeader(new QName("customer"));
if (header != null) {
Element customerElement = (Element) header.getObject();
String name = customerElement.getElementsByTagName("name").item(0).getTextContent();
String password = customerElement.getElementsByTagName("password").item(0).getTextContent();
if ("lzj".equals(name) && "123456".equals(password)) {
System.out.println("通过服务器端拦截器……");
return;
}
System.out.println("不能通过服务器端拦截器……");
throw new IllegalArgumentException("用户名或密码不合法……");
}
}
}
4、 创建服务器端的测试程序,设置自定义的入拦截器,并发布服务。
package com.lzj.webservice.ws.test;
import java.util.List;
import javax.xml.ws.Endpoint;
import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws22.EndpointImpl;
import org.apache.cxf.message.Message;
import com.lzj.webservice.ws.WebserviceImpl;
import com.lzj.webservice.ws.interceptor.CheckUserInterceptor;
public class WebservicePublish {
public static void main(String[] args) {
String address = "http://localhost:8989/Webservice-cxf-interceptor-server/webservice";
Endpoint endpoint = Endpoint.publish(address, new WebserviceImpl());
EndpointImpl endpointImpl = (EndpointImpl)endpoint;
//添加服务器端的入日志拦截器
List<Interceptor<? extends Message>> inInterceptors = endpointImpl.getInInterceptors();
inInterceptors.add(new CheckUserInterceptor());
System.out.println("发布webservice成功");
}
}
<二>客户端
1、 生成客户端代码
在cmd中进入客户端工程的src目录,用如下命令生成代码:
Wsdl2java http://localhost:8989/Webservice-cxf-interceptor-server/webservice
生成代码如下(注意:要在运行服务器端工程的前提下,再用上述命令生成代码,否则会报一个拒绝连接的错误):
2、 把cxf框架下lib目录下的jar包导入到客户端工程中,并创建客户端的出拦截器,用于添加用户的姓名和密码。
package com.lzj.webservice.ws.interceptor;
import java.util.List;
import javax.xml.namespace.QName;
import org.apache.cxf.binding.soap.SoapMessage;
import org.apache.cxf.headers.Header;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.xml.utils.DOMHelper;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class AddUserInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
private String name;
private String password;
/*在准备协议阶段进行拦截*/
private static String phase = Phase.PRE_PROTOCOL;
public AddUserInterceptor(String name, String password) {
super(phase);
this.name = name;
this.password = password;
}
/*如果不设置拦截器,客户端准备发送请求的协议是下面的形式
<Body>
<sayHello>
<arg0>lzj</arg0>
<sayHello>
</Body>
现在设置拦截器,在发送请求之前,要在发送的内容中加上用户的姓名和密码,则在准备协议时应准备成下面的形式
<Envelope>
<head>
<!--头中可以添加多个内容,本例中只添加一个-->
<customer>
<name>lzj</name>
<password>123456</password>
</customer>
<customer2>
<name>zhangsan</name>
<password>123456</password>
</customer2>
<head>
<Body>
<sayHello>
<arg0>lzj</arg0>
<sayHello>
</Body>
</Envelope>
*/
@Override
public void handleMessage(SoapMessage msg) throws Fault {
List<Header> headers = msg.getHeaders();
/*
准备下面内容
<customer>
<name>lzj</name>
<password>123456</password>
</customer>
* */
Document document = DOMHelper.createDocument();
Element rootElement = document.createElement("customer");
Element nameElement = document.createElement("name");
nameElement.setTextContent("lzj");
rootElement.appendChild(nameElement);
Element passwordElement = document.createElement("password");
passwordElement.setTextContent("123456");
rootElement.appendChild(passwordElement);
headers.add(new Header(new QName("customer"), rootElement));
System.out.println("客户端拦截器开始执行……");
}
}
3、 创建客户端的测试程序,并设置自定义的出拦截器
package com.lzj.webservice.ws.client;
import java.util.List;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.Interceptor;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.message.Message;
import com.lzj.webservice.ws.Webservice;
import com.lzj.webservice.ws.WebserviceImplService;
import com.lzj.webservice.ws.interceptor.AddUserInterceptor;
public class WsClient {
public static void main(String[] args) {
WebserviceImplService factory = new WebserviceImplService();
Webservice ws = factory.getWebserviceImplPort();
//获取发送请求的客户端对象
Client client = ClientProxy.getClient(ws);
//添加日志出拦截器
List<Interceptor<? extends Message>> outInterceptors = client.getOutInterceptors();
outInterceptors.add(new AddUserInterceptor("lzj", "123456"));
String result = ws.sayHello("lzj");
System.out.println("result : " + result);
}
}
<服务器端和客户端的输出结果>
先运行服务器端程序,然后运行客户端程序
客户端输出日志如下:
客户端拦截器开始执行……
result : hello lzj
由日志可以看出,客户端先执行了自定义的出拦截器,然后执行了服务器端的响应。
服务器端输出日志如下:
发布webservice成功
通过服务器端拦截器……
hello:lzj
由日志可以看出,服务器端先执行了自定义了入拦截器,然后响应客户端的请求。
客户端工程目录:
服务器工程目录: