WebService是什么呢?顾名思义,是Web系统提供的服务,其目的呢,往大了说:是系统实现多异构模块协同合做,服务实现SOA(Services oriented Architecture面向服务的体系结构),往小了说:咱们的系统模块可以定义一些功能开放给其余模块,也能够调用其余模块的一些功能。前端
WebService的实现基本原理就是选择一个各异构系统(结构、开发方式、运行环境等都不彻底相同)都能处理的交互信息的方式,让各系统模块可以“听懂”彼此的请求,也能够“告诉”彼此本身的请求,进而实现功能的调用,将追求的功能转为一组合适的通用交互API,开发者只须要根据开放出的API在本身的系统中发出合乎要求的请求就能够了。所以,XML这一相对来讲通用性很是强的消息传递方式被选为了完成这一任务的“传递语言”:服务发布方用XML来描述本身系统内开放功能的XML格式的API,其中方法的名称、入参、返回值等都在XML中有明确规定,服务调用方则将本身须要功能的方法、入参、返回值等信息经过XML告知服务发布方,如此一来,就实现了WebService的交互。java
若是每一个系统都要从头开始构建本身的WebService描述,处理服务消息发布、接收等任务的话系统的重用性将大大下降,所以,就像全部的系统级开发同样,在WebService这里也有不少现成的框架来帮助开发者快速部署本身的Web服务。如今的主流WebService服务框架主要有:web
一、JWS(Java WebService)是Java语言对WebService服务的一种实现,用来开发和发布服务。而从服务自己的角度来看JWS服务是没有语言界限的。可是Java语言为Java开发者提供便捷发布和调用WebService服务的一种途径。这是最原生的JAVA语言层面上支持的WebService,只不过功能较弱。spring
二、Axis2是Apache下的一个重量级WebService框架,准确说它是一个Web Services / SOAP / WSDL 的引擎,是WebService框架的集大成者,它能不但能制做和发布WebService,并且能够生成Java和其余语言版WebService客户端和服务端代码。这是它的优点所在。可是,这也不可避免的致使了Axis2的复杂性,使用过的开发者都知道,它所依赖的包数量和大小都是很惊人的,打包部署发布都比较麻烦,不能很好的与现有应用整合为一体。可是若是你要开发Java以外别的语言客户端,Axis2提供的丰富工具将是你不二的选择。apache
三、XFire是一个高性能的WebService框架,在Java6以前,它的知名度甚至超过了Apache的Axis2,XFire的优势是开发方便,与现有的Web整合很好,能够融为一体,而且开发也很方便。可是对Java以外的语言,没有提供相关的代码工具。XFire后来被Apache收购了,缘由是它太优秀了,收购后,随着Java6 JWS的兴起,开源的WebService引擎已经再也不被看好,渐渐的都败落了。浏览器
四、CXF是Apache旗下一个重磅的SOA简易框架,它实现了ESB(企业服务总线)。CXF来自于XFire项目,通过改造后造成的,就像目前的Struts2来自WebWork同样。能够看出XFire的命运会和WebWork的命运同样,最终会淡出人们的视线。CXF不可是一个优秀的Web Services / SOAP / WSDL 引擎,也是一个不错的ESB总线,为SOA的实施提供了一种选择方案,固然他不是最好的,它仅仅实现了SOA架构的一部分。服务器
在这里,咱们使用Spring这一依赖注入框架和CXF这一WebService框架来写一个Demo用以展现WebService构建Java下Web服务的基本过程。架构
咱们使用Eclipse在JDK6下开发,导入的CXF框架版本2.7,合适的版本对环境的搭建很是重要,不匹配的版本没法正常协同工做。app
新建Web工程HowIsTheWeather,导入CXF的lib目录下的所有包,其中包括Spring的包,这些包将在Eclipse的默认构建环境下加入项目编译的classpath下。框架
建立WebService服务接口com.rocking.weather.WeatherService:
@WebService public interface WeatherService { public String getWeather(String city); }
实现以上接口的实现类com.rocking.weather.WeatherServiceImpl:
@WebService(endpointInterface = "com.rocking.weather.WeatherService", serviceName = "WeatherService") public class WeatherServiceImpl implements WeatherService{ @Override public String getWeather(String city) { //fake weather data: String shanghai = "12C,almost sunny"; String beijing = "7C,most part fog"; String guangzhou = "20C,a little rain"; if(city.equals("shanghai")) return shanghai; else if(city.equals("beijing")) return beijing; else if (city.equals("guangzhou")) return guangzhou; else return "no data"; } }
在Web.xml中声明基本的CXF的服务servlet和Spring的IOC容器:
Spring IOC容器使用ContextLoaderListener监听器来注入须要的bean,bean的配置详情写在classpath下的applicationContext-server.xml(实际也就是src下的applicationContext-server.xml,在构建后classpath下就会出现这个配置文件)。
同全部的servlet同样,servlet存在的目的就是将web前端的URL请求映射到后台的处理器中去,所以web.xml中须要配置映射原则,这里咱们能够看到CXFServlet用于映射带有ws标识的URL到本身的处理范围里,也就是说,一旦知足这样模式的URL,都将请求WebService。
web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name>HowIsTheWeather</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext-server.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>CXFServlet</servlet-name> <display-name>CXFServlet</display-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>CXFServlet</servlet-name> <url-pattern>/ws/*</url-pattern> </servlet-mapping> </web-app>
applicationContext-server.xml则负责Spring容器中的bean注入的详情,这里配置的bean都将在容器启动的时候装配注入进去,jaxws能够看做是一种特殊的bean,WebService服务实体bean,这是CXF框架为Spring提供的集成支持,在这里咱们能够看到,当URL的请求地址为/getWeather时,将会调用咱们已经注入的服务bean处理这个请求,进而完成外界对系统开放的WebService的调用。
applicationContext-server.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <jaxws:endpoint id="WeatherService" implementor="com.rocking.weather.WeatherServiceImpl" address="/getWeather" /> </beans>
将咱们的工程部署到Tomcat服务器中去,启动服务器查看以下URL:http://localhost:8080/HowIsTheWeather/ws
在这里浏览器会返回CXF渲染的web页面用以说明当前的CXFServlet可以导向的服务有哪些:WeatherService使用/getWeather的URL就能够调用。
咱们再使用这样的URL来查看这个服务具体的定义:http://localhost:8080/HowIsTheWeather/ws/getWeather?wsdl
这个URL用以经过WSDL(WebService Description Language)查看Web服务:也就是咱们开头所说的用通用格式来描述WebService的功能、入参和返回值,使咱们的调用者明白服务的使用方法:具体详情能够查看咱们的这个服务的WSDL页面。
至此,说明咱们的服务已经部署到咱们的服务器上了,接下来须要有客户端来调用这个服务,咱们仍然使用Spring的依赖注入来处理:
public class Client { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( "applicationContext-client.xml"); WeatherService weatherService = (WeatherService) context.getBean("client"); String response = weatherService.getWeather("beijing"); System.out.println(response); } }
applicationContext-client.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <bean id="client" class="com.rocking.weather.Client" factory-bean="clientFactory" factory-method="create" /> <bean id="clientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean"> <property name="serviceClass" value="com.rocking.weather.WeatherService" /> <property name="address" value="http://localhost:8080/HowIsTheWeather/ws/getWeather" /> </bean> </beans>
这样咱们就在同一系统中实现了WebService的调用,可是,咱们不得不说这不是咱们的要求,由于谁也不会大费周折在项目内部从WebService获取服务。咱们的目的是在另外一个项目中获取这个项目提供的开放服务,而另外一个系统须要的,就是这个服务提供的接口:WeatherService,这样一来,咱们能够直接使用这个接口的源文件,或者打成jar包提供给调用方。
在这里,咱们新建一个通常的Java项目,将CXF的包导入classpath,将WeatherService接口导入到和原来在服务提供方同样的包路径下(这和jar包的自动路径是同样的),而后配置好Spring的配置文件:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="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/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <import resource="classpath:META-INF/cxf/cxf.xml" /> <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" /> <import resource="classpath:META-INF/cxf/cxf-servlet.xml" /> <bean id="client" class="com.rocking.weather.client.Client" factory-bean="clientFactory" factory-method="create" /> <bean id="clientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean"> <property name="serviceClass" value="com.rocking.weather.WeatherService" /> <property name="address" value="http://localhost:8080/HowIsTheWeather/ws/getWeather" /> </bean> </beans>
或者这样注入:
<jaxws:client id="WeatherService" serviceClass="com.rocking.weather.WeatherService" address="http://localhost:8080/HowIsTheWeather/ws/getWeather"/>
Client客户端能够调用接口,像调用本地方法同样调用远程服务提供的方法。
public class Client { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( "applicationContext-client.xml"); WeatherService weatherService = (WeatherService) context.getBean("client"); String response = weatherService.getWeather("shanghai"); System.out.println(response); } }
以上内容就是咱们展现的CXF和Spring集成的WebService开发过程,例子虽小,可是阐明了WebService建立的基本过程。
如今咱们回过头来想,咱们当初所说的WebService的调用方使用XML格式发送消息接收消息并无看到啊,咱们这里直接使用了导入的本地接口像调用本地方法同样调用远程方法,看来和XML没有什么区别啊,其实这就是WebService框架带来的好处,咱们在代码中虽然没有出现XML,可是这些请求会在CXF的帮助下变成XML在服务器端和服务交互,获得的结果也会以相似的样子返回来,只不过咱们看到的都是冰层之上的事情了,这也从一个侧面反映了WebService框架开发带来的好处。
另外,咱们在服务的WSDL页面上其实已经看到了CXF为咱们的代码生成的使用WSDL描述的XML化的服务“说明书”,若是想要更完全地经过服务源码获取能够移植到客户端的接口,可使用CXF的wsdl2java工具来生成接口。