GWT 如何减小 RPC 调用的代码序列化程序的大小



我发现GWT在我的应用程序上生成的超过60%的javaScript代码是针对RPC序列化程序的。我还发现序列化程序在服务接口之间不共享,我的意思是,如果我在 2 个 rpc 服务接口上引用了例如 AccountDTO 类型,我将获得 2 个序列化程序类,而不是相同类型的 1 个。为了减小编译代码的大小,我想也许我可以使用延迟绑定来替换我拥有一个大接口的所有服务接口。如果可能的话,也许GWTCompiler将只生成一个AccountDTO序列化程序,而不是2个。

不确定这是一个好主意,或者是否有更好的解决方案来解决我的问题。

我试图实现的是这样的:

// Define new interface that extends all service interfaces
public interface GenericService extends RemoteService,
                    AccountingService,
                    FinancialService,..., { }
public interface GenericServiceAsync extends AccountingServiceAsync,
                         FinancialServiceAsync, ..., { }
// At Application.gwt.xml do:
<module>
...
...
    <replace-with class="com.arballon.gwt.core.client.GenericService">
        <when-this-is class="com.arballon.gwt.core.client.AccountingService>
    </replace-with>
    <replace-with class="com.arballon.gwt.core.client.GenericService">
        <when-this-is class="com.arballon.gwt.core.client.FinancialService>
    </replace-with>
    ...
    ...

但目前我收到错误:

[错误] 'file:/C:/Users/Daniel/EclipseWorkspace/ADK/src/com/arballon/gwt/core/client/FinancialService.java中的错误 [错误]第 31 行:找不到重新绑定结果"com.arballon.gwt.core.client.GenericService">

对这个问题的任何想法将不胜感激。问候

丹尼尔

GWT 的 RPC 生成代码构建了多个类来完成它的工作,如您所指出的:每个通过网络的类型都有一个*_FieldSerializer,一个 RemoteService 异步类型的*_Proxy类。该代理类型需要*_TypeSerializer,这是问题的根源 - 出于某种原因,GWT 在字符串>js 函数映射中连接所有序列化/反序列化方法,可能是为了方便快速查找 - 但这种设置代码的代价是需要在最终构建中的代码行。更优化的方法可以让每个FieldSerializer都有一个注册方法,其中它将其方法添加到代理拥有的静态映射中 - 然而,这很困扰,但 GWT 尝试不引用instantiate()的优化,deserialize()serialize()方法,如果它没有出现,它们将被调用。

您的问题源于具有许多可以序列化的类型,以及您尝试构建RemoteService类型,每个类型描述特定的功能单元,但重用许多模型类型。令人钦佩的目标,特别是因为它可能会使您的服务器端代码看起来更好,但显然 GWT 为此咬了你一口。

我试图在freenode(如niloc132(上为您提供的解决方案是构建一个大型RemoteService类型,您将其命名为GeneralService,以及一个匹配的GeneralServiceAsync,每个类型都扩展了所有现有的rpc服务类型。我的第一个想法是使用<replace-with>告诉生成器系统,当您希望每个 RemoteService 类型将其替换为 GeneralService 时,但正如 Tahir 指出的那样,这没有意义 - GWT 不会将重新绑定结果传递回自身以继续执行查找。相反,我建议当您需要服务异步类型时,请执行以下操作:

AccountingServiceAsync service = (AccountingServiceAsync) GWT.create(GeneralService.class)

来自GeneralService的重新绑定结果将实现GeneralServiceAsync,它本身可以分配给AccountingServiceAsync。如果没记错的话,您说您有提供这些服务的静态方法/字段 - 更改这些站点以始终创建GeneralServiceAsync实例。只要您不对任何RemoteService子类型调用GWT.create,而是GeneralService ,就可以将TypeSerializers数限制为 1。

作为旁注,RemoteServiceProxy子类型是无状态的,因此确保只创建一个实例可能会更容易一致地生成,但不节省运行时内存或时间,因为它们几乎肯定会编译为静态方法。但是,*_TypeSerializer类确实具有状态,但每个类只有一个实例,因此将所有RemoteService组合在一起可能会节省非常少量的工作内存。

好吧,经过两次往返,我们终于找到了我想与之分享的问题的解决方案,以防它可以帮助其他人。首先,我必须提到科林·阿尔沃斯的帮助,没有他的支持,这个解决方案根本不可能。另外,我必须提到,我对最终的解决方案并不感到骄傲,但它对我们有用,目前是我们拥有的最好的解决方案。

我们最终所做的是,正如Colin在上一篇文章中评论的那样,替换我们每个服务接口的GWT.create,以创建GenericBigService接口。

所以我们的第一个补丁是这样的:

1(创建GenericBigService接口,扩展我们拥有的所有服务接口(目前有52个接口(,并创建其异步兄弟。我们通过 Phytom 脚本完成此操作。

所以我们的 GenericBigInterface 看起来像这样:

package com.arballon.gwt.core.client;
import com.google.gwt.user.client.rpc.RemoteService;
public interface GenericBigService extends RemoteService,
                                       AccountingService,
                                       ActionClassifierService,
                                       AFIPWebService,
                                       AnalyticalService,
                                       AuthorizationService,
                                       BudgetService,
                                       BusinessUnitService,
                                       CatalogPartService,
                                       CategoryService,
                                       ClientDepositService,
                                       .....
                                       .....
{ }

2(我们在每个服务接口中都有一个Util内部静态类来实例化异步实例,在那里我们替换GWT.create来创建GenericBigInterface。

我们的一个服务界面如下所示:

public interface FinancialPeriodBalanceCategoryService extends RemoteService {
    /**
 * Utility class for simplifying access to the instance of async service.
 */
public static class Util {
    private static FinancialPeriodBalanceCategoryServiceAsync instance;
    public static FinancialPeriodBalanceCategoryServiceAsync getInstance() {
        if (instance == null) {
            instance = GWT.create(GenericBigService.class);
((ServiceDefTarget)instance).setServiceEntryPoint(GWT.getModuleBaseURL()+"FinancialPeriodBalanceCategoryService");
        }
        return instance;
    }
}

我们必须进行serServiceEntyPoint调用,以维护我们的网络.xml不被修改。

当我们第一次编译它时,它可以编译正常,但它不起作用,因为在运行时服务器调用会抛出异常:

IncompatibleRemoteServiceException Blocked attempt to access interface GenericBigService 

,未由财务期间余额类别服务实现

好吧,这绝对是正确的,我们使用它没有实现的接口调用服务,这是丑陋的部分出现的时候。我们找不到可以编码的更好的解决方案,我们决定实现的解决方案是:

我们将 RPC.java 替换为我们自己的副本,并像这样替换代码:

在解码请求方法中,我们做到了:

  if (type != null) {
    /*if (!implementsInterface(type, serviceIntfName)) {
      // The service does not implement the requested interface
      throw new IncompatibleRemoteServiceException(
          "Blocked attempt to access interface '" + serviceIntfName
              + "', which is not implemented by '" + printTypeName(type)
              + "'; this is either misconfiguration or a hack attempt");
    }*/
    if (!implementsInterface(type, serviceIntfName)) {
          if(!serviceIntfName.contains("GenericBigService")){
              throw new IncompatibleRemoteServiceException(
                      "Blocked attempt to access interface '" + serviceIntfName
                          + "', which is not implemented by '" + printTypeName(type)
                          + "'; this is either misconfiguration or a hack attempt");
          }
    }

这样做的好处是:

1

(我们花了1小时20分钟来完成6个排列,只用了20分钟。

2(在开发模式下,所有开始运行得更快。启动或多或少保持不变,但一旦启动,执行就会非常顺利。

3(编译大小的减小是另一个不小的有趣结果,我们将剩余段从6Mb减少到1.2Mb,我们在aprox中减少了JS大小的整个编译。50% 至 60%。

我们对GWT-RPC非常满意,我们不想离开它,但是typeSerializers确实是一个问题,主要是因为生成的JS的大小。有了这个解决方案,我知道不是很优雅,但它有效,而且有效。再次感谢科林的帮助!

问候Daniel<</p>

div class="one_answers">

对于任何 GWT-RPC 服务,GWt 将生成一个代理,一个类型序列化程序。对于可能通过GWT传递的每个对象,您将拥有一个FieldSerializer类。每个类只能有一个 FieldSerializer。因此,您不可能为一个帐户DTO使用两个字段序列化程序。

您尝试使用的延迟绑定规则将不起作用。例如,你有这样的东西:MyServiceAsync = GWT.create(MyService.class(;

延迟绑定规则会将其更改为:

MyService异步同步 = 新MyServiceAsync_Proxy((;

您的规则实际上会执行以下操作:

MyServiceAsync sync = new MyGenericService(( ;//无效,因为 MyGenericService 是一个接口

因此,您的解决方案将不起作用。

由于您说 60% 的应用程序生成的代码是与 RPC 相关的东西,我怀疑您有 RPC 类型爆炸问题。

检查 GWT 在编译期间是否没有引发任何警告,或者为 RPC TypeSerializer 生成存根,很可能您在服务中有一些非常常见的接口。

如果你想有一个更好的解决方案,为什么不使用命令模式。 这样,您只需要一个接受命令子类型并返回结果子类型的 GWT 服务(您可以使用泛型使其类型安全(。

好处是你只需要在一个 gwt servlet 中声明一个方法,然后你可以调度到任何其他服务器端服务。

命令模式还可以为您提供许多额外的好处,因为您有一个中央控制点来执行安全检查或允许您透明地批处理请求

因此,您在服务器端接触GWT的机会会小得多。

据我了解,GWT 代码生成应该提供接口的具体实现。然后将此实现转换为用于特定排列的 JavaScript。

另一方面,您的示例正在将一个接口替换为另一个接口。如果你从GWT编译器的眼睛看到它,也许你会看到这个配置的问题。

假设您是 GWT 编译器,并且在客户端代码中看到要转换为 JavaScript 的以下行

AccountingServiceAsync accountingServiceAsync = (AccountingServiceAsync) GWT.create(AccountingService.class);
accountingServiceAsync.recordTransaction(transaction,callback);

所以你需要找出应该发生什么,在第2行。具体来说,你需要知道在哪里可以找到accountingServiceAsync.recordTransaction((的实现。因此,您查看所有配置,以查找是否有规则指定应将哪个实现类用于会计服务(而不是异步(。但遗憾的是,你找不到任何东西。但随后您注意到会计服务也是一个远程服务。因此,您再次深入研究您的配置。而且,啊哈,它有一个规则,指定您可以使用ServiceInterfaceProxyGenerator生成RemoteService实现。您很高兴地将提供会计服务实现的任务交给ServiceInterfaceProxyGenerator。

但是假设不是这个美好的结局,而是你的配置告诉你会计服务可以用GenericService取代,你说,"嘿,酷,把它带上"。但就在这时,你发现GenericService也是一个接口。显然,你会被关闭,说"现在,我要用另一个界面做什么,我所需要的只是会计服务的实现"。在这一点上,你希望通过向程序员抛出一个神秘的错误来与他保持平衡。

所以,到目前为止,所有这些都解释了为什么你的解决方案(理论上(不起作用.至于你对臃肿的javascript的实际关注,我很惊讶这个问题甚至存在,因为GWT人员在优化编译的JavaScript方面付出了大量的努力。您是如何测试编译输出的重复的?

相关内容

  • 没有找到相关文章

最新更新