转载:http://www.programgo.com/article/98912703200/

1、什么是JAX-WS

JAX-WS (JavaTM API for XML-Based Web Services)规范是一组XML web services的JAVA API。JAX-WS允许开发者可以选择RPC-oriented或者message-oriented 来实现自己的web services。JAX-WS2.0 (JSR 224)是Sun新的web services协议栈,是一个完全基于标准的实现。在binding层,使用的是the Java Architecture for XML Binding (JAXB, JSR 222),在parsing层,使用的是the Streaming API for XML (StAX, JSR 173),同时它还完全支持schema规范。在JDK6.0之后已经整合到SDK中,所以我们以下的实例只需要在JDK6后即可,不需要再添加额外的依赖包

这是网上找到的JAX-WS的原理图

为了方便传送过来【以下内容来自http://blog.csdn.net/kkdelta/article/details/4017747

写道
1、客户端开发者通过URL得到WSDL文件。(通过HTTP访问可以得到,http://<endpoint-address>?wsdl)  
2、客户端根据WSDL的描述,通过HTTP POST发送SOAP消息给服务器端。  
3、服务器端Listener接受到SOAP请求消息,对JAVA来说,通常是一个servlet或者EJB。Listener把消息转发给 Dispatcher,有时候listener和Dispatcher也可能是同一个类。Dispatcher会把请求消息交给WebService的运行终端。  
4、这时候,服务器端会将HTTP request转成服务器端的消息类型,形成javax.xml.ws.handler.MessageContext,并处理SOAP消息的头信息,如mustUnderstand。  
5、如果在服务器端配置了handler,会调用handler的handleMessage方法,通常用handler来保存消息,解密或者保证消息到达的顺序。handler通过在@HandlerChain标注配置handlers.xml文件内容为:

  1. <handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
  2. <handler-chain>
  3. <handler>
  4. <handler-name>WSSOAPHandler</handler-name>
  5. <handler-class>com.cxf.test.WSSOAPHandler</handler-class>
  6. </handler>
  7. </handler-chain>
  8. <handler-chain>
  9. <handler>
  10. <handler-name>WSHandler</handler-name>
  11. <handler-class>com.cxf.test.WSHandler</handler-class>
  12. </handler>
  13. </handler-chain>
  14. </handler-chains>

6、SOAP消息被反序列化为JAVA对象,传到真正的实现业务的类。  
7、调用真正的业务方法,执行后利用JAXB注解序列化成SOAP返回消息。  
8、如果配置了handler,会调用handler的handleMessage方法。如果业务方法有异常抛出,把异常转为SOAP fault 消息。  
9、Listener通过HTTP把response返回给客户端。  
总体来讲:整个过程是一个Requset XML(SOAP)-->JAXB-->JAVA Object-->JAXB-->Response XML(SOAP)的过程 

2、从一个例子开始

只需要几个简单的annotation即可发布一个基于JAX-WS的webservice服务:

  1. @WebService
  2. public interface HelloJAXWS {
  3. public String sayHello();
  4. }
  5. @WebService
  6. public class HelloJAXWSImpl implements HelloJAXWS {
  7. @WebMethod
  8. public String sayHello() {
  9. return "Hello, JAX-WS";
  10. }
  11. }

在一个main函数中发布该服务:

  1. Endpoint.publish("http://localhost:8080/service/helloJaxWs", new HelloJAXWSImpl());

这样我们就可以在浏览器通过上面的URL访问:

http://localhost:8080/service/helloJaxWs?wsdl

如果看到返回的xml格式的文档表示我们就发布成功。

下面来看看客户端代码,在JDK中也提供了相应的工具来根据wsdl生成客户端代码这就是wsimport,可以通过wsimport -help来查看相应的参数。这里采用了以下几个参数

  1. wsimport -verbose -p com.sample.sws.ws -keep http://localhost:8080/service/helloJaxWs?wsdl
  2. -verbose 显示编译的信息
  3. -p 指定客户端package
  4. -keep 保存生成客户端的源文件和编译好的class文件

在指定的package下就可以看到生成的service代理类,当然具体的类名会根据之前@WebService()后的参数设置,比如我server端的服务类声明如下:

  1. @WebService(serviceName = "helloJaxWsService", endpointInterface = "org.ws.server.ws.chap1.impl.HelloJAXWSImpl")
  2. @SOAPBinding(style = Style.DOCUMENT)

生成的客户端代理类也是根据上面的serviceName来HelloJaxWsService

下面是客户端调用的代码

  1. HelloJAXWSImpl hellpJaxWs = new HelloJaxWsService().getHelloJAXWSImplPort();
  2. System.out.println(hellpJaxWs.sayHello());

这样就完成了一个创建webservice->发布->生成客户端代码->客户端调用的整个过程

3、传递复杂对象

从上面2中的例子是返回String对象,其实JAX-WS原生对复杂对象都有很好的支持,如我们一些业务上的PO或List对象等。下面来看一个例子:

  1. @WebService
  2. public interface JaxWsService {
  3. public Address getAddress(String id);
  4. public Person getPerson(String id);
  5. List<Address> getAddressList(Address address);
  6. }

这里申明了Address和Person对象,其中Person包含一个List<Address>属性:

  1. public class Person implements Serializable {
  2. private static final long serialVersionUID = 8336803120311071811L;
  3. private String            username;
  4. private Date              birthday;
  5. private List<Address>     addresses;
  6. //以下省略若干
  7. }
  8. public class Address implements Serializable {
  9. private static final long serialVersionUID = 5038515089191304705L;
  10. private String            street;
  11. private String            city;
  12. private int               port;
  13. //以下省略若干
  14. }

在对上面的接口做简单的实现,按照上面的流程生成客户端代码调用即可达到预期的效果。这里不在赘述例子可见代码,这里想要说明的是JAX-WS中对大多数普通对象不需要做任何处理即可传输

4、wsimport与wsgen

这两个工具位于$JAVA_HOME$/bin目录

  • wsimport 前面已经用到这个工具,用来解析一个存在的WSDL文档,生成客户端代码。通过wsimport查询参数或者http://jax-ws.java.net/jax-ws-ea3/docs/wsimport.html
  • wsgen 工具还可以用来从一个Web服务中生成对应的WSDL文档

  1. @WebService
  2. public class JaxWsWebServiceImpl implements JaxWsWebService {
  3. @WebMethod
  4. public @WebResult
  5. String getMessage() {
  6. return "Hello";
  7. }
  8. }
  1. H:\git\webservice\sws\sws-server\target\classes>wsgen -verbose -keep -cp "." -wsdl org.ws.server.ws.chap8.impl.JaxWsWebServiceImpl
  2. 生成6个文件:
  3. JaxWsWebServiceImplService.wsdl
  4. JaxWsWebServiceImplService_schema1.xsd
  5. org\ws\server\ws\chap8\impl\jaxws\GetMessage.java
  6. org\ws\server\ws\chap8\impl\jaxws\GetMessage.class
  7. org\ws\server\ws\chap8\impl\jaxws\GetMessageResponse.java
  8. org\ws\server\ws\chap8\impl\jaxws\GetMessageResponse.class

详情介绍见这里

5、Endpoint.publish初探

从上面我们知道发布一个webservice我们只需要通过

  1. public static Endpoint publish(String address, Object implementor)

即可,其实这后面依赖于JavaSE6提供了一个轻量级的HttpServer。位于rt.jar下的com.sun.net.httpserver,具体可见这里同时这里还有一个小实例

接上文JAX-WS:创建简单的webservice,JAX-WS对大部分复杂对象传递都能直接支持。在服务端传递了自定义的复杂对象Person:

  1. public class Person implements Serializable {
  2. private static final long serialVersionUID = 8336803120311071811L;
  3. private String            username;
  4. private Date              birthday;
  5. private List<Address>     addresses;
  6. //getter/setter

我们来看看根据wsimport生产的客户端中Person的定义:

  1. @XmlAccessorType(XmlAccessType.FIELD)
  2. @XmlType(name = "person", propOrder = {
  3. "addresses",
  4. "birthday",
  5. "username"
  6. })
  7. public class Person {
  8. @XmlElement(nillable = true)
  9. protected List<Address> addresses;
  10. @XmlSchemaType(name = "dateTime")
  11. protected XMLGregorianCalendar birthday;
  12. protected String username;
  13. //getter/setter
  14. }

在生产的客户端代码中多了很多annotation声明,在前面也说了JAX-WS是一种基于xml传递的webservice,也就是需要将传递的参数转化为xml文档,而这其中转化的过程就是我们见到的客户端对象中各种annotation声明,其背后的技术也就是前面说的JAXB(Java Architecture for XML Binding)简单地说就是一种将JAVA对象和XML互相转换的技术。包含在包
javax.jws
javax.xml.bind下提供了一系列解组、编组已经运行时绑定的API
javax.xml.bind.annotation 定义将 Java 程序元素定制成XML模式映射的注释
javax.xml.bind.annotation.adapters XmlAdapter 及其规范定义的子类允许任意 Java 类与 JAXB 一起使用

----------------------------------------

主要的接口或类:

HandlerChain 将 Web Service 与外部定义的处理程序链关联。 
Oneway 指示给定 @WebMethod 只有一条输入消息并且没有输出。 
WebMethod 定制一个公开为 Web Service 操作的方法。 
WebParam 定制单个参数到 Web Service 消息部分和 XML 元素的映射关系。 
WebResult 定制返回值到 WSDL 部分和 XML 元素的映射关系。 
WebService 将 Java 类标记为实现 Web Service,或者将 Java 接口标记为定义 Web Service 接口。

Element 这是一个元素标记接口。 
Marshaller Marshaller 类负责管理将 Java 内容树序列化回 XML 数据的过程。  
Unmarshaller Unmarshaller 类管理将 XML 数据反序列化为新创建的 Java 内容树的过程,并可在解组时有选择地验证 XML 数据。 
UnmarshallerHandler 作为 SAX ContentHandler 实现的 Unmarshaller。 
ValidationEvent此事件指示在执行解组操作期间验证传入的 XML 数据时、对 Java 内容树执行按需验证时或将 Java 内容树编组回 XML 数据时遇到问题。 
DatatypeConverter可以使用 javaType 绑定声明来自定义 XML 模式数据类型到 Java 数据类型的绑定。 
JAXBContext JAXBContext 类提供到 JAXB API 的客户端入口点。 
XmlAdapter 修改 Java 类型以适应自定义编组

主要的Annotation

XmlAccessorOrder  控制类中字段和属性的排序。 
XmlAccessorType  控制默认情况下是否对字段或 Javabean 属性进行系列化。 
XmlAttribute   将 JavaBean 属性映射到 XML 属性。 
XmlElement   将 JavaBean 属性映射到派生于属性名称的 XML 元素。 
XmlElementWrapper  生成一个包装 XML 表示形式的包装器元素。 
XmlList    用来将属性映射到列表简单类型。 
XmlMimeType   关联控制属性 XML 表示形式的 MIME 类型。  
XmlRootElement   将类或枚举类型映射到 XML 元素。 
XmlTransient   阻止将 JavaBean 属性映射到 XML 表示形式。 
XmlType   将类或枚举类型映射到 XML 模式类型。 
XmlValue   支持将类映射到带有 simpleContent 的 XML 模式复杂类型或 XML 模式简单类型。

1、如何解码转码

JAXBContext是JAXB API 的客户端入口,从java到xml的互相转换对应Marshaller和Unmarshaller。一个简单的例子是

  1. JAXBContext jaxbContext = JAXBContext.newInstance(claszz);
  2. Marshaller marshaller = jaxbContext.createMarshaller();
  3. marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
  4. marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
  5. marshaller.setProperty(Marshaller.JAXB_ENCODING, "utf-8");
  6. marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://sws.org/sample");
  7. marshaller.marshal(t, System.out);
  8. marshaller.marshal(t, new File("c:\\t.xml"));

这里只是java转xml的例子。设置了格式化输出、encoding等属性、schema等信息,同时输出到控制台和指定文件路径

2、常用annotation
常用的annotation用来绑定转换运行过程中的各种属性,如绑定属性、绑定的顺序、格式化处理等。下面介绍一些常用

@XmlRootElement(name="",namespace="")
标注此类作为xml的一个root节点,常用在顶层类或枚举类型。以下注释一起使用: XmlType、XmlEnum、XmlAccessorType、 XmlAccessorOrder。作为生产xml的root节点,名称可通过name来指定,默认会为该类名

@XmlAccessorType(XmlAccessType.FIELD)
控制对JavaBean的访问方式,一般有PROPERTY(getter/setter)、FIELD,PUBLIC_MEMBER(public getter/setter,fileds)、NONE可用于包,顶层类。若用于包表示该包下所有的类都照此处理,顶层类也即如果该类有其他的实体也将照此处理
默认为@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)

@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
指定序列化javabean的顺序

@XmlElement
@XmlElement 注释可以与以下程序元素一起使用: JavaBean 属性 ,非 static、非 transient 字段 ,XmlElements 中的程序元素

  1. @XmlElement(name = "firstName")
  2. private String name;
  3. private int age;

如果顶层类中标志了访问类型,如Filed。那么各个fileds的名称会被处理为xml中的节点,若按照上面的标注,此时节点名会是firstName

  1. <firstName>nico</firstName>
  2. <age>20</age>

@XmlTransient 
@XmlTransient 注释对于解决 JavaBean 属性名称与字段名称之间的名称冲突,或者用于防止字段/属性的映射。

  1. @XmlTransient
  2. private String lastName;

则lastName不会被映射

@XmlList 
@XmlList 来将属性映射到列表简单类型。比较两种序列方式:

  1. List<String> data;
  2. <foo><data>aaaa</data><data>bbbb</data></foo>
  3. @XmlList
  4. List<String> data;
  5. <foo><data>aaaa bbbb</data></foo>

@XmlJavaTypeAdapter(class)
用来描述自定义的adapter,需要继承XmlAdapter,用来对自定义序列化的格式。如我们对时间的格式化输出

@XmlValue   
支持将类映射到带有 simpleContent 的 XML 模式复杂类型或 XML 模式简单类型。 简单说就是可以指定格式

  1. <msg name="name">robin</msg>

下面是一个Person的javabean,包含了一系列的声明:

  1. @XmlRootElement(name = "per")
  2. @XmlAccessorType(XmlAccessType.FIELD)
  3. @XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
  4. public class Person {
  5. @XmlElement(name = "firstName")
  6. private String         name;
  7. @XmlTransient
  8. private String         lastName;
  9. @XmlJavaTypeAdapter(value = DateXmlAdapter.class)
  10. private Date           birthDay;
  11. @XmlList
  12. private List<String>   lang;
  13. private List<KeyValue> msg;
  14. //getter/setter
  15. }

其中DateXmlAdapter如下:

  1. public class DateXmlAdapter extends XmlAdapter<String, Date> {
  2. @Override
  3. public String marshal(Date v) throws Exception {
  4. SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
  5. return format.format(v);
  6. }
  7. @Override
  8. public Date unmarshal(String v) throws Exception {
  9. SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
  10. return format.parse(v);
  11. }
  12. }

KeyValue:

  1. @XmlAccessorType(XmlAccessType.FIELD)
  2. public class KeyValue {
  3. @XmlAttribute
  4. private String name;
  5. @XmlValue
  6. private String value;

序列化结果为:

  1. Person p = new Person();
  2. p.setName("robin");
  3. p.setLastName("yang");
  4. p.setBirthDay(new Date());
  5. List<String> lang = Arrays.asList(new String[] { "java", "javascript", "python" });
  6. p.setLang(lang);
  7. List<KeyValue> msg = new ArrayList<KeyValue>();
  8. msg.add(new KeyValue("aa", "aaa-value"));
  9. msg.add(new KeyValue("bbb", "bbb-value"));
  10. p.setMsg(msg);
  1. <per>
  2. <birthDay>2012-11-17</birthDay>
  3. <lang>java javascript python</lang>
  4. <msg name="aa">aaa-value</msg>
  5. <msg name="bbb">bbb-value</msg>
  6. <firstName>robin</firstName>
  7. </per>

3、JAX-WS传递Map

前面在说到可以传递复杂对象时并没有指定Map,是因为并不支持原生的Map传递。如果需要传递我们需要做一个简单的包装,同时需要利用到上面说的XmlJavaTypeAdapter来对bean到xml转换转换。下面的例子是传递Map实例:

  1. @WebService
  2. public interface JaxWsMapService {
  3. @WebMethod
  4. public WrapperMapResult getPersonMap();
  5. }

WrapperMapResult 只是对Map的一个简单封装:

  1. @XmlRootElement
  2. @XmlAccessorType(XmlAccessType.FIELD)
  3. public class WrapperMapResult {
  4. private String              message;
  5. private boolean             success;
  6. @XmlJavaTypeAdapter(value = MapAdapter.class)
  7. private Map<String, Person> map;

其中最重要的是MapAdapter:

  1. public class MapAdapter extends XmlAdapter<MapConvertor, Map<String, Person>> {
  2. @Override
  3. public Map<String, Person> unmarshal(MapConvertor convertor) throws Exception {
  4. List<MapEntry> entries = convertor.getEntries();
  5. if (entries != null && entries.size() > 0) {
  6. Map<String, Person> map = new HashMap<String, Person>();
  7. for (MapEntry mapEntry : entries) {
  8. map.put(mapEntry.getKey(), mapEntry.getValue());
  9. }
  10. return map;
  11. }
  12. return null;
  13. }
  14. @Override
  15. public MapConvertor marshal(Map<String, Person> map) throws Exception {
  16. MapConvertor convertor = new MapConvertor();
  17. for (Map.Entry<String, Person> entry : map.entrySet()) {
  18. convertor.addEntry(new MapConvertor.MapEntry(entry));
  19. }
  20. return convertor;
  21. }
  22. }

这里声明了MapConvertor用来解析Map<String, Person>

  1. @XmlType(name="MapConvertor")
  2. @XmlAccessorType(XmlAccessType.FIELD)
  3. public class MapConvertor {
  4. private List<MapEntry> entries = new ArrayList<MapConvertor.MapEntry>();  //这里的思路是将Map传入List中传递
  5. public void addEntry(MapEntry entry) {
  6. entries.add(entry);
  7. }
  8. public List<MapEntry> getEntries() {
  9. return entries;
  10. }
  11. //结构依赖于需要Conver的Map,比如这里Map<String, Person>
  12. public static class MapEntry {
  13. private String key;
  14. private Person value;
  15. public MapEntry() {
  16. }
  17. public MapEntry(String key, Person person) {
  18. this.key = key;
  19. this.value = person;
  20. }
  21. public MapEntry(Map.Entry<String, Person> entry) {
  22. this.key = entry.getKey();
  23. this.value = entry.getValue();
  24. }
  25. public String getKey() {
  26. return key;
  27. }
  28. public void setKey(String key) {
  29. this.key = key;
  30. }
  31. public Person getValue() {
  32. return value;
  33. }
  34. public void setValue(Person value) {
  35. this.value = value;
  36. }
  37. }
  38. }

其他的步骤按照wsimport生产客户端代码调用即可,这样就完成了JAX-WS中map的传递

JAX-WS:背后的技术JAXB及传递Map的更多相关文章

  1. 全网显示 IP 归属地,这背后的技术你知道吗?

    为了进一步规范国内的网络舆论,国家规定了各互联网平台都需要显示 IP 归属地信息.微博.抖音.公众号等多个平台纷纷上线了 IP 归属地功能,这标志着国内言论的进一步规范化.但互联网平台商们是怎么通过 ...

  2. 阿里云全球首次互联网8K直播背后的技术解读

    3月28日,云栖大会·深圳峰会现场,阿里云发布并现场演示了阿里视频云最新8K互联网直播解决方案.这是全球发布的首个8K视频云解决方案,也是全球首次8K互联网视频直播. 视频地址:https://v.q ...

  3. Digg工程师讲述Digg背后的技术

    虽然最近业绩有所下滑,也出现了一些技术故障,但Digg作为首屈一指的社会化新闻网站,其背后的技术还是值得一探,Digg工程师 Dave Beckett 在今年4月份写一篇名为<How Digg ...

  4. 【沙龙报名中】与微信&云开发官方团队零距离互动,揭秘爆款微信小游戏背后的技术!

    有人说 微信小程序游戏的百花齐放 活像十几年前的4399小游戏称霸互联网的景象 " 歪,斗地主吗,三缺二, 不用下app,小程序就能玩,我保证不抢地主让你抢!" ...... &q ...

  5. Activity之间通过intent 传递Map

    //传递 Map<String,Object> data=orderlist.get(arg2-1); SerializableMap tmpmap=new SerializableMap ...

  6. Intent 传递Map数据

    android开发默认情况下,通过Bundle bundle=new Bundle();传递值是不能直接传递map对象的,解决办法: 第一步:封装自己的map,实现序列化即可 /** *序列化map供 ...

  7. android传递数据bundle封装传递map对象

    android开发默认情况下,通过Bundle bundle=new Bundle();传递值是不能直接传递map对象的,解决办法: 第一步:封装自己的map,实现序列化即可 ? 1 2 3 4 5 ...

  8. 揭秘|一探腾讯基于Kubeflow建立的多租户训练平台背后的技术架构

    腾讯业务及组织架构现状 先简单和大家介绍一下腾讯内部的业务及相关组织架构的现状,有助于帮助大家理解为什么我们会基于后面的架构来设计整套方案. 下图的应用大多数人经常会用到,比如微信.腾讯视频.游戏等等 ...

  9. 从蓝光到4K,腾讯视频高码率下载背后的技术

    欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 由 腾讯技术工程官方号 发布在云+社区 蓝光和4k视频正逐渐普及,4K视频峰值码率超10Mbit/s.架构平台部TVideo平台从资源,链路.缓 ...

随机推荐

  1. org.apache.commons.httpclient

    org.apache.commons.httpclient /** * post 方法 * @param url * @param params * @return */ public static ...

  2. linux,shell输入反斜杠显示'W'。

    linux,shell输入反斜杠显示'W'. solution: 字体必须为"Courier New".

  3. Oracle:Authid Current_User使用

    由于用户拥有的role权限在存储过程是不可用的.遇到这种情况,我们一般需要显示授权,如grant create table to user;但这种方法太麻烦,有时候可能需要进行非常多的授权才能执行存储 ...

  4. 为ecshop红包增加”转赠”功能

    ecshop促销中使用红包激励用户购物,要想炒热活动,红包就需要有物以稀为贵的感觉.有人求有人送,这样红包之间的转赠有助于拉动第二梯队的顾客.但是如果已经把红包添加到自己的账户了怎么办?如果ecsho ...

  5. [BIM]案例

    以下是中建三局BIM小组的项目,用以参考: BIM协同设计与质量控制 现实建筑物实体都是以三维空间状态存在,若用三维设计表达更具有优势.如复杂管综设计,一般情况下,二维AutoCAD设计是在建筑.结构 ...

  6. android pbap client 蓝牙

    一.  简介: 此功能具体使用的是bluetoothV2.1之后的Phone Book Access Profile功能,简称PBAP .目前MTK Android中只实现了server端的功能,并没 ...

  7. app framework map及ajax方法

    $(function () { $.ajax({ url: 'Ashx/GetProductList.ashx', contentType: "JSON", success: fu ...

  8. DedeCMS生成首页html静态文件的教程

    http://www.mubanzhijia.com/jishujiaocheng/826.html 在dedecms后台点击"更新主页Html"时,发生了什么?dedecms生成 ...

  9. common-pool2 使用

    common-pool2提供了3中对象池管理方式,它们的使用方式基本一样,这里以GenericObjectPool对象池为例介绍其使用方式,一般实现自己的对象池需要经过2个步骤 1.实现PooledO ...

  10. Educational Codeforces Round 16 B

    Description You are given n points on a line with their coordinates xi. Find the point x so the sum ...