¿Qué es exactamente Field Injection y cómo evitarlo?

Leí en algunos mensajes sobre Spring MVC y Portlets que no se recomienda la inyección de campo. Porque estoy tratando de obtener un Así que me pregunté si estoy usando inyección de campo y no puedo responderlo. Como lo entiendo, la inyección de campo es si se inyecta un Bean en un atributo con @Autowired como este:

CartController.java:

 ... @Autowired private Cart cart; ... 

BookshopConfiguartion.java:

 @Configuration public class BookShopConfiguration { @Bean public Cart cart(){ return new Cart(); } //more configuration 

Mi Cart.java se usa para almacenar y proporcionar información sobre los libros en el carrito.

Durante mi investigación, leí sobre la inyección de constructor :

MyComponent.java:

 ... public class MyComponent{ private Cart cart; @Autowired public MyComponent(Cart cart){ this.cart = cart; } ... 

¿Cuáles son las ventajas y desventajas de ambos tipos de inyecciones?


EDIT 1: Como esta pregunta está marcada como duplicada de esta pregunta, la revisé. Porque no hay ningún ejemplo de código ni en la pregunta ni en las respuestas, no está claro para mí si estoy en lo cierto al adivinar qué tipo de inyección estoy usando.

Tipos de inyección

Hay tres opciones para cómo las dependencias pueden ser inyectadas en un bean:

  1. A través de un constructor
  2. A través de setters u otros métodos
  3. A través de la reflexión, directamente en los campos

Está utilizando la opción 3. Eso es lo que sucede cuando usa @Autowired directamente en su campo.


Directrices de inyección

Una guía general, recomendada por Spring (ver las secciones DI basado en Constructor o DI basado en Setter ) es la siguiente:

  • Para dependencias obligatorias o cuando se busca la inmutabilidad, use la inyección de constructor
  • Para dependencias opcionales o cambiables, use setter injection
  • Evite la inyección de campo en la mayoría de los casos

Inconvenientes de inyección de campo

Las razones por las que la inyección de campo está mal vista son las siguientes:

  • No puedes crear objetos inmutables, como puedes con la inyección de constructor
  • Sus clases tienen un acoplamiento estrecho con su contenedor DI y no pueden usarse fuera de este
  • Sus clases no se pueden crear instancias (por ejemplo, en pruebas unitarias) sin reflexión. Necesita el contenedor DI para crear instancias de ellos, lo que hace que sus pruebas sean más parecidas a las pruebas de integración
  • Sus dependencias reales están ocultas desde el exterior y no se reflejan en su interfaz (ya sean constructores o métodos)
  • Es realmente fácil tener como diez dependencias. Si estuvieras usando la inyección de constructor, tendrías un constructor con diez argumentos, lo que indicaría que hay algo sospechoso. Pero puede agregar campos inyectados usando inyección de campo indefinidamente. Tener demasiadas dependencias es una señal de alerta de que la clase generalmente hace más de una cosa, y que puede violar el Principio de Responsabilidad Individual.

Conclusión

Dependiendo de sus necesidades, debe usar principalmente inyección de constructor o alguna combinación de inyección de constructor e instalador. La inyección de campo tiene muchos inconvenientes y debe evitarse. La única ventaja de la inyección de campo es que es más conveniente escribir, lo que no supera todos los contras.


Otras lecturas

Escribí un artículo de blog sobre por qué la inyección de campo generalmente no se recomienda: Inyección de dependencia de campo considerada dañina .

Esta es una de las discusiones interminables en el desarrollo de software, pero los principales influenciadores en la industria se están volviendo más obstinados sobre el tema y comenzaron a sugerir la inyección de constructores como la mejor opción.

Inyección de constructor

Pros:

  • Mejor capacidad de prueba . No necesita ninguna biblioteca burlona o un contexto Spring en pruebas unitarias. Puede crear un objeto que quiera probar con la palabra clave nueva . Tales pruebas son siempre más rápidas porque no dependen del mecanismo de reflexión. ( Esta pregunta fue hecha 30 minutos después. Si el autor hubiera usado la inyección de constructor, no habría aparecido).
  • Inmutabilidad . Una vez que se establecen las dependencias, no se pueden cambiar.
  • Código más seguro . Después de la ejecución de un constructor, su objeto está listo para usar, ya que puede validar todo lo que se pasó como parámetro. El objeto puede estar listo o no, no hay un estado intermedio. Con la inyección de campo se introduce un paso intermedio cuando el objeto es frágil.
  • Expresión más limpia de dependencias obligatorias . La inyección de campo es ambigua en este asunto.
  • Hace que los desarrolladores piensen en el diseño . dit escribió sobre un constructor con 8 parámetros, que en realidad es el signo de un mal diseño y el objeto de Dios anti-patrón . No importa si una clase tiene 8 dependencias en su constructor o en campos, siempre es incorrecta. Las personas son más reacias a agregar más dependencias a un constructor que a través de campos. Funciona como una señal para su cerebro de que debe detenerse por un momento y pensar en la estructura de su código.

Contras:

  • Más código (pero los IDEs modernos alivian el dolor).

Básicamente, la inyección de campo es lo opuesto.

Cuestion de gusto. Es tu decisión.

Pero puedo explicar por qué nunca uso la inyección de constructor .

  1. No quiero implementar un constructor para todos mis @Service , @Repository y @Controller . Quiero decir, hay alrededor de 40-50 frijoles o más. Cada vez que agrego un nuevo campo tendré que extender el constructor. No. No lo quiero y no tengo que hacerlo.

  2. ¿Qué sucede si su Bean (Servicio o Controlador) requiere la inyección de muchos otros granos? Un constructor con 8 parámetros es muy feo.

  3. Si uso CDI, el constructor no me concierne.


EDITAR : Vojtech Ruzicka dijo:

la clase tiene demasiadas dependencias y probablemente esté violando el principio de responsabilidad única y debe ser refactorizada

Sí. Teoría y realidad. Aquí hay un ejemplo: DashboardController mapeado a una sola ruta *:8080/dashboard .

My DashboardController recostack una gran cantidad de información de otros servicios para mostrarlos en una página de panel / descripción general del sistema. Necesito este solo controlador. Por lo tanto, debo asegurar solo esta ruta (filtro de función de usuario o autenticación básica).