Esta solución funcionará, pero me gustaría proponerle una solución ligeramente diferente.
Específicamente, ya que va a atravesar una estructura de objeto profunda, esto realmente parece un trabajo para el patrón de visitante. Además, lo que estás describiendo parece llamar a un inyector de dos etapas: una etapa de "arranque" que puede inyectar cosas necesarias para la jerarquía creada por pivote (pero no puede inyectar ningún elemento creado por pivote) y una segunda etapa ese es el inyector real usado por su aplicación (que puede inyectar cualquier cosa).
Lo que yo sugeriría es este patrón básico: hacer un visitante que atraviesa la jerarquía y, a medida que avanza, hace la inyección en aquellas cosas que lo necesitan y registra aquellas cosas que necesitan ser inyectadas en otro lugar. Luego, cuando termina de visitar todo, usa Injector.createChildInjector
para crear un nuevo Injector
que puede inyectar cosas del Injector
original y cosas de la jerarquía creada por pivote.
En primer lugar definir un visitante que puede golpear todo en esta jerarquía:
public interface InjectionVisitor {
void needsInjection(Object obj);
<T> void makeInjectable(Key<T> key, T instance);
}
a continuación, definir una interfaz para todos los elementos de pivote creados:
public interface InjectionVisitable {
void acceptInjectionVisitor(InjectionVisitor visitor);
}
Se podría implementar esta interfaz en su clases creadas por pivote como (suponiendo este código en la clase FooContainer
):
public void acceptInjectionVisitor(InjectionVisitor visitor) {
visitor.needsInjection(this);
visitor.makeInjectable(Key.get(FooContainer.class), this);
for (InjectionVisitable child : children) {
child.acceptInjectionVisitor(visitor);
}
}
Tenga en cuenta que las dos primeras afirmaciones son opcionales; es posible que algunos objetos de la jerarquía dinámica no necesiten inyección y también podría ser que algunos de ellos no deseen inyectarse más adelante. También, observe el uso de Key
- esto significa que si quieres algo de la clase que sea inyectable con una anotación particular, se puede hacer algo como:
visitor.makeInjectable(Key.get(Foo.class, Names.named(this.getName())), this);
Ahora bien, ¿Cómo se implementa InjectionVisitor
? Así es como:
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);
}
});
}
}
A continuación, utilizar esto en su método main
como:
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();
Tenga en cuenta que la forma createChildInjector
obras asegura que si tiene alguna @Singleton
cosas con destino en el material inyectado en la jerarquía de pivote , obtendrá las mismas instancias inyectadas por su inyector real: el fullInjector
delegará el injertoion en el firstStageInjector
siempre que el firstStageInjector
sea capaz de manejar la inyección.
Editado para agregar: Una extensión interesante de esto (si desea profundizar en la magia profunda de Guice) es modificar InjectionImpl
para que registre el lugar en su código fuente que llamó al makeInjectable
. Esto le permite obtener mejores mensajes de error de Guice cuando su código accidentalmente le dice al visitante acerca de dos cosas diferentes vinculadas a la misma clave. Para hacer esto, usted quiere añadir un StackTraceElement
a BindRecord
, grabar el resultado de new RuntimeException().getStackTrace()[1]
dentro del método makeInjectable
, y luego cambiar a handleBinding
:
private <T> handleBinding(BindRecord<T> record) {
binder().withSource(record.stackTraceElem).bind(record.key).toInstance(record.value);
}
¡Guau! Esta es una respuesta bastante completa y elaborada. Lo aprecio mucho. Y gracias por compartir la magia de Guice :) Voy a intentar el enfoque sugerido. (La respuesta se aceptará mañana.) – dragonfly