使用 Apache CXF 实现 Web Service 详解

1.Web service的概念

什么是WebService呢?从表面上看,Web Service就是一个应用程序,它向外界暴露出一个可以经过Web进行调用的API。这就是说,你可以用编程的方法经过Web调用来实现某个功能的应用程序。从深层次上看,Web Service是一种新的Web应用程序分支,它们是自包含、自描述、模块化的应用,能够在网络(一般为Web)中被描述、发布、查找以及经过Web来调用。一旦部署之后,其余Web Service应用程序能够发现并调用它部署的服务。Web Service是基于网络的、分布式的模块化组件,它执行特定的任务,遵照具体的技术规范,这些规范使得Web Service能与其余兼容的组件进行互操做。它可使用标准的互联网协议,像超文本传输协议HTTP和XML,将功能体如今互联网和企业内部网上。Web Service平台是一套标准,它定义了应用程序如何在Web上实现互操做性。你能够用你喜欢的任何语言,在你喜欢的任何平台上写Web Service。Web Service是构建互联网分布式系统的基本部件。"网络服务"(WebService)的本质,就是经过网络调用其余网站的资源。 php

举例来讲,写一个“四川大地震图片墙”,它能动态显示关于四川地震的最新图片。可是,全部的图片都不是储存在本身的服务器上,而是来自flickr.com。只须要发出一个动态请求,要求flickr.com向本身提供图片。这种状况下,flickr.com提供的就是一种Web service。若是把图片都存放在本地服务器,不调用flickr.com,那么我就是在使用“本地服务”。 html

因此,Webservice让你的网站可使用其余网站的资源,好比在网页上显示天气、地图、twitter上的最新动态等等。 java

2.Web Service架构和云

若是一个软件的主要部分采用了“网络服务”,即它把存储或计算环节“外包”给其余网站了,那么咱们就说这个软件属于Web Service架构。 程序员

Web Service架构的基本思想,就是尽可能把非核心功能交给其余人去作,本身全力开发核心功能。好比,若是你要开发一个相册软件,彻底可使用Flickr的网络服务,把相片都储存到它上面,你只要全力作好相册自己就能够了。整体上看,凡是不属于你核心竞争力的功能,都应该把它“外包”出去。 web

最近很红的“云计算”(cloudcomputing)或者“云服务”(cloud services),实际上就是Web Service的同义词,不过更形象一些罢了。它们不说你把事情交给其余计算机去作,而说你把事情交给“云”去作。 spring

3.Web Service的优点

Web Servcie最主要的优势是,使用不一样程序和在不一样系统平台上开发出来的程序,均可以相互通讯。SOAP做为Web Service的基本通讯协议,简单,投入和使用的代价也很小。Web Service使用标准的互联网协议-XML、HTTP和TCP/IP。 apache

Web Service有如下的优越性: 编程

1)平台无关。无论你使用什么平台,均可以使用Web service。 api

2)编程语言无关。只要遵照相关协议,就可使用任意编程语言,向其余网站要求Web service。这大大增长了web service的适用性,下降了对程序员的要求。 数组

3)对于Webservice提供者来讲,部署、升级和维护Web service都很是单纯,不须要考虑客户端兼容问题,并且一次性就能完成。

4)对于Webservice使用者来讲,能够轻易实现多种数据、多种服务的聚合(mashup),所以可以作出一些之前根本没法想像的事情。

4.Web Service 三个基本技术 

Web Service经过标准通讯协议,在互联网上发布有用的程序模块(以服务的方式),目前大部分是用SOAP来做通讯协议。

Web Service提供一份详细的接口说明书,来帮助用户构建应用程序,这个接口说明书叫做WSDL(Web Service Description Language)。

一般已发布的WebService要注册到管理服务器,这样便于使用者查询和使用。这个是经过UDDI(Universal Discovery Description and Integration)来完成的。

4.1 SOAP 

