Hice una pregunta general de Spring: Auto-cast Spring Beans y varias personas respondieron que llamar a Spring's ApplicationContext.getBean()
debería evitarse tanto como sea posible. ¿Porqué es eso?
¿De qué otra forma debería obtener acceso a los beans que configuré Spring para crear?
Estoy usando Spring en una aplicación que no es web y había planeado acceder a un ApplicationContext
objeto compartido como lo describe LiorH .
Enmienda
Acepto la respuesta a continuación, pero aquí hay una toma alternativa de Martin Fowler que analiza los méritos de la Inyección de dependencias frente al uso de un Localizador de servicios (que es esencialmente lo mismo que llamar a un envuelto ApplicationContext.getBean()
).
En parte, Fowler afirma: " Con el localizador de servicios, la clase de aplicación lo solicita [el servicio] explícitamente mediante un mensaje al localizador. Con la inyección no hay una solicitud explícita, el servicio aparece en la clase de aplicación, de ahí la inversión de control. La inversión del control es una característica común de los marcos, pero es algo que tiene un precio. Tiende a ser difícil de entender y genera problemas cuando se trata de depurar. En general, prefiero evitarlo [Inversión de control ] a menos que lo necesite. Esto no quiere decir que sea algo malo, solo que creo que necesita justificarse sobre la alternativa más directa " .
new MyOtherClass()
objeto? Yo sé de @Autowired, pero yo sólo he utilizado en los campos y se rompe ennew MyOtherClass()
..applicationContext.getBean
no es inyección de dependencia: está accediendo al marco directamente, usándolo como un localizador de servicios .Provider<Foo>
lugar de aFoo
y llamandoprovider.get()
cada vez que necesites un nueva instancia. No se hace referencia al contenedor en sí, y puede crear fácilmente unaProvider
para realizar pruebas.Las razones para preferir el Localizador de servicios a la Inversión de control (IoC) son:
Service Locator es mucho, mucho más fácil para que otras personas lo sigan en su código. IoC es 'mágico', pero los programadores de mantenimiento deben comprender sus configuraciones complicadas de Spring y toda la miríada de ubicaciones para descubrir cómo conectar sus objetos.
IoC es terrible para depurar problemas de configuración. En ciertas clases de aplicaciones, la aplicación no se iniciará cuando esté mal configurada y es posible que no tenga la oportunidad de pasar por lo que sucede con un depurador.
IoC se basa principalmente en XML (las anotaciones mejoran las cosas, pero todavía hay mucho XML por ahí). Eso significa que los desarrolladores no pueden trabajar en su programa a menos que conozcan todas las etiquetas mágicas definidas por Spring. Ya no es suficiente conocer Java. Esto dificulta la experiencia de los programadores (es decir, en realidad es un diseño deficiente utilizar una solución más complicada cuando una solución más simple, como Service Locator, cumplirá los mismos requisitos). Además, el soporte para diagnosticar problemas XML es mucho más débil que el soporte para problemas Java.
La inyección de dependencia es más adecuada para programas más grandes. La mayoría de las veces la complejidad adicional no vale la pena.
A menudo se usa Spring en caso de que "desee cambiar la implementación más adelante". Hay otras formas de lograr esto sin la complejidad de Spring IoC.
Para las aplicaciones web (Java EE WARs), el contexto de Spring está vinculado de manera efectiva en el momento de la compilación (a menos que desee que los operadores trabajen en torno al contexto en la guerra explosiva). Puede hacer que Spring use archivos de propiedades, pero con los archivos de propiedades de los servlets tendrá que estar en una ubicación predeterminada, lo que significa que no puede implementar varios servlets de la misma vez en el mismo cuadro. Puede usar Spring con JNDI para cambiar las propiedades en el momento del inicio del servlet, pero si está usando JNDI para parámetros modificables por el administrador, la necesidad de Spring disminuye (ya que JNDI es efectivamente un Localizador de servicios).
Con Spring puede perder el control del programa si Spring está enviando sus métodos. Esto es conveniente y funciona para muchos tipos de aplicaciones, pero no para todas. Es posible que deba controlar el flujo del programa cuando necesite crear tareas (subprocesos, etc.) durante la inicialización o necesite recursos modificables que Spring no sabía cuando el contenido estaba vinculado a su WAR.
Spring es muy bueno para la gestión de transacciones y tiene algunas ventajas. Es solo que IoC puede ser una ingeniería excesiva en muchas situaciones e introducir una complejidad injustificada para los mantenedores. No use IoC automáticamente sin pensar en formas de no usarlo primero.
fuente
Es cierto que incluir la clase en application-context.xml evita la necesidad de usar getBean. Sin embargo, incluso eso es realmente innecesario. Si está escribiendo una aplicación independiente y NO desea incluir su clase de controlador en application-context.xml, puede usar el siguiente código para que Spring conecte automáticamente las dependencias del controlador:
He necesitado hacer esto un par de veces cuando tengo algún tipo de clase independiente que necesita usar algún aspecto de mi aplicación (por ejemplo, para pruebas) pero no quiero incluirla en el contexto de la aplicación porque no en realidad parte de la aplicación. Tenga en cuenta también que esto evita la necesidad de buscar el bean usando un nombre de cadena, que siempre he pensado que es feo.
fuente
@Autowired
anotación también.Uno de los mejores beneficios de usar algo como Spring es que no tiene que conectar sus objetos entre sí. La cabeza de Zeus se abre y aparecen tus clases, completamente formadas con todas sus dependencias creadas y conectadas, según sea necesario. Es mágico y fantástico.
Cuanto más dices
ClassINeed classINeed = (ClassINeed)ApplicationContext.getBean("classINeed");
, menos magia obtienes. Menos código es casi siempre mejor. Si su clase realmente necesitaba un bean ClassINeed, ¿por qué no lo conectó?Dicho esto, algo obviamente necesita crear el primer objeto. No hay nada de malo en que tu método principal adquiera un bean o dos a través de getBean (), pero debes evitarlo porque cada vez que lo usas, realmente no estás usando toda la magia de Spring.
fuente
La motivación es escribir código que no dependa explícitamente de Spring. De esa manera, si elige cambiar de contenedor, no tiene que volver a escribir ningún código.
Piense en el contenedor como algo invisible para su código, que cubre mágicamente sus necesidades, sin que se lo pidan.
La inyección de dependencia es un contrapunto al patrón del "localizador de servicios". Si va a buscar dependencias por nombre, también podría deshacerse del contenedor DI y usar algo como JNDI.
fuente
Usar
@Autowired
oApplicationContext.getBean()
es realmente lo mismo. En ambos sentidos, obtiene el bean que está configurado en su contexto y en ambos sentidos su código depende de spring. Lo único que debe evitar es crear instancias de su ApplicationContext. ¡Haz esto solo una vez! En otras palabras, una línea comosolo debe usarse una vez en su aplicación.
fuente
La idea es que confíe en la inyección de dependencia ( inversión de control o IoC). Es decir, sus componentes están configurados con los componentes que necesitan. Estas dependencias se inyectan (a través del constructor o establecedores): no se obtiene usted mismo.
ApplicationContext.getBean()
requiere que nombre un bean explícitamente dentro de su componente. En cambio, al usar IoC, su configuración puede determinar qué componente se usará.Esto le permite volver a cablear su aplicación con diferentes implementaciones de componentes fácilmente, o configurar objetos para probar de manera sencilla al proporcionar variantes simuladas (por ejemplo, un DAO simulado para que no golpee una base de datos durante la prueba)
fuente
Otros han señalado el problema general (y son respuestas válidas), pero solo ofreceré un comentario adicional: no es que NUNCA debas hacerlo, sino que lo hagas lo menos posible.
Por lo general, esto significa que se hace exactamente una vez: durante el arranque. Y luego es solo para acceder al bean "raíz", a través del cual se pueden resolver otras dependencias. Este puede ser un código reutilizable, como el servlet base (si se desarrollan aplicaciones web).
fuente
Una de las premisas de Spring es evitar el acoplamiento . Defina y use Interfaces, DI, AOP y evite usar ApplicationContext.getBean () :-)
fuente
Una de las razones es la comprobabilidad. Digamos que tienes esta clase:
¿Cómo puedes probar este frijol? Por ejemplo, así:
Fácil, verdad?
Si bien aún depende de Spring (debido a las anotaciones), puede eliminar su dependencia de Spring sin cambiar ningún código (solo las definiciones de las anotaciones) y el desarrollador de la prueba no necesita saber nada sobre cómo funciona Spring (tal vez debería de todos modos, pero permite revisar y probar el código por separado de lo que hace spring).
Todavía es posible hacer lo mismo cuando se utiliza el ApplicationContext. Sin embargo, debes burlarte de
ApplicationContext
que es una interfaz enorme. Necesita una implementación ficticia o puede usar un marco de imitación como Mockito:Esta es una gran posibilidad, pero creo que la mayoría de la gente estaría de acuerdo en que la primera opción es más elegante y simplifica la prueba.
La única opción que realmente es un problema es esta:
Probar esto requiere grandes esfuerzos o su bean intentará conectarse a stackoverflow en cada prueba. Y tan pronto como tenga una falla en la red (o los administradores en stackoverflow lo bloqueen debido a una tasa de acceso excesiva), tendrá pruebas de falla al azar.
Como conclusión, no diría que usar el
ApplicationContext
directamente es automáticamente incorrecto y debe evitarse a toda costa. Sin embargo, si hay mejores opciones (y las hay en la mayoría de los casos), utilice las mejores opciones.fuente
Solo he encontrado dos situaciones en las que se requería getBean ():
Otros han mencionado el uso de getBean () en main () para obtener el bean "principal" para un programa independiente.
Otro uso que he hecho de getBean () es en situaciones en las que una configuración interactiva del usuario determina la composición del bean para una situación particular. De modo que, por ejemplo, parte del sistema de arranque recorre una tabla de base de datos usando getBean () con una definición de bean scope = 'prototype' y luego configurando propiedades adicionales. Presumiblemente, hay una interfaz de usuario que ajusta la tabla de la base de datos que sería más amigable que intentar (re) escribir el XML de contexto de la aplicación.
fuente
Hay otro momento en que usar getBean tiene sentido. Si está reconfigurando un sistema que ya existe, donde las dependencias no se invocan explícitamente en los archivos de contexto de Spring. Puede comenzar el proceso haciendo llamadas a getBean, para que no tenga que conectar todo de una vez. De esta manera, puede construir lentamente su configuración de resorte colocando cada pieza en su lugar con el tiempo y alineando las brocas correctamente. Las llamadas a getBean eventualmente serán reemplazadas, pero a medida que comprenda la estructura del código, o le falte, puede comenzar el proceso de cablear más y más beans y usar cada vez menos llamadas para getBean.
fuente
sin embargo, todavía hay casos en los que necesita el patrón del localizador de servicios. por ejemplo, tengo un bean de controlador, este controlador puede tener algunos beans de servicio predeterminados, que pueden ser inyectados por la dependencia de la configuración. aunque también podría haber muchos servicios adicionales o nuevos que este controlador puede invocar ahora o más tarde, que luego necesitan el localizador de servicios para recuperar los beans de servicio.
fuente
Debe usar: ConfigurableApplicationContext en lugar de para ApplicationContext
fuente