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工具來生成接口。