SOAP是Web Service的基本通讯协议,是一种规范,用来定义SOAP消息的XML格式(XMLFormat)。包含在一对 SOAP 元素(SOAPElements)中的、结构正确的 XML 段就是 SOAP 消息。

SOAP 规范还介绍了如何将程序数据表示为 XML,以及如何使用 SOAP 进行远程过程调用 (RPC)。

最后SOAP规范还定义了HTTP消息是怎样传输SOAP消息的。这并不表明SOAP只能用HTTP来做为传输协议,MSMQ、SMTP、TCP/IP均可以作SOAP的传输协议。

4.2 WSDL 

Web Services Description Language的缩写,是一个用来描述Web服务和说明如何与Web服务通讯的XML语言。WSDL是Web Service的描述语言,用于描述Web Service的服务,接口绑定等,为用户提供详细的接口说明书。

举个例子,你要使用供应商的WebService构建应用程序。你能够向供应商索取使用Web Service的范例,而后按照范例来构建应用程序。这样可能出现意料不到的错误,好比说,你在程序中使用的客户代码的数据类型是integer,而供应商使用的数据类型是string。WSDL详细定义客户端消息的格式,须要什么样的参数,这样能够避免没必要要的错误。

要查看 WSDL 的值,能够假设您要调用由您的一位业务伙伴提供的 SOAP 方法。您能够要求对方提供一些 SOAP 消息示例,而后编写您的应用程序以生成并使用与示例相似的消息。WSDL 经过明确的表示法指定请求消息必须包含的内容以及响应消息的样式。

WSDL 文件用于说明消息格式的表示法以 XML 架构标准为基础,这意味着它与编程语言无关,并且以标准为基础,所以适用于说明可从不一样平台、以不一样编程语言访问的 XML Web Service 接口。除说明消息内容外,WSDL 还定义了服务的位置,以及使用什么通讯协议与服务进行通讯。WSDL 文件定义了编写使用 XML Web Service 的程序所需的所有内容。

4.3 UDDI

UniversalDescription Discovery and Integration即统一描述、发现和集成协议。UDDI实现了一组可公开访问的接口,经过这些接口,网络服务能够向服务信息库注册其服务信息、服务需求者能够找到分散在世界各地的网络服务。

UDDI 目录条目是介绍所提供的业务和服务的 XML 文件。能够把它比喻成电话本,电话本里记录的是电话信息,而UDDI记录的是Web Service信息。你能够不把Web Service注册到UDDI。但若是要让全球的人知道你的Web Service,最好仍是注册到UDDI。 

UDDI 目录还包含若干种方法,可用于搜索构建您的应用程序所需的服务。例如,您能够搜索特定地理位置的服务提供商或者搜索特定的业务类型。以后,UDDI 目录将提供信息、联系方式、连接和技术数据,以便您肯定能知足须要的服务。

UDDI 容许您查找提供所需的Web 服务的公司。若是您已经知道要与谁进行业务合做,但尚不了解它还能提供哪些服务,这时该如何处理呢?WS-Inspection规范容许您浏览特定服务器上提供的 XML Web Service 的集合,从中查找所需的服务。

UDDI 目录说明文件也是一个XML文档,它包括下面三个部分:

“白页(WhitePaper)”介绍提供Web Service的公司信息,好比名称、地址、联系方式等等;

“黄页(YellowPaper)” 说明UDDI目录的分类,包括基于标准分类法的行业类别,好比说金融、服务和印刷等等;

“绿页(greenPaper)” 详细介绍了访问服务的接口,以便用户可以编写应用程序以使用 Web 服务。

5.Web Service的开源实现

Web Service更可能是一种标准,而不是一种具体的技术。不一样的平台,不一样的语言大都提供Web Service的开发实现,在JAVA领域,Web Service的框架不少,例如:Axis1&2,Xfire,CXF,java6自带Web Service引擎。

