由于篇幅有限,此处生出源代码,如有需要的在评论里面留下联系的方式,笔者到时联系你。转载请标明读者链接,谢谢。
一.JAXB简介
JAXB 是 CXF 一个默认的数据绑定,如果你不指定其他数据绑定在你的 Spring 配置中或者在 API ,你可以得到 JAXB 的数据绑定。自从 2.3.x 版本以后默认的使用 JAXB2.2 , maven 使用者运行在 jdk6 之上将需要使用 java 认证的重写机制来使用 JAXB2.2 来替代 JAXB2.1 。
JAXB 使用 java 注解结合文件路径在 XML 和 Java 之间构建映射。 JAXB 支持编码优先和架构优先编程。架构优先支持创建客户代理、动态、运行时。详解见 CXF 的 DynamicClientFactory 类。
CXF 使用 JAXB 引用实现。为了学习更多关于注解类或者怎样把一个 schema 生成实体类,详见官网。
二. 解组和编组
解组:( unmarshalling )把数据从存储媒介上转化到内存中的过程,正好与编组相反。因此需要把 xml 文档解组到 Java VM 中。这里的复杂性不是在扁平数据中,因为这不是必需的,而在于从正确的数据到正确 Java 代码变量的映射。如果映射是错误的,就不可能正确的访问数据。当然,如果再尝试重新编组还会造成更大的问题,并且问题传播的很快。
编组:<!-- [if gte mso 9]><xml> <o:OfficeDocumentSettings> <o:AllowPNG/> </o:OfficeDocumentSettings> </xml><![endif]-->( marshalling )是把内存中的数据转化到存储媒介上的过程。因此在 java 和 XML 环境中,编组就是把一些 Java 对象转换成一个或多个 XML 文档。在数据库环境中,则是把 Java 表示的数据存入数据库。显然,编组的秘密在于把 Java 实例中的面向对象结构转化成适用于 XML 的扁平结构,或者 RDBMS 中的关系结构。
三.代码以及分析
首先需要加入cxf所需要的包,在本次测试中,笔者添加了cxf的lib中全部的包。
下面直接上代码:
package com.syc.jaxb; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @XmlJavaTypeAdapter(UserAdapter.class) public interface User { String getName(); }
此处注释的XmlJavaTypeAdapter是对自定义编组使用实现XmlAdapter的适配器,即UserAdapter这个类,该类实现了XmlAdapter适配器,因此要实现解组和编组两个方法。可能有的初学读者会提到:为什么我们要实现编组和解组呢?原因很简单,CXF不能直接支持List、Map等。
package com.syc.jaxb; import javax.xml.bind.annotation.XmlType; @XmlType(name="User") public class UserImpl implements User{ String name; public UserImpl(){} public UserImpl(String n){ this.name =n; } @Override public String getName() { return name; } public void setName(String n){ this.name = n ; } }
该类是实现了User接口,并且重写getName()方法,此类可以看成一个User实体类,其实也没有什么区别。这里注释的XmlType是将UserImpl映射到 XML 模式类型,即我们得到的客户端的时候显示的类的名字是User。
package com.syc.jaxb; import java.util.Map; import javax.jws.WebService; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @WebService public interface HelloWorld { String sayHi(String text); String sayHiToUser(User user); @XmlJavaTypeAdapter(UserMapAdapter.class) Map<Integer,User> getUsers(); }
该接口是暴露的服务,该服务有三个方法,暴露的服务必须使用@WebService注释,不然在发布服务的时候找不到不要发布的服务类。由于第三个方法返回的是一个Map类型,但 JAXB 不能将一些 Java 类型自然映射到 XML 表示形式,例如 ,HashMap 或者其他非 JavaBean 类。如参数类型为接口,以及 Map ,这需要特殊的 XmlAdapter 类进行处理。因此这里我们需要注释成让XmlAdapter适配器来处理。
package com.syc.jaxb; import java.util.LinkedHashMap; import java.util.Map; import javax.jws.WebService; @WebService(endpointInterface="com.syc.jaxb.HelloWorld", serviceName="HelloWorld") public class HelloWorldImpl implements HelloWorld { Map<Integer,User> users = new LinkedHashMap<Integer, User>(); @Override public String sayHi(String text) { System.out.println("sayHello called..."); return "Hello " +text; } @Override public String sayHiToUser(User user) { System.out.println("sayUserHello called..."); users.put(users.size()+1, user); return "Hello"+ user.getName(); } @Override public Map<Integer, User> getUsers() { System.out.println("getMapUsers called..."); return users; } }
该类是服务类的一个实现。
package com.syc.jaxb; import javax.xml.bind.annotation.adapters.XmlAdapter; public class UserAdapter extends XmlAdapter<UserImpl, User>{ @Override public User unmarshal(UserImpl v) throws Exception { return v; } @Override public UserImpl marshal(User v) throws Exception { if(v instanceof UserImpl){ return (UserImpl)v; } return new UserImpl(v.getName()); } }
在这里,UserImpl是value类型,User是alue类型。UserAdapter类继承了XmlAdapter类,因此需要重写marshal()和unmarshal()方法,即编组和解组,在此处,编组就是将bound类型修改为value类型,然后将value类型编组为xml表示形式;解组就是JAXB绑定框架首先将XML表示形式解组为value类型,然后将value类型修改为bound类型。在这里我们可以这么理解,编组即bound->value->xml形式,解组即xml形式->value->bound。
package com.syc.jaxb; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlType; @XmlType(name="UserMap") @XmlAccessorType(XmlAccessType.FIELD) public class UserMap { @XmlElement(nillable=false,name="entry") List<UserEntry> entries = new ArrayList<UserEntry>(); public List<UserEntry> getEntries() { return entries; } public void addEntry(UserEntry entry){ entries.add(entry); } @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name="UserEntry") static class UserEntry{ public UserEntry(){ super(); } public UserEntry(Map.Entry<Integer, User> entry){ super(); this.id = entry.getKey(); this.user = entry.getValue(); } public UserEntry(Integer id,User user){ super(); this.id = id; this.user = user; } @XmlElement(required=true,nillable=false) Integer id; User user; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } } }
UserMap类是让cxf支持MAP的关键,该类可以将map里面的key-value转换为User实体类,把key当作user的id值,把value当作user的name值,然后再把user保存到一个集合中,即实现了hash值到实体类的转换。
package com.syc.jaxb; import java.util.LinkedHashMap; import java.util.Map; import javax.xml.bind.annotation.adapters.XmlAdapter; public class UserMapAdapter extends XmlAdapter<UserMap, Map<Integer,User>>{ @Override public Map<Integer, User> unmarshal(UserMap v) throws Exception { Map<Integer, User> map = new LinkedHashMap<Integer, User>(); for(UserMap.UserEntry e : v.getEntries()){ map.put(e.getId(), e.getUser()); } return map; } @Override public UserMap marshal(Map<Integer, User> v) throws Exception { UserMap map = new UserMap(); for(Map.Entry<Integer, User> e : v.entrySet()){ UserMap.UserEntry u = new UserMap.UserEntry(); u.setId(e.getKey()); u.setUser(e.getValue()); map.getEntries().add(u); } return map; } }
这里即对UserMap和Map对象的编组和解组。
package com.syc.jaxb; import javax.xml.ws.Endpoint; public class Server { public static void main(String[] args) { System.out.println("Starting Server... "); String address = "http://localhost:8899/hello"; HelloWorldImpl implementor = new HelloWorldImpl(); Endpoint.publish(address, implementor); try { Thread.sleep(5*60*1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Exiting Server... "); System.exit(0); } } 用WSDL2JAVA生成的客户端,调用服务端代码 package com.syc.jaxb.client; import java.util.List; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; public class HelloClient { public static void main(String[] args) { JaxWsProxyFactoryBean factoryBean = new JaxWsProxyFactoryBean(); factoryBean.setAddress("http://localhost:8899/hello"); factoryBean.setServiceClass(HelloWorld.class); HelloWorld h = (HelloWorld) factoryBean.create(); System.out.println(h.sayHi("shenyc")); User u = new User(); u.setName("syc"); System.out.println(h.sayHiToUser(u)); User u1 = new User(); u1.setName("ssss"); System.out.println(h.sayHiToUser(u1)); User u2 = new User(); u2.setName("aaaaa"); System.out.println(h.sayHiToUser(u2)); UserMap users = h.getUsers(); List<UserEntry> list = users.getEntry(); for(UserEntry ue : list){ System.out.println(ue.getId()+":"+ue.getUser().getName()); } } }