一、基础
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注册,以使别的企业能够发现的访问协议的实现标准。
- 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.....");
}
}
客户端:服务端的接口客户端也是需要的,正常使用工具即可生成。
自定义拦截器:
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);
}
}
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);
}
}