java - medium - Daga 2 Constructores de Inyección
inject annotation java (1)
Estoy empezando a usar Dagger 2 en una aplicación que estoy desarrollando, pero tengo algunas preguntas sobre cómo funciona Dagger 2.
Tengo toda la lógica detrás de los métodos @Provides y la anotación @Inject para inicializar sus dependencias, pero la anotación @Inject a los constructores de clases me molesta.
Por ejemplo:
Soy mi aplicación, tengo un módulo definido, el ContextModule, para recuperar el contexto de mi aplicación:
ContextModule.java
@Module
public class ContextModule {
private final Context context;
public ContextModule(Context context) {
this.context = context;
}
@Provides
public Context context() {
return this.context;
}
}
Este módulo es usado por mi BaseActivityComponent:
BaseActivityComponent.java
@BaseActivityScope
@Component(modules = ContextModule.class)
public interface BaseActivityComponent {
void injectBaseActivity(BaseActivity baseActivity);
}
Hasta ahora todo bien ... entonces tengo una clase AuthController, que depende del contexto y quiero inyectarla en mi BaseActivity. Así que en mi AuthControllers.class tengo algo como:
public class AuthController {
private Context context;
@Inject
public AuthController(Context context) {
this.context = context;
}
public void auth() {
// DO STUFF WITH CONTEXT
}
}
Y lo inyecto en mi BaseActivity como:
public class BaseActivity extends AppCompatActivity {
@Inject
AuthController authController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BaseActivityComponent component = DaggerBaseActivityComponent.builder()
.contextModule(new ContextModule(this))
.build();
component.injectBaseActivity(this);
authController.auth();
}
}
Ahora mi pregunta es, ¿cómo sabe Dagger que mis AuthControllers es una dependencia para BaseActivity? Solo por declarar
@Inject
AuthController authController;
Es como si hubiera creado un ControllerModule como:
@Module(includes = ContextModule.class)
public class ControllerModule {
@Provides
AuthController authController(Context context) {
return new AuthController(context);
}
}
Y luego en mi BaseActivityComponent agregaría mi getControl de AuthController y cambiaría mi módulo de dependencia a ControllersModule:
@BaseActivityScope
@Component(modules = ControllersModule.class)
public interface BaseActivityComponent {
void injectBaseActivity(BaseActivity baseActivity);
AuthController getAuthController();
}
Cuando llamo a injectBaseActivity ("this") "le dice" a Dagger que todas las anotaciones de @Inject son dependencias de mi clase, y luego busca en mi proyecto constructores anotados de @Inject que coinciden con ese tipo.
Pensé que algo bueno de Dagger 2 es que los archivos del Módulo podrían usarse como una "documentación" de mis dependencias tres. Pero si solo agregue @Inject en todos los constructores de los que tengo control, ¿no podría ser un poco confuso en el futuro, ya que no sabe qué depende realmente de qué? (Quiero decir, sabes lo que depende de qué, solo tienes que buscar muchos archivos para descubrirlo)
¿Hay alguna práctica recomendada para utilizar las anotaciones @Inject en constructores o cuándo agregar el método @Provides en los archivos de módulos? Obtengo que al usar @Inject en el constructor no necesito cambiar la definición del constructor en mi archivo de Módulo, pero ¿hay algún inconveniente?
Gracias.
Cuando llamo a injectBaseActivity (esto), "le dice" a Dagger que todas las anotaciones de @Inject son dependencias de mi clase, y luego busca en mi proyecto los constructores anotados de @Inject que coinciden con ese tipo.
Exactamente. Pero no se hace cuando se llama a injectBaseActivity
, pero todo sucede durante el tiempo de compilación. Esta es una manera de procesar las anotaciones (otra hace uso de la reflexión en tiempo de ejecución).
Cuando construyes tu proyecto, el procesador de anotaciones-daga que incluyes (como dependencia) en tu archivo build.gradle recibe una lista de todos tus campos, clases, etc. anotados por la anotación @Inject
y construye un gráfico de dependencias con él . Luego resuelve el gráfico, generando un código fuente que proporciona todas las dependencias para los elementos en el gráfico.
injectBaseActivity
simplemente ejecuta el código que se generó anteriormente y asigna todas las dependencias a su objeto. Es el código fuente adecuado, que puede leer y depurar.
La razón por la que esto es un paso de compilación, en pocas palabras, es el rendimiento y la validación. (por ejemplo, si tiene algún ciclo de dependencia, obtiene un error de compilación)
¿Cómo sabe Dagger que mi AuthControllers es una dependencia para BaseActivity?
@Inject
AuthController authController;
Al anotar el campo, @Inject
sabe que quiere un AuthController
. Hasta ahora tan bueno. Ahora Dagger buscará algunos medios para proporcionar el controlador, buscándolo dentro del componente, las dependencias de los componentes y los módulos de los componentes. También se verá si la clase se puede suministrar por sí sola, ya que conoce su constructor.
¿Cómo sabe Dagger sobre el constructor de objetos si no lo incluye en ningún módulo?
@Inject
public AuthController(Context context) { /**/ }
Al anotar el constructor con inyectar, también se le dijo a Dagger que existe una clase llamada AuthController
y que necesita un contexto para que se pueda crear una instancia. Es básicamente lo mismo que agregarlo a tu módulo.
Se debe usar un método de módulo @Provides
si no tiene el código fuente para simplemente agregar la anotación @Inject
al constructor, o si el objeto necesita una mayor inicialización. O en tu caso ...
[...] los archivos del Módulo podrían usarse como una "documentación" de mi árbol de dependencias [...]
Sí, por supuesto que podrías hacer eso. Pero a medida que su proyecto crezca, tendrá que mantener una gran cantidad de código innecesario, ya que se podría haber hecho lo mismo con una simple anotación en el constructor.
¿Hay alguna práctica recomendada para utilizar las anotaciones @Inject en constructores o cuándo agregar el método @Provides en los archivos de módulos?
Si desea proporcionar versiones diferentes para un contexto diferente (p. Ej., Implementar una interfaz de 2 maneras diferentes) también existe la anotación @Binds
que le indica a @Binds
qué clase desea proporcionar como implementación.
Aparte de eso, creo que siempre se debe utilizar inyección de constructor cuando sea posible. Si algo cambia, no tiene que tocar ninguna otra parte de su código, y es solo menos código que escribe, y por lo tanto menos lugares donde podría incluir un error.
Además, Dagger puede optimizar y sabe mucho al saber más, y si implementa un código innecesario, tendrá que trabajar con la sobrecarga que introdujo.
Por supuesto, al final todo depende de lo que creas que es mejor. Después de todo, es usted quien tiene que trabajar con su código;)