1)从JavaSE6.0开始,Java引入了对Web Service的原生支持。咱们只须要简单的使用Java的Annotation标签便可将标准的Java方法发布成Web Service。但不是全部的Java类均可以发布成Web Service。Java类若要成为一个实现了Web Service的bean,它须要遵循下边这些原则:

Ø  这个类必须是public类

Ø  这些类不能是final的或者abstract

Ø  这个类必须有一个公共的默认构造函数

Ø  这个类绝对不能有finalize()方法

2)Axis2(Apache eXtensible Interaction System)是Apache下的一个重量级WebService框架,准确说它是一个Web Services / SOAP /WSDL 的引擎,是WebService框架的集大成者,它能不但能制做和发布WebService,并且能够生成Java和其余语言版WebService客户端和服务端代码。这是它的优点所在。可是,这也不可避免的致使了Axis2的复杂性,使用过的开发者都知道,它所依赖的包数量和大小都是很惊人的,打包部署发布都比较麻烦,不能很好的与现有应用整合为一体。可是若是你要开发Java以外别的语言客户端,Axis2提供的丰富工具将是你不二的选择。

3)XFire是一个高性能的WebService框架,在Java6以前,它的知名度甚至超过了Apache的Axis2,XFire的优势是开发方便,与现有的Web整合很好,能够融为一体,而且开发也很方便。可是对Java以外的语言,没有提供相关的代码工具。XFire后来被Apache收购了,缘由是它太优秀了,收购后,随着Java6 JWS的兴起,开源的WebService引擎已经再也不被看好,渐渐的都败落了。

4)Apache CXF是Apache旗下一个重磅的SOA简易框架,它实现了ESB(企业服务总线)。CXF 继承了 Celtix 和 XFire 两大开源项目的精华,不只提供了对JAX-WS 全面的支持,而且提供了多种Binding 、DataBinding、Transport 以及各类 Format 的支持,而且能够根据实际项目的须要,采用代码优先(Code First)或者 WSDL 优先(WSDL First)来轻松地实现 Web Services 的发布和使用。并且能够自然的和Spring进行无缝集成。Apache CXF已是一个正式的Apache顶级项目。

6.基于 Apache CXF的Web Service开发

Web Service 支持不一样语言开发,而不关心服务端或者客户端采用何种语言。这里讲解利用cxf进行Web Service开发。

这里先讲解Java中的web服务规范:

Java中共有三种Web Service规范,分别是JAXM&SAAJ、JAX-WS(JAX-RPC)、JAX-RS。

JAX-WS(Java API ForXML-WebService),JDK1.6自带的版本为JAX-WS2.1,其底层支持为JAXB。早期的基于SOAP的JAVA的Web服务规范JAX-RPC(Java API For XML-RemoteProcedure Call)目前已经被JAX-WS规范取代,JAX-WS是JAX-RPC的演进版本,但JAX-WS并不彻底向后兼容JAX-RPC,两者最大的区别就是RPC/encoded样式的WSDL,JAX-WS已经不提供这种支持。

这里的JAX-WS规范咱们采用Apache CXF做为实现。

简单的说下注意事项:当你使用的是JDK1.5的时候,就必需要有jaxws-api-2.0.jar这个包的支持,若是使用的是JDK1.6就不用使用这个包了。由于1.6里已经有相关实现。

6.1 简单的CXF应用

6.1.1 服务端开发

1)首先下载CXF开发要用到的相关包,目前最新版本是apache-cxf-2.3.1。下载地址:

http://www.apache.org/dyn/closer.cgi?path=%2Fcxf%2F2.3.1%2Fapache-cxf-2.3.1.zip

下载解压后在apache-cxf-2.3.1目录下lib目录下,有全部要用到的jar包。

2)建一个web项目,将apache-cxf-2.1.3\lib目录下全部包添加到项目中。

