JavaWeb服务,第3部分:Axis2数据绑定

相关文章:

Java Web服务,第1部分: Java Web服务在未来一年内的发展

Java Web服务,第2部分: 深度探索Axis2:AXIOM

Apache Axis2 Web 服务框架一开始就设计用于支持多种 XML 数据绑定方法。当前的版本 提供对 XMLBeans 和 JiBX 数据绑定以及专门针对 Axis2 开发的自定义 Axis 数据绑定 (Axis Data Binding,ADB)的全面支持。本文将说明如何将这些不同的数据绑定方法与 Axis2 结合使用,并说明为什么可能会为应用程序优先选择其中的一种方法。

尽管 XML 消息交换是 Web 服务的核心,但大部分 Web 服务应用程序都不会对 XML 的问 题进行考虑。相反,这些应用程序希望交换特定于应用程序的业务数据。在这种情况下,XML 仅仅是用于表示业务数据以支持 Web 服务接口的一个格式而已。XML 可很好地满足此用途, 因为它提供了独立于平台的表示形式,可供各种工具进行处理。但应用程序最终需要将 XML 转换为其内部数据结构(或反向转换),以便在应用程序内使用此数据。

数据绑定 是指处理 XML 和应用程序数据结构间的这种转换的技术。可以为应用程序编写 自定义数据绑定代码,但大部分开发人员发现使用数据绑定框架更为方便,此类框架将以通 用的方式处理此转换工作,适用于各种应用程序。Apache Axis2 Web 服务框架的一个主要优 势在于,此框架从最开始就设计为使用各种数据绑定框架。可以选择最适合您的需求的数据 绑定方法,并使用此方法来处理 XML 与数据结构间的转换,同时使用 Axis2 框架(及扩展 )来处理实际的 Web 服务工作。

本文将通过使用三个受支持的不同数据绑定实现的同一个 Web 服务的示例代码说明如何 使用 Axis2 提供的数据绑定灵活性。可以通过其中了解为何可能会优先选择其中某个数据绑 定。

链接到 Axis2

在本系列的前一篇文章中,我们已经了解了 Axis2 用于 XML 消息绑定的 AXIOM 文档模 型。AXIOM 与其他文档模型的不同之处在于,它支持根据需要绑定模型,而不用一次性完成 此工作。当使用数据绑定框架在 XML 和应用程序数据结构之间进行转换时,数据绑定 XML 通常只是 AXIOM 文档模型的一个虚拟部件。除非由于某些原因而需要此模型(用于使用 WS -Security 进行加密或签名时),否则就不会扩展为完整文档模型。

为了隔离应用程序,避免直接使用 AXIOM 的情况,Axis2 支持从 Web 服务描述语言 (Web Services Description Language,WSDL)服务描述生成链接代码。所生成的链接代码 使用所选数据绑定框架处理数据结构与 XML 之间的转换细节,让您的应用程序直接访问数据 结构。Axis2 还从另一方面提供了有一定限制的支持,从现有代码生成 WSDL。

Axis2 可为服务客户机和服务提供者生成链接代码。客户机链接代码采用存根类的形式, 始终从 Axis2 org.apache.axis2.client.Stub 类进行扩展。提供者(或服务器)链接代码 采用服务特定的实现框架的形式提供,并提供实现 org.apache.axis2.engine.MessageReceiver 接口的消息接收器类。客户机和服务器链接代 码生成工作都由 WSDL2Java 工具进行处理。接下来,我们将了解实际的链接代码,然后将详 细讨论如何使用 WSDL2Java 工具,最后将简单说明如何从现有代码着手进行相关工作。

客户机链接代码

客户端存根代码为应用程序代码定义访问方法,以调用服务操作。首先要创建存根类的实 例,通常使用缺省构造函数(如果服务端点总是与用于生成存根的 WSDL 中定义的端点相同 ),或使用接受以字符串形式提供的其他端点引用的构造函数进行此工作。创建了存根的实 例后,可以选择使用 org.apache.axis2.client.Stub 基类定义的方法来配置各个功能。然 后可以调用服务特定的访问方法来实际调用操作。

清单 1 给出了一个示例,说明如何使用更改为客户机系统 (localhost) 上的缺省 Tcpmon 端口 8800 的服务端点(其在 WSDL 中的任意设定值)创建存根。Tcpmon 是用于监 视 Web 服务交换的一个流行工具,因此在客户机代码中使用此选项通常非常有用。创建了存 根实例后,超时值的缺省值将改变(调试提供者代码时也很有用,因为很容易就会超过标准 的 20 秒超时设置),并会调用服务方法。

清单 1. 客户机存根用法示例

LibraryStub stub = new LibraryStub ("http://localhost:8800/axis2/services/library");stub.getServiceClient().getOptions().setTimeoutInMilliseconds(10000000);Types[] types = stub.getTypes();

清单 1 中的代码显示了同步方法调用,其中的客户机线程将阻塞在服务调用内,在调用 完成且结果可用之后才会返回。Axis2 还支持使用回调接口进行异步调用。清单 2 显示了经 过修改的清单 1 代码,其中使用了一个小小的异步调用(应用程序代码仅仅等待操作完成, 而不进行任何有用的工作)。

清单 2. 客户机存根异步示例LibraryStub stub = new LibraryStub ("http://localhost:8800/axis2/services/library");TypesCallback cb = new TypesCallback();stub.startgetTypes(cb);Type[] types;synchronized (cb) {while (!cb.m_done) { try {  cb.wait(); } catch (Exception e) {}}types = cb.m_result;if (types == null) { throw cb.m_exception;}}...private static class TypesCallback extends LibraryCallbackHandler{private boolean m_done;private Types[] m_result;private Exception m_exception;public synchronized void receiveResultgetTypes(Type[] resp) {  m_result = resp;  m_done = true;  notify();}public synchronized void receiveErrorgetTypes(Exception e) {  m_exception = e;  m_done = true;  notify();}}

对于 HTTP 连接(如清单 2 中的情况),响应通常将立即返回到客户机。在将请求同响 应分离的传输——如 Java™ Message Service (JMS) 或简单邮件传输协议 (Simple Mail Transfer Protocol,SMTP)——上操作时,异步调用最有用,因 为在这种情况下,请求发出时间和接收到响应的时间存在很大的延迟。当然,使用 HTTP 访 问的服务还可能涉及到大量的处理延迟。对于具有此类延迟的 HTTP 服务,可以使用 WS- Addressing 来支持分离的响应,异步调用非常适合用于处理这些响应。

