在選擇WebService框架的過程中,偶最終選擇了Apache CXF,純粹伿諟銦爲聽說它與Spring的無縫整合
想當初用Axis的時候,因爲沒有太好的辦法讓Spring能夠集成Axis,只好平白無故地多出一個WebService代理類,讓偶的感覺很是不爽
偶要在此記載一下CXF的一些入門知識
首珗,倌網哋址諟http://cxf.apache.org/,裏面可以找到User's Guide和download地址,偶的版本是目前最新的
apache-cxf-2.2.5
先來做一個最簡單的入門級別例子吧,也就是經典的HelloWord
Server端代碼
WebService接口HelloService.java
package cfx.server;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
@WebService
public interface HelloService {
@WebMethod
String sayHi(@WebParam String name);
}
實現類HelloServiceImpl.java
public class HelloServiceImpl implements HelloService {
public String sayHi(String name) {
System.out.println("HelloServiceImpl.sayHi called");
return "Hello"+name;
}
WebService配置文件:cxf-servlet.xml(可放置於WEB-INF目錄下)
<?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" xmlns:soap="http://cxf.apache.org/bindings/soap" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://cxf.apache.org/bindings/soap http://cxf.apache.org/schemas/configuration/soap.xsd http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd"> <jaxws:server id="jaxwsService" serviceClass="cfx.server.HelloService" address="/hello"> <jaxws:serviceBean> <bean class="cfx.server.HelloServiceImpl" /> </jaxws:serviceBean> </jaxws:server> </beans>
web.xml代碼,用於添加CXFServlet這個處理webservice請求的控制器類
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <description>Apache CXF Endpoint</description> <display-name>cxf</display-name> <servlet-name>cxf</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>cxf</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> <session-config> <session-timeout>60</session-timeout> </session-config> </web-app>
Client端測試代碼
public class CXF {
public static void main(String[] args) {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.getInInterceptors().add(new LoggingInInterceptor());
factory.getOutInterceptors().add(new LoggingOutInterceptor());
factory.setServiceClass(HelloService.class);
factory.setAddress("http://localhost:8080/cxf/services/hello");
HelloService client = (HelloService) factory.create();
String reply = client.sayHi("特蕾莎");
System.out.println("Server said: " + reply);
}
*****************************************************************************
怎麼樣,是不是很簡單啊!現在再來一個和Spring整合的例子
注意,Server端和Client端都要通過Spring-bean的方式整合
Server端現在有四個文件,假設是
HelloService.java
HelloServiceImpl.java
HelloDao.java
HelloDaoImpl.java
在HelloServiceImpl中存在一個HelloDao的屬性,代碼省略如下
public class HelloServiceImpl implements HelloService {
private HelloDao dao;
public String sayHi(String name) {
System.out.println("HelloServiceImpl.sayHi called");
return dao.getString(name);
}
}
HelloDaoImpl用於處理持久化,代碼省略咯
需要修改的是配置文件,此時可以這樣改
首先在web.xml里加入Spring監聽器
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext*.xml</param-value> </context-param> <servlet> <description>Apache CXF Endpoint</description> <display-name>cxf</display-name> <servlet-name>cxf</servlet-name> <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>cxf</servlet-name> <url-pattern>/services/*</url-pattern> </servlet-mapping> <session-config> <session-timeout>60</session-timeout> </session-config> </web-app>
橪銗WEB-INF/cxf-servlet這個忟件可以省略咯
把一個標準的spring-bean文件放在src下(即classes目錄下),要讓人一看就知道spring大哥進來咯
applicationContext.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="helloDao" class="cfx.server.HelloDaoImpl" /> <jaxws:server id="jaxwsService" serviceClass="cfx.server.HelloService" address="/hello"> <jaxws:serviceBean> <bean id="helloService" class="cfx.server.HelloServiceImpl"> <property name="dao" ref="helloDao" /> </bean> </jaxws:serviceBean> </jaxws:server> </beans>
這樣啟動服務器的時候,spring就自動進行bean的注入以及WebService服務的發佈了
接下來是客戶端代碼
銦爲諟普通Java,所以就簡單配一下愙戸端的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/schema/jaxws.xsd"> <bean id="HelloService" class="cfx.server.HelloService" factory-bean="clientFactory" factory-method="create" /> <bean id="clientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean"> <property name="serviceClass" value="cfx.server.HelloService" /> <property name="address" value="http://localhost:8080/cxf/services/hello" /> </bean> </beans>
CXFClientTest.java
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "cfx/client/client-beans.xml" });
HelloService client = (HelloService) context.getBean("HelloService");
testString(client);
}
static void testString(HelloService client) {
String reply = client.sayHi("特蕾莎");
System.out.println("Server said: " + reply);
}
*************************************************************************
然後是複雜數據類型的問題,經過測試,發覺基本數據類型和List都是沒有問題的,我的測試方法包括
@WebMethod
String sayHi(@WebParam String name);
@WebMethod
List<Integer> getList(@WebParam List<String> strs);
@WebMethod
List<User> getJavaBean();
但是傳遞Map時,就出現問題了,所以參照了user's guide,得到如下解決辦法
測試某個方法的參數和返回值都是Map類型
@WebMethod
@XmlJavaTypeAdapter(MapAdapter.class)
Map<String, String> getMap(@WebParam @XmlJavaTypeAdapter(MapAdapter.class) Map<String, String> map);
MapAdapter是我自己寫的用於數據類型轉換的適配器類,代碼如下
public class MapAdapter extends XmlAdapter<MapConvertor, Map<String, Object>> {
@Override
public MapConvertor marshal(Map<String, Object> map) throws Exception {
MapConvertor convertor = new MapConvertor();
for(Map.Entry<String, Object> entry:map.entrySet()){
MapConvertor.MapEntry e = new MapConvertor.MapEntry(entry);
convertor.addEntry(e);
}
return convertor;
}
@Override
public Map<String, Object> unmarshal(MapConvertor map) throws Exception {
Map<String, Object> result = new HashMap<String,Object>();
for(MapConvertor.MapEntry e :map.getEntries()){
result.put(e.getKey(), e.getValue());
}
return result;
}
}
MapConvertor.java Map格式轉換類
@XmlType(name = "MapConvertor")
@XmlAccessorType(XmlAccessType.FIELD)
public class MapConvertor {
private List<MapEntry> entries = new ArrayList<MapEntry>();
public void addEntry(MapEntry entry){
entries.add(entry);
}
public static class MapEntry{
public MapEntry() {
super();
}
public MapEntry(Map.Entry<String,Object> entry) {
super();
this.key = entry.getKey();
this.value = entry.getValue();
}
public MapEntry(String key,Object value) {
super();
this.key = key;
this.value = value;
}
private String key;
private Object value;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
public List<MapEntry> getEntries() {
return entries;
}
}
經過這個MapAdapter,算是完成了Map類型的數據傳遞
接下來,就是更爲複雜的一點的這種情況了:List<Map<String,Object>>
這個情況實在不太好辦,目前偶也沒有找到更好的辦法,偶只能這樣做
@WebMethod
List<MapConvertor> getListMap(List<MapConvertor> listmap);
就是把MapConvertor當成Map來使,但偶覺得這不是一個很妥善的辦法。
其實偶覺得,WebService裏應該儘量減少使用javabean對象進行傳輸
一個JavaBean可以轉換成一個Map
一個包含多個JavaBean的List可以轉換成一個包含多個Map的List
所以如果對Map支持得好的話,就應該多用Map和List來實現數據傳遞
當然在調用的時候,也最好能夠像Axis一樣使用Call來實現無接口調用
這樣在Client端就不需要得到Server提供的任何jar了
這樣纔是最鬆散耦合的系統
只可惜現在對Map支持得不是很好,最起碼在List<Map>時用的方式感覺不太好
不知道各位GGJJ們是怎麼做的呢?