注:这些包里面有jetty,cxf,spring的相关包,能够根据须要进行添加,若是不和spring进行整合,则不须要spring相关包。Jetty是一个相似于tomcat的web服务器,内置在cxf中,用于发布web服务。若是用jetty发布服务,则须要添加它的包,若是用tomcat则不须要。

3)写好一个接口和实现类(具体见demo中IHelloService和HelloServiceImpl),本demo中数据绑定方式采用cxf默认的jaxb方式,也能够采用aegis,其优势是不用jaxb中的注解方式。(基于SOAP的Web服务可用单个Java类的实现,可是最好是用“接口+实现”的方式来实现最佳。Web服务的接口称为SEI,即ServiceEndpoint Interface;而Web服务的实现称为SIB,即Service Implementation Bean。 SIB能够是一个POJO,也能够是无状态的会话EJB。)

4)发布服务

一种是经过CXF内置的Jetty应用服务器发布(见方法一,二),一种是经过tomcat发布(见方法三)。

Ø  方法一:使用SunJAX-WS 2中Endpoint.publish进行发布。(不须要其余配置与包

Endpoint endpoint =

Endpoint.publish("http://localhost:8080/WSCXF/helloService",

new HelloServiceImpl());//这里是实现类

System.out.println("WS发布成功!");

Ø  方法二:用CXF的JaxWsServerFactoryBean类进行发布。(须要CXF相关包)

HelloServiceImpl impl = new HelloServiceImpl();

JaxWsServerFactoryBean factoryBean = newJaxWsServerFactoryBean();

factoryBean.setAddress("http://localhost:8080/WSCXF/helloService");

factoryBean.setServiceClass(IHelloService.class);//接口类

factoryBean.setServiceBean(impl);

factoryBean.create();

System.out.println("WS发布成功!");

方法一或者方法二都是发布到Jetty下。在main方法中运行方法一或者方法二代码,web服务就发布成功了。

Ø  方法三:利用cxf和spring整合在tomcat下进行发布。具体方法在后面的spring整合cxf时讲到。

Ø  方法四:重写loadBus方法。

书写一个类覆盖org.apache.cxf.transport.servlet.CXFNonSpringServlet的loadBus方法指定BUS以及发布你的web服务。

具体可查阅资料:

http://blog.csdn.net/zq9017197/archive/2010/12/26/6099684.aspx

查看web服务是否发布成功:

访问http://localhost:8080/WSCXF/helloService?wsdl 查看wsdl文件

6.1.2 客户端调用

客户端调用只须要服务端提供一个webservice的发布地址便可。不关心服务端发布方式等。

1)客户端代码生成

Ø  方法一:使用MyEclipse工具生成。

new-other-myeclipse-web service-web service client根据设置向导能够生成客户端,但最好使用CXF的wsdl2java来完成,由于CXF2.2+版本开始支持JAX-WS2.1规范,而MyEclipse自带的是xfire的一个插件,生成的客户端代码可能不是最新规范的。

Ø  方法二:经过wsdl2java的命令生成客户端代码。

先进入dos窗口,进入apache-cxf-2.3.1\bin所在目录,输入命令:

wsdl2java -pcom.jaxb.client -d e:/ http://127.0.0.1:8080/WSCXF/helloService?wsdl

命令格式为:wsdl2java –p 包名 –d 生成代码存放目录wsdl的url

其中的wsdl的url为要调用的webservice的服务地址

附加:wsdl2java用法:

wsdl2java -p com-d src -all  aa.wsdl

-p 指定其wsdl的命名空间,也就是要生成代码的包名;

-d 指定要产生代码所在目录;

-client 生成客户端测试web service的代码;

-server 生成服务器启动web service的代码;

-impl 生成web service的实现代码;

-ant  生成build.xml文件;

-all 生成全部开始端点代码:types,serviceproxy, service interface, server mainline, client mainline, implementation object,and an Ant build.xml file。

详细用法见:http://cwiki.apache.org/CXF20DOC/wsdl-to-java.html

2)新建一个web客户端项目,将生成的客户端代码拷贝到src下。

