问题?自定义WebService和客户端调用《二》,客户端调用有几种方式
一、JDK创建WebService服务和使用
Oracle前身sun公司,早就意识到WebService的强大,和它的未来前景,所以在JDK中早就封装了这样的创建WebService服务。
查看DJK-API文档:
1.新建Java或web工程,建一个类(这个类作为服务端):
<span style="font-family:Microsoft YaHei;font-size:18px;"><span style="font-family:Microsoft YaHei;font-size:18px;">package com.itcast.ws; import javax.jws.WebService; import javax.xml.ws.Endpoint; @WebService //表示这个类,代表的是启动服务,ServiceName名字可以改,默认不写的话,名字为类名加Service public class HelloService { public String sayhello(String name){//能够访问的借口方法,必须是public方法 System.out.println("Hello: "+name); return "Hello: "+name; } public String sayhello1(){ return "成功调用第二个服务器方法"; } public static void main(String[] args) { /* * 调用静态的方法Endpoint.publish创建一个服务 * 1.address:URI,指定要使用的地址和传输/协议,也就是服务地址 * 2.implementor:实现者,也就是服务端 */ <pre name="code" class="java"><span style="white-space:pre"> </span>//使用自建端口号之前,cmd中使用netstat -a 检查一下端口是否被占用</span></span>Endpoint.publish("http://localhost:9090/hello", new HelloService());System.out.println("启动服务:new HelloService");}}
/**如果改成private方法的话就会出现以下错误,我们做这个webservcie服务就是为了,*公布数据给别人用的,给成私有的,就达不到目的了,自然就会出错。Static、finally也不行
*Exception in thread "main"com.sun.xml.internal.ws.model.RuntimeModelerException: The web *service definedby the class com.itcast.ws.HelloService does not contain any valid WebMethods.
*/
运行:如果出现错:Exception in thread "main" com.sun.xml.internal.ws.model.RuntimeModelerException:解决办法将jdk升级到1.7版本,MyEclispe的preference下java install(一般情况下,MyEclispe用的而是自身带的JDK,而不是安装得JDK,需要配置一下)
否则就运行成功:
或者网页调用:http://localhost:9090/hello或http://localhost:9090/hello?wsdl 看看wsdl服务描述文档
2.新建客户端,并且调用该服务,在调用过程中,该服务必须一直开启
用wsimport -s . http://localhost:9090/hello?wsdl来生成本地代码,在上一文章中已经说过怎么做了。
把生成的文件夹拷入到客户端项目src下,并且新建一个测试调用类:
<span style="font-family:Microsoft YaHei;font-size:18px;"><span style="font-family:Microsoft YaHei;font-size:18px;">package com.itcast.test; import org.junit.Test; import com.itcast.ws.HelloService; import com.itcast.ws.HelloServiceService; public class test1 { @Test public void testService(){ HelloServiceService helloServiceService = new HelloServiceService(); HelloService service = helloServiceService.getHelloServicePort(); String name = service.sayhello("李*****"); System.out.println("成功获取数据包: "+name+" "+service.sayhello1()); } } </span></span>
说到这里了,我们就会忍不住提出,那么通过wsimport -s .生成的代码,导入到工程中,我怎么知道,调用哪个服务,调用哪个接口呢?就需要深入看看WebService的深入理解了。
二、深入了解WebService配置
1.注解声明类为@WebService,不然就会报错。
不然报这个错误:class cn.itcast.ws.HelloService has neither @WebService nor @WebServiceProvider annotation
必须有至少一个public方法,且这个方法不能是static,final
不然就hi报这个错误:cn.itcast.ws.HelloService does not contain any valid WebMethods.
/**如果改成private方法的话就会出现以下错误,我们做这个webservcie服务就是为了,*公布数据给别人用的,给成私有的,就达不到目的了,自然就会出错。Static、finally也不行
*Exception in thread "main" com.sun.xml.internal.ws.model.RuntimeModelerException: The web *service defined by the class com.itcast.ws.HelloService does not contain any valid WebMethods.
*/
2.如何查看说明wsdl?
前面提到,怎么知道调用哪个服务,怎么知道调用哪个接口,这里就会讲到了。
服务 |
|
端口 |
|
方法 |
|
参数 |
访问看看:http://localhost:9080/hello?xsd=1
|
返回值 |
3.MyEclipes提供WebService工具
它有两个作用:
1.第一个作用可以用来测试服务端的数据。
2.第二个作用可以获取发出SAOP请求的代码,(它会返回WSDL代码,在AJAX市级开发中使用特别多)和返回SAOP结果的代码,在AJAX之中用得比较多。比如以下:
注意:这个工具有一个最不好的缺点,就是它不支持SAOP1.2,它只支持1.1版本。
三、Ajax调用WebService服务
响应的一样,首先WebService服务的搭建,有服务接口,与接口中的方法,开启服务。
然后用MyEclipes提供WebService工具获取到接口的SAOP请求的代码,如以上那张图片。
首先你得有AJAX的基础才能做这一块哈。
<span style="font-family:Microsoft YaHei;font-size:18px;"><!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> <script type="text/javascript"> /* 思路: 1.创建一个XMLHTTP对象 2.打开链接open("POST",url) 3.设置请求头Content-Type 4.设置回调函数,处理返回值 5.从返回的XML中解析我们要的内容 */ var xmlHttpReqquest = new ActiveXObject("Microsoft.XMLHTTP"); //alert(xmlHttpReqquest); //发送SOAP请求 function sendMsg(){ //获取用户输入的内容 var name = document.getElementById("name").value; var url = "http://localhost:9080/hello"; var requestBody = "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:q0=\"http://ws.itcast.cn/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" + "<soapenv:Body><q0:sayHello><arg0>"+name+"</arg0></q0:sayHello></soapenv:Body></soapenv:Envelope>"; xmlHttpReqquest.open("POST", url); xmlHttpReqquest.setRequestHeader("Content-Type","text/xml;charset=utf-8"); xmlHttpReqquest.onreadystatechange = _back; xmlHttpReqquest.send(requestBody); } //接收SOAP返回,从返回XML中解析 function _back(){ //处理完毕,处理成功 if(xmlHttpReqquest.readystate==4){ //处理完 if(xmlHttpReqquest.status==200){ //成功 var xml = xmlHttpReqquest.responseXML; //alert(xml); //获取return标签的第一个 var ele = xml.getElementsByTagName("return")[0]; alert(ele.text); } } } </script> </head> <body> <input type="text" id="name" name="name" value=""/> <input type="button" name="send" value="send" onclick="sendMsg();"/> </body> </html> </span>
四、HttpURLConnection方式调用WebService服务
同样的这种方式,也需要SAOP请求的消息。同样用MyEclispe工具获取
如:
<span style="font-family:Microsoft YaHei;font-size:18px;">package com.itcast.test; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import org.junit.Test; public class RequestUrlConnectionTest { @Test public void uriTest() throws IOException{ /* *1.创建一个url *2.打开一个连接 *3.设置相关参数 *4.创建输出流,用来发送SAOP请求 *5.发送完,接收数据 *6.用输入流获取webservice中的内容 */ URL url = new URL("http://localhost:9090/hello"); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("POST");//必须设置为POST方式,而且必须是大写的 connection.setDoInput(true);//因为有输入参数也有输出参数所以都为真 connection.setDoOutput(true); connection.setRequestProperty("Content-Type", "text/xml;charset=utf-8"); OutputStream out = connection.getOutputStream(); String argo="<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:q0=\"http://ws.itcast.cn/\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"+"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><soapenv:Body><q0:sayhello><arg0>jack</arg0></q0:sayhello></soapenv:Body></soapenv:Envelope>"; out.write(argo.getBytes());//发送SAOP请求 InputStream stream = connection.getInputStream(); byte[] b = new byte[1024]; int len=0; StringBuffer buffer = new StringBuffer(); while((len=stream.read(b))!=-1){ String s = new String(b, 0, len, "utf-8"); buffer.append(s); } System.out.println(buffer.toString()); } } </span>
这个QName的链接地址要WSDL说明书
用cmd打包好的本地调用代码,复制到客户端项目中,只留下服务接口(那个是服务的接口,参照说明书),其他都删掉,里面的错的可以删掉。
<span style="font-family:Microsoft YaHei;font-size:18px;">package com.itcast.test; import java.net.MalformedURLException; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.ws.Service; import org.junit.Test; import com.itcast.ws.HelloService; public class QnameTest { /* * 1.创建service对象,service.create(wsdlDocumentLocation, serviceName) * wsdlDocumentLocation:URL服务地址,而且需要wsmdl描述 * serviceName:Qname,这就是用这个方式的核心,因为他只用到service接口 * Qname:需要两个参数,一个是打包的空间,另外一个是这个服务名称(描述文件中的的服务名) * * 2.通过service对象调用接口 * 3.通过接口对象来调用服务端方法 */ @Test public void qnametest1() throws MalformedURLException{ URL url = new URL("http://localhost:9090/hello?wsdl"); QName serviceName = new QName("http://ws.itcast.com/", "HelloServiceService"); Service service = Service.create(url, serviceName ); HelloService helloService = service.getPort(HelloService.class); String name = helloService.sayhello("Mr_Li"); String naem1 = helloService.sayhello1(); System.out.println("调用service成功:"+name+"\n"+naem1); } } </span>
六、注解开发
前面我用的调用接口服务,需要知道说明书上写明那个服务,然后我们才去写,因为那些服务名称都是它自动加上的基本结构为:服务名+Service,还有命名空间是跟建得包是烦着过来的。如果采取注解的形式去开发,那么就不需要知道服务名称(针对于程序猿)。
@WebService |
|
@WebService(serviceName="MyService") |
修改产生的服务的名称 |
@WebService(serviceName="MyService",targetNamespace="http://cn.itcast.ws/") |
修改命名空间 |
@WebMethod(operationName="hello") |
修改方法名称 |
@WebParam(name="name") |
修改参数名称 |
@WebResult(name="returnMsg") |
修改返回名称 |
@WebMethod(exclude=true) |
将指定的public方法排除,用户不能访问 |
如:
<span style="font-family:Microsoft YaHei;font-size:18px;">package com.itcast.ws; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebResult; import javax.jws.WebService; import javax.xml.ws.Endpoint; @WebService(serviceName="MyService",targetNamespace="http://com.itcast.ws/") //表示这个类,代表的是启动服务,ServiceName名字可以改,默认不写的话,名字为类名加Service public class HelloService { @WebMethod(operationName="Hello1") @WebResult(name="returnResult") public String sayhello(@WebParam(name="name")String name){ System.out.println("Hello: "+name); return "Hello: "+name; } @WebMethod(operationName="Hello2",exclude=true) public String sayhello1(){ return "成功调用第二个服务器方法"; } public static void main(String[] args) { /* * 调用静态的方法Endpoint.publish创建一个服务 * 1.address:URI,指定要使用的地址和传输/协议,也就是服务地址 * 2.implementor:实现者,也就是服务端 */ Endpoint.publish("http://localhost:9090/hello", new HelloService()); System.out.println("启动服务:new HelloService"); } } </span>
执行命令看看是不是这样的:
http://localhost:9090/hello?wsdl
http://localhost:9090/hello?xsd=1
七、监控工具TCP/IP监控
有时候我们需要监听整个webservice的调用状态,需要用到MyEclipse的一个监听工具(只有高版本才有这个工具)
采用的是TCP/IP的方式来监听的。需要监听的端口号和被监听的端口号不同。
八、总结
1.UDDI/WSDL/SOAP的概念
2.wsimport –s . http::///// 生产本地调用代码
3.注意:支持soap1.0协议
4.发布服务 Endpoint.publish静态方法
参数1访问地址,参数2实现类。
5.Service类,它有静态方法create可以创建一个Web服务
6.调用方式:
A. 客户端的方式
B. MyEclipse自带工具WebService Expolor 帮助测试,直接获取到发送和接收的SOAPxml
C. Ajax ,不需生成本地调用代码,可以直接访问,在实际项目中使用比较多
D. HttpURLConnection对象,是以流方式
E. QName方式,内部使用方式,实际开发中常采用实际开发中可以选择其中一种
7.对象封装
业务比较复杂,按原有的方式写po类,按原有的方式写Service层,在Server服务端调用业务层service。实现业务逻辑。
总结:还没完呢,请看下一篇文章点击打开文章链接