Quiero llevar a cabo una cadena de elementos de procesamiento y conectarlos a través de Guice. Supongamos la siguiente ruta:
interface A
implementada por la class AImpl
necesita alguna entrada interface B
implementada por la class BImpl
necesita A
interface C
implementada por la class CImpl
necesita B
interface D
implementada por class DImpl
necesita C
La dependencia de A solo puede resolverse en tiempo de ejecución y no en el momento de la configuración. El enfoque habitual sería utilizar la inyección asistida en este caso para crear una fábrica, que tome las instancias que faltan como parámetros, como esta:
public interface AFactory { public A createA(String input); }
Pero lo que realmente quiero es algo como esto:
public interface DFactory { public D createD(String inputForA); }
No quiero pasar manualmente dependencias específicas de AImpl
través de toda la jerarquía. ¿Es posible lograr esto con Guice? Si no es así, ¿cuál es la mejor manera de eludir este problema de manera elegante y al mismo tiempo conservar los beneficios de la inyección?
Modo trampa: pega la input
en una variable estática o singleton ThreadLocal
. Configúrelo antes de que comience la tubería y límpielo una vez que termine. Enlaza todo lo demás a través de DI.
Fancy way: en A
, consulte la @PipelineInput String inputString
pero no la vincule en su inyector principal. De lo contrario, vincula las dependencias como lo harías normalmente, incluso refiriéndote a @PipelineInput
en otras clases relacionadas con la @PipelineInput
. Cuando necesites una D
, DFactory
de la implementación de una DFactory
, a la que llamo PipelineRunner
.
public class PipelineRunner { @Inject Injector injector; // rarely a good idea, but necessary here public D createD(final String inputForA) { Module module = new AbstractModule() { @Override public void configure() { bindConstant(inputForA).annotatedWith(PipelineInput.class); } }; return injector.createChildInjector(new PipelineModule(), module) .getInstance(D.class); } }
Naturalmente, los bashs de enlace para A
, B
, C
y D
fallarán fuera de PipelineRunner
por falta de una @PipelineInput String
@PipelineInput: obtendrá una CreationException
cuando cree el inyector con esas dependencias insatisfechas, como descubrió, pero aquellas Las dependencias basadas en tuberías deberían ser fáciles de separar en un Módulo que instale en el inyector hijo.
Si esto le parece demasiado raro, recuerde que los PrivateModules también se ” implementan utilizando inyectores originales “, y que el objective de la dependency injections es hacer que una dependencia como inputForA
esté disponible para todo el gráfico de objetos de forma desacoplada.
Veo tres opciones. Dependen de la frecuencia con que cambie la input
de A
1) Vincula la input
como una constante en tu módulo. Esto solo funciona, si conoce ese valor antes de crear el Injector
y nunca desea cambiar el valor. Ver bindConstant
2) Use un submódulo privado que enlace A
o el valor de input
dentro de ese módulo. Básicamente puede tener dos o tres gráficos de instancia con diferente valor. Ver newPrivateBinder .
3) Use un Scope
RequestScope
, SessionScope
, … De esta manera puede cambiar la entrada a menudo, pero debe ingresar / abandonar el scope en algún momento para definirlo. Consulte ámbitos personalizados para ver un ejemplo.