在前面的隨筆中,我講了我的網站架構,這樣的架構決定了我的網站中必須得用到WebService。比如,在用戶注冊的時候,用戶數據主要是保存在內容服務器中,但是同時也要將部分數據提交到索引服務器中,這時,就可以讓內容服務器訪問索引服務器提供的WebService來提交數據;還可以讓內容服務器通過定時任務,訪問索引服務器的WebService來提交統計數據。
我的網站使用SpringSide 2.0開發,在SpringSide 2.0中,默認使用的是XFire來提供WebService,但是我按照文檔進行操作,結果卻失敗了。于是我向江南白衣請教,白衣推薦我使用CXF的最新版本,于是我到官方網站下載了CXF的最新版,按照示例來了一遍,很快就成功了。由此可見,使用CXF不僅簡單,而且成功率高。因此,我在這里把我的經驗和大家分享。
第一步,下載CXF的最新版本,下載地址如下圖:
第二步,將CXF中的lib文件夾中的下列jar文件拷貝到我們項目的webapp/WEB-INF/lib目錄下:
commons-logging-1.1.jar
geronimo-activation_1.1_spec-1.0-M1.jar?(or?Sun's?Activation?jar)
geronimo-annotation_1.0_spec-1.1.jar?(JSR?250)
geronimo-javamail_1.4_spec-1.0-M1.jar?(or?Sun's?JavaMail?jar)
geronimo-servlet_2.5_spec-1.1-M1.jar?(or?Sun's?Servlet?jar)
geronimo-ws-metadata_2.0_spec-1.1.1.jar?(JSR?181)
jaxb-api-2.0.jar
jaxb-impl-2.0.5.jar
jaxws-api-2.0.jar
neethi-2.0.jar
saaj-api-1.3.jar
saaj-impl-1.3.jar
stax-api-1.0.1.jar
wsdl4j-1.6.1.jar
wstx-asl-3.2.1.jar
XmlSchema-1.2.jar
xml-resolver-1.2.jar
cxf-2.0-incubator.jar
這里有一些包我的項目中本身已經帶有了,只不過CXF中提供的版本要更新一些。把這些包拷貝到項目中后,可以刪除項目中的較低的版本,同時刪除所有和XFire有關的包。當然,不刪除也可以,因為我試過了,就算項目中存在多個不同版本的包,也不會發生沖突。
當然,光拷貝這些包到項目中,還不能保證開發的順利進行,還需要在Eclipse中設置項目的庫,如下圖:

在這里,我不得不說一下另外一個問題,那就是啟動Tomcat服務器的時候,經常發生java.lang.OutOfMemoryError: PermGen space異常,出現這個異常是什么原因呢?在網上搜到的答案是這樣的:PermGen space的全稱是Permanent Generation space,是指內存的永久保存區域,這塊內存主要是被JVM存放Class和Meta信息的,Class在被Loader時就會被放到PermGen space中,它和存放類實例(Instance)的Heap區域不同,GC(Garbage Collection)不會在主程序運行期對PermGen space進行清理,所以如果你的應用中有很多CLASS的話,就很可能出現PermGen space錯誤,這種錯誤常見在web服務器對JSP進行pre compile的時候。如果你的WEB APP下都用了大量的第三方jar, 其大小超過了jvm默認的大小(4M)那么就會產生此錯誤信息了。
本來,使用SpringSide 2.0就已經包含了許多的第三方包,容易出現這個問題,現在加入CXF依賴的這些包,就不可避免要出現這個問題了。這個問題的解決方法有兩個,其一是不使用SUN的JDK。當然,我也懶得去下載一個別的JDK,因此就選擇了第二個方法,那就是修改Tomcat的啟動文件。
找到SpringSide2.0\misc\servers\tomcat-5.5.17\bin文件夾下的catalina.bat文件,使用記事本打開,找到如下行:
set JAVA_OPTS=
將這一行進行修改,加入啟動參數,如下:
set JAVA_OPTS=%JAVA_OPTS% -Xms512m -Xmx1024m -XX:MaxNewSize=512m -XX:MaxPermSize=512m -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties":noJuli
解決了以上這些問題,就可以正式使用CXF了。
第三步,修改webapp/WEB-INF/web.xml文件,將以前的
<servlet>
??<servlet-name>xfire</servlet-name>
??<servlet-class>org.codehaus.xfire.spring.XFireSpringServlet</servlet-class>
</servlet>
<servlet-mapping>
??<servlet-name>xfire</servlet-name>
??<url-pattern>/service/*</url-pattern>
</servlet-mapping>
修改為:
<servlet>
??<servlet-name>cxf</servlet-name>
??<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>
<servlet-mapping>
??<servlet-name>cxf</servlet-name>
??<url-pattern>/service/*</url-pattern>
</servlet-mapping>
即可。
第四步,定義一個提供WebService的接口。在我的項目中,我準備只提供一個WebService,即IndexService,這個服務中提供多個方法來分別滿足索引服務器的各種功能。目前,我還只開發到了用戶注冊模塊,需要向索引服務器提交用戶數據,因此,暫時提供一個addUser方法作為示例,如下:
package?com.yumdays.service;
import?javax.jws.WebService;
import?com.yumdays.model.SUser;
@WebService
public?interface?IndexService?{
????public?boolean?addUser(SUser?user,String?adminName,String?adminPassword);
}
而它的實現類如下:
package?com.yumdays.service;
import?com.yumdays.model.SUser;
import?javax.jws.WebService;
@WebService(endpointInterface?=?"com.yumdays.service.IndexService")
public?class?IndexServiceImpl?implements?IndexService?{
????public?boolean?addUser(SUser?user,?String?adminName,?String?adminPassword)?{
????????//?TODO?自動生成方法存根
????????return?false;
????}
}
?第五步,在項目的src/resource/spring目錄下,刪除所有和XFire有關的配置文件,添加一個cxf-beans.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-2.0.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="indexServiceBean"?class="com.yumdays.service.IndexServiceImpl"?/>
????<jaxws:endpoint?id="indexService"?implementor="#indexServiceBean"?address="/IndexService"?/>
??????
</beans>
現在,重新構建項目,部署,啟動Tomcat,就可以通過訪問
http://www.yumdays.com/service/IndexService?wsdl來測試該WebService是否成功被部署了。如下圖:
第六步,創建客戶端,這一步非常的容易,只需要下面這樣的配置:
<?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-2.0.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.yumdays.service.IndexService"?factory-bean="clientFactory"?factory-method="create"/>
????
????<bean?id="clientFactory"?class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
??????<property?name="serviceClass"?value="com.yumdays.service.IndexService"/>
??????<property?name="address"?value="http://www.yumdays.com/service/IndexService"/>
????</bean>
????
</beans>
就可以獲得一個名稱為client的bean,通過該bean,就可以非常方便的訪問索引服務器提供的功能。