3)调用web服务

Ø  方法一:使用标准的JAX-WS的API完成客户端调用(不须要导入任何CXF包)

//注意。此处http://service.jaxb.com/来源于wsdl文件中targetNamespace

QName qName =

 newQName("http://service.jaxb.com/","HelloServiceImplService");

HelloServiceImplService helloServiceImplService =

new HelloServiceImplService(

new URL("http://localhost:8080/WSCXF/helloService?wsdl"),qName);

IHelloService helloService

=(IHelloService)helloServiceImplService.getPort(IHelloService.class);

Ø  方法二:使用CXF中JaxWsProxyFactoryBean客户端代理工厂调用web服务(须要导入CXF相关包)

JaxWsProxyFactoryBean soapFactoryBean = newJaxWsProxyFactoryBean();

soapFactoryBean.setAddress("http://localhost:8080/WSCXF/helloService");

soapFactoryBean.setServiceClass(IHelloService.class);

Object o = soapFactoryBean.create();

IHelloService helloService = (IHelloService)o;

Ø  方法三:

String endPointAddress = "http:// localhost:8080/WSCXF/helloService";

Service service = Service.create(

newQName("http://service.jaxb.com/","IHelloService"));

service.addPort(

new QName("http://service.jaxb.com/","IHelloServicePort");,

SOAPBinding.SOAP11HTTP_BINDING, endPointAddress);

IHelloService helloService =service.getPort(IHelloService.class);

Ø  方法四:(须要导入CXF相关包)

JaxWsDynamicClientFactory dcf =JaxWsDynamicClientFactory.newInstance();

org.apache.cxf.endpoint.Client client =

dcf.createClient("http://127.0.0.1:8080/WSCXF/helloService?wsdl");

//sayHello 为接口中定义的方法名称  张三为传递的参数   返回一个Object数组

Object[] objects=client.invoke("sayHello","张三");

//输出调用结果

System.out.println(objects[0].toString());

7.CXF和Spring整合

CXF能够很好的与Spring整合,而后发布在tomcat下,只须要简单的Spring配置便可。

7.1 服务端开发

1)新建web项目,并添加相关包。(包括spring和cxf相关包)

2)写好一个接口和实现类。(见demo)

3)新建beans.xml文件。

<?xml version="1.0"encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:jaxws="http://cxf.apache.org/jaxws"

xsi:schemaLocation="

http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd

http://cxf.apache.org/jaxwshttp://cxf.apache.org/schemas/jaxws.xsd">

 

<!--spring发布web服务配置 -->

<importresource="classpath:META-INF/cxf/cxf.xml" />

<importresource="classpath:META-INF/cxf/cxf-extension-soap.xml" />

<importresource="classpath:META-INF/cxf/cxf-servlet.xml" />

 

<bean id="helloService"class="com.jaxb.service.HelloServiceImpl" />

<!--

<bean id="helloService"class="com.aegis.service.HelloServiceImpl" />

-->

<!--endpoint 方式发布web服务和 server方式同样 -->

<!--

<jaxws:endpointid="helloServiceWs" address="/helloService"

     implementor="#helloService"/>

-->

<!--

     另外一种写法,建议不要用这种方法 ,若是实现类有的属性要经过spring依赖注入的话,

     这种方法只是简单的new个实现类,他的属性没有经过spring依赖注入给注入值

-->

<!--

<jaxws:endpointid="helloServiceWs" address="/helloService"

     implementor="com.jaxb.service.HelloServiceImpl"/>

-->

 

<!—下面这个是wss4j的配置,后面会讲到相关知识,须要配置在spring配置文件中 -->

<!--wss4j 服务端配置 -->

<bean id="wss4jInInterceptor"

class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">

     <constructor-arg>

         <map>

             <entrykey="action" value="UsernameToken" />

             <!--<entry key="passwordType" value="PasswordText" />-->

             <!--密码使用MD5密文发送 -->

             <entrykey="passwordDigest" value="PasswordText" />

             <entrykey="passwordCallbackClass"

                 value="com.security.service.ServerPasswordCallbackHandler"/>

         </map>

     </constructor-arg>

</bean>

 

<jaxws:serverid="helloServiceWs" address="/helloService">

     <jaxws:serviceBean>

         <refbean="helloService" />

     </jaxws:serviceBean><!--使用这种方法发布web服务 -->

     <jaxws:inInterceptors>

         <refbean="wss4jInInterceptor" />

     </jaxws:inInterceptors><!—wss4j配置 -->

     <!--<jaxws:serviceFactory>

         <refbean="jaxWsServiceFactoryBean" />

     </jaxws:serviceFactory>  --><!—数据绑定方式配置 -->

</jaxws:server>

<!-- 经过Spring建立数据绑定的类-->

   <!--<bean id="aegisBean"class="org.apache.cxf.aegis.databinding.AegisDatabinding" />

   <bean id="jaxWsServiceFactoryBean"

class="org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean">

       <property name="wrapped" value="true" />

       <property name="dataBinding" ref="aegisBean"/>

   </bean> -->

</beans>

4)配置web.xml

<context-param>

     <param-name>contextConfigLocation</param-name>

     <param-value>/WEB-INF/beans.xml</param-value>

</context-param>

<listener>

<listener-class>

org.springframework.web.context.ContextLoaderListener

</listener-class>

</listener>

 

<!—在tomcat中发布须要配置servlet -->

<servlet>

     <servlet-name>CXFServlet</servlet-name>

     <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>

     <load-on-startup>2</load-on-startup>

</servlet>

<servlet-mapping>

     <servlet-name>CXFServlet</servlet-name>

     <url-pattern>/ws/*</url-pattern>

</servlet-mapping>

5)发布web项目

由于在web.xml里面配置了servlet,则能够将项目发布到tomcat下,启动tomcat便可。

6)访问wsdl

http://localhost:8080/WSCXF/ws/helloService?wsdl

7.2 客户端调用

1)新建一个web客户端项目,用wsdl2java生成客户端代码。将生成的客户端代码拷贝到src下。添加spring相关包。

2)配置spring配置文件。

beans.xml存放在src目录下,具体配置以下:

<?xml version="1.0"encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:jaxws="http://cxf.apache.org/jaxws"

xsi:schemaLocation="

http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd

http://cxf.apache.org/jaxwshttp://cxf.apache.org/schemas/jaxws.xsd">

 

<!-- wss4j 配置在客户端,后面有讲到相关知识 -->

<!--wss4j 客户端配置 -->

<beanid="wss4jOutInterceptor"

class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">

     <constructor-arg>

         <map>

             <entrykey="action" value="UsernameToken" />

             <entrykey="user" value="Fetion" />

             <!--<entry key="passwordType" value="PasswordText" />-->

             <!--密码使用MD5密文发送 -->

             <entrykey="passwordDigest" value="PasswordText" />

             <entrykey="passwordCallbackClass"

                 value="com.security.client.ClientPasswordCallbackHandler"/>

         </map>

     </constructor-arg>

</bean>

<jaxws:client id="helloServeiceClient"

     address="http://localhost:8080/WSCXF/ws/helloService"serviceClass="com.jaxb.client.IHelloService">

     <jaxws:outInterceptors>

         <refbean="wss4jOutInterceptor" />

        </jaxws:outInterceptors><!--wss4j客户端配置-->

</jaxws:client>

</beans>

 

2)调用web服务

在main方法中运行:

ClassPathXmlApplicationContext app = newClassPathXmlApplicationContext("beans.xml");//此处beans.xml放在src下,也须要放在其余目录下,但须要注明清楚

//获取webservice服务的操做接口

IHelloServicehelloService = (IHelloService)app.getBean("helloServeiceClient");

相关文章
相关标签/搜索