这个解决方案会工作,但我想提出一个稍微不同的一个给你。
具体而言,由于您要遍历深层对象结构,因此这看起来像访问者模式的工作。另外,你描述的内容似乎要求提供一个两阶段注入器:一个“引导”阶段,可以注入由透视图创建的层次结构所需的东西(但不能注入任何透视图创建的元素),第二阶段这是您的应用程序使用的真正的注入器(可以注入任何东西)。
我想提出的是这种基本模式:让访问者遍历层次结构,并且它会注入那些需要它的东西,并记录那些需要注入到别处的东西。然后,当它完成所有事情时,它使用Injector.createChildInjector
来创建一个新的Injector
,它可以从原始Injector
注入东西,并从透视图创建的层次结构中注入东西。
首先定义一个游客可以在这个层次打击一切:
public interface InjectionVisitor {
void needsInjection(Object obj);
<T> void makeInjectable(Key<T> key, T instance);
}
然后定义一个接口为您的所有支点创建的元素:
public interface InjectionVisitable {
void acceptInjectionVisitor(InjectionVisitor visitor);
}
你会实现这个接口您的(假设该代码在FooContainer
类中):
public void acceptInjectionVisitor(InjectionVisitor visitor) {
visitor.needsInjection(this);
visitor.makeInjectable(Key.get(FooContainer.class), this);
for (InjectionVisitable child : children) {
child.acceptInjectionVisitor(visitor);
}
}
请注意,前两个语句是可选的 - 可能是枢轴层次结构中的某些对象不需要注入,也可能是其中一些稍后不想注入的对象。另外,请注意使用的Key
- 这意味着,如果你想要一些类是注射用特定的注解,你可以这样做:
visitor.makeInjectable(Key.get(Foo.class, Names.named(this.getName())), this);
现在,你如何实现InjectionVisitor
?具体方法如下:
public class InjectionVisitorImpl implements InjectionVisitor {
private static class BindRecord<T> {
Key<T> key;
T value;
}
private final List<BindRecord<?>> bindings = new ArrayList<BindRecord<?>>();
private final Injector injector;
public InjectionVisitorImpl(Injector injector) {
this.injector = injector;
}
public void needsInjection(Object obj) {
injector.injectMemebers(obj);
}
public <T> void makeInjectable(Key<T> key, T instance) {
BindRecord<T> record = new BindRecord<T>();
record.key = key;
record.value = instance;
bindings.add(record);
}
public Injector createFullInjector(final Module otherModules...) {
return injector.createChildInjector(new AbstractModule() {
protected void configure() {
for (Module m : otherModules) { install(m); }
for (BindRecord<?> record : bindings) { handleBinding(record); }
}
private <T> handleBinding(BindRecord<T> record) {
bind(record.key).toInstance(record.value);
}
});
}
}
然后,您在您的main
方法使用为:
PivotHierarchyTopElement top = ...; // whatever you need to do to make that
Injector firstStageInjector = Guice.createInjector(
// here put all the modules needed to define bindings for stuff injected into the
// pivot hierarchy. However, don't put anything for stuff that needs pivot
// created things injected into it.
);
InjectionVisitorImpl visitor = new InjectionVisitorImpl(firstStageInjector);
top.acceptInjectionVisitor(visitor);
Injector fullInjector = visitor.createFullInjector(
// here put all your other modules, including stuff that needs pivot-created things
// injected into it.
);
RealMainClass realMain = fullInjector.getInstance(RealMainClass.class);
realMain.doWhatever();
注意的方式createChildInjector
工作确保如果您有任何@Singleton
东西,在东西束缚注入枢轴层次,您将获得由您的实际注射器注射的相同实例 - 只要firstStageInjector
能够处理注射,fullInjector
就会将注射代表委托给firstStageInjector
。
编辑添加:对此的一个有趣的扩展(如果您想深入深入Guice魔法)是修改InjectionImpl
,以便它在您的源代码中记录makeInjectable
的位置。然后,当代码意外地告诉访问者绑定到同一个密钥的两个不同的东西时,这会让你从Guice中获得更好的错误消息。要做到这一点,你想添加一个StackTraceElement
到BindRecord
,记录的new RuntimeException().getStackTrace()[1]
方法makeInjectable
内的结果,然后更改handleBinding
到:
private <T> handleBinding(BindRecord<T> record) {
binder().withSource(record.stackTraceElem).bind(record.key).toInstance(record.value);
}
哇!这是相当彻底和精心的答案。我非常感激。并感谢分享Guice魔术:) 我会尝试建议的方法。 (答案明天就会被接受。) – dragonfly 2010-05-18 13:40:41