除了存根类(如果使用异步支持生成的话,还包括回调处理程序类)外,还有为客户机代 码生成的接口。接口定义与 WSDL portType 所定义的操作匹配的服务方法。存根实现此接口 ,并添加一些供内部使用的方法。可以直接使用存根,如清单 1 和清单 2 中所示,还可以 使用接口来仅仅使用属于服务定义的方法。无论采用哪种方式,调用服务方法时,存根都将 使用所选数据绑定框架处理将请求数据对象转换为 XML,以及将返回的 XML 转换为响应数据 对象的工作。

如果希望在客户机上直接使用 XML,则根本不需要使用生成的客户机存根类;可以转而使 用 org.apache.axis2.client.ServiceClient 类。这样做意味着需要首先配置服务和操作, 然后调用 ServiceClient.createClient() 方法为操作创建 org.apache.axis2.client.OperationClient。为了方便起见,WSDL2Java 工具(本文稍后讨 论)提供了相应的选项,可在即使直接使用 XML 的情况下生成存根类。这种情况下生成的存 根与数据绑定示例类似,但其中传递的是 AXIOM 元素而不是数据对象。

服务器链接代码

Axis2 的服务器端链接代码是作为 Axis2 服务器配置的一部分定义的消息接收器类。此 消息接收器必须实现 org.apache.axis2.engine.MessageReceiver 接口。此接口定义单个 void receive(org.apache.axis2.context.MessageContext) 方法。在接收到请求消息时, Axis2 框架将调用此方法,然后由此方法负责处理请求的所有处理工作(包括在合适的情况 下生成响应)。

如果直接使用 XML(采用 AXIOM 元素的形式),则可以利用服务器端链接的标准 org.apache.axis2.receivers.RawXML*MessageReceiver 类之一(其中 * 描述服务使用的消 息交换类型)。否则,就可以使用生成的消息接收器类,其在基于 Axis2 AXIOM 的接口和使 用数据对象的服务代码之间进行适配。此服务代码以框架实现的形式生成,其中包含直接引 发异常的服务方法。您需要向框架添加自己的代码,以完成服务器端挂钩。

清单 3 显示了服务端框架的示例(为了便于阅读,进行了格式调整),其中的 getBook () 方法保持生成时的原样,getTypes() 方法通过委托到实际实现类进行实现。

清单 3. 服务器框架示例public class LibrarySkeleton{  private final LibraryServer m_server;  public LibrarySkeleton() {    m_server = new LibraryServer();  }  /**   * Auto generated method signature   *   * @param isbn   * @return book value   */  public com.sosnoski.ws.library.Book getBook(java.lang.String isbn) {    //Todo fill this with the necessary business logic    throw new java.lang.UnsupportedOperationException("Please implement " +      this.getClass().getName() + "#getBook");  }  /**   * Get the types of books included in library.   *   * @return types   */  public com.sosnoski.ws.library.Type[] getTypes() {    return m_server.getTypes();  }}

直接向此类添加代码的缺点在于,如果服务器接口更改,则需要重新生成此类并包含更改 。可以通过添加扩展生成的框架的独立实现类来避免这种情况,从而能在不更改生成的代码 的情况下重写框架方法。为此,需要对生成的 services.xml 服务描述进行更改。所需的工 作很简单,直接使用实现类名称替换框架类名称即可。本文稍后将讨论的数据绑定示例全部 使用独立的实现类方法。可以在下载部分获得这些示例 Ant build.xml 文件,以了解如何自 动进行替换。

Axis2 工具

Axis2 提供了一系列工具来帮助开发人员使用此框架。其中最重要的是允许从 WSDL 服务 定义生成 Java 链接代码(在下面讨论)的工具和从现有 Java 代码生成 WSDL 服务定义的 工具。

从 WSDL 生成代码

Axis2 提供了 WSDL2Java 工具,用于从 WSDL 服务定义生成代码。可以通过将 org.apache.axis2.wsdl.WSDL2Java 类作为 Java 应用程序运行来直接使用此工具,也可以 通过 Ant 任务、Maven 插件或 Eclipse 或 IDEA 插件。拥有这么多选择的缺点在于,从功 能和错误修补方面而言,备选方案通常滞后于基本 Java 应用程序,因此通常可能最好直接 运行 Java 应用程序(本文将对此进行讨论)。

WSDL2Java 提供很多不同的命令行选项,而且选项的数量还会随着时间的增加而增加。 Axis2 文档包括了选项的完整参考,这里将仅仅讨论一些最为重要的内容:

-o path — 设置用于输出类和文件的目标目录(缺省输出到工作目录)

-p package-name — 设置生成的类的目标包(缺省为从 WSDL 命名空间生成)

-d name — 设置数据绑定框架(adb 表示 ADB,xmlbeans 表示 XMLBeans,jibx 表示 JiBX 以及 none 表示无数据绑定;adb 为缺省选项)

-uw — 取消 doc/lit-wrapped 消息的包装,仅适用于受支持的框架(目前包括 ADB 和 JiBX)

-s — 仅生成同步客户机接口

-ss — 生成服务器端代码

-sd — 生成服务器端部署文件

-uri path — 为要生成的服务设置指向 WSDL 的路径

还有一些专门针对特定数据绑定框架的 WSDL2Java 选项。稍后讨论数据绑定示例时会看 到几个此类选项。

从代码生成 WSDL

Axis2 还提供了 Java2WSDL 工具,可用于从现有服务代码生成 WSDL 服务定义。不过, 此工具有很多限制,包括无法使用 Java 集合类以及在从 Java 类生成的 XML 的结构处理方 面不灵活。造成这些限制的部分原因是,由于 Web 服务开发方式的改变,使得大家对此领域 没有太多的兴趣。

总的说来,Web 服务和 SOA 领域的很多权威都对从现有代码生成 Web 服务不屑一顾。他 们感觉从代码着手会增加 XML 消息结构与特定实现间的偶合,而 Web 服务的总体原则是 XML 应该独立于实现。对此当然有很多支持的声音,但也有人表示反对。其中一个原因涉及 到从头编写 WSDL 服务和 XML 模式定义的困难性。WSDL 和模式都是复杂的标准,用于处理 这些定义的可用工具都要求对标准足够了解,才能够有效地加以使用。如果开发人员在不以 标准为基础的情况进行此工作,所得到的 WSDL 和模式经常比从代码生成的更为凌乱。另一 个问题非常现实。开发人员通常拥有实现某个功能的现有代码,需要将其作为 Web 服务公开 ,而他们希望能够在不用进行大量更改的情况下使用现有代码。因此从代码生成 WSDL 在可 预知的未来一段时间内将仍然可能是个需要考虑的问题。

如果希望使用更为强大的工具代替 Java2WSDL,可以尝试我开发的 Jibx2Wsdl(有关更多 信息,请参见参考资料)。Jibx2Wsdl 可从提供的一个或多个服务类生成完整的 WSDL 绑定 、模式绑定和 JiBX 绑定定义。它支持 Java 5 枚举和通用集合,并同时保留了与旧版本 Java 虚拟机(Java Virtual Machine,JVM)的兼容性,可自动从 Java 源文件将 Javadoc 作为生成的 WSDL 和模式定义的文档导出。Jibx2Wsdl 还提供了广泛的自定义机制来控制服 务和 XML 表示形式从 Java 类派生的方式,其中甚至允许将 Java 5 之前的集合与类型化数 据一起使用。尽管 Jibx2Wsdl 专门设计为通过 JiBX 数据绑定框架(也是我创建的)简化将 现有类作为 Web 服务部署的工作,但生成的 WSDL 和模式都是独立于数据绑定的。可以将其 与其他 Java 数据绑定框架甚至其他平台一起使用——生成所需的一切对象,然 后去掉 JiBX 绑定并保留其他部分即可。

如果您使用 Java 5 或更高版本,则另一个备选方案是使用 Java Architecture for XML Binding (JAXB) 2.0 和 Java API for XML Web Services (JAX-WS) Annotation 来将数据 对象和服务类作为 Web 服务公开。这些 Annotation 并不提供与 Jibx2Wsdl 相同级别的自 定义,但其允许直接在源代码中嵌入配置信息,有些开发人员很喜欢这样做。Axis2 的 1.2 版为 JAXB 2.0 和 JAX-WS 提供了试验支持,这而且会在将来的版本中进一步改进。 Jibx2Wsdl 以后的版本也可能支持使用 JAXB 2.0 和 JAX-WS Annotation 进行自定义。(本 系列后续文章中将更为深入地讨论 JAXB 2.0 和 JAX-WS,请关注关于从代码生成 WSDL 的这 个主题。)

数据绑定的比较

Axis2(对于 1.2 版)完全支持三种数据绑定备选方案,而且目前正在进行添加更多绑定 支持的工作。本文将对使用三种完全受支持的数据绑定框架的示例代码进行比较,并讨论每 个框架与 Axis2 一起使用的一些优缺点。

清单 4 中所示的示例代码(也包括在下载部分的示例下载中)用于图书馆服务,该服务 维护按主题类型整理的书籍集合。在此服务上定义了多个操作,包括:

getBook

getTypes

addBook

getBooksByType

清单 4 提供了此服务的 WSDL 的部分内容,仅仅显示了 getBook 操作中涉及的部分。

清单 4. 图书馆服务的 WSDL                                                                     ...                                       ...            ...             ...                          vi       ...  ...

实际的服务实现代码非常简单,即采用硬编码书籍清单填充图书馆实例。客户机代码按照 以下顺序执行一系列查询:

一个 getBook

一个 getTypes

两个 addBook,第二个返回尝试添加重复书籍 ID 的 SOAP 错误

一个 getBooksByType

示例间的实现细节有所差别,因为每个示例使用适合于其数据绑定的数据对象。除非专门 说明,否则所显示的所有代码对 Axis2 1.1.1 和 1.2 都完全一样。Axis2 1.3 版(撰写本 文时正在进行开发)要求对代码进行一些小的更改,因为其中与服务错误对应的生成异常类 的命令发生了变化。提供了两个版本的代码供下载(请参见下载部分)。

在本文中,我们将仅讨论客户机代码,不过提供的下载(请参见下载部分)包括了所有示 例的客户机和服务器代码以及 Ant 构建文件。接下来,让我们分析三个数据绑定框架对应的 客户机代码,了解每种方法的优缺点。

Axis2 数据绑定

ADB 是 Axis2 的数据绑定扩展。与其他数据绑定框架不同,ADB 代码仅可用于 Axis2 Web 服务。这个限制是 ADB 的一大局限,但也带来了一些好处。由于 ADB 与 Axis2 进行了 集成,因此其代码可针对 Axis2 要求进行优化。这方面的一个例子就是,ADB 构建于位于 Axis2 核心的 AXis 对象模型(AXis Object Model,AXIOM)文档模型(我们已在本系列的 前一篇文章中对此进行了讨论)之上。ADB 还提供了一些目前其他数据绑定框架所没有的增 强功能,包括自动附件处理。WSDL2Java 提供了对 ADB 代码生成的全面支持,其中包括生成 与 XML 模式组件对应的数据模型类。

ADB 模式支持具有一定的局限。在当前的 Axis2 1.2 版本中,这些局限包括模式功能, 如使用 maxOccurs=”unbounded” 的组合器、使用 attributeFormDefault=”qualified” 的模式定义和其他一些类似的变体。但 Axis2 1.2 ADB 模式支持比 Axis2 1.1 版要好得多,而且此支持 Axis2 框架的每个发布版 本将逐步改进,直到支持所有主要模式功能为止。

ADB 代码生成的基本形式是使用直接模型,其中包含与每个操作使用的输入与输出消息对 应的独立类。清单 5 显示了使用此基本 ADB 代码生成模式的示例应用程序的客户机代码中 最有意义的部分。此客户机代码说明了与 ADB 生成的类的交互,如用作 getBook() 方法调 用的参数的 GetBookDocument 和 GetBookDocument.GetBook 类以及用于从此调用获取返回 结果的 GetBookResponseDocument 和 BookInformation 类。

清单 5. ADB 客户机代码

// create the client stubAdbLibraryStub stub = new AdbLibraryStub(target);// retrieve a book directlyString isbn = "0061020052";GetBook gb = new GetBook();gb.setIsbn(isbn);GetBookResponse gbr = stub.getBook(gb);BookInformation book = gbr.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 definedGetTypesResponse gtr = stub.getTypes(new GetTypes());TypeInformation[] types = gtr.getGetTypesReturn();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 bookString title = "The Dragon Never Sleeps";isbn = "0445203498";try {  AddBook ab = new AddBook();  ab.setType("scifi");  ab.setAuthor(new String[] { "Cook, Glen" });  ab.setIsbn(isbn);  ab.setTitle(title);  stub.addBook(ab);  System.out.println("Added '" + title + '/'');  ab.setTitle("This Should Not Work");  stub.addBook(ab);  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 instanceCallbackHandler cb = new CallbackHandler();// retrieve all books of a type asynchronouslyGetBooksByType gbbt = new GetBooksByType();gbbt.setType("scifi");stub.startgetBooksByType(gbbt, 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) {  BookInformation[] books = cb.m_response.getGetBooksByTypeReturn();  ...

清单 5 使用利用 -u WSDL2Java 选项生成的代码,该代码特定于 ADB。通过使用此选项 ,将为每个消息和数据模型类生成独立的 Java 源文件;如果未 使用此选项,ADB 代码生成 操作会将所有这些类作为生成的存根的静态内部类生成。使用独立的类要方便得多,因此, 如果使用 ADB,则应该使用 -u 选项。

代码生成的直接形式会导致为每个操作的输入和输出生成大量的独立类(不会受到 -u 选 项的影响,此选项仅仅以不同的方式组织相同的类而已)。这些生成的消息类经常包含极少 (对于 GetTypes 类,甚至没有)有用数据,但生成的消息签名需要这些类。幸运的是,还 有适用于很多常见服务的备用代码生成形式。

ADB 取消包装

Web 服务经常以方法调用形式基于现有编程 API 开发,在此情况下,将现有 API 嵌入到 Web 服务内的做法将非常方便。这种做法非常简单;为服务定义的操作(如果要深究的话, 从技术上是为端口类型定义的)实际上等效于接口定义中的方法调用。唯一的主要区别在于 ,服务将输入和输出定义为 XML 消息,而不是调用参数和返回值。因此,为了在 Web 服务 定义中嵌入现有 API,您只需要约定如何将调用参数和返回值表示为 XML 消息结构即可。

幸运的是,Microsoft® 早期就确立了此领域的一个约定,为其他人节约了建立自己 约定的时间。此约定称为 wrapped document/literal,是 .NET 在将方法调用作为 Web 服 务操作公开时使用的缺省表示形式。实际上,此包装方法规定每个输入消息都是仅包含子元 素序列的 XML 元素,而每个输出消息都是具有单个子元素的 XML 元素。除了完全 .NET 互 操作性外,Microsoft 实现还有一些其他并不重要的技术细节,但用于图书馆示例(请参见 清单 4 中给出的部分代码)的消息并不是为了这些细节而采用此方法。

WSDL2Java 支持在 ADB 代码生成中对此类 wrapped doc/lit 服务进行取消包装操作。当 对合适的 WSDL 服务定义使用取消包装操作时,生成的客户机存根(以及服务器代码框架) 将更为简单和直接。清单 6 显示了与清单 5 等效的客户机应用程序代码,不过其中向 WSDL2Java 传递了 -uw 参数,以生成取消包装接口。清单 5 中增加的复杂性层次的消息类 几乎都从清单 6 中去掉了(除了 GetTypes 类),服务方法直接接受参数和返回值,而不是 嵌入在消息类中。实际上,ADB 仍然生成消息类,并在生成的代码中使用这些类,但代码通 常会忽略这些类,而直接使用数据。

清单 6. ADB 取消包装客户机代码

// create the client stubAdbUnwrapLibraryStub stub = new AdbUnwrapLibraryStub(target);// retrieve a book directlyString isbn = "0061020052";BookInformation 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 definedTypeInformation[] types = stub.getTypes(new 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 bookString 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 instanceBooksByTypeCallback cb = new BooksByTypeCallback();// retrieve all books of a type asynchronouslystub.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) {  BookInformation[] books = cb.m_books;  ...

ADB 取消包装支持的主要问题在于其尚不完全稳定。清单 6 中的代码适合与 Axis2 1.2 一起使用,其中包含了相对于 Axis2 1.1.1 中使用的 ADB 取消包装进行的几项大改进。但 WSDL2Java 工具要求修改与其他示例使用的 WSDL 文档的结构,以便在此示例中使用,即将 数据类的内联模式移动到独立的模式文档中。最重要的是,清单 6 中所示的代码并不能全部 都正常工作;代码的最后一部分(即使用异步操作的部分)在运行时出现错误,由于生成的 ADB 客户机存根代码中的错误引发了类强制转换异常。

Axis2 1.2 的后续版本发布时,ADB 取消包装问题应该得到了极大的消除。但 ADB 并不 是让 Axis2 支持取消包装的唯一数据绑定框架。JiBX 也提供取消包装支持,JiBX 版本从 Axis2 1.1.1 发布以来就稳定了。可以在本文稍后(讨论了 Axsi 2 数据绑定主要选项后) 了解 JiBX 客户机代码。

XMLBeans

XMLBeans 是包含数据绑定层的通用 XML 处理框架。其源自一个 BEA Systems 项目,后 来提交给了 Apache Foundation。XMLBeans 是 Axis2 支持的第一种数据绑定形式,并将继 续作为与 Axis2 一起使用的热门选项(特别是使用复杂模式定义时)。

清单 7 显示了示例应用程序的 XMLBeans 客户机代码中最有意义的部分。对于基本(非 取消包装)ADB 代码,每个操作的输入和输出都有一个对应的独立类。但 XMLBeans 与 ADB 并不相同,其中具有针对包含输入或输出类的文档添加的类(例如,除了 GetBook 类外,还 有 GetBookDocument)。其直接效果就是在使用 XMLBeans 代替 ADB 时会添加一个对象创建 层。Axis2 中没有为 XMLBeans 提供取消包装支持,因此没有办法避免这个添加的对象层。 所得到的结果是 XMLBeans 生成的类比其他数据绑定框架的对等项使用更为复杂。

清单 7. XMLBeans 客户机代码

// create the client stubXmlbeansLibraryStub stub = new XmlbeansLibraryStub(target);// retrieve a book directlyString 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 definedGetTypesDocument 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 bookString 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 instanceBooksByTypeCallback cb = new BooksByTypeCallback();// retrieve all books of a type asynchronouslyGetBooksByTypeDocument 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 仍然能正常读写缺少所需的 元素(因而无效)的文档。清单 8 显示了此代码更改,并给出了发送到服务 器进行添加请求的 XML 以及检索书籍时从服务器返回的 XML。对于检索响应,XML 文档包括 <title> 元素,但使用了 xsi:nil=”true” 属性,这在模式中是不允许的 ,因此同样也无效。</p> <p>清单 8. XMLBeans 客户机和无效文档</p> <p><code>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);   ...   scifi  0445203498  Cook, Glen    ...    Cook, Glen  <title />   </code></p> <p>这是未设置所需值的一个简单示例。对于更为复杂的模式,XMLBeans 生成的 API 可能会 隐藏更多的缺陷。XMLBeans 用户邮件列表的最近一次讨论曾谈及这样的情况,即必须将值添 加到两个不同的列表,以更改生成正确输出的顺序。因此 XMLBeans 要求开发人员既要了解 模式,还要了解生成的代码如何与模式相关,以确保应用程序代码构建有效的 XML 文档。数 据绑定框架的一个主要好处是,通常能够对开发人员隐藏模式的这些细节,而 XMLBeans 显 然在这方面做得并不好。</p> <p>可以通过调用生成类中包括的 validate() 方法来避免 XMLBeans 处理和生成无效 XML 文档的问题。如果使用 XMLBeans,则应至少在测试和开发期间使用此方法来检查所有文档。 不过,验证对性能具有很大的影响(正如您在下一篇文章中将看到的,即使不对每个文档调 用 validate(),XMLBeans 也已经非常慢了),因此很多应用程序都应该在生产部署中避免 验证开销。就结果信息而言,验证也具有相当的局限性。为了避免导致验证错误,应该对出 现错误的文档运行独立的模式验证。</p> <p>JiBX</p> <p>JiBX(我自己开发的)是主要侧重使用现有 Java 类(而不是从模式进行代码生成)的数 据绑定框架。对于 JiBX,要首先创建绑定定义,以定义 Java 数据对象与 XML 之间如何转 换,然后使用可通过添加实现转换的方法(作为字节码)来增强数据类文件的工具对该绑定 进行编译。JiBX 运行时框架将随后使用这些添加的方法来在数据和 XML 之间进行转换。</p> <p>JiBX 方法提供了一些独有的优势,也有自己独有的缺点。就好的一面而言,JIBX 可让您 在 Web 服务接口添加到现有服务代码的情况下直接使用现有类。Jibx2Wsdl 工具特别适合这 种用途,因为它会生成所需的所有内容,从而方便地将现有代码作为 Axis2 服务部署。可以 在使用单个数据模型的情况下为相同的类定义不同的绑定,以同时用于文档的不同 XML 版本 。通过修改绑定,甚至通常能在重构数据类的情况下保持相同的 XML 表示形式。</p> <p>清单 9 显示了 JiBX 客户机代码中有意义的部分,其中使用了匹配消息元素的类。此代 码与清单 5 中所示的 ADB 对等项类似,因此这里就不详细讨论了。唯一值得注意的差异是 ,由于数据类和消息类都在用户的控制之下,因此可以方便地向通过 JiBX 使用的类添加常 规构造函数(如 AddBookRequest 的情况)和其他支持方法。</p> <p>清单 9. JIBX 客户机代码</p> <pre>// create the server instanceJibxLibraryStub stub = new JibxLibraryStub(target);// retrieve a book directlyString 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 definedGetTypesResponse 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 bookString 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 instanceBooksByTypeCallback cb = new BooksByTypeCallback();// retrieve all books of a type asynchronouslystub.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();</pre> </p> <p>清单 10 显示了对等的 JiBX 取消包装代码。和 ADB 取消包装代码类似,理解和使用服 务调用的取消包装形式比使用直接代码简单得多。JiBX 和 ADB 版本唯一重要的区别在于, 对于 JiBX,并不需要在不传递值时创建对象,而 getTypes() 调用的 ADB 版本则要求进行 此工作。JiBX 取消包装支持也比 ADB 版本更为稳定一些,因为从 Axis2 1.1.1 起就提供了 全面支持。</p> <p>清单 10. JIBX 取消包装客户机代码</p> <pre>// create the server instanceJibxUnwrapLibraryStub stub = new JibxUnwrapLibraryStub(target);// retrieve a book directlyString 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 definedType[] 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 bookString 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 instanceBooksByTypeCallback cb = new BooksByTypeCallback();// retrieve all books of a type asynchronouslystub.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;</pre> </p> <p>JiBX 取消包装支持在使用的类方面也与 ADB 有差别。当使用 ADB 取消包装时,所有消 息元素的类仍然在后台生成和使用。对于 JiBX,使用直接处理时必须将类定义为与消息元素 对应,如清单 9 中所示;对于取消包装处理,只有作为值传递的类需要在绑定定义中加以定 义和包括。无论采用哪种方法,JiBX 绑定定义都需要在运行 Axis2 WSDL2Java 工具前创建 ,而且需要使用 -Ebindingfile 命令行参数传入。</p> <p>JiBX 绑定方法最大的缺陷(至少从 Web 服务方面可以这样说)可能就是,JiBX 目前对 从 XML 模式定义工作的支持非常微弱。不过,即使是 Xsd2Jibx 工具提供的这个微弱支持, 也尚未集成到 Axis2 WSDL2Java 代码生成中。这意味着需要在运行 WSDL2Java 生成 Axis2 链接代码前创建 Java 数据类和绑定定义。JiBX 所需的字节码增强步骤在某些环境中也可能 出现问题,因为通常需要在应用程序构建时进行此工作,可在类中得到没有源代码可用的代 码。</p> <p>正如前面所提到的,JiBX 数据绑定提供了一些独特的好处。就 Axis2 使用而言,JiBX 也提供了优于其他框架的优势,即支持能够插入到 Axis2 版本中纠正发布之后发现的错误的 错误修复程序版本(有关详细信息,请参见参考资料部分的“获得相关产品和技术 ”)。对于其他框架,获取错误修复程序的唯一方式就是迁移到 Axis2 的更高版本, 而这样通常会带来其他问题。预计将来 JiBX 将提供稳定的从模式生成代码和绑定的支持。 到提供此功能的那一天,就可以将 JiBX 视为 Axis2 的优秀全能数据绑定备选方案了。到那 时,使用现有 Java 代码的做法将是最佳的,而 Jibx2Wsdl 工具恰恰在这方面提供了出色的 支持。</p> <p>总结</p> <p>Axis2 目前提供对三种不同数据绑定框架的全面支持:</p> <p>ADB 专门为 Axis2 设计,仅能用于 Axis2 环境中。对于 Axis2 1.3 版,提供了对从模 式生成代码的良好支持(并在不断改进)。另外还支持常规取消包装服务方法和自动附件处 理,从而使其成为了从现有 WSDL 服务定义着手时的首要选项。</p> <p>XMLBeans 提供对在生成的 Java 代码中建模模式结构的最全面支持。但其会创建给定模 式的最为复杂的 Java 模型,并不支持使用取消包装服务方法来实现更为简单的接口。缺省 情况下,甚至不会对输入或输出 XML 文档执行基本模式结构规则,从而很可能会创建无效的 XML 文档或接受此类文档进行处理。</p> <p>JiBX 是唯一支持使用现有 Java 类的备选方案。另外还提供了对取消包装服务方法出色 的支持。但集成到 Axis2 中的 JiBX 支持并不处理从模式生成代码的情况,甚至 JiBX 工具 所提供的从模式生成代码的独立支持目前还仅限于模式支持。</p> <p>Axis2 提供了这些数据绑定框架间的选择,这非常好,因为并没有那一个选项能满足所有 需求。将来 Axis2 还将提供对 JAXB 2.0 的全面支持,我将在本系列关于相关 JAX-WS 2.0 的后续文章中对此进行讨论。了解每个框架的优缺点可帮助您根据自己的需求作出最适当的 选择,并在投入生产前了解潜在的问题区域。在本系列的下一篇文章中,我们将了解 Axis2 数据绑定框架比较的另一个方面:性能。</p> <p>来源:http://www.ibm.com/developerworks/cn/webservices/ws-java3/</p> <p> 辽远或偏僻的地方,而会常常想起这一次的旅行,想起那座山,那个城,那些人……</p> </div> <center> <a href="https://www.note234.com/336871.html" rel="bookmark" title="JavaWeb服务,第3部分:Axis2数据绑定"><img src="https://www.note234.com/wp-content/themes/generatepress/images/74.jpg" alt="JavaWeb服务,第3部分:Axis2数据绑定" /></a> </center> <footer class="entry-meta" aria-label="条目 meta"> <span class="cat-links"><span class="gp-icon icon-categories"><svg viewBox="0 0 512 512" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em"><path d="M0 112c0-26.51 21.49-48 48-48h110.014a48 48 0 0143.592 27.907l12.349 26.791A16 16 0 00228.486 128H464c26.51 0 48 21.49 48 48v224c0 26.51-21.49 48-48 48H48c-26.51 0-48-21.49-48-48V112z" /></svg></span><span class="screen-reader-text">分类 </span><a href="https://www.note234.com/category/bckf" rel="category tag">编程开发</a></span> <span class="tags-links"><span class="gp-icon icon-tags"><svg viewBox="0 0 512 512" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em"><path d="M20 39.5c-8.836 0-16 7.163-16 16v176c0 4.243 1.686 8.313 4.687 11.314l224 224c6.248 6.248 16.378 6.248 22.626 0l176-176c6.244-6.244 6.25-16.364.013-22.615l-223.5-224A15.999 15.999 0 00196.5 39.5H20zm56 96c0-13.255 10.745-24 24-24s24 10.745 24 24-10.745 24-24 24-24-10.745-24-24z"/><path d="M259.515 43.015c4.686-4.687 12.284-4.687 16.97 0l228 228c4.686 4.686 4.686 12.284 0 16.97l-180 180c-4.686 4.687-12.284 4.687-16.97 0-4.686-4.686-4.686-12.284 0-16.97L479.029 279.5 259.515 59.985c-4.686-4.686-4.686-12.284 0-16.97z" /></svg></span><span class="screen-reader-text">标签 </span><a href="https://www.note234.com/tag/axis" rel="tag">axis</a>、 <a href="https://www.note234.com/tag/web" rel="tag">web</a>、 <a href="https://www.note234.com/tag/%e7%bb%91%e5%ae%9a" rel="tag">绑定</a></span> <nav id="nav-below" class="post-navigation" aria-label="文章"> <div class="nav-previous"><span class="gp-icon icon-arrow-left"><svg viewBox="0 0 192 512" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414"><path d="M178.425 138.212c0 2.265-1.133 4.813-2.832 6.512L64.276 256.001l111.317 111.277c1.7 1.7 2.832 4.247 2.832 6.513 0 2.265-1.133 4.813-2.832 6.512L161.43 394.46c-1.7 1.7-4.249 2.832-6.514 2.832-2.266 0-4.816-1.133-6.515-2.832L16.407 262.514c-1.699-1.7-2.832-4.248-2.832-6.513 0-2.265 1.133-4.813 2.832-6.512l131.994-131.947c1.7-1.699 4.249-2.831 6.515-2.831 2.265 0 4.815 1.132 6.514 2.831l14.163 14.157c1.7 1.7 2.832 3.965 2.832 6.513z" fill-rule="nonzero" /></svg></span><span class="prev"><a href="https://www.note234.com/336870.html" rel="prev">ECLIPSE小组开发环境实践:第2部分</a></span></div><div class="nav-next"><span class="gp-icon icon-arrow-right"><svg viewBox="0 0 192 512" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414"><path d="M178.425 256.001c0 2.266-1.133 4.815-2.832 6.515L43.599 394.509c-1.7 1.7-4.248 2.833-6.514 2.833s-4.816-1.133-6.515-2.833l-14.163-14.162c-1.699-1.7-2.832-3.966-2.832-6.515 0-2.266 1.133-4.815 2.832-6.515l111.317-111.316L16.407 144.685c-1.699-1.7-2.832-4.249-2.832-6.515s1.133-4.815 2.832-6.515l14.163-14.162c1.7-1.7 4.249-2.833 6.515-2.833s4.815 1.133 6.514 2.833l131.994 131.993c1.7 1.7 2.832 4.249 2.832 6.515z" fill-rule="nonzero" /></svg></span><span class="next"><a href="https://www.note234.com/336872.html" rel="next">JAVA提高教程(5)-认识Set集合之EnumSet</a></span></div> </nav> </footer> </div> </article> <div class="inside-article"> <h3>相关文章:</h3> <ul><li class="widget-common-media-post"> <div class="mtb10 magazine-media-item"><div class="description t-sm c-sub text-3line"><a href="https://www.note234.com/284.html">支付宝社保怎么查 支付宝怎么绑定社保卡</a></div> </div></li><li class="widget-common-media-post"> <div class="mtb10 magazine-media-item"><div class="description t-sm c-sub text-3line"><a href="https://www.note234.com/443206.html">Wish信用卡绑定流程及使用注意事项,如何绑定信用卡</a></div> </div></li><li class="widget-common-media-post"> <div class="mtb10 magazine-media-item"><div class="description t-sm c-sub text-3line"><a href="https://www.note234.com/443160.html">shopify如何取消信用卡绑定并注意账单管理</a></div> </div></li><li class="widget-common-media-post"> <div class="mtb10 magazine-media-item"><div class="description t-sm c-sub text-3line"><a href="https://www.note234.com/443125.html">速卖通收款账户设置及收款方式介绍</a></div> </div></li><li class="widget-common-media-post"> <div class="mtb10 magazine-media-item"><div class="description t-sm c-sub text-3line"><a href="https://www.note234.com/441974.html">Shopify平台如何绑定收款账户?收款时需要注意什么?</a></div> </div></li><li class="widget-common-media-post"> <div class="mtb10 magazine-media-item"><div class="description t-sm c-sub text-3line"><a href="https://www.note234.com/441220.html">Shopify如何绑定银行卡?如何设置收款方式?</a></div> </div></li><li class="widget-common-media-post"> <div class="mtb10 magazine-media-item"><div class="description t-sm c-sub text-3line"><a href="https://www.note234.com/439504.html">国内是否可以使用PayPal进行支付?支付流程指南</a></div> </div></li><li class="widget-common-media-post"> <div class="mtb10 magazine-media-item"><div class="description t-sm c-sub text-3line"><a href="https://www.note234.com/439482.html">Shopify如何绑定支付宝?绑定支付宝有哪些好处?</a></div> </div></li><li class="widget-common-media-post"> <div class="mtb10 magazine-media-item"><div class="description t-sm c-sub text-3line"><a href="https://www.note234.com/438606.html">如何将TikTok商家账号与TikTok达人账号绑定?如何进行带货?</a></div> </div></li><li class="widget-common-media-post"> <div class="mtb10 magazine-media-item"><div class="description t-sm c-sub text-3line"><a href="https://www.note234.com/438521.html">Shopify如何取消绑定信用卡?操作步骤详解!</a></div> </div></li></ul> <h4>你感兴趣的文章:</h4> <ul> <li><a href="https://www.note234.com/160567.html">ie浏览器字体大小设置</a></li> <li><a href="https://www.note234.com/267351.html">CSS属性之定位属性(图文详解)</a></li> <li><a href="https://www.note234.com/160379.html">用DiskGenius备份硬盘分区表</a></li> <li><a href="https://www.note234.com/179278.html">卡萨帝BCD-622WDCAU1冰箱的外观设计如何?</a></li> <li><a href="https://www.note234.com/361323.html">Kali Linux 安全渗透教程&lt;第六更&gt;1.4.2 安装至USB驱动器K</a></li> <li><a href="https://www.note234.com/168026.html">康佳幻影3D手机的续航能力如何?</a></li> </ul> <h4>标签云:</h4> <div class="customtag"> <a href='https://www.note234.com/wangzhan/tag/%e4%ba%9a%e6%b4%b2%e9%ab%98%e6%b8%85%e7%94%b5%e5%bd%b1%e5%9c%a8%e7%ba%bf/'>亚洲高清电影在线</a>, <a href='https://www.note234.com/wangzhan/tag/%e5%85%8d%e8%b4%b9%e9%ab%98%e6%b8%85%e7%94%b5%e5%bd%b1/'>免费高清电影</a>, <a href='https://www.note234.com/wangzhan/tag/%e5%85%ab%e6%88%92%e5%bd%b1%e9%99%a2%e5%a4%9c%e9%97%b4/'>八戒影院夜间</a>, <a href='https://www.note234.com/wangzhan/tag/%e5%85%ab%e6%88%92%e7%94%b5%e5%bd%b1%e6%9c%80%e6%96%b0%e5%a4%a7%e7%89%87/'>八戒电影最新大片</a>, <a href='https://www.note234.com/wangzhan/tag/%e5%87%ba%e8%bd%a8%e5%9c%a8%e7%ba%bf%e7%94%b5%e5%bd%b1/'>出轨在线电影</a>, <a href='https://www.note234.com/wangzhan/tag/%e5%8d%88%e5%a4%9c%e7%94%b5%e5%bd%b1%e9%99%a2/'>午夜电影院</a>, <a href='https://www.note234.com/wangzhan/tag/%e5%9c%a8%e7%ba%bf%e5%bd%b1%e9%99%a2a1166/'>在线影院a1166</a>, <a href='https://www.note234.com/wangzhan/tag/%e5%9c%a8%e7%ba%bf%e7%94%b5%e5%bd%b1%e9%99%a2/'>在线电影院</a>, <a href='https://www.note234.com/wangzhan/tag/%e5%9c%a8%e7%ba%bf%e8%a7%82%e7%9c%8b%e7%be%8e%e5%89%a7%e4%b8%8b%e8%bd%bd/'>在线观看美剧下载</a>, <a href='https://www.note234.com/wangzhan/tag/%e6%97%a5%e6%9c%ac%e7%88%b1%e6%83%85%e7%94%b5%e5%bd%b1/'>日本爱情电影</a>, <a href='https://www.note234.com/wangzhan/tag/%e6%97%a5%e9%9f%a9%e9%ab%98%e6%b8%85%e7%94%b5%e5%bd%b1%e5%9c%a8%e7%ba%bf/'>日韩高清电影在线</a>, <a href='https://www.note234.com/wangzhan/tag/%e7%94%b5%e5%bd%b1%e5%a4%a9%e5%a0%82%e7%bd%91/'>电影天堂网</a>, <a href='https://www.note234.com/wangzhan/tag/%e7%9b%b4%e6%92%ad%e7%9b%92%e5%ad%90app/'>直播盒子app</a>, <a href='https://www.note234.com/wangzhan/tag/%e8%81%9a%e5%90%88%e7%9b%b4%e6%92%ad/'>聚合直播</a>, <a href='https://www.note234.com/wangzhan/tag/%e9%ab%98%e6%b8%85%e7%be%8e%e5%89%a7/'>高清美剧</a>, <a href='https://www.note234.com/wangzhan/tag/%e9%ab%98%e6%b8%85%e7%be%8e%e5%89%a7%e5%9c%a8%e7%ba%bf%e8%a7%82%e7%9c%8b/'>高清美剧在线观看</a> <a href='https://www.note234.com/wangzhan/tag/ehviewer-e%e7%ab%99/'>EhViewer-E站</a>, <a href='https://www.note234.com/wangzhan/tag/e%e7%ab%99/'>E站</a>, <a href='https://www.note234.com/wangzhan/tag/e%e7%ab%99%e7%bb%bf%e8%89%b2%e7%89%88/' >E站绿色版</a>, <a href='https://www.note234.com/wangzhan/tag/qqmulu-com/'>qqmulu.com</a>, <a href='https://www.note234.com/wangzhan/tag/qq%e7%9b%ae%e5%bd%95%e7%bd%91/'>qq目录网</a>, <a href='https://www.note234.com/wangzhan/tag/qq%e7%bd%91%e7%ab%99%e7%9b%ae%e5%bd%95/'>qq网站目录</a>, </div></div> </main> </div> <div class="widget-area sidebar is-right-sidebar" id="right-sidebar"> <div class="inside-right-sidebar"> <aside id="block-11" class="widget inner-padding widget_block"><div class="_2ho2ho6w023"></div> <script type="text/javascript"> (window.slotbydup = window.slotbydup || []).push({ id: "u6953233", container: "_2ho2ho6w023", async: true }); </script> <!-- 多条广告如下脚本只需引入一次 --> <script type="text/javascript" src="//cpro.baidustatic.com/cpro/ui/cm.js" async="async" defer="defer" > </script></aside> </div> <div id="box_float"> <div id="float" class="div1"> <aside id="block-12" class="widget inner-padding widget_block"> <div class="_x3rdkhcqm5n"></div> <script type="text/javascript"> (window.slotbydup = window.slotbydup || []).push({ id: "u6958514", container: "_x3rdkhcqm5n", async: true }); </script> </aside></div> </div> </div> </div> </div> </div> <div class="site-footer"> <footer class="site-info" aria-label="站点" itemtype="https://schema.org/WPFooter" itemscope> <div class="inside-site-info grid-container"> <div class="copyright-bar"> <p></p> <p>Copyright © 2023 <a href="https://www.note234.com/" target="_blank">摘录笔记网</a></p> <a href="https://www.note234.com/tags" target="_blank">tags</a>,<a href="https://www.note234.com/wp-sitemap.xml" target="_blank">xml地图</a>,<a href="https://www.note234.com/sitemap.html" target="_blank">网站地图</a>,<a href="https://www.note234.com/lxwm" target="_blank">联系我们</a>,<a href="https://www.yiwen55.com/" target="_blank">易海博客</a>,<a href="https://www.note234.com/wangzhan/" target="_blank">免费网站</a>,<a href="https://beian.miit.gov.cn/" target="_blank" rel="nofollow">闽ICP备20014300号-1</a> </div> </div> </footer> </div> <a title="回到顶部" aria-label="回到顶部" rel="nofollow" href="#" class="generate-back-to-top" data-scroll-speed="400" data-start-scroll="300"> <span class="gp-icon icon-arrow-up"><svg viewBox="0 0 330 512" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414"><path d="M305.863 314.916c0 2.266-1.133 4.815-2.832 6.514l-14.157 14.163c-1.699 1.7-3.964 2.832-6.513 2.832-2.265 0-4.813-1.133-6.512-2.832L164.572 224.276 53.295 335.593c-1.699 1.7-4.247 2.832-6.512 2.832-2.265 0-4.814-1.133-6.513-2.832L26.113 321.43c-1.699-1.7-2.831-4.248-2.831-6.514s1.132-4.816 2.831-6.515L158.06 176.408c1.699-1.7 4.247-2.833 6.512-2.833 2.265 0 4.814 1.133 6.513 2.833L303.03 308.4c1.7 1.7 2.832 4.249 2.832 6.515z" fill-rule="nonzero" /></svg></span> </a><script id="generate-a11y">!function(){"use strict";if("querySelector"in document&&"addEventListener"in window){var e=document.body;e.addEventListener("mousedown",function(){e.classList.add("using-mouse")}),e.addEventListener("keydown",function(){e.classList.remove("using-mouse")})}}();</script><!--[if lte IE 11]> <script src="https://www.note234.com/wp-content/themes/generatepress/assets/js/classList.min.js" id="generate-classlist-js"></script> <![endif]--> <script id="generate-menu-js-extra"> var generatepressMenu = {"toggleOpenedSubMenus":"1","openSubMenuLabel":"\u6253\u5f00\u5b50\u83dc\u5355","closeSubMenuLabel":"\u5173\u95ed\u5b50\u83dc\u5355"}; </script> <script src="https://www.note234.com/wp-content/themes/generatepress/assets/js/menu.min.js" id="generate-menu-js"></script> <script id="generate-back-to-top-js-extra"> var generatepressBackToTop = {"smooth":"1"}; </script> <script src="https://www.note234.com/wp-content/themes/generatepress/assets/js/back-to-top.min.js" id="generate-back-to-top-js"></script> <script> var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?95d0945b5c7afc67da1ee2166f8ca4b1"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); </script> </body> </html> <!-- Cached by ezCache --> <!-- Cache created in 0.84s -->