当在WebService的接口定义中使用到了Map接口,如下所示
Map<String,Cat> getAllCats();
此时运行服务器端,会报如下错误,指出CXF对Map接口不支持。
2014-5-2 17:11:38 org.apache.cxf.service.factory.ReflectionServiceFactoryBean buildServiceFromClass
信息: Creating Service {http://impl.cxf.ws.onyas.com/}HelloWorldWs from class com.onyas.ws.cxf.HelloWorld
Exception in thread "main" javax.xml.ws.WebServiceException: org.apache.cxf.service.factory.ServiceConstructionException
at org.apache.cxf.jaxws.EndpointImpl.doPublish(EndpointImpl.java:371)
at org.apache.cxf.jaxws.EndpointImpl.publish(EndpointImpl.java:251)
at org.apache.cxf.jaxws.spi.ProviderImpl.createAndPublishEndpoint(ProviderImpl.java:155)
at javax.xml.ws.Endpoint.publish(Endpoint.java:170)
at onyas.ServerMain.main(ServerMain.java:13)
Caused by: org.apache.cxf.service.factory.ServiceConstructionException
at org.apache.cxf.jaxb.JAXBDataBinding.initialize(JAXBDataBinding.java:341)
at org.apache.cxf.service.factory.AbstractServiceFactoryBean.initializeDataBindings(AbstractServiceFactoryBean.java:86)
at org.apache.cxf.service.factory.ReflectionServiceFactoryBean.buildServiceFromClass(ReflectionServiceFactoryBean.java:490)
at org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean.buildServiceFromClass(JaxWsServiceFactoryBean.java:704)
at org.apache.cxf.service.factory.ReflectionServiceFactoryBean.initializeServiceModel(ReflectionServiceFactoryBean.java:550)
at org.apache.cxf.service.factory.ReflectionServiceFactoryBean.create(ReflectionServiceFactoryBean.java:265)
at org.apache.cxf.jaxws.support.JaxWsServiceFactoryBean.create(JaxWsServiceFactoryBean.java:215)
at org.apache.cxf.frontend.AbstractWSDLBasedEndpointFactory.createEndpoint(AbstractWSDLBasedEndpointFactory.java:102)
at org.apache.cxf.frontend.ServerFactoryBean.create(ServerFactoryBean.java:159)
at org.apache.cxf.jaxws.JaxWsServerFactoryBean.create(JaxWsServerFactoryBean.java:211)
at org.apache.cxf.jaxws.EndpointImpl.getServer(EndpointImpl.java:456)
at org.apache.cxf.jaxws.EndpointImpl.doPublish(EndpointImpl.java:334)
... 4 more
Caused by: com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
java.util.Map is an interface, and JAXB can't handle interfaces.
this problem is related to the following location:
at java.util.Map
at private java.util.Map com.onyas.ws.cxf.jaxws_asm.GetAllCatsResponse._return
at com.onyas.ws.cxf.jaxws_asm.GetAllCatsResponse
java.util.Map does not have a no-arg default constructor.
this problem is related to the following location:
at java.util.Map
at private java.util.Map com.onyas.ws.cxf.jaxws_asm.GetAllCatsResponse._return
at com.onyas.ws.cxf.jaxws_asm.GetAllCatsResponse
at com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException$Builder.check(IllegalAnnotationsException.java:66)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:422)
at com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:270)
at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:103)
at com.sun.xml.internal.bind.v2.ContextFactory.createContext(ContextFactory.java:81)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:202)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:376)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:574)
at org.apache.cxf.common.jaxb.JAXBContextCache.createContext(JAXBContextCache.java:340)
at org.apache.cxf.common.jaxb.JAXBContextCache.getCachedContextAndSchemas(JAXBContextCache.java:228)
at org.apache.cxf.jaxb.JAXBDataBinding.createJAXBContextAndSchemas(JAXBDataBinding.java:482)
at org.apache.cxf.jaxb.JAXBDataBinding.initialize(JAXBDataBinding.java:339)
... 15 more
如果在CXF开发中遇到不支持的类型,则需要我们自已来处理。处理思路是:提供一个转换器,这个转换器负责把CXF处理不了的类型转换成CXF处理的了的类型。
步骤:
1、使用@XmlJavaTypeAdapter来修饰CXF无法处理的类型,使用该Annotation时,通过value属性指定一个转换器
2、实现自己的转换器,需要开发一个CXF能处理的类型
代码如下所示:
1、加注释:
package com.onyas.ws.cxf;
import java.util.List;
import java.util.Map;
import javax.jws.WebService;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import com.onyas.ws.cxf.domain.Cat;
import com.onyas.ws.cxf.domain.User;
import com.onyas.ws.cxf.util.MapAdapter;
@WebService
public interface HelloWorld {
String sayHi(String name);
List<Cat> getCatsByUser(User user);
@XmlJavaTypeAdapter(MapAdapter.class) Map<String,Cat> getAllCats();
}
实现类:
@Override
public Map<String, Cat> getAllCats() {
Map<String, Cat> result = new HashMap<String, Cat>();
int i = 1;
for (List<Cat> cats : catDb.values()) {
for (Cat cat : cats) {
result.put("第" + i++ + "只猫", cat);
}
}
return result;
}
2、写转换器:
package com.onyas.ws.cxf.util;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import com.onyas.ws.cxf.domain.Cat;
import com.onyas.ws.cxf.util.StringCat.Entry;
/**
* ValueType 指的是CXF能处理的类型
* BoundType 指的是CXF无法处理的类型
* StringCat 是自已定义的Bean
*
* 该转换器主要完成StringCat与Map<String,Cat>类型的相互转换
*/
public class MapAdapter extends XmlAdapter<StringCat, Map<String,Cat>> {
/**
* 把Map<String, Cat>转换成StringCat
*/
@Override
public StringCat marshal(Map<String, Cat> v) throws Exception {
StringCat sc = new StringCat();
for(String s:v.keySet()){
sc.getEntries().add(new Entry(s,v.get(s)));
}
return sc;
}
/**
* 把StringCat转换成Map<String, Cat>
*/
@Override
public Map<String, Cat> unmarshal(StringCat v) throws Exception {
Map<String,Cat> result = new HashMap<String,Cat>();
for(Entry entry :v.getEntries()){
result.put(entry.getKey(), entry.getValue());
}
return result;
}
}
3、自定义的类型
package com.onyas.ws.cxf.util;
import java.util.ArrayList;
import java.util.List;
import com.onyas.ws.cxf.domain.Cat;
public class StringCat {
private List<Entry> entries=new ArrayList<Entry>();
public List<Entry> getEntries() {
return entries;
}
public void setEntries(List<Entry> entries) {
this.entries = entries;
}
public static class Entry{
private String key;
private Cat value;
public Entry() {
}
public Entry(String key, Cat cat) {
this.key = key;
this.value = cat;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public Cat getValue() {
return value;
}
public void setValue(Cat value) {
this.value = value;
}
}
}
4、发布服务端
客户端用wsdl2java重新生成,然后如下
StringCat sc = hw.getAllCats();
for(Entry entry:sc.getEntries()){
System.out.println(entry.getKey()+"--"+entry.getValue().getName());
}