如何用注解处理器替换 GWT.create?

How can I replace GWT.create with an Annotation Processor?

我想创建一个注释处理器来替换对 GWT.create 的调用。

With an annotation processor, you'd have to generate both classes and then dynamically (at runtime) select among them, depending on the context (you could generate a factory to help doing that, but you'd still have to somehow feed the factory with the current context, e.g. the current locale).

来源:

我得到了我的注释处理器 运行,它很好地生成了 类。我不知道的部分是运行时选择部分。

如何选择运行时?

我假设您已经涵盖了代码生成方面,并且只关注我们如何在 GWT 中选择正确的实现:

but you'd still have to somehow feed the factory with the current context, e.g. the current locale).

我们可以按照您的建议在运行时执行此操作,但由于最近添加了对 System.getProperty 的支持,您也可以在编译时执行此操作。

当然,第一步是为您可能想要访问的每个实现生成代码。以语言环境为例,您可能有 Foo_en.javaFoo_es.javaFoo_de.java

接下来,我们需要一种一致的方法来获得任何一个实现 - 也许是生成的 FooFactory,使用如下方法:

public static Foo getFoo(String locale) {
  if ("en".equals(locale)) {
    return new Foo_en();
  } else if /*...
  ...*/

  throw new IllegalArgumentException("Locale " + locale + " is not supported");
}

如果您在 运行时 询问用户他们想要哪个语言环境,您可以将该值传递给该工厂方法以获得您想要的实现。同样,如果您可以在运行时从某物中读取该值,您可以再次获得正确的实例并继续前进。


但是如果您真的想让编译器为您选择呢?让我们保留注释处理器生成的代码,但将选择语言环境的阶段移至编译器及其排列。

与在现有 GWT 代码中一样,为语言环境指定 属性 和几个值。然后,不用询问用户或在运行时在您自己的 Java 代码中决定您想要的语言环境,而是使用与 GWT 通常使用的相同的选择脚本连接(检查 url、cookie、元标记、用户代理本身等)- 如果需要,您可以为此构建自己的 property-provider

和以前一样,我们可以使用getFoo(locale),但现在我们使用System.getProperty来读出我们在.gwt.xml文件中创建的属性。这将被静态编译为每个排列的正确常量。但不是每次我们需要一个实例时都调用 FooFactory.getFoo(System.getProperty("locale")),让我们在生成的 FooFactory 中创建另一个方法:

public static Foo getFoo() {
  return getFoo(System.getProperty("locale"));
}

现在我们只需调用 FooFactory.getFoo(),我们就会得到当前排列的正确 class。


Dagger 问题:Thomas 可能更适合解决这个问题,但是不,Dagger2 不像 Guice 那样 bind(Foo).to(Bar).in(Scope),因为那需要 运行 代码来解析绑定,而 Dagger 仅根据它可以通过反射类型看到的内容进行操作,而不是 运行 代码。这意味着您最终会得到很多这样的 @Provides 方法,或者使用应该使用什么来实现什么的详细信息来注释您的实际类型。

FWIW 到目前为止我还没有采用 Dagger,因为一些怪癖需要我重新思考一些问题,而且我还没有花时间重新思考:

首先,您不会被迫替换您的GWT.create()电话。因为 GWT 生成器做两件事,你可能(这并不总是可能)用注释处理器替换代码生成部分,用简单的 <replace-with> 规则替换 "selection" 部分(目标 class由注释处理器生成的 es)。
另请注意,GWT.create() 实际上在 GWT 上下文之外(例如在服务器端)使用 ServerGwtBridge 可用,这让您可以注册 class 实例化器。注释处理器可以生成这样的实例化器,或者您可以使用反射对其进行编码。

如果你确实想替换你的 GWT.create() 调用,那么你必须使用工厂(直接在调用点,或者包装到外观/代理中)。该工厂也可能由注释处理器生成。

与任何此类工厂一样,代码将是 if…else 级联或 switch…case,最终 new 适当(生成)class;但条件非常符合您的需求。

在 GWT 上下文中,您可以使用 GWT.create(UserAgent.class).getCompileTimeValue() 访问 user.agent 绑定 属性 值,从而替换 *.gwt.xml<when-property-is name="user.agent" value"…" />使用 switch…case(请注意,您必须自己处理回退;例如,当没有规则专门匹配 ie9 时,ie9 回退到 ie8)。其他值得注意的值是 LocaleInfo.getCurrentLocale() 作为 locale 绑定 属性 的替代品(但你的注释处理器不太可能知道 属性 的所有可能值,除非你通过它们作为选项或处理器读取 GWT 模块文件——您可能也必须将其名称作为选项传递)。 LocaleInfo.getCurrentLocale().isRTL() 更有趣。您还可以使用 GWT.isClient()Window.Navigator 的属性;从 GWT 2.8 开始,您可以使用 System.getProperty() 访问您的配置和绑定属性(例如 System.getProperty("user.agent", "unknown"))。最后,当然还有 JSNI 来检测当前浏览器支持的特性;但是 GWT 编译器无法优化该代码,所有生成的 classes 将在最终的 JS 中编译。

当你不能使用这样的 static 值时,你仍然可以将相关的 context 作为参数传递给工厂。