10-WebService
一、基础
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("<soapenv:Envelope xmlns:soapenv=\"http://schemas" +
".xmlsoap.org/soap/envelope/\" xmlns:ws=\"http://ws/\">");
sb.append("<soapenv:Header/>");
sb.append("<soapenv:Body>");
sb.append("<ws:sayHello>");
sb.append("<arg0>123</arg0>");
sb.append("</ws:sayHello>");
sb.append("</soapenv:Body>");
sb.append("</soapenv:Envelope>");
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 = "<?xml version = \"1.0\" ?>"
+ "<soapenv:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:web=\"http://webservices.b.com\">"
+ " <soapenv:Header/>"
+ " <soapenv:Body>"
+ " <web:order soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ " <in0 xsi:type=\"web:OrderRequest\">"
+ " <mobile xsi:type=\"soapenc:string\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">?</mobile>"
+ " <orderStatus xsi:type=\"xsd:int\">?</orderStatus>"
+ " <productCode xsi:type=\"soapenc:string\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">?</productCode>"
+ " </in0>" + " </web:order>"
+ " </soapenv:Body>" + "</soapenv:Envelope>";
String querySoapXml = "<?xml version = \"1.0\" ?>"
+ "<soapenv:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:web=\"http://webservices.b.com\">"
+ " <soapenv:Header/>"
+ " <soapenv:Body>"
+ " <web:query soapenv:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
+ " <in0 xsi:type=\"web:QueryRequest\">"
+ " <endTime xsi:type=\"xsd:dateTime\">?</endTime>"
+ " <mobile xsi:type=\"soapenc:string\" xmlns:soapenc=\"http://schemas.xmlsoap.org/soap/encoding/\">?</mobile>"
+ " <startTime xsi:type=\"xsd:dateTime\">?</startTime>"
+ " </in0>" + " </web:query>"
+ " </soapenv:Body>" + "</soapenv:Envelope>";
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("<web:toTraditionalChinese xmlns:web=\"" + targetNamespace + "\">");
source.append("<web:sText>").append(param).append("</web:sText>");
source.append("</web:toTraditionalChinese>");
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<Source> dispatch = service.createDispatch(portQName, Source.class, Service.Mode.PAYLOAD);
//.NET的服务端Soap1.1需要,不加会报错误:服务器未能识别 HTTP 头 SOAPAction 的值
Map<String, Object> 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("<web:toTraditionalChinese xmlns:web=\"" + targetNamespace + "\">");
source.append("<web:sText>").append(param).append("</web:sText>");
source.append("</web:toTraditionalChinese>");
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<Source> dispatch = service.createDispatch(portQName, Source.class, Service.Mode.PAYLOAD);
//.NET的服务端Soap1.1需要,不加会报错误:服务器未能识别 HTTP 头 SOAPAction 的值
Map<String, Object> 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("<soapenv:Envelope xmlns:soapenv=\"" + soapNamespace + "\" xmlns:web=\"" + targetNamespace + "\">");
source.append("<soapenv:Header/>");
source.append("<soapenv:Body>");
source.append("<web:toTraditionalChinese>");
source.append("<web:sText>").append(param).append("</web:sText>");
source.append("</web:toTraditionalChinese>");
source.append("</soapenv:Body>");
source.append("</soapenv:Envelope>");
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<Source> dispatch = service.createDispatch(portQName, Source.class, Service.Mode.MESSAGE);
//.NET的服务端Soap1.1需要,不加会报错误:服务器未能识别 HTTP 头 SOAPAction 的值
Map<String, Object> 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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>01_jaxws_server</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>../03_jaxws_spring_server</module>
</modules>
<name>01_jaxws_server</name>
<dependencies>
<!-- 要进行jaxws 服务开发 -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.0.1</version>
</dependency>
<!-- 内置jetty web服务器 -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.0.1</version>
</dependency>
<!-- 日志实现 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
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!";
}
}
自定义拦截器:
- 拦截器执行顺序
//InPhases
Phase.RECEIVE->Phase.PRE_STREAM->Phase.USER_STREAM->Phase.POST_STREAM->Phase.READ->Phase.PRE_PROTOCOL->Phase.USER_PROTOCOL->Phase.POST_PROTOCOL
->Phase.UNMARSHAL->Phase.PRE_LOGICAL->Phase.USER_LOGICAL->Phase.POST_LOGICAL->Phase.PRE_INVOKE->Phase.INVOKE->Phase.POST_INVOKE
//OutPhases
Phase.SETUP->Phase.PRE_LOGICAL->Phase.USER_LOGICAL->Phase.POST_LOGICAL->Phase.PREPARE_SEND->Phase.PRE_STREAM
->Phase.PRE_PROTOCOL->Phase.WRITE->Phase.PRE_MARSHAL->Phase.MARSHAL->Phase.POST_MARSHAL->Phase.USER_PROTOCOL
->Phase.POST_PROTOCOL->Phase.USER_STREAM->Phase.POST_STREAM->Phase.SEND->Phase.SEND_ENDING->Phase.POST_STREAM_ENDING
->Phase.USER_STREAM_ENDING->Phase.POST_PROTOCOL_ENDING->Phase.USER_PROTOCOL_ENDING->Phase.MARSHAL_ENDING->Phase.WRITE_ENDING
->Phase.PRE_PROTOCOL_ENDING->Phase.PRE_STREAM_ENDING->Phase.PREPARE_SEND_ENDING->Phase.POST_LOGICAL_ENDING->Phase.USER_LOGICAL_ENDING
->Phase.PRE_LOGICAL_ENDING->Phase.SETUP_ENDING
public class MyInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
public MyInterceptor() {
super(Phase.PRE_INVOKE); // 在调用方法之前调用自定拦截器
}
@SuppressWarnings("null")
public void handleMessage(SoapMessage message) throws Fault {
List<Header> 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<SoapMessage> {
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<Header> 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
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>WebService_CXF_Web</display-name>
<servlet>
<servlet-name>cxf</servlet-name>
<!--默认会去类路径下找cxf-servlet.xml配置文件-->
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>cxf</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
</web-app>
5.cxf-servlet.xml文件
<?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"
xmlns:soap="http://cxf.apache.org/bindings/soap" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/bindings/soap
http://cxf.apache.org/schemas/configuration/soap.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd
">
<!--
jaxws:server代表org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean,服务实例工厂
等价于JaxWsServiceFactoryBean jf = new JaxWsServiceFactoryBean();
address:写服务的相对路径
serviceClass:sei接口类
jaxws:serviceBean:sei的实现类
-->
<jaxws:server address="/mobile" serviceClass="com.esb.ws.server.MobileAddress">
<jaxws:serviceBean>
<ref bean="mobileAddress"/>
</jaxws:serviceBean>
</jaxws:server>
<bean id="mobileAddress" class="com.esb.ws.server.MobileAddressImpl"></bean>
</beans>
6.启动TomCat。
3.3 Web项目的CXF与Spring集成
pox.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>03_jaxws_spring_server</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>03_jaxws_spring_server Maven Webapp</name>
<dependencies>
<!-- CXF WS开发 -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
<!-- 运行tomcat7方法:tomcat7:run -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 指定端口 -->
<port>8080</port>
<!-- 请求路径 -->
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
web.xml:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--1. cxfsevlet配置-->
<servlet>
<servlet-name>cxfservlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<!--1. cxfsevlet配置放在这里第一个访问WS服务会慢,所以放在listener-->
<!-- <init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
-->
</servlet>
<servlet-mapping>
<servlet-name>cxfservlet</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
<!--2.spring容器配置:-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
applicationContext.xml:
<?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:cxf="http://cxf.apache.org/core"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/core
http://cxf.apache.org/schemas/core.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd">
<!--
<!-- 引入cxf的一些核心配置 -->
<!--
cxf.xml配置cxf的一些核心处理器;
cxf-extension-soap.xml配置cxf的一些扩展功能;
cxf-servlet.xml用于自定义cxf的特性,默认为空
<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" />
Spring整合cxf发布服务,关键点:
1. 服务地址
2. 服务类
服务完整访问地址: http://localhost:8080/ws/hello
-->
<jaxws:endpoint id="userService" implementor="com.lzj.webservice.ws.UserServiceImpl" address="/hello">
<!--配置服务器端的入拦截器
<jaxws:inInterceptors>
<bean class="com.lzj.webservice.ws.interceptor.CheckUserInterceptor"></bean>
</jaxws:inInterceptors>
-->
</jaxws:endpoint>
</beans>
服务端:
@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:
<?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:cxf="http://cxf.apache.org/core"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/core
http://cxf.apache.org/schemas/core.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd">
<!--
Spring整合cxf客户端配置:
1. 服务地址 http://localhost:8080/ws/hello
2. 服务接口类型
-->
<jaxws:client
id="helloService"
serviceClass="com.itheima.service.HelloService"
address="http://localhost:8080/ws/hello"></jaxws:client>
</beans>
四、 ApacheCXF框架实现JAX-RS
基于restful风格的webservice,请求使用的是http协议,可以传递xml/json数据
4.1 代码实现
服务端:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>05_jaxrs_server</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>../07_jaxrs_spring_server</module>
</modules>
<name>05_jaxrs_server</name>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-client</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>1.3.7</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
</plugins>
</build>
</project>
实体类:
@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:
* <User>
* <id></id>
* <username></username>
* <city></city>
* </User>
* json:
* {"User": {"id":100, "username":"jack","city":"广州" }}
*
*/
@XmlRootElement(name = "User")
public class User {
private Integer id;
private String username;
private String city;
private List<Car> cars = new ArrayList<Car>();
}
接口:
// 访问当前服务接口对应的路径
@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<User> 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<User> findAllUsers() {
List<User> users = new ArrayList<User>();
User user1 = new User();
user1.setId(1);
user1.setUsername("小明");
user1.setCity("北京");
List<Car> carList1 = new ArrayList<Car>();
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:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>cxfservlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>cxfservlet</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
<!--2.spring容器配置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>07_jaxrs_spring_server</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>../08_jaxrs_spring_client</module>
</modules>
<packaging>pom</packaging>
<name>07_jaxrs_spring_server Maven Webapp</name>
<dependencies>
<!-- cxf 进行rs开发 必须导入 -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>3.0.1</version>
</dependency>
<!-- 日志引入 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.12</version>
</dependency>
<!-- 客户端 -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-client</artifactId>
<version>3.0.1</version>
</dependency>
<!-- 扩展json提供者 -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>3.0.1</version>
</dependency>
<!-- 转换json工具包,被extension providers 依赖 -->
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>1.3.7</version>
</dependency>
<!-- spring 核心 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- spring web集成 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- spring 整合junit -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<!-- junit 开发包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
<!-- 运行tomcat7方法:tomcat7:run -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!-- 指定端口 -->
<port>8080</port>
<!-- 请求路径 -->
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
applicationContext.xml:
<?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:cxf="http://cxf.apache.org/core"
xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/core
http://cxf.apache.org/schemas/core.xsd
http://cxf.apache.org/jaxws
http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/jaxrs
http://cxf.apache.org/schemas/jaxrs.xsd">
<!--
Spring整合cxf发布基于restful风格的服务,关键点:
1. 服务地址
2. 服务类
服务完整访问地址:
http://localhost:8080/ws/hello
-->
<jaxrs:server address="/userService">
<jaxrs:serviceBeans>
<bean class="com.itheima.service.UserServiceImpl"></bean>
</jaxrs:serviceBeans>
</jaxrs:server>
</beans>
沿用上面的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<User> 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<User> findAllUsers() {
List<User> users = new ArrayList<User>();
User user1 = new User();
user1.setId(1);
user1.setUsername("小明");
user1.setCity("北京");
List<Car> carList1 = new ArrayList<Car>();
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);
}
}