清单 7. XMLBeans 客户机代码// create the client stub XmlbeansLibraryStub stub = new XmlbeansLibraryStub(target); // retrieve a book directly String isbn = "0061020052"; GetBookDocument gbd = GetBookDocument.Factory.newInstance(); GetBookDocument.GetBook gb = gbd.addNewGetBook(); gb.setIsbn(isbn); gbd.setGetBook(gb); GetBookResponseDocument gbrd = stub.getBook(gbd); BookInformation book = gbrd.getGetBookResponse().getGetBookReturn(); if (book == null) { System.out.println("No book found with ISBN '" + isbn + '''); } else { System.out.println("Retrieved '" + book.getTitle() + '''); } // retrieve the list of types defined GetTypesDocument gtd = GetTypesDocument.Factory.newInstance(); gtd.addNewGetTypes(); GetTypesResponseDocument gtrd = stub.getTypes(gtd); TypeInformation[] types = gtrd.getGetTypesResponse().getGetTypesReturnArray(); System.out.println("Retrieved " + types.length + " types:"); for (int i = 0; i < types.length; i++) { System.out.println(" '" + types[i].getName() + "' with " + types[i].getCount() + " books"); } // add a new book String title = "The Dragon Never Sleeps"; isbn = "0445203498"; try { AddBookDocument abd = AddBookDocument.Factory.newInstance(); AddBookDocument.AddBook ab = abd.addNewAddBook(); ab.setAuthorArray(new String[] { "Cook, Glen" }); ab.setIsbn(isbn); ab.setTitle(title); ab.setType("scifi"); stub.addBook(abd); System.out.println("Added '" + title + '''); title = "This Should Not Work"; ab.setTitle(title); stub.addBook(abd); System.out.println("Added duplicate book - should not happen!"); } catch (AddDuplicateFaultException e) { System.out.println("Failed adding '" + title + "' with ISBN '" + isbn + "' - matches existing title '" + e.getFaultMessage().getAddDuplicate().getBook().getTitle() + '''); } // create a callback instance BooksByTypeCallback cb = new BooksByTypeCallback(); // retrieve all books of a type asynchronously GetBooksByTypeDocument gbtd = GetBooksByTypeDocument.Factory.newInstance(); gbtd.addNewGetBooksByType().setType("scifi"); stub.startgetBooksByType(gbtd, cb); long start = System.currentTimeMillis(); synchronized (cb) { while (!cb.m_done) { try { cb.wait(100L); } catch (Exception e) {} } } System.out.println("Asynchronous operation took " + (System.currentTimeMillis()-start) + " millis"); if (cb.m_response != null) { BookInformation[] books = cb.m_response.getGetBooksByTypeResponse().getGetBooksByTypeReturnArray(); ...
清单 7 中的客户和对应的服务器代码可在 Axis2 1.1.1 下正常执行,但由于1.2 版中针对 XMLBeans 的错误处理代码生成存在问题,会在添加重复书籍 ID 时遇到意外异常,从而失败。这个问题应该已经在下一个 Axis2 版本中得到解决。
尽管 XMLBeans 声称 100% 支持 XML 模式,但这个说法的准确性有待商榷。对于几乎任何模式构造,XMLBeans 都生成了一组能用于读写匹配此模式的文档。但与本文讨论的其他数据绑定框架不同的是,XMLBeans 缺省情况下并不进行任何工作来执行模式。例如,如果将清单 7 代码中添加的设置书籍标题的代码行注释掉,XMLBeans 仍然能正常读写缺少所需的 <title> 元素(因而无效)的文档。清单 8 显示了此代码更改,并给出了发送到服务器进行添加请求的 XML 以及检索书籍时从服务器返回的 XML。对于检索响应,XML 文档包括 <title> 元素,但使用了 xsi:nil="true" 属性,这在模式中是不允许的,因此同样也无效。
清单 8. XMLBeans 客户机和无效文档 AddBookDocument abd = AddBookDocument.Factory.newInstance(); AddBookDocument.AddBook ab = abd.addNewAddBook(); ab.addAuthor("Cook, Glen"); ab.setIsbn(isbn); ab.setType("scifi"); // ab.setTitle(title); System.out.println("Validate returned " + abd.validate()); stub.addBook(abd); ... <addBook xmlns="http://ws.sosnoski.com/library/wsdl"> <type>scifi</type> <isbn>0445203498</isbn> <author>Cook, Glen</author> </addBook> <getBooksByTypeResponse xmlns="http://ws.sosnoski.com/library/wsdl"> ... <getBooksByTypeReturn isbn="0445203498" type="scifi"> <author xmlns="http://ws.sosnoski.com/library/types">Cook, Glen</author> <title xmlns="http://ws.sosnoski.com/library/types" xmlns:xsi="http://www.w3.org/2001 /XMLSchema-instance" xsi:nil="true"/> </getBooksByTypeReturn> </getBooksByTypeResponse>
这是未设置所需值的一个简单示例。对于更为复杂的模式,XMLBeans 生成的 API 可能会隐藏更多的缺陷。XMLBeans 用户邮件列表的最近一次讨论曾谈及这样的情况,即必须将值添加到两个不同的列表,以更改生成正确输出的顺序。因此 XMLBeans 要求开发人员既要了解模式,还要了解生成的代码如何与模式相关,以确保应用程序代码构建有效的 XML 文档。数据绑定框架的一个主要好处是,通常能够对开发人员隐藏模式的这些细节,而 XMLBeans 显然在这方面做得并不好。
可以通过调用生成类中包括的 validate() 方法来避免 XMLBeans 处理和生成无效 XML 文档的问题。如果使用 XMLBeans,则应至少在测试和开发期间使用此方法来检查所有文档。不过,验证对性能具有很大的影响(正如您在下一篇文章中将看到的,即使不对每个文档调用 validate(),XMLBeans 也已经非常慢了),因此很多应用程序都应该在生产部署中避免验证开销。就结果信息而言,验证也具有相当的局限性。为了避免导致验证错误,应该对出现错误的文档运行独立的模式验证。
JiBX
JiBX(我自己开发的)是主要侧重使用现有 Java 类(而不是从模式进行代码生成)的数据绑定框架。对于 JiBX,要首先创建绑定定义,以定义 Java 数据对象与 XML 之间如何转换,然后使用可通过添加实现转换的方法(作为字节码)来增强数据类文件的工具对该绑定进行编译。JiBX 运行时框架将随后使用这些添加的方法来在数据和 XML 之间进行转换。
JiBX 方法提供了一些独有的优势,也有自己独有的缺点。就好的一面而言,JIBX 可让您在 Web 服务接口添加到现有服务代码的情况下直接使用现有类。Jibx2Wsdl 工具特别适合这种用途,因为它会生成所需的所有内容,从而方便地将现有代码作为 Axis2 服务部署。可以在使用单个数据模型的情况下为相同的类定义不同的绑定,以同时用于文档的不同 XML 版本。通过修改绑定,甚至通常能在重构数据类的情况下保持相同的 XML 表示形式。
清单 9 显示了 JiBX 客户机代码中有意义的部分,其中使用了匹配消息元素的类。此代码与清单 5 中所示的 ADB 对等项类似,因此这里就不详细讨论了。唯一值得注意的差异是,由于数据类和消息类都在用户的控制之下,因此可以方便地向通过 JiBX 使用的类添加常规构造函数(如 AddBookRequest 的情况)和其他支持方法。
清单 9. JIBX 客户机代码// create the server instance JibxLibraryStub stub = new JibxLibraryStub(target); // retrieve a book directly String isbn = "0061020052"; GetBookResponse bresp = stub.getBook(new GetBookRequest(isbn)); Book book = bresp.getBook(); if (book == null) { System.out.println("No book found with ISBN '" + isbn + '''); } else { System.out.println("Retrieved '" + book.getTitle() + '''); } isbn = "9999999999"; bresp = stub.getBook(new GetBookRequest(isbn)); book = bresp.getBook(); if (book == null) { System.out.println("No book found with ISBN '" + isbn + '''); } else { System.out.println("Retrieved '" + book.getTitle() + '''); } // retrieve the list of types defined GetTypesResponse tresp = stub.getTypes(new GetTypesRequest()); Type[] types = tresp.getTypes(); System.out.println("Retrieved " + types.length + " types:"); for (int i = 0; i < types.length; i++) { System.out.println(" '" + types[i].getName() + "' with " + types[i].getCount() + " books"); } // add a new book String title = "The Dragon Never Sleeps"; isbn = "0445203498"; try { AddBookRequest abr = new AddBookRequest("scifi", isbn, title, new String[] { "Cook, Glen" }); stub.addBook(abr); System.out.println("Added '" + title + '''); title = "This Should Not Work"; abr = new AddBookRequest("scifi", isbn, title, new String[] { "Nobody, Ima" }); System.out.println("Added duplicate book - should not happen!"); } catch (AddDuplicateFaultException e) { System.out.println("Failed adding '" + title + "' with ISBN '" + isbn + "' - matches existing title '" + e.getFaultMessage().getBook().getTitle() + '''); } // create a callback instance BooksByTypeCallback cb = new BooksByTypeCallback(); // retrieve all books of a type asynchronously stub.startgetBooksByType(new GetBooksByTypeRequest("scifi"), cb); long start = System.currentTimeMillis(); synchronized (cb) { while (!cb.m_done) { try { cb.wait(100); } catch (Exception e) {} } } System.out.println("Asynchronous operation took " + (System.currentTimeMillis()-start) + " millis"); if (cb.m_response != null) { Book[] books = cb.m_response.getBooks();
清单 10 显示了对等的 JiBX 取消包装代码。和 ADB 取消包装代码类似,理解和使用服务调用的取消包装形式比使用直接代码简单得多。JiBX 和 ADB 版本唯一重要的区别在于,对于 JiBX,并不需要在不传递值时创建对象,而 getTypes() 调用的 ADB 版本则要求进行此工作。JiBX 取消包装支持也比 ADB 版本更为稳定一些,因为从 Axis2 1.1.1 起就提供了全面支持。
清单 10. JIBX 取消包装客户机代码// create the server instance JibxUnwrapLibraryStub stub = new JibxUnwrapLibraryStub(target); // retrieve a book directly String isbn = "0061020052"; Book book = stub.getBook(isbn); if (book == null) { System.out.println("No book found with ISBN '" + isbn + '''); } else { System.out.println("Retrieved '" + book.getTitle() + '''); } // retrieve the list of types defined Type[] types = stub.getTypes(); System.out.println("Retrieved " + types.length + " types:"); for (int i = 0; i < types.length; i++) { System.out.println(" '" + types[i].getName() + "' with " + types[i].getCount() + " books"); } // add a new book String title = "The Dragon Never Sleeps"; isbn = "0445203498"; try { stub.addBook("scifi", isbn, new String[] { "Cook, Glen" }, title); System.out.println("Added '" + title + '''); title = "This Should Not Work"; stub.addBook("xml", isbn, new String[] { "Nobody, Ima" }, title); System.out.println("Added duplicate book - should not happen!"); } catch (AddDuplicateFaultException e) { System.out.println("Failed adding '" + title + "' with ISBN '" + isbn + "' - matches existing title '" + e.getFaultMessage().getBook().getTitle() + '''); } // create a callback instance BooksByTypeCallback cb = new BooksByTypeCallback(); // retrieve all books of a type asynchronously stub.startgetBooksByType("scifi", cb); long start = System.currentTimeMillis(); synchronized (cb) { while (!cb.m_done) { try { cb.wait(100L); } catch (Exception e) {} } } System.out.println("Asynchronous operation took " + (System.currentTimeMillis()-start) + " millis"); if (cb.m_books != null) { Book[] books = cb.m_books;
JiBX 取消包装支持在使用的类方面也与 ADB 有差别。当使用 ADB 取消包装时,所有消息元素的类仍然在后台生成和使用。对于 JiBX,使用直接处理时必须将类定义为与消息元素对应,如清单 9 中所示;对于取消包装处理,只有作为值传递的类需要在绑定定义中加以定义和包括。无论采用哪种方法,JiBX 绑定定义都需要在运行 Axis2 WSDL2Java 工具前创建,而且需要使用 -Ebindingfile 命令行参数传入。
JiBX 绑定方法最大的缺陷(至少从 Web 服务方面可以这样说)可能就是,JiBX 目前对从 XML 模式定义工作的支持非常微弱。不过,即使是 Xsd2Jibx 工具提供的这个微弱支持,也尚未集成到 Axis2 WSDL2Java 代码生成中。这意味着需要在运行 WSDL2Java 生成 Axis2 链接代码前创建 Java 数据类和绑定定义。JiBX 所需的字节码增强步骤在某些环境中也可能出现问题,因为通常需要在应用程序构建时进行此工作,可在类中得到没有源代码可用的代码。
正如前面所提到的,JiBX 数据绑定提供了一些独特的好处。就 Axis2 使用而言,JiBX 也提供了优于其他框架的优势,即支持能够插入到 Axis2 版本中纠正发布之后发现的错误的错误修复程序版本(有关详细信息,请参见参考资料部分的“获得相关产品和技术”)。对于其他框架,获取错误修复程序的唯一方式就是迁移到 Axis2 的更高版本,而这样通常会带来其他问题。预计将来 JiBX 将提供稳定的从模式生成代码和绑定的支持。到提供此功能的那一天,就可以将 JiBX 视为 Axis2 的优秀全能数据绑定备选方案了。到那时,使用现有 Java 代码的做法将是最佳的,而 Jibx2Wsdl 工具恰恰在这方面提供了出色的支持。
总结
Axis2 目前提供对三种不同数据绑定框架的全面支持:
ADB 专门为 Axis2 设计,仅能用于 Axis2 环境中。对于 Axis2 1.3 版,提供了对从模式生成代码的良好支持(并在不断改进)。另外还支持常规取消包装服务方法和自动附件处理,从而使其成为了从现有 WSDL 服务定义着手时的首要选项。
XMLBeans 提供对在生成的 Java 代码中建模模式结构的最全面支持。但其会创建给定模式的最为复杂的 Java 模型,并不支持使用取消包装服务方法来实现更为简单的接口。缺省情况下,甚至不会对输入或输出 XML 文档执行基本模式结构规则,从而很可能会创建无效的 XML 文档或接受此类文档进行处理。
JiBX 是唯一支持使用现有 Java 类的备选方案。另外还提供了对取消包装服务方法出色的支持。但集成到 Axis2 中的 JiBX 支持并不处理从模式生成代码的情况,甚至 JiBX 工具所提供的从模式生成代码的独立支持目前还仅限于模式支持。
Axis2 提供了这些数据绑定框架间的选择,这非常好,因为并没有那一个选项能满足所有需求。将来 Axis2 还将提供对 JAXB 2.0 的全面支持,我将在本系列关于相关 JAX-WS 2.0 的后续文章中对此进行讨论。了解每个框架的优缺点可帮助您根据自己的需求作出最适当的选择,并在投入生产前了解潜在的问题区域。在本系列的下一篇文章中,我们将了解 Axis2 数据绑定框架比较的另一个方面:性能。
描述 |
名字 |
大小 |
下载方法 |
Sample code (Axis2 1.2 and earlier) |
code.zip |
82KB |
HTTP |
Sample code (Axis2 1.3) |
code1_3.zip |
87KB |
HTTP |
(编辑:aniston)
|