论坛首页 Java企业应用论坛

Apache CXF入门范例以及对传递List<Map>类型的疑惑

浏览 15077 次
精华帖 (0) :: 良好帖 (2) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-11-28   最后修改:2009-11-29

在选择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们是怎么做的呢?

   发表时间:2009-12-05  
这样更省事
package cn.com.pansky.dss.common.ws;

import java.util.HashMap;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
/**
 * XML to Map及Map to XML序列化
 * @author shenwujie
 *
 */
public class MapAdapter extends XmlAdapter<String,HashMap> {

	@Override
	public String marshal(HashMap map) throws Exception {
		XStream xs = new XStream(new DomDriver());
		return xs.toXML(map);
	}

	@Override
	public HashMap unmarshal(String xmlData) throws Exception {
		XStream xs = new XStream(new DomDriver());
		return (HashMap) xs.fromXML(xmlData);
	}

}

0 请登录后投票
   发表时间:2010-02-02  
博主,你的
@WebMethod 
List<User> getJavaBean();
这个方法是怎么弄的啊?????????????????
0 请登录后投票
   发表时间:2010-03-02  
如果方法中用了这种map 会提示一下内容,请问该如何解决呢
警告: {http://service.ws.upc.red.com/}findUser requires a wrapper bean but problems with ASM has prevented creating one.  Operation may not work correctly.
0 请登录后投票
   发表时间:2010-07-05  
楼主,用MAP作为数据传递的能数。在客户端是要构建一个MapConvertor将MapEntry作为他的参数。MapEntry只能是一对key,value.要是要多个要用List<MapEntry>。再把这个List<MapEntry>赋给MapConvertorr的entries。楼主是这样的吗。
MapEntry不能像MAP这样添加多组<key,value>。如何能实现这样的功能。
0 请登录后投票
   发表时间:2010-09-15  
这娃比较懒! 嘎嘎
0 请登录后投票
   发表时间:2010-10-22  
kiki 写道
这样更省事
package cn.com.pansky.dss.common.ws;

import java.util.HashMap;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
/**
 * XML to Map及Map to XML序列化
 * @author shenwujie
 *
 */
public class MapAdapter extends XmlAdapter<String,HashMap> {

	@Override
	public String marshal(HashMap map) throws Exception {
		XStream xs = new XStream(new DomDriver());
		return xs.toXML(map);
	}

	@Override
	public HashMap unmarshal(String xmlData) throws Exception {
		XStream xs = new XStream(new DomDriver());
		return (HashMap) xs.fromXML(xmlData);
	}

}



这种方式要求客户端也要发送一个xml字符串,让为客户端也xStream转码?还是你告诉调用者map类型数据,就转成对应的xml?
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics