Webservice的简单运用

  • 需求总结:目前软件应用中会出现大量的需求:同一个应用会有不同的客户端访问,或者不同的应用之间也会相互调用服务(A程序和B程序可能不是同一个语言开发的应用)   
      
    基于Socket创建Web服务  
    为什么要使用Socket呢,我们来看下图  
       
      
    Socket原理图回顾:  
       
      
    编写SocketService,完成字母小写转大写功能  
      
    ServerSocket服务器端代码如下:  
    public static void main(String[] args) throws IOException {  
            // 1:建立服务器端的tcp socket服务,必须监听一个端口  
            ServerSocket ss = new ServerSocket(9999);  
            // 2: 通过服务器端的socket对象的accept方法获取连接上的客户端对象  
            Socket s = null;  
            // 3:获取客户端的数据  
            while (true) {  
                // 接受Socket服务,如果有,没有则堵塞,等待  
                s = ss.accept();  
                System.out.println("accept success.......");  
                // 从Socekt输入流中获取客户端发送过来的输出流  
                InputStream in = s.getInputStream();  
                byte[] buf = new byte[1024];  
                int len = in.read(buf);  
                String str = new String(buf, 0, len);  
                System.out.println("从客户端传送来的数据如下:");  
                System.out.println(str);  
                // 通过服务器端Socket输出流,写数据,会传送到客户端Socket输入流中  
                OutputStream out = s.getOutputStream();  
                // 把字母转化为大写  
                out.write(str.toUpperCase().getBytes());  
                s.close();  
            }  
        }  
      
    通过Java客户端访问SocketService服务  
    public static void main(String[] args) throws Exception {  
            Scanner input=new Scanner(System.in);  
            // 1: 创建一个基于TCP协议的socket服务,在建立对象时,要指定连接服务器和端口号  
            Socket s=new Socket("127.0.0.1",9999);  
            // 2: 通过建立的Socket对象获取Socket中的输出流,的调用getOutStream方法  
            OutputStream out=s.getOutputStream();  
            System.out.println("请输入要转化的字母,或者单词!");  
            // 3: 写入到Socket输出流中  
            String word=input.next();  
            out.write(word.getBytes());  
            // 4: 通过建立的Socket对象获取Socket中的输入流,输入流会接受来自服务器端数据  
            InputStream in=s.getInputStream();  
            byte[] buf=new byte[1024];  
            // 5: 获取输入字节流的数据,注意此方法是堵塞的,如果没有获取数据会一直等待  
            int len=in.read(buf);  
            String str=new String(buf,0,len);  
            System.out.println("服务返回来的结果如下:");  
            System.out.println(str);  
            // 关闭Socket  
            s.close();  
        }   }  
      
    注意先启动服务器端在启动客户端:否则会出现连接异常  
      
    服务器端显示结果如下:  
    accept success.......  
    从客户端传送来的数据如下:  
    hello  
      
    客户端端显示结果如下:  
    请输入要转化的字母,或者单词!  
    hello  
    服务返回来的结果如下:  
    HELLO  
      
      
    通过IE来访问SocketService  
       
    accept success.......  
    从客户端传送来的数据如下:  
    POST / HTTP/1.1  
    Accept: image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* 
    Accept-Language: zh-CN 
    User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0) 
    Content-Type: application/x-www-form-urlencoded 
    Accept-Encoding: gzip, deflate 
    Host: 127.0.0.1:9999 
    Content-Length: 10 
    Connection: Keep-Alive 
    Cache-Control: no-cache 
     
    word=hello 
     
    返回给IE客户端结果如下: 
    POST / HTTP/1.1 
    ACCEPT: IMAGE/JPEG, APPLICATION/X-MS-APPLICATION, IMAGE/GIF, APPLICATION/XAML+XML, IMAGE/PJPEG, APPLICATION/X-MS-XBAP, APPLICATION/VND.MS-EXCEL, APPLICATION/VND.MS-POWERPOINT, APPLICATION/MSWORD, */*  
    ACCEPT-LANGUAGE: ZH-CN  
    USER-AGENT: MOZILLA/4.0 (COMPATIBLE; MSIE 8.0; WINDOWS NT 6.1; TRIDENT/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; MEDIA CENTER PC 6.0)  
    CONTENT-TYPE: APPLICATION/X-WWW-FORM-URLENCODED  
    ACCEPT-ENCODING: GZIP, DEFLATE  
    HOST: 127.0.0.1:9999  
    CONTENT-LENGTH: 8  
    CONNECTION: KEEP-ALIVE  
    CACHE-CONTROL: NO-CACHE  
      
    WORD=AAA  
    HTTP协议格式简单回顾:  
    Http请求由三部分组成,分别是:请求行、消息报头、请求正文  
      
    请求行:  
    以一个方法符号开头,以空格分开,后面跟着请求的URI和协议的版本,格式如下:Method Request-URI HTTP-Version  
      
    消息报头:  
    Accept: Accept请求报头域用于指定客户端接受哪些类型的信息  
    Host:(发送请求时,该报头域是必需的)请求报头域主要用于指定被请求资源的Internet主机和端口号,它通常从HTTP URL中提取出来的 默认80则省略  
    Content-Type报头域中所引用的媒体类型,必须采用相应的解码机制  
    Content-Type:text/html;charset=UTF-8  
      
    HTTP响应也是由三个部分组成,分别是:状态行、消息报头、响应正文  
      
    HTTP-Version Status-Code Reason-Phrase  
    其中,HTTP-Version表示服务器HTTP协议的版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。  
    HTTP/1.1 200 OK  
      
    问题思考  
    目前我们用Java语言创建一个Socket服务器端, 然用 Java Socket访问没有任何问题. 用C# Socket客户端访问也没有任何问题(说明不同过的语言编写的程序可以通过Socket通信) 用IE访问Socket服务端同样也没问题,IE本身是用VC++语言开发的  
    但是, 如果我们的服务复杂一点呢. 例如 我过去的单词 操作1:小写转大写,2:大写转小写 3:英文转中文…… 而且我们发现IE在发送请求的时候不但传输的数据还传输了协议. 而且目前我们的很多应用都是基于Web开发的. 如果我们处理Web的请求. 那么还需要从发送过来的协议中获取数据.所以最好的解决办法就是规定格式, 客户端统一格式发送,只要格式统一,服务器端就从指定的格式中获取数据就OK了  
    “Socket+规范格式”创建Web服务  
    服务器端代码修改后如下:  
    public static void main(String[] args) throws Exception {  
            // 1:建立服务器端的tcp socket服务,必须监听一个端口  
            ServerSocket serverSocket = new ServerSocket(7777);  
            Socket socket = null;  
            while (true) {  
                // 接受Socket服务,如果有,没有则堵塞,等待  
                socket = serverSocket.accept();  
                // 从Socekt输入流中获取客户端发送过来的输出流  
                InputStream inputStream = socket.getInputStream();  
                byte[] bytes = new byte[1024];  
                int size = inputStream.read(bytes);  
                String word=new String(bytes, 0, size);  
                System.out.println("字节数为:" + size + "内容为:" + word);  
                /*--根据不同的类型选择不同的服务,格式是由服务器端制定   type=int,word=string--*/  
                String[] content=word.split(",");  
                // 获取服务的类型  
                int type=Integer.parseInt(content[0].split("=")[1]);  
                // 获取处理服务的数据  
                String val=content[1].split("=")[1];  
                String result=wordService(val, type);       /*-------------------------------------------------------*/  
                OutputStream out = socket.getOutputStream();  
                System.out.println("返回的结果如下:" + result);  
                out.write(result.getBytes());  
                socket.close();  
            }  
        }  
        //  type=1 小写转大写  type=2大写转小写  
        public static String wordService(String val,int type){  
            return type==1?val.toUpperCase():val.toLowerCase();  
        }  
      
    Socket客户端访问SocektFormatService服务端代码如下:  
    public static void main(String[] args) throws Exception {  
    // 1: 创建一个基于TCP协议的socket服务,在建立对象时,要指定连接服务器和端口号  
            Socket s=new Socket("127.0.0.1",9999);  
            // 2: 通过建立的Socket对象获取Socket中的输出流,的调用getOutStream方法  
            OutputStream out=s.getOutputStream();  
    //      out.write("type=2,word=HELLO".getBytes());  
            out.write("type=1,word=hello".getBytes());  
        // 4: 通过建立的Socket对象获取Socket中的输入流,输入流会接受来自服务器端数据  
            InputStream in=s.getInputStream();  
            byte[] buf=new byte[1024];  
            // 5: 获取输入字节流的数据,注意此方法是堵塞的,如果没有获取数据会一直等待  
            int len=in.read(buf);  
            String str=new String(buf,0,len);  
            System.out.println("服务返回来的结果如下:");  
            System.out.println(str);  
            // 关闭Socket  
            s.close();  
        }  
      
    IE调用SocektFormatService服务:  
    如果要让IE调用则 Socket服务端首先要从传输过来的HTTP协议中获取传输过来的信息(去除请求行、消息报头)然后在进行服务的处理.因为Socket是基于TCP协议的 而IE是基于HTTP协议的,不同协议格式不一样. 所以处理起来比较麻烦,此处省略  
      
    解决方案一的缺点:  
    1.  Socket服务不利于推广(如果让别人知道你的Socket服务)  
    2.  Socket处于传输层, 有可能无法穿越防火墙  
    3.  Socket本身处于传输层,要处理应用层的服务要解析HTTP协议比较麻烦,但是现在我们的应用都是基于web的, web中的数据传输就需要HTTP协议  
    4.  即使定义了格式. 后面如果格式改变则客户端格式都需要同步变化维护困难  
    WebService相关介绍   
    WebService概念介绍:  
        Web Service, 能使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件, 就可相互交换数据或集成。依据Web Service规范实施的应用之间, 无论它们所使用的语言、 平台或内部协议是什么, 都可以相互交换数据  
    通俗的讲,Web Service就是一个部署在Web服务器上的一个,它向外界暴露出一个能够通过Web进行调用的API。这就是说,你能够用编程的方法通过Web来调用这个应用程序。我们把调用这个Web Service 的应用程序叫做客户端,发布这个web服务的机应用程序器称为Web Service服务器  
    WebService技术规则:  
     1.  基于web的系统级接口规范  
        - 一个普通适用标准 :http+xml  
        - 任何网络通信的操作系统  
        - 自包含、自描述、模块化  
        - 发布、定位、通过web调用  
     2.  web service 技术和规则  
        - xml(可拓展标记语言)  
        - soap(简易对象访问协议)  soap – http+xmlsocket  
        - wsdl(web services 描述语言)   
        - uddi(通用描述、发现及整合)  
    3. 跨平台的可互操作性  
       - 基于xml  
       - 基于xsd(xml schema )  
       - 基于http  
    4. webservice 适用场景  
       - 跨防火墙的通信  
       - 异构系统间应用程序集成(jee:(EJB RMI)、php、c++、.net (com+))  
    WebService的优势介绍:  
    a)  异构平台的互通性  
    理论上, Web Service 最大的优势是提供了异构平台的无缝街接技术手段。由于不同的用户使用不同的硬件平台,不同的操作平台,不同的操作系统,不同的软件,不同的协议通信,这就产生了互相通信的需求。 Web Service 使任何两个应用程序,只要能读写XML,那么就能互相通信。  
      
    b)  更广泛的软件复用  
    软件的复用技术通过组合已有模块来搭建应用程序,能大幅度提高软件的生产效率和质量。用户只要获得了描述 Web Service 的 WSDL 文件,就可以方便地生成客户端代理,并通过代理访问 Web Service 。  
      
    c)  成本低、可读性强、应用范围广  
    Web Service 可用基于 XML 的 SOAP 来表示数据和调用请求。并且通过 HTTP 协议传输 XML 格式的数据  
      
    d)  迅捷的软件发行方式  
    Web Service 将彻底地改变软件的发行方式。软件供应商可以把软件分解成若Web Service 模块构成的系统,直接在 Web 上发布  
    WebService专业名称介绍:  
    a) XML: Extensible Markup Language -扩展性标记语言  
    XML,用于传输格式化的数据,是Web服务的基础。  
    •   namespace-命名空间。  
    •   xmlns=“http://itcast.cn” 使用默认命名空间。  
    •   xmlns:itcast=“http://itcast.cn”使用指定名称的命名空间。  
    b)  WSDL:  WebService Description Language – Web服务描述语言。调用该webservice api 的接口描述  
    •   通过XML形式说明服务在什么地方-地址。  
    •   通过XML形式说明服务提供什么样的方法 – 如何调用。  
    c)  SOAP: Simple Object Access Protocol(简单对象访问协议)    
    •   SOAP作为一个基于XML语言的协议用于有网上传输数据。  
    •   SOAP = 在HTTP的基础上+XML数据。  
    •   SOAP是基于HTTP的。  
    •   SOAP的组成如下  
      
    1.  Envelope – 必须的部分。以XML的根元素出现。  
    2.  Headers – 可选的。  
    3.  Body – 必须的。在body部分,包含要执行的服务器的方法。和发送到服务器的数据。  
    WebService工作原理:  
      
       
    调用已发布的WebService  
    WebService服务演示  
    a)  登录http://www.webxml.com.cn  
    b)  单击手机查询服务  
    c)  选择要调用的方法 例如: getMobileCodeInfo  
    d)  输入要查询的手机号单击”调用” 截图如下, 免费用户 UserID为null  
       
    e)  可以看到返回如下结果:  
    <?xml version="1.0" encoding="utf-8" ?>   
      <string xmlns="http://WebXml.com.cn/">18323455678:重庆 重庆 重庆移动全球通卡</string>  
      
    HttpClient工具使用  
    HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。  
      
    为什么要使用HttpClient工具:  
    原生态的Socket基于传输层,现在我们要访问的WebService是基于HTTP的属于应用层,所以我们的Socket通信要借助HttpClient发HTTP请求,这样格式才能匹配  
      
    HttpClient使用步骤如下:  
    1.  创建 HttpClient 的实例   
    2.  创建某种连接方法的实例,在这里是 GetMethod。在 GetMethod 的构造函数中传入待连接的地址  
    3.  配置要传输的参数,和消息头信息  
    4.  调用第一步中创建好的实例的 execute 方法来执行第二步中创建好的 method 实例  
    5.  通过response读取字符串  
    6.  释放连接。无论执行方法是否成功,都必须释放连接   
      
    Socket + HttpClient 访问WS  
    Get方式:  
    public static void getMethod() throws Exception {  
            // 创建get对象,类似get请求  
            GetMethod getMethod = new GetMethod(        "http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getMobileCodeInfo?mobileCode=18373551982&userID=");  
            // 发送get请求  
            int code = http.executeMethod(getMethod);  
            System.out.println("返回的消息码为:" + code);  
            System.out.println("返回的消息为:" + getMethod.getResponseBodyAsString());  
            getMethod.releaseConnection();  
        }  
    POST方式:  
    public static void postMethod() throws Exception {  
            // 创建post请求,类似Post请求  
            PostMethod postMethod = new PostMethod(             "http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx/getMobileCodeInfo");  
            // 设置请求的正文内容  
            postMethod.setRequestBody("mobileCode=18373551982&userID=");  
            // 设置传送信息的格式  
            postMethod.setRequestHeader("Content-Type",  
                    "application/x-www-form-urlencoded");  
            // 发送post请求  
            int code = http.executeMethod(postMethod);  
            System.out.println("返回消息码为:" + code);  
            System.out.println("返回的消息为:" + postMethod.getResponseBodyAsString());  
            postMethod.releaseConnection();  
        }  
    SOAP1.1方式:  
        public static void postSoap1() throws Exception {  
            // 创建一个post请求,类似Post请求  
            PostMethod postMethod = new PostMethod("http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx");  
            // 设置传送信息的格式  
            postMethod.setRequestHeader("Content-Type","text/xml; charset=utf-8");  
            postMethod.setRequestBody(new FileInputStream("C:/a.txt"));  
            int code = http.executeMethod(postMethod);  
            System.out.println("消息码为:" + code);  
            System.out.println("返回的消息为:" + postMethod.getResponseBodyAsString());  
            postMethod.releaseConnection();  
        }  
      
    Java方式访问WebService  
    1.  选中要调用的服务单击”服务说明”  
    a)  例如选中:  
    b)   http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL  
    2.  获取wsdl文件.使用JDK1.6以上的版本的wsimport命令  
    3.  切换到c:\ws文件夹 在dos输入:wsimport wsdl目标地址  
    a)  C:\ws>wsimport http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL  
    4.  可以把class文件打成jar包 jar cvf  test.jar 打包目录  
    5.  拷贝的项目中调参考WSDL用即可调用源码如下:  
    public static void main(String[] args) {  
            MobileCodeWS mobileCodeWs=new MobileCodeWS();  
            MobileCodeWSSoap mobileCodeWSSoap=mobileCodeWs.getMobileCodeWSSoap();  
            String tel=mobileCodeWSSoap.getMobileCodeInfo("18373551982",null);  
            System.out.println(tel);  
        }  
      
    WSDL协议简单介绍  
    WSDL – WebService Description Language – Web服务描述语言  
    通过XML形式说明服务在什么地方-地址。  
    通过XML形式说明服务提供什么样的方法 – 如何调用。  
    <wsdl:service name="MobileCodeWS">  
      <wsdl:documentation xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"><a href="http://www.webxml.com.cn/" target="_blank">WebXml.com.cn</a> <strong>国内手机号码归属地查询WEB服务</strong>,提供最新的国内手机号码段归属地数据,每月更新。<br />使用本站 WEB 服务请注明或链接本站:<a href="http://www.webxml.com.cn/" target="_blank">http://www.webxml.com.cn/</a> 感谢大家的支持!<br /> </wsdl:documentation>   
    - <wsdl:port name="MobileCodeWSSoap" binding="tns:MobileCodeWSSoap">  
      <soap:address location="http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx" />   
      </wsdl:port>  
      
    <wsdl:binding name="MobileCodeWSSoap" type="tns:MobileCodeWSSoap">  
      
    JDK6的WebService发布  
    注意MyEclipse8.5自带的JDK版本太低, 如果要发布自己的WebService则要升级到jdk1.6.0.013及以后版本  
    创建第一个Web服务:  
    WebService服务类代码如下:  
    @WebService // 添加了此注解,代表是一个WebService  
    public class HelloWorld {  
        // 非 static final private 方法默认会发布  
        public String sayHi(String name) {  
            return "hello" + name;  
        }  
    }  
    发布web服务:  
    public static void main(String[] args) {  
            String address="http://127.0.0.1:9999/helloworld";  
            // 注册并且发布一个服务,arg0: 服务地址 , arg1:要发布的服务对象  
            Endpoint endPoint=Endpoint.publish(address,new HelloWorld());  
            // 可以停止服务,或者手动停止  
            //endPoint.stop();  
        }  
      
    获取发布web服务的信息  
    访问wsdl网址为: http://127.0.0.1:9999/helloworld?wsdl  
    通过wsimport生成Java代码:   
    Wsimport http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL   // 只能生成 class格式  
    wsimport –s . http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL   // 能生成 class格式和Java源文件  
    Wsimport –s . –p a.b.c  
    http://webservice.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL // 能生成 class格式和Java源文件  并且可以自定义包名  
    jar -cvf mobile.jar .  // 把当前路径打包成jar文件, 包名为mobile.jar  
      
    调用第一个WebService服务  
    public static void main(String[] args) {  
            HelloWorldService helloWorldService=new HelloWorldService();  
            HelloWorld helloWorld=helloWorldService.getHelloWorldPort();  
            System.out.println(helloWorld.sayHi("china"));  
    }  
      
    代码注意事项:  
    1.  在类上添加@WebService注解,代表发布一个WebService服务  
    2.  通过EndPoint(端点服务)发布一个webService。Endpoint也是jdk提供的一个专门用于发布服务的类,它的publish方法接收两个参数,一个是本地的服务地址,二是提供服务的类。它位于javax.xml.ws.*包中。  
    3.  Endpoint.publish(String address, Object implementor) 静态方法在给定地址处针对指定的实现者对象创建并发布端点  
    4.  给类添加上@WebService注解后,类中所有的非静态方法都将会对外公布  
    5.  如果希望某个方法不对外公开,可以在方法上添加@WebMethod(exclude=true),阻止对外公开。  
    6.  如果一个类上,被添加了@WebService注解,则必须此类至少有一个可以公开的方法,否则将会启动失败。  
    7.  protected、private、final、static方法不能对外公开 代码如下:  
      
    @WebService // 添加了此注解,代表是一个WebService  
    public class HelloWorld {  
        // 非 static final private 方法默认会发布  
        public String sayHi(String name) {  
            return "hello" + name;  
        }  
        @WebMethod(exclude=true)  
        public void exclude(){  
            // 被注解排除的方法  
        }  
        protected void protected1(){  
            //受保护的方法默认不发布  
        }  
        private void private1(){  
            // 私有方法默认不发布  
        }  
        public static void static1(){  
            // static 方法默认不发布  
        }  
        public final void final1(){  
            // final 方法默认不发布  
        }  
    }  
    WSDL语法分析  
    前面获取web的发布信息是通过wsimport 来解析wsdl文件得到Java类来实现的. 如论是获取发布的服务,还是调用发布的服务.都需要参考wsdl文件.接下面我们就来分析wsdl文件的结构  
    分析服务类相关信息  
    // HelloWorldService 就是我们注册的服务,默认在原来的类名后面加上Service  
    <service name="HelloWorldService">  
      // 可以通过getHelloWroldPort()获取基于soap1.1的服务,此服务接受的类型取决,  
    // HelloWorldServiceSoapBinding绑定的类型  
    <port name="HelloWorldPort" binding="tns:HelloWorldPortBinding">  
    // 提供服务的地址,和端口,SOAP后面介绍  
      <soap:address location="http://127.0.0.1:9999/helloworld" />   
      </port>  
    </service>  
    分析如何调用服务方法  
    // 上图中的HelloWorldPortBinding参照的就是此HelloWorldPortBinding它的类型为HelloWorld接口类型.  
    <binding name="HelloWorldPortBinding" type="tns:HelloWorld">  
      // SOAP1.1协议 http://schemas.xmlsoap.org/soap/http  
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />   
          // 此服务拥有操作的方法  
    <operation name="sayHi">  
      <soap:operation soapAction="" />   
    <input>  
          <soap:body use="literal" />   
      </input>  
     <output>  
          <soap:body use="literal" />   
      </output>  
      </operation>  
    </binding>  
      
    WSDL详解  
      
    WSDL 文档的组成部分  
    元素  定义  
    <portType>    web service 执行的操作  
    <message> web service 使用的消息  
    <types>   web service 使用的数据类型  
    <binding> web service 使用的通信协议  
      
    WSDL 文档的主要结构:  
    <definitions>  
    <types>  
       definition of types........  
    </types>  
    <message>  
       definition of a message....  
    </message>  
    <portType>  
       definition of a port.......  
    </portType>  
    <binding>  
       definition of a binding....  
    </binding>  
    </definitions>  
    WSDL元素介绍  
        定义一个功能,在Java中叫做方法,在C中叫做函数;在Java中叫类,在C语言中叫做结构体。这些名词都不一样。  
        WSDL规范为了不会产生歧义,就定义了一些新的名词来表述功能与服务。  
    <portType>(WSDL端口)  
    <portType> 元素是最重要的 WSDL 元素。 它可描述一个 web service、可被执行的操作,以及相关的消息。 可以把 <portType> 元素比作传统编程语言中的一个函数库(或一个模块、或一个类)。   
    <operation>子元素:  
    对服务中所支持的操作的抽象描述,一般单个Operation描述了一个访问入口的请求/响应消息对。  
    <message> (WSDL消息)  
    <message> 元素定义一个操作的数据元素。 每个消息均由一个或多个部件组成。可以把这些部件比作传统编程语言中一个函数调用的参数。   
    通信消息的数据结构的抽象类型化定义。使用Types所定义的类型来定义整个消息的数据结构。  
    <types>(WSDL types)  
    <types> 元素定义 web service 使用的数据类型。 为了最大程度的平台中立性,WSDL 使用 XML Schema 语法来定义数据类型。   
      
    <binding> (WSDL Bindings)  
    <binding> 元素为每个端口定义消息格式和协议细节。  
    示例  
    这是某个 WSDL 文档的简化的片段:   
    <message name="getTermRequest">  
       <part name="term" type="xs:string"/>  
    </message>  
      
    <message name="getTermResponse">  
       <part name="value" type="xs:string"/>  
    </message>  
      
    <portType name="glossaryTerms">  
      <operation name="getTerm">  
            <input message="getTermRequest"/>  
            <output message="getTermResponse"/>  
      </operation>  
    </portType>  
      
      
    在这个例子中,<portType> 元素把 "glossaryTerms" 定义为某个端口的名称,把 "getTerm" 定义为某个操作的名称。  
      
    操作 "getTerm" 拥有一个名为 "getTermRequest" 的输入消息,以及一个名为 "getTermResponse" 的输出消息。  
      
    <message> 元素可定义每个消息的部件,以及相关联的数据类型。  
      
    对比传统的编程,glossaryTerms 是一个函数库,而 "getTerm" 是带有输入参数 "getTermRequest" 和返回参数 getTermResponse 的一个函数。  
      
    说明  
    Types、Message、PortType、Opration为抽象定义。  
    Binding、Port、Service是实现。  
      
    Port:  
    定义为协议/数据格式绑定与具体web访问地址组合的单个服务访问点。  
      
    Service:  
    相关服务访问点的集合。  
      
      
    问题思考:  
    通过上面的学习我们已经知道如何通过阅读WSDL文档来调用Web服务.但是到目前为止我们还不能通过AJAX的方式来调用我们自己发布的服务.因为我们并不知道我们自己发布服务的SOAP信息,可以使用MyEclipse提供的工具WebServiceExplorer  
      
    WebServiceExplorer使用  
      
    WebServiceExplorer使用步骤  
    1.  首先打开web service Explore浏览器单击右边的WSDL PAGE在Open WSDL中输入网址http://127.0.0.1:9999/helloworld?wsdl  然后单击go既可  
    2.  在图的左边会罗列出已经注册的web服务类和服务方法(注意JDK1.6不支持SOAP1.2协议.所以在网上发布了wsdl服务如果是基于1.2的则得不到任何有效信息),测试任何一个方法可以得到类似如下格式的代码:  
      
    SOAP Request Envelope:  
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:q0="http://myws.ws.e.itcast.cn/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">  
    <soapenv:Body>  
    <q0:sayHi>  
            <arg0>china</arg0>   
          </q0:sayHi>  
      </soapenv:Body>  
    </soapenv:Envelope>  
      
    SOAP Response Envelope:      
    <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">  
    <S:Body>  
         <ns2:sayHiResponse xmlns:ns2="http://myws.ws.e.itcast.cn/">  
         <return>hello---->china</return>   
       </ns2:sayHiResponse>  
      </S:Body>  
    </S:Envelope>  
      
    我们通过WebServiceExplorer工具会发现.基于Java代码的WS服务的调用本质上还是发送了HTTP1.1协议+传输的数据, 不但有数据本身而且还有对数据的描述. HTTP 请求体传输的是一种特殊的XML,它就是SOAP  
      
    ServiceExplorer阶段总结:  
    通过上面的AJAX调用我们会发现. 的确可以实现相同的功能. 但是细心的同学们会发现. 我们自己生成的SOAP和官方提供的SOAP不同. 那么我们如何来修改标签和命名空间的潜前缀呢. 这就要我们下面学到的注解了  
    使用WebService注解来修改WSDL文件  
    WSDL文件的内容,一般由服务默认生成,但为了更好的向开发人员提供使用说明书,一般应做一些简单的修改。至少不应该暴露我们的包结构。而targetNamespace默认情况下为倒置的包名,这已经暴露了我们的包结构。通过在类文件上添加以下注解,可以修改wsdl生成的各元素,而不是直接去修改wsdl文件,直接去修改wsdl文件是无效的。  
    WebService的注解包括:  
    1.  @WebService-定义服务   --类上  
    2.  @WebMethod-定义方法   - 方法  
    3.  @WebResult-定义返回值 – 返回值   
    4.  @WebParam-定义参数 – 参数  
      
    WebService注解的使用  
    1.  通过WebService的注解,可以更加形像的描述Web服务。从而生成WSDL文档。  
    2.  当修改了WebService注解之后,同时会影响客户端生成的代码。  
    3.  调用的方法名和参数名也发生了变化。  
    4.  即使是没有修改源代码,只修改了注解,客户端的代码也必须要重新生成(注意是生成而不是下载)。否则调用将会失败。  
    5.  生成本地调用代码,依然使用wsimport工具  
      
    @WebService注解:  
    1.  @WebService 标注要暴露为Web Services的类或接口 ,用于修饰类或接口,包含的属性有:  
    2.  targetNamespace属性:定义命名空间,默认为”http://”+”包名倒排”  
    3.  name属性:Web Service 的名称,默认为发布服务的类名。   
    4.  serviceName: ws服务的名词,默认在类名后面添加了service   
    5.  endpointInterface属性:定义服务抽象 Web Service 协定的服务端点接口的完整名称,接口也必须声明WebService注解,包括方法的注解必须也要添加到接口中,否则会无效, 而且WS在没有注解的情况下.生成WS的时候会自动生成一个注解.所以可以不用指定接口  
      
    @WebMethod  
    1.  此注解用在方法上,用于修改对外暴露的方法  
    2.  operationName属性:与此方法匹配的 wsdl:operation 的名称  
    3.  exclude属性:标注此方法是否被暴露,默认为false  
      
    注意:如果所有方法上都没有指定@WebMethod,则默认是所有的方法都是对外暴露的方法。如果有任一方法指定了@WebMethod,则只有指定这个注解的才是对外暴露的方法。  
      
    @WebResult  
    @WebResult 定义返回值,返回值类型不能为接口类或抽象类,而且必须有个不带参的构造函数,包含属性  
    name属性:返回值的名称  
      
    一个添加注解的案例:  
    // 修改web服务的名称,和命名空间  
    @WebService(name="Hello",targetNamespace="http://icast.cn")  
    public class HelloWorld {  
        @WebMethod(operationName="aaa")  
        // 修改webResult 和 webParam会影响到JS的调用模式  
        public @WebResult(name="returnName") String sayHi(@WebParam(name="yourname") String name){  
            return "Hello" + name;  
        }  
              
        public static void main(String[] args){  
            // 发布一个web服务,指定IP和实现者  
            Endpoint end=Endpoint.publish("http://127.0.0.1:4567/hello",new HelloWorld());  
        }  
    }  
    CXF框架概念介绍  
    Apache CXF 是一个开源的 Services 框架,CXF 帮助您来构建和开发 Services 这些 Services 可以支持多种协议,比如:SOAP、POST/HTTP、RESTful HTTP CXF 大大简化了 Service可以天然地和 Spring 进行无缝集成。  
    ServerFactoryBean来发布web服务  
    服务类代码如下:  
    // 注解是无效的  
    @WebService(name="Hello",targetNamespace="http://icast.cn")  
    public class HelloWorld {  
        public String sayHi(String name) {  
            return "hello---->" + name;  
        }  
    }  
      
    发布类代码如下:  
    public static void main(String[] args) {  
            // 发布服务的类, 类似Endpoint  
            ServerFactoryBean serverFactoryBean=new ServerFactoryBean();  
            // 注册服务器地址和端口  
            serverFactoryBean.setAddress("http://127.0.0.1:9999/hello");  
            // 注册哪个类提供服务  
            serverFactoryBean.setServiceBean(new HelloWorld());  
            // 发布一个cxf服务  
            serverFactoryBean.create();  
    // 一分钟有服务终止  
            Thread.sleep(1 * 60 * 1000);  
            // 正常退出程序  
            System.exit(0);  
    }  
      
    ServerFactoryBean注意事项:  
    这种方式没有添加webService注解,也就是说没有注解也可以发布webService服务,但是这种方式不是很规范,比如我们不可以通过注解的方式来修改WSDL的标签信息,   
    JaxWsServerFactoryBean来发布web服务  
    1.  JaxWsServerFactoryBean是ServerFactoryBean的子类,也是功能扩展类。  
    2.  此类,必须要在被发布为服务的类上添加@WebService注解,如果不加注解,虽然不出错,但也不会对外暴露任何方法。使用此类生成的wsdl文件更加规范  
    3.  更重要的是可以通过添加拦截器拦截请求.  
      
    客户端代码如下:  
    @WebService  
    public class HelloWorld {  
        public String sayHi(String name) {  
            return "hello---->" + name;  
        }  
    }  
      
    发布类代码如下:  
    public static void main(String[] args)throws Exception {  
            // 发布服务的类, 类似Endpoint  
            JaxWsServerFactoryBean jaxWsServer=new JaxWsServerFactoryBean();  
            // 注册服务器地址和端口  
            jaxWsServer.setAddress("http://127.0.0.1:9999/hello");  
            // 注册哪个类提供服务  
            jaxWsServer.setServiceBean(new HelloWorld());  
            // 配置输入输出日志拦截器  
            jaxWsServer.getInInterceptors().add(new LoggingInInterceptor());  
            jaxWsServer.getOutInterceptors().add(new LoggingOutInterceptor());  
            // 发布一个cxf服务  
            jaxWsServer.create();  
            // 一分钟有服务终止  
            Thread.sleep(1 * 60 * 1000);  
            // 正常退出程序  
            System.exit(0);  
        }  
      
    访问:http://127.0.0.1:9999/hello?wsdl 控制台第一次握手拦截器日志如下:  
    ----------------------------  
    ID: 1  
    Address: http://127.0.0.1:9999/hello?wsdl  
    Http-Method: GET  
    Content-Type:   
    Headers: {Accept=[*/*], accept-encoding=[gzip, deflate], Accept-Language=[zh-CN], connection=[Keep-Alive], Content-Type=[null], Host=[127.0.0.1:9999], User-Agent=[Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0)]} 
    -------------------------------------- 
     
    通过Java客户端调用服务类 
    public static void main(String[] args) { 
            HelloWorldService helloWorldService=new HelloWorldService(); 
            HelloWorld helloWorld=helloWorldService.getHelloWorldPort(); 
            System.out.println(helloWorld.sayHi("test")); 
    } 
     
    拦截器日志如下: 
    ---------------------------- 
    ID: 1 
    Address: http://127.0.0.1:9999/hello?wsdl 
    Http-Method: GET 
    Content-Type: text/xml 
    Headers: {Accept=[*/*], Cache-Control=[no-cache], connection=[keep-alive], content-type=[text/xml], Host=[127.0.0.1:9999], Pragma=[no-cache], User-Agent=[Apache CXF 2.4.2]}  
    --------------------------------------  
    2013-1-6 11:28:17 org.apache.cxf.interceptor.AbstractLoggingInterceptor log  
    信息: Inbound Message  
    ----------------------------  
    ID: 2  
    Address: http://127.0.0.1:9999/hello  
    Encoding: UTF-8  
    Http-Method: POST  
    Content-Type: text/xml; charset=UTF-8  
    Headers: {Accept=[*/*], Cache-Control=[no-cache], connection=[keep-alive], Content-Length=[191], content-type=[text/xml; charset=UTF-8], Host=[127.0.0.1:9999], Pragma=[no-cache], SOAPAction=[""], User-Agent=[Apache CXF 2.4.2]} 
    Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHi xmlns:ns2="http://jaxws.cxf.g.itcast.cn/"><arg0>test</arg0></ns2:sayHi></soap:Body></soap:Envelope> 
    -------------------------------------- 
    2013-1-6 11:28:17 org.apache.cxf.interceptor.AbstractLoggingInterceptor log 
    信息: Outbound Message 
    --------------------------- 
    ID: 2 
    Encoding: UTF-8 
    Content-Type: text/xml 
    Headers: {} 
    Payload: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:sayHiResponse xmlns:ns2="http://jaxws.cxf.g.itcast.cn/"><return>hello---->test</return></ns2:sayHiResponse></soap:Body></soap:Envelope> 
    -------------------------------------- 
    CXF与Spring集成发布WebService 
    前面的服务发布包括创建服务类都是硬编码的方式.是否可以使用配置的方式来发布服务类呢. 答案是肯定的这就要同Spring集成了, 由于cxf的项目已经集成了Spring(自带了Spring lib)所以CXF的服务都是在Spring的配置文件中完成的, 并且我们需要把项目发布到tomcat中,所以我们可以创建一个web项目 
     
    配置开发环境: 
       建立一个web项目 
       准备所有jar包,将CXF_HOME\lib项目下的所有jar包,全部都拷贝新项目的lib目录下.其中里面已经包含了Sring3.0的jar包  其中jetty 服务器的包可以不要.因为我们要部署的tomcat服务器中了 
       在web.xml中配置cxf的核心servlet,CXFServlet 
       此配置文件的作用类 拦截/ws/*的所有请求 类似Struts2的过滤器  
    <servlet> 
            <servlet-name>cxf</servlet-name> 
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> 
            <load-on-startup>1</load-on-startup> 
        </servlet> 
    <servlet-mapping> 
            <servlet-name>cxf</servlet-name> 
            <url-pattern>/ws/*</url-pattern> 
    </servlet-mapping> 
     
    通过Spring配置文件发布服务 
    <beans xmlns="http://www.springframework.org/schema/beans" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
        xmlns:context="http://www.springframework.org/schema/context" 
        xmlns:jaxws="http://cxf.apache.org/jaxws"  
        xmlns:p="http://www.springframework.org/schema/p" 
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.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">     
        <!-- 这样配置自身的服务也可以使用 --> 
        <bean id="userImpl" class="cn.itcast.i.cxf.spring.ws.UserImpl" /> 
        <!-- id:逻辑名  serviceClass=服务接口类  address:调用的路径  http://localhost:8888/项目名/ws/hello?wsdl> --> 
        <jaxws:server id="userService" serviceClass="cn.itcast.i.cxf.spring.ws.IUser" address="/hello"> 
            <jaxws:serviceBean> 
                <ref bean="userImpl" /> 
            </jaxws:serviceBean> 
            <jaxws:inInterceptors> 
                <bean class="org.apache.cxf.interceptor.LoggingInInterceptor" /> 
            </jaxws:inInterceptors> 
            <jaxws:outInterceptors> 
                <bean class="org.apache.cxf.interceptor.LoggingOutInterceptor" /> 
            </jaxws:outInterceptors> 
        </jaxws:server> 
    </beans> 
     
    服务接口如下: 
    @WebService 
    public interface IUser { 
        public void saveUser(User user); 
        public User getUser(int uid); 
    } 
    服务类如下: 
    public class UserImpl implements IUser { 
        private List<User> users=new ArrayList<User>(); 
     
        public User getUser(int uid) { 
            for(User temp:users){ 
                if(temp.getUid()==uid){ 
                    return temp; 
                } 
            } 
            return null; 
        } 
        public void saveUser(User user) { 
            // TODO Auto-generated method stub 
            users.add(user); 
        } 
    } 
     
    实体类如下: 
    public class User { 
        private int uid; 
        private String uname; 
        public int getUid() { 
            return uid; 
        } 
        public void setUid(int uid) { 
            this.uid = uid; 
        } 
        public String getUname() { 
            return uname; 
        } 
        public void setUname(String uname) { 
            this.uname = uname; 
        } 
    } 
     
    通过JSP+Servlet调用本地服务: 
     
    Servlet在web.xml中配置如下: 
    <servlet> 
            <servlet-name>cxf</servlet-name> 
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> 
            <load-on-startup>1</load-on-startup> 
        </servlet> 
        <servlet> 
            <servlet-name>UserServlet</servlet-name> 
        <servlet-class>cn.itcast.i.cxf.servlet.UserServlet</servlet-clas> 
        </servlet> 
     
    Servlet核心代码调用如下: 
    public void doPost(HttpServletRequest request, HttpServletResponse response) 
                throws ServletException, IOException { 
            User user = new User(); 
            user.setUid(Integer.parseInt(request.getParameter("uid"))); 
            user.setUname(request.getParameter("uname")); 
            userImpl.saveUser(user); 
        } 
        public void init() throws ServletException { 
            // Put your code here 
            WebApplicationContext springContext = WebApplicationContextUtils 
                    .getWebApplicationContext(this.getServletContext()); 
            userImpl = (IUser) springContext.getBean("userImpl"); 
        }  
     
    WEB页面调用代码如下: 
    <form action="/day01/servlet/UserServlet" method="post"> 
                用户编号:<input type="text" name="uid" /><br/> 
                用户名:<input type="text" name="uname" /><br/> 
                <input type="submit" value="提交" /> 
     </form> 
     
     
    通过Java远程调用访问CXF+Spring服务如下: 
        public static void main(String[] args) { 
            IUserService userService=new IUserService(); 
            User user=new User(); 
            user.setUid(1); 
            user.setUname("admin"); 
            userService.getIUserPort().saveUser(user); 
            User temp=userService.getIUserPort().getUser(1); 
            System.out.println(temp.getUid() + "|" + temp.getUname()); 
        } 
     
    通过ajax远程调用访问CXF+Spring服务如下: 
    <body> 
            <button οnclick="mobile()">cxf+Spring测试</button> 
        </body> 
        <script language="javascript"> 
            // 1:创建XMLHTTP对象 
            var xhr=null; 
            function mobile(){ 
                // 声明在访问的ws的地址 
                var url="http://localhost:8888/day01/ws/hello"; 
                // 书写要发送的XML文件,即 SOAP 
                var soap='<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body>' + 
                '<ns2:getUser xmlns:ns2="http://ws.spring.cxf.i.itcast.cn/"><arg0>1</arg0></ns2:getUser></soap:Body></soap:Envelope>'; 
                // 3:打开连接 
                xhr.open("POST",url,true); 
                xhr.setRequestHeader("Content-Type","text/xml; charset=utf-8"); 
                xhr.setRequestHeader("Accept","*/*");  
                xhr.onreadystatechange=callBack;  
                xhr.send(soap);  
            }  
      
            function callBack(){  
                if(xhr.readyState==4){  
                    var a=xhr.responseXML;  
                    alert(xhr.responseXML.getElementsByTagName("uid")[0].text);  
                    alert(xhr.responseXML.getElementsByTagName("uname")[0].text);  
                }  
            }  
      
            function init(){  
                xhr=new ActiveXObject("MSXML2.XMLHTTP.3.0");  
            }  
      
            init();  
        </script>  
    天气预报的案例:(每天限制访问数量)  
    访问:http://www.webxml.com.cn 获取天气预报服务 通过wsImport转化为Java代码调用如下:可以获取省份、城市的最后获取具体城市的天气预报  
    注意事项:我们可以通过AJAX的方式调用Java代码, 然后发送请求,这样回来只有数据没有XML文件,不存在解析问题, 看是很好,但是Java代码本质上也发送的是SOAP请求。   
    这样设计效率很低, 除非回来的数据需要处理,例如中文翻译的时候下载的声音。其它的情况还是建议直接发送XML数据的  
      
    采用纯Java方式了解天气预报调用流程  
    public static void main(String[] args) {  
            // 通过查看WSDL文档获取服务  
            WeatherWS ws=new WeatherWS();  
            // 获取SOAP1.1服务 JDK默认不支持1.2  
            WeatherWSSoap weacherSoap=ws.getWeatherWSSoap();  
            // 获得中国省份、直辖市、地区和与之对应的ID  
            for(String temp:weacherSoap.getRegionProvince().getString()){  
                System.out.println("支持的省份ID" + temp);  
            }  
            // 输入参数:theRegionCode =市、ID和名称        
    for(String temp:weacherSoap.getSupportCityString("湖南").getString()){  
                System.out.println("支持的城市ID:" + temp);  
            }  
            System.out.println("------获取获得某个城市天气预报数据--------");  
            for(String temp:weacherSoap.getWeather("1834",null).getString()){  
                System.out.println(temp);  
            }  
        }  


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Web Service是一种基于Web的应用程序接口(API),它允许不同的应用程序之间进行交互,使用标准的Web技术进行通信。Web Service通常使用XML格式的数据进行交换,这使得它们可以跨越不同的操作系统和编程语言来实现应用程序集成和数据交换。 Web Service的使用可以分为两个方面,一方面是Web Service的提供者,另一方面是Web Service的使用者。 Web Service的提供者需要遵守一定的规范来创建Web Service,并将它们发布到互联网上,以便其他应用程序可以访问它们。 Web Service的使用者可以通过调用Web Service的方法来访问远程应用程序的功能。使用Web Service时,需要知道Web Service的URL和方法名称,并提供必要的参数。 以下是使用Web Service的简单示例: 1. 创建一个Web Service客户端程序 在编程语言中创建一个Web Service客户端程序,例如Java、C#、Python等。 2. 指定Web Service的URL Web Service的URL是Web Service提供者发布Web Service时指定的地址。您需要知道Web Service的URL才能访问它。 3. 创建Web Service请求 创建一个Web Service请求,包括要调用的方法名称和必要的参数。 4. 发送Web Service请求 将Web Service请求发送到Web Service提供者的URL。 5. 处理Web Service响应 接收Web Service提供者返回的响应,并解析响应以获取所需的数据或执行所需的操作。 这些步骤可以在不同的编程语言和开发环境中实现,具体实现方式可能会有所不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值