【问题标题】:How to use injection via GIN with UiBinder and Widgets?如何通过 GIN 与 UiBinder 和小部件一起使用注入?
【发布时间】:2012-07-11 04:06:12
【问题描述】:

我正在使用 GWT 2.4 和 gwt-platform 0.7 和 gin 1.5.0。

我已经为我的 GWT 应用程序的动态(实时)翻译构建了一个库。因此,当LocaleChangeEvent 被触发时,每个小部件都会收到通知,然后让我的TranslationDictionary 显示新字符串。

这些小部件实际上是这样的:

public class LocaleAwareLabel extends Label implements LocaleChangeEventHandler {
    TranslationDictionary dictionary;
    String translationToken;

    public LocaleAwareLabel(TranslationDictionary dictionary, EventBus eventBus, String translationToken) {
        this.dictionary = dictionary;
        this.translationToken = translationToken;
        eventBus.addHandler(LocaleChangeEvent.TYPE, this);
        getCurrentTranslationFromDictionary();
    }

    public void getCurrentTranslationFromDictionary() {
        this.setText(dictionary.getTranslation(translationToken));
    }

    @Override
    public void onLocaleChange(LocaleChangeEvent event) {
        getCurrentTranslationFromDictionary();
    }
}

如您所见:我不能轻易地将这个小部件与 UiBinder 一起使用,目前我在我的 View 中注入 EventBusTranslationDictionary 并像这样使用 @UiField(provided=true)

@UiField(provided=true)
LocaleAwareLabel myLabel;

@Inject
public MyView(TranslationDictionary dictionary, EventBus eventBus) {
    widget = uiBinder.createAndBindUi(this);

    myLabel = new LocaleAwareLabel(dictionary, eventBus, "someTranslationToken");
}

我想要的: 使用不带@UiField(provided=true) 的小部件,所以我可以简单地将它们放在ui.xml 中,如下所示:

<custom:LocaleAwareLabel ui:field="myLabel" translationToken="someTranslationToken" />

我知道我可以通过 UiBinder 设置 translationToken 使用:

public void setTranslationToken(String translationToken) {
    this.translationToken = translationToken;
}

但是由于EventBusTranslationDictionary,我仍然无法使用零参数构造函数。另外我不能在构造函数中调用getCurrentTranslationFromDictionary(),因为translationToken的值当然是在构造函数之后设置的。

如果有人可以提供解决方案会很好,也许有代码示例。

还有 P.S.我是一个完全的注射菜鸟,但据我了解,杜松子酒可能会以某种方式解决我的问题。但我不知道怎么做。

谢谢!

【问题讨论】:

    标签: gwt dependency-injection uibinder gwt-gin


    【解决方案1】:

    目前Dependency Injection和UiBinder有点概念上的不匹配,不过我目前使用的方式是:

    private final Provider<LocaleAwareLabel> labelProvider;
    
    @Inject
    public MyView(TranslationDictionary dictionary, 
                  EventBus eventBus, 
                  Provider<LocaleAwareLabel> labelProvider) {
    
      this.dictionary = dictionary;
      this.labelProvider = labelProvider;
      initWidget(uiBinder.createAndBindUi(this));
    }
    
    @UiFactory
    public LocaleAwareLabel buildLocaleAwareLabel() {
      return labelProvider.get();
    }
    

    然后我可以在我的 ui.xml 中创建任意数量的标签:

    <g:HTMLPanel>
      <custom:LocaleAwareLabel translationToken="abc"/>
      <custom:LocaleAwareLabel translationToken="xyz"/>
    </g:HTMLPanel>
    

    在 LocaleAwareLabel 中,我使用 setter 方法作为翻译标记,并覆盖 onLoad()

    private String translationToken;
    
    @Inject
    public LocaleAwareLabel(TranslationDictionary dictionary, EventBus eventBus) {
      this.dictionary = dictionary;
      initWidget(uiBinder.createAndBindUi(this));
    }
    
    @Override
    protected void onLoad() {
      super.onLoad();
      doSomethingWithTheTranslationToken(); // do this here, not in the constructor!
    }
    
    public void setTranslationToken(final String translationToken) {
      this.translationToken = translationToken;
    }
    

    AssistedInject 示例

    MyView 更改为:

    private final LocaleAwareLabelFactory labelFactory;
    
    @Inject
    public MyView(TranslationDictionary dictionary, 
                  EventBus eventBus, 
                  LocaleAwareLabelFactory labelFactory) {
    
      this.dictionary = dictionary;
      this.labelFactory = labelFactory;
    
      initWidget(uiBinder.createAndBindUi(this));
    }
    
    @UiFactory
    public LocaleAwareLabel buildLocaleAwareLabel(final String translationToken) {
      return labelFactory.create(translationToken);
    }
    

    LocaleAwareLabel:

    public interface LocaleAwareLabelFactory {
      LocaleAwareLabel create(final String translationToken);
    }
    
    @Inject
    public LocaleAwareLabel(TranslationDictionary dictionary, 
                            EventBus eventBus, 
                            @Assisted String translationToken) {
    
      this.dictionary = dictionary;
      initWidget(uiBinder.createAndBindUi(this));
      doSomethingWithTheTranslationToken(); // now you can do it in the constructor!
    }
    

    在您的 Gin 模块中,添加:

    install(new GinFactoryModuleBuilder().build(LocaleAwareLabelFactory.class));
    

    确保将 guice 的辅助注入 jar(例如 guice-assistedinject-snapshot.jar)添加到您的类路径中。

    【讨论】:

    • 谢谢。我稍后会尝试。我已经在对 Thomas Broyer 帖子的评论中问过:gwt-platform 的 GinUiBinder 怎么样?我还没有找到任何真正的文档。 “听起来”这个名字可以将 Gin 与 UiBinder 一起使用,但我真的不知道......
    • @Chris:这基本上是我建议使用 AssistedInject 的方法:用您的工厂替换 Provider,将参数添加到 @UiFactory 方法(类似于 @UiConstructor)并将它们传递给工厂。
    • 您能否为@AssistedInject 提供一个示例。我真的无法想象你的意思。
    • @Benjamin:好的,我添加了一个示例。顺便说一句,静态注入有一些缺点。官方Guice documentation 说:“这个API 不推荐用于一般用途,因为它存在许多与静态工厂相同的问题:测试笨拙,依赖不透明,并且依赖于全局状态。”如果多个模块尝试在静态字段上设置不同的提供程序,您可能会遇到麻烦......
    • @Thomas:感谢 AssistedInject 的想法!
    【解决方案2】:

    目前这是不可能的,因为 UiBinder 永远不会调用 GIN 来实例化小部件。为此,您必须使用 @UiFactory 方法,或使用 @Uifield(provided=true) 提供已构建的实例。

    已经有人要求增强以更好地整合两者。你可以在这里追踪它:http://code.google.com/p/google-web-toolkit/issues/detail?id=6151

    作为替代方案,您可以requestStaticInjectionProvider 注入static 字段,并从您的构造函数中调用提供者的get()

    public class LocaleAwareLabel extends Label {
       @Inject Provider<EventBus> eventBusProvider;
       @Inject Provider<TranslationDictionary> dictionaryProvider;
    
       private final TranslationDictionary dictionary;
       private final EventBus eventBus;
       private final string translationToken;
    
       @UiConstructor
       public LocaleAwareLabel(String translationToken) {
           this(dictionaryProvider.get(), eventBusProvider.get(), translationToken);
       }
    
       // For cases where you can use injection, or inject params yourself
       @Inject
       public LocaleAwarelabel(TranslationDictionary dictionary, EventBus eventBus,
               String translationToken) {
           this.dictionary = dictionary;
           this.eventBus = eventBus;
           this.translationToken = translationToken;
       }
    

    您可能还对 GIN 对 Assisted-Inject 的支持感兴趣,以便更轻松地编写 @UiFactory 方法或初始化 @UiField(provided=true) 字段。

    【讨论】:

    • 谢谢。 @UiFactory 看起来与 @UiField(provided=true) 几乎相同。 @UiConstructor 方法看起来更像我想要的,但仍然不完美!在我的脑海中还有另外两种可能性(但我不知道如何实现它们):1. 我在某处读到了有关 GinUiBinder(来自 gwt-platform)的信息。 2. 从我读过的内容来看,我认为 gin 可以提供某种自动注入 EventBusTranslationDictionary 的工厂,然后只提供 LocaleAwareLabel(String translationToken) 构造函数。还是因为 @UiConstructor 注释不可能?
    • 忘了问:静态注入有什么缺点吗?第一个想法:requestStaticInjection 方法看起来并不真正应该有人使用它(仅用于紧急情况)。第二个想法是:在 Java 世界中 static 是邪恶的(出于性能原因等),但我无法想象它在 GWT 中是如何处理的。
    • AFAIK,GinUiBinder 是一项实验,不再适用于较新版本的 GWT(不过我可能错了)。另一点是AssistedInject,但它创建了一个工厂,它对你的类没有任何魔力。至于static,静态state 是邪恶的,无论是哪种语言或平台;在这种情况下,这并不是真正的问题,只是感觉不好/奇怪。
    • 感谢您的回复。我想我会使用静态注入。所以我不必用仅用于小部件的东西来污染我的视图。还是我忘记了什么?
    猜你喜欢
    • 2011-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-04
    • 2012-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多