WebServices和Xfire学习总结
用了3天多的时间学习了WebServices,第一次接触它,便对它一见钟情。但苦于网上的资料都是东拼西凑,XFire官方文档写的又不好,例子居然都少了services.xml文件。而且我看到有很多像我一样的初学者在开始学习WS时不知道如何下手,所以写了这篇文章,希望能对向学习WS的朋友有所帮助。在看不懂程序时一定要去查API。
WebServices概述
WebService是一种分布式环境.可以通过接口和代理远程访问对象,并可在这些对象上进行操作,并且它的设计理念相当好--“双跨”--跨平台、跨语言。
学习WebService,就不能不知道两个概念SOAP、WSDL。
SOAP概述:
SOAP意思是简单对象访问协议(Simple Object Access Protocol)。的确如它的名字一样,SOAP是很简单的。它是一个基于XML的协议,允许程序组件和应用程序彼此使用一种标准的Internet协议--HTTP来通讯。SOAP是一种独立的平台,它不依赖程序语言,它是简单的,弹性的,很容易扩展的。目前,应用程序能够彼此使用一种基于DCOM和CORBA技术的远程过程调用(RPC)来进行相互通讯,但HTTP不被设计为这个目的。RPC在Internet上应用是非常困难的,它们会出现许多兼容性和安全性的问题,因为防火墙和代理服务器通常都会阻断(block)这些类型的流量。应用程序之间最好的通讯方式是通过HTTP协议,因为HTTP是支持所有Internet浏览器和服务器的。基于这个目的,SOAP协议被创建出来。
那么,它们是如何运作的呢?比如,一个应用程序(A)需要和另一个应用程序(B)在SOAP的帮助下进行彼此通讯。它们将使用下面的框架图来完成这个过程: 这个SOAP信封(SOAP envelope)是一个包含以下内容的XML文档:
具体的,一个SOAP信封在一个HTTP数据包之上,我理解它最终会被HTTP协议打包,它是这个样子:
被发送到SOAP Service的SOAP Envelope
<?xml version=1.0 encoding=UTF-8?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/ soap/envelope/" xmlns:xsi="http://www.w3.org/2001/ XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <SOAP-ENV:Body> <ns1:sayHi xmlns:ns1="urn:HelloWorld_SOAPService" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/ soap/encoding/"> <ourName xsi:type="xsd:string">Superman</ourName> </ns1:sayHi> </SOAP-ENV:Body> </SOAP-ENV:Envelope>: 从SOAP Service接收的SOAP Envelope <?xml version=1.0 encoding=UTF-8?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/ soap/envelope/" xmlns:xsi="http://www.w3.org/2001/ XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <SOAP-ENV:Body> <ns1:sayHiResponse xmlns:ns1="urn:HelloWorld_SOAPService" SOAP-ENV:encodingStyle="http://schemas.xmlsoap. org/soap/encoding/"> <return xsi:type="xsd:string">Hello my friend,Superman! Glad to see you!</return> </ns1:sayHiResponse> </SOAP-ENV:Body> </SOAP-ENV:Envelope> 要理解SOAP Envelope中的所有标签的含义,需要花一点时间阅读 http://www.w3.org/2001/06/soap-envelope 命名空间规范。 其实在后面的学习中,因为Java提供了各种XML的API(JDOM),再搭配上XFire这个SOAP架构,所以一般只需要知道SOAP的工作原理就够了,基本上不会涉及到自己去写一个SOAP信封。
WSDL概述
你会怎样向别人介绍你的Web service有什么功能,以及每个函数调用时的参数呢?你可能会自己写一套文档,你甚至可能会口头上告诉需要使用你的Web service的人。这些非正式的方法至少都有一个严重的问题:当程序员坐到电脑前,想要使用你的Web service的时候,他们的工具(如Visual Studio)无法给他们提供任何帮助,因为这些工具根本就不了解你的Web service。解决方法是:用机器能阅读的方式提供一个正式的描述文档。Web service描述语言(WSDL)就是这样一个基于XML的语言,用于描述Web service及其函数、参数和返回值。因为是基于XML的,所以WSDL既是机器可阅读的,又是人可阅读的,这将是一个很大的好处。一些最新的开发工具既能根据你的Web service生成WSDL文档,又能导入WSDL文档,生成调用相应Web service的代码。 XFire XFire是一个开源的SOAP框架,用它可以快速开发WS。 它在Eclipse下的配置也是相当简单,甚至你可能都不需要做什么配置。 在Eclipse下新建一个Web Service Project工程,这个工程与传统的Web Project工程不同的不过是通过向导添加XFire以及services.xml。
然后我们next,这时的界面是传统Web Project工程的界面,需要输入工程名什么的,填好,next
这个界面设置你的XFire参数,这些参数会被写进Web.xml中,next
我的MyEclipse集成了XFire1.1,当然还可以通过User Libraries加载你的XFire包。
这时就配置好了,这里要说明的是,services.xml文件会自动加载到类路径下,具体路径是/Apache Software Foundation/Tomcat 5.5/webapps/SimpleWSExam/WEB-INF/classes/META-INF/xfire
web.xml:
首先先来写WS服务器端,通过XFire你无须任何其它的操作,只需要为已经实现的POJOs设计相应的窄接口就OK了,当然我们也可以反过来,先设计接口,再实现它,为了让例子最简单,例子采用后者
服务器端某接口:
HelloWorldService.java
/**
?* HelloWorldService 中声明需要发布成 Web 服务的所有 Java 方法 HelloWorldService 作为Web服务接口 ?*/ public interface HelloWorldService { ?/** ? * sayHello 方法声明了 Web 服务对外暴露的接口 ? * ? * @return 返回给客户端的字符串 ? */ ?public String sayHello(String name); ?}
接口实现:
HelloWorldServiceImpl.java
/**
?* HelloWorldServiceImpl 中为 Web 服务接口中声明的所有 Java 方法提供具体实现 HelloWorldServiceImpl ?* 作为 Web 服务的实现类 ?*/ public class HelloWorldServiceImpl implements HelloWorldService {
?/*
? * sayHello 方法为 HelloWorldService 服务接口定义的 sayHello 方法提供具体实现 ? * ? * @see org.vivianj.XFire.pojo.HelloWorldService#sayHelloToXFire() ? */ ?public String sayHello(String name) { ??return "Hello World!"+name; ?}
}
然后我们配置它,XFire让我们只需要在services.xml中配置即可
services.xml
关于services.xml各元素信息,请参阅官方文档http://xfire.codehaus.org/services.xml+Reference。 现在我们的Web服务已经配置好了,我们可以在地址栏中输入http://localhost:8080/SimpleWSExam/services,可以看到 单击wsdl连接,可以看到关于这个WS的WSDL。在XFire下配置WS就是这么简单! 现在我们来写客户端, HelloWorldClient.java public class HelloWorldClient { 一个众人皆知的道理是,WS是只读的,也就是说,WS不能有setxxx()。 如果取的值是List或者是Map,则需要在与源文件相同的目录下创建名为xxxx.aegis.xml的文件,其中xxxx是服务的类名,比如我们在接口中添加public List getList();并实现之,如果客户端想调用这个函数,需要写HelloWorldService.aegis.xml: ?Aegis 是 XFire 的缺省的绑定方式,可以将 XML 映射成 POJO。,在官方例子中,aegis文件中对类的描述基本是全部的,不像我这里只是写上必须的描述。关于aegis,请见其它更详细的资料。当然XFire不仅仅可以绑定aegis,它还支持JAXB2、XMLBeans、Castor、Jibx。 身份验证 XFire提供了四种身份验证的方式,分别是http验证,JSR181,Handler验证,WS-Security。 这里我们只谈Handler验证。 SOAP的原理告诉我们一个WS交互的流程是客户端发送请求->服务器接收请求->服务器发送数据->客户端接收数据,handler可以让我们在这四个操作中之前进行编码,所以,我们的验证进行在客户端发送请求和服务器接收请求的时候,因此,我们要写两个handler,然后把它们装配上就可以了。 服务器接收请求: AuthentificationHandler.java: package org.vivianj.xfire.handlers; import org.codehaus.xfire.MessageContext; public class AuthentificationHandler extends AbstractHandler { ?public void invoke(MessageContext cfx) throws Exception { ????? if (token == null) ????? { ????????? throw new org.codehaus.xfire.fault.XFireFault("请求必须包含身份验证信息", ??????? ??? org.codehaus.xfire.fault.XFireFault.SENDER); ????? } ????? String username = token.getChild("Username").getValue(); ????? String password = token.getChild("Password").getValue(); ????? try ????? { ????????? //进行身份验证 ???????? if(username.equals("jackal") && password.equals("talent")) ???????? // cfx.setProperty(User_KEY,user); ????? } ????? catch (Exception e) ????? { ????????? throw new?? org.codehaus.xfire.fault.XFireFault("非法的用户名和密码",?? org.codehaus.xfire.fault.XFireFault.SENDER); ????? } ? } } 客户端发送请求: ClientAuthenticationHandler.java package org.vivianj.xfire.handlers; import org.codehaus.xfire.MessageContext;
??????? private String username = null; ??????? private String password = null; ??????? public ClientAuthenticationHandler() { ??????? } ??????? public ClientAuthenticationHandler(String username,String password) { ??????????? this.username = username; ??????????? this.password = password; ??????? public void setUsername(String username) { ??????????? this.username = username; ??????? } ??????? public void setPassword(String password) { ??????????? this.password = password; ??????? } ??????? public void invoke(MessageContext context) throws Exception { ???????????? Element el = new Element("header"); ??????????? Element auth = new Element("AuthenticationToken"); ??????????? Element username_el = new Element("Username"); ??????????? username_el.addContent(username); ??????????? Element password_el = new Element("Password"); ??????????? password_el.addContent(password); ??????????? auth.addContent(username_el); ??????????? auth.addContent(password_el); ??????????? el.addContent(auth);? ??? } 可以看到,客户端在发送数据前做的事情是:往SOAP信封里加入了一个头信息,并在这个头信息中包含了AuthenticationToken属性,它又包含了Username以及Password两个属性,而用户名和密码的值通过形参穿过来。这里面用到了JDOM,JDOM是一个针对JAVA的轻量级的文档对象模型。 服务器接受到SOAP信封后先找到这个验证信息,然后验证它。 装配: 客户端装配: 只需要在客户端程序HelloWorldClient.java中加入 ?Client client = Client.getInstance(hello); ?client.addOutHandler(new ClientAuthenticationHandler("jackal","talent")); 服务器端装配: 需要在services.xml中开始写入,重写后的文件 services.xml: ? 如果你有Spring+Struts的经验,你会发现XFire+Spring和它很像。 web.xml: 这个web.xml有这几点要注意: 1.XFire的servlet-class已经不是 org.codehaus.xfire.transport.http.XFireConfigurableServlet,而是org.springframework.web.servlet.DispatcherServlet。 2.在contextConfigLocation中我们又加载了XFire自身的xfire.xml。 xfire-servlet.xml: 如果你有struts+spring的经验,你一定对action-servlet.xml不陌生,没错,这里我们需要xfire-servlet.xml,"xfire"映射web.xml文件中<servlet-name>,而且他的路径应该是根目录下 META-INF/xfire/service.xml文件可以省略了,因为web服务的定义在xfire-servlet.xml中可以找到。 这个文件的上半部分将HelloWorldService这个URL和hello这个bean联系在一起。下半部分定义了Web服务的bean和服务接口。其中HelloWorldBean是我们在applicationContext.xml中配置的那个Bean。 最后来看看applicationContext.xml 如果有兴趣的朋友,还可以看看SpringSide2.0中关于这部分的源码。 最后说一句,与spring集成后,就不能通过http://localhost:8080/services或者http://localhost:8080/services/HelloService查看是否通,而只能通过http://localhost:8080/services/HelloService?wsdl. ?
?
?
?
?
? (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |