1:简单的说,SOAP就是在HTTP的基础上传输XML数据,以实现远程调用。
因为HTTP和XML格式的数据已经被广泛的应用。而SOAP又架构在这两种技术之上,所以WebService为什么会流行也就不难理解了。
2:老生常谈,无论你的服务端是什么语言书写的,只要接收SOAP协议的XML数据,并返回SOAP协议的XML数据,就可以被任何语言调用。
3、以下是通过纯ajax向服务器发送XML数据并解析的代码:
<%@page language="java" import="java.util.*"pageEncoding="UTF-8"%>
通过Ajax向服务器发送XML数据
---
服务器使用jaxp进行解析
packagecn.itcast;
importjava.io.IOException;
importjava.io.InputStream;
importjava.io.PrintWriter;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importjavax.xml.parsers.DocumentBuilder;
importjavax.xml.parsers.DocumentBuilderFactory;
importorg.w3c.dom.Document;
importorg.w3c.dom.Element;
importorg.w3c.dom.NodeList;
/**
* 接收XML数据使用jaxp进行解析
* @author
*/
publicclass One extends HttpServlet{
public void doGet(HttpServletRequestrequest, HttpServletResponseresponse)
throws ServletException,IOException{
doPost(request,response);
}
public void doPost(HttpServletRequestrequest, HttpServletResponseresponse)
throws ServletException,IOException{
//获取输入流,如果在输出,请在组成String时使用UTF-8
InputStreamin = request.getInputStream();
String names = "";
// 使用JAXP解析
try {
DocumentBuilderFactoryfactory = DocumentBuilderFactory
.newInstance();
DocumentBuilderbuilder = factory.newDocumentBuilder();
Document dom= builder.parse(in);
NodeList nl = dom.getElementsByTagName("name");
for(inti=0;i<nl.getLength();i++){
Element el = (Element)nl.item(i);
String name = el.getTextContent();
System.err.println(">>:"+name);
names +=name;
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage(),e);
}
response.setContentType("text/xml;charset=UTF-8");
PrintWriterout = response.getWriter();
out.print("{\"name\":\""+names+"\"}");
}
}
-----以下通过jQuery+Dom4j实现发XML数据--------
$(function(){
$("#jq").click(function(){
varxml = "" +
"
alert(xml);
$.ajax({
url:'Two',
type:'post',
dataType:'json',//设置返回的数据类型
data:xml,//直接发xml数据
contentType:'application/x-www-form-urlencoded',
success:function(data){
alert("返回的信息是:"+data.name);
},
complete:function(http,textStatus){
alert("over..."+textStatus);
}
});
});
});
----------------------------
packagecn.itcast;
importjava.io.IOException;
importjava.io.InputStream;
importjava.io.PrintWriter;
importjava.util.Iterator;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importorg.dom4j.Document;
importorg.dom4j.Element;
importorg.dom4j.io.SAXReader;
/**
* JQuery+Dom4j
* @author xx
*/
publicclass Two extends HttpServlet{
public void doPost(HttpServletRequestrequest, HttpServletResponseresponse)
throws ServletException,IOException{
String result = "";
//中文newString(b,0,len,"UTF-8")
InputStreamin = request.getInputStream();
//以下用dom4j解析
SAXReadersax = new SAXReader();
try {
Document dom= sax.read(in);
Element root = dom.getRootElement();
Iterator
while(it.hasNext()){
String nm = it.next().getStringValue();
System.err.println(nm);
result+=nm;
}
} catch (Exception e) {
throw new RuntimeException(e.getMessage(),e);
}
response.setContentType("text/html;charset=UTF-8");
PrintWriterout = response.getWriter();
//返回json数据
out.print("{\"name\":\""+result+"\"}");
}
}
1、将wsdl文件,保存成本地一样可以通过此工具访问远程服务。为file:///D:/abc.xml
在MyEclipse中调用WebService可以快速验证你的服务器端程序,从而省去了自己书写客户端。
1:这是发出的消息格式:
<soapenv:Envelopexmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"xmlns:q0="http://itcast.com/"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-<soapenv:Body>
-
soapenv:Body>
soapenv:Envelope>
2:以下是接收到的XML格式
<S:Envelopexmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
-<S:Body>
-
S:Body>
S:Envelope>
3:上面的1和2就是SOAP(SimpleObject Access Protocol)简单对像访问协议的格式。
1:我们已经说过了,WebService是通过向服务器发出XML格式的数据实现远程调用,然后服务器也返回XML数据给客户端,那么这个XML是什么格式的呢?
下面我将使用MyEclipse中的WebServiceExplorer工具向我们的WebService发起请求,并查看它的XML数据格式。
2:通过HttpWatchprofession Edition只可以看到获取wsdl文档的具体信息。
且必须安装HttpWatchprofession Edition版本的才可以,如果是Basic版本的,将不会看到Stream(数据流)信息。
3、使用MyEclipse的TCP/IPMonitor这个工具来查看具体的请求过程。
TCP/IPMonitor不仅可以看到SOAP数据,还可以获取HTTP请求和接收的头信息。
3.1、此工具位于:window>showview>other>MyEclipseCommon(常用工具)>TCP/IPMonitor
3.2、此工具,相当于一个代理商,启动后它将监听本地的某个端口,然后再将请求转发给指定的目标IP和端口。
获取到数据后,再将数据原封不动的返回给客户。在客户看来,永远首先访问的都应该是这个代理,否则
我们将看不到数据传输的过程。
3.3、配置选项:
在打开的TCP/IPMonitor界面上:viewMenu(右上方向下的小箭头)>Properties>Add(右侧添加)
设置成以下属性:
步1:
localmonitoring port(监听本地的端口号):9876,随意设置一个4位的端口号,一会将通过http://127.0.0.1:9876的形式访问
hostname(要监听的服务器,如www.baidu.com):127.0.0.1–因为本机发布了一个WebService所以监听本机IP.也可以是任意的主机。
Port(要监听的目标服务器的端口):6666 - 因为我们发布的WebService为http://127.0.0.1:6666/helloworld所以,6666是需要监听的端口号。
Type(监听的类型):
--TCP/IP :将使用原始地址继续访问下一个请求,如用户输入:http://127.0.0.1:9876/helloworld?wsdl此时将返回wsdl服务访问地址同前。
-- HTTP :将使用目标地址继续访问下一个请求。如用户输入: http://127.0.0.1:9876/helloworld?wsdl在请求方法时将使用http://127.0.0.1:6666/helloworld来访问sayHi方法。此
种方式将不再会被代理。因为已经不是正在监听的端口号了。
在监听类型处,我们选择TCP/IP。
建议同学们,将两种方式都设置一下,然后通过在地址栏输入:http://127.0.0.1:9876/helloworld?wsdl,在返回的wsdl文件中
查看:<soap:addresslocation=“http:……”/>处地址的变化情况。
timeout:设置访问不成功的连接时间,保持为0,即不设置。
在设置好后,点OK按扭,然后再点右方的Start按扭,监听便已经启动。
步2:
在MyEclipse的WebService上配置WSDLURL为:http://127.0.0.1:9876/helloworld?wsdl,注意使用的是MyEclipseTCP/IP Monitor的端口。而不是直接去访问我们发布的
http://127.0.0.1:6666/helloworld?wsdl
在WebServiceExplorer上请求sayHi方法,传递参数,并查看http头信息和发送及返回的xml信息。
无论是MyEclipseWebServiceExplorer,Httpwatch,还是TCP/IPMonitor都是一些辅助工具。虽然对编写代码没有什么用处,但对我如何监控问题,发现问题的能力有所帮助。
更能从工具的使用中,发现代码的问题进行调整或改进。
看董wsdl文件,能帮助我们顺利弄清如何调用一个WebService,以及它有哪些类的哪些方法可以调用。
1、SOAPBinding(style=Style.DOCUMENT或Stype.RPC)指定了消息的格式,即相互交流以语言。此值默认为DOCUMENT。并在wsdl文
件的soap:binding….type上出现。请大家注意观察。
(目前我的使用的WebService规范是基于jax-ws的,而rpc是基于jax-rpc的,已经过时。)
以下是wsdl文档的部分代码:
<soap:bindingtransport="http://schemas.xmlsoap.org/soap/http"style="document"/>
Style指定了消息格式
Transport的最后部分指定了使用什么样的协议。此处定义为http协议。
所以,binding部分,定义了如何调用,使用什么样的协议和消息格式,有什么样的方法可以调用。
portType又指定了一个可以调用的Java类,
请看本目录下的wsdl文件的图示说明!03_WSDL文件分析.bmp
--types定义了命名空间的信息,其中namespace是倒置的包名。
schemaLocation是xsd地址信息,可以拷贝此地址,并查看xsd的定义
。
开始的二行注释是Jax-ws的版本,及是由哪个jdk生成的。
1、上面所有的内容,都是由系统发布时自动生成的,那么如何才可以修改这个文档呢?
1、另有:SOAPBinding-指定WebService到SOAP协议的影射关系?
使用不同版本的Jdk对发布ws的影响.
1.5不支持.
1.6.0_20前版本必须使用完整注解.
1.6.0_21以后可以只使用@WebService对类进行注解.
2、关于namespace约定名的说明,@WebService(targetNameSpace=…..)
targetNamespace
定义导出的服务接口的名域(namespace),默认是倒置的服务接口Java包名。如demo.cxf.UserService的名域将会是http://cxf.demo/
1:WebService的注解都位于javax.jws包下。
主要包含以下几个注解(直接查看JDK文档,关于它里面的配置属性也直接看JDK6的文档。)
我们只讨论以下加个注解:
WebMethod WebParam WebResult WebService
2:以下是加了注解的示例:
package com.itcast;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.WebParam.Mode;
import javax.xml.ws.Endpoint;
/**
* 一个加了很多注解的代码
*/
@WebService(name="myName",//对应portType name="myName"
portName="myPort", //对应服务中的port name="myPort"
serviceName="myService",//对应service name="myService"
targetNamespace="http://leaf.com/mynamespace")//可以随意书写类似于java中的package
public class HelloWorld{
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@WebMethod(action="myAction",//定义一个soapAction="myAction"用于找到这个方法以执行
operationName="myOperationName")//定义可以调用的方法,会生成相应类的具体方法,operation name=".."
public @WebResult(name="mySayHelloResult")String//定义返回值的名称
sayHello(){
return "HelloWorld";
}
@WebMethod(action="mySayHiAction",operationName="mySayHiOperationName")
public @WebResult(name="mySayHiResult")String sayHi(@WebParam(name="myParaName",
//将参数放到头信息中,用于保护参数,默认在body中
header=true,
mode=Mode.IN)
String name){
String str = "你好:"+name+",当前时间是:"+sdf.format(new Date());
return str;
}
public static void main(String[] args) {
Endpoint.publish("http://127.0.0.1:6666/helloworld",new HelloWorld());
}
}
3:将上面的程序对外发布以后,我们通过MyEclipse的WebService Explorer来访问
你会发现和以前不一样的提示信息,但其实,仍然还是调用的那同一个方法。
4:请同学们在去观察SOAP请求和返回文档的修改。在MyEclipse WebService Explorer的返回信息窗口中点Soure即可以看到。观察变化加以分析。
5:再次使用wsimport –s . http://127.0.0.1:6666/helloworld?wsdl生成java代码然后调用,看看还是哪些类名吗?
以下是调用代码(可以用面目全非来形容,但完成的还是同样的工作。)
package com.leaf.mynamespace;
public class Main {
public static void main(String[] args) {
//通过分析wsdl可知从myService中调用getMyPort返回myName
MyName myName = new MyService().getMyPort();
//通过myName的mySayHiOperationName来调用sayHi方法
String str = myName.mySayHiOperationName("王健");
System.err.println(str);
}
}
package cn.leaf.two;
import java.util.Date;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
/**
* 发布第一个web服务
*/
@WebService
(serviceName="WjService"//修改Service的名称,即:new WjService();
,name="One"//定义Port名称,即端口-new WjService().getOnePort();返回接口即One
,targetNamespace="http://wj.cn"//定义命名空间,默认为倒置的包名
,portName="one"//定义获取的方法,此值可以覆盖name的定义,即port name="getOne"
)
public class OneService{
@WebMethod(operationName="sayHello"//修改方法名
)
public
@WebResult(name="ReturnMsg")
String sayHi(
@WebParam(name="yourName")
String name){
return name+",你好,现在时间是:"+new Date();
}
public static void main(String[] args) throws Exception {
//发布服务
Endpoint ed= Endpoint.publish("http://127.0.0.1:9999/one",
new OneService());
System.err.println("服务发布成功");
}
}