如何在UiBinder和Widgets中使用GIN注入?

8
我正在使用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中使用这个小部件,但我会在我的视图中注入 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的值。
如果有人能提供一个解决方案,最好带上代码示例。
顺便说一句,我对注入完全不了解,但从我的理解来看,gin 可能会解决我的问题。但我不知道怎么做。
谢谢!
2个回答

6

目前依赖注入和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的assistedinject jar文件(例如guice-assistedinject-snapshot.jar)添加到您的类路径中。

谢谢。我稍后会尝试一下。我已经在Thomas Broyer的帖子评论中问过了:gwt-platform的GinUiBinder怎么样?我没有找到真正的文档。名称“听起来”像它可能使用了Gin和UiBinder,但我不是很确定... - Benjamin M
@Chris:这基本上是我建议使用AssistedInject的方法:用你的工厂替换Provider,在@UiFactory方法中添加参数(类似于@UiConstructor),并将它们传递给工厂。 - Thomas Broyer
你能否提供一个 @AssistedInject 的例子?我无法想象你的意思。 - Benjamin M
@Benjamin:好的,我添加了一个例子。顺便说一下,静态注入有一些缺点。官方的Guice文档说:“不建议一般使用此API,因为它遇到了许多与静态工厂相同的问题:测试起来很笨拙,使依赖关系变得不透明,并且依赖于全局状态。”如果多个模块尝试在静态字段上设置不同的提供程序,您可能会遇到麻烦... - Chris Lercher
@Thomas:感谢你提供的AssistedInject想法! - Chris Lercher
看起来比之前的例子好看一点。但是staticInjection要短得多。我认为我可以忽略大部分问题:这些都是非常基本的小部件,不需要测试,TranslationDictionary本身可以在没有staticInjection的情况下进行测试。对于这个依赖项,我认为我可以忽略不透明性。这个全局状态的东西是我无法想象的JavaScript中唯一的东西。我会写一个小应用程序并查看漂亮的编译代码。还有一个问题:你说的“在静态字段上设置不同的提供者”是什么意思? - Benjamin M

5
目前这是不可能的,因为UiBinder不会调用GIN来实例化小部件。为此,您必须使用@UiFactory方法或使用@Uifield(provided=true)提供已构建的实例。 已经有一个增强请求来更好地整合两者。您可以在此处跟踪它:http://code.google.com/p/google-web-toolkit/issues/detail?id=6151 作为替代方案,您可以使用requestStaticInjection将Provider注入到静态字段中,并从构造函数中调用提供程序的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 注释而不可能? - Benjamin M
忘记问了:静态注入有什么缺点吗?我的第一个想法是:方法requestStaticInjection看起来不像是真正应该使用它的(只用于紧急情况)。第二个想法是:在Java世界中,static是邪恶的(出于性能等原因),但我无法想象它在GWT中如何处理。 - Benjamin M
据我所知,GinUiBinder是一个实验性质的项目,不再适用于较新版本的GWT(但我可能错了)。另一个要点是AssistedInject,但它只是创建一个工厂,而不会对你的类进行任何魔法操作。至于“static”,无论是哪种语言或平台,静态状态都是有害的;在这种情况下,它并不是真正的问题,只是感觉很糟糕/奇怪。 - Thomas Broyer
谢谢您的回复。我想我会选择静态注入。这样我就不必在视图中添加仅适用于小部件的内容。或者我有遗漏的地方吗? - Benjamin M

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接