2011-03-25 14 views
18

Estoy interesado en la inyección directa de dependencia de campo. Tradicionalmente, Spring admite tanto la inyección de constructor (proporcionando argumentos a los constructores) como la inyección basada en el instalador (los establecedores de llamadas en una llamada).¿Por qué Spring no admite la inyección directa de dependencia de campo (excepto para autocableado)?

Sin embargo, Spring también es capaz de inyección de campo directa (configuración de campos de miembros de un objeto sin un método setter), como se evidencia al anotar campos con @Autowired. El autoenvío está limitado a solo "beans", por lo que los valores primitivos no se pueden inyectar (aunque esto se puede evitar mediante la creación de beans de la clase "java.lang.String"; esto funciona, pero tiene las advertencias normales de autoenvío). Además de esto, Spring admite @Value para establecer valores directamente en los campos de miembros desde propiedades, etc.

Sin embargo, Spring no permite que las propiedades se establezcan directamente en los campos de miembros (sin autoenvío).

Mi pregunta es: ¿por qué?

Obviamente es capaz de hacerlo, entonces ¿por qué no? ¿Hay algún efecto secundario negativo que lo impida? ¿O la capacidad es de alguna manera limitada, de modo que solo el autoenvío tiene sentido? ¿Necesita algunos hacks más grandes que llamar setters?

Tenga en cuenta que no deseo discutir los méritos relativos de tener instaladores y captadores en general, solo las razones por las cuales Spring ha hecho esta elección.

+1

Siempre me he preguntado lo mismo y asumió la razón se relaciona con "hacer que el código limpio" y la prevención de la emisión de advertencias del compilador en torno a este valor, se establece-nunca-y-que-está-intentar-a -use-it. –

Respuesta

8

La anotación @Autowired usa la reflexión para hacer que los campos privados sean accesibles (consulte this related question). Puedo ver tres cosas por las que no se usa en los archivos de configuración de Spring.

  1. Desde la configuración pasa por las propiedades del bean (captadores y definidores) que no se puede decir - en el caso probable de que ambos existen - si se quiere, por ejemplo, llame a setValue o establezca el valor del miembro.
  2. Rompe la encapsulación. La configuración de Spring no tiene ningún motivo para conocer las variables de miembros privados. Con una anotación que está bien, ya que está justo allí en el código fuente.
  3. Problemas de seguridad. Un administrador de seguridad más restrictivo podría no permitir que los campos privados sean accesibles a través de la reflexión.
+0

Para el caso 1, yo diría que es trivial preferir el setter y solo buscar un campo en caso de que el setter no exista. Para el caso 2, no veo el punto si se permite que Spring llame a setters privados. Si Spring no puede llamar a entidades privadas, entonces no sé por qué las mismas restricciones de acceso no se pudieron aplicar a las variables miembro. Y, estaría bien con una anotación también, simplemente no autoconectado. Para el caso 3, ¿podría un administrador de seguridad más restrictivo no permitir que se pueda acceder a emisores privados por reflexión? No veo por qué las restricciones de acceso deben ser diferentes. – Nakedible

+0

La inyección de setter privado tampoco es una buena idea. Enfatizo en el punto 2: si acceso a miembros o métodos privados, no importa, aún rompe la encapsulación y va en contra de los principios tanto de OOP como de inyección de dependencia. También seguirán existiendo los mismos problemas de seguridad. – Daff

+3

Si el método setter es * public *, ¿por qué el campo member no debe ser * public * también? O bien, si el setter es * private *, ¿cómo tiene un mayor impacto la configuración de un campo * private * member? – Nakedible

10

Creo que encontré la respuesta yo mismo. Me acerqué al código fuente de Spring y vi cómo se implementaban realmente las funciones. Esto es lo que encontré:

Establecer las propiedades a través de XML es probablemente la parte más antigua de Spring, y depende mucho de las clases "java.beans" para la introspección, enumeración de propiedades, etc. Y, obviamente, esas no admiten introspección de campo en absoluto. Además de esto, está la maquinaria de conversión de tipos que determina cómo se puede convertir el valor de la propiedad en un valor adecuado para la propiedad en cuestión. No hay piezas claramente separables aquí.

Todas las cosas de @Autowired etc. se implementan en un BeanPostProcessor, que tiene su propia mecánica de coincidencia de tipos, que no tiene nada que ver con la conversión de tipos. Es por eso que solo inyecta frijoles. Lo mismo para @Value, es algo que se resuelve allí mismo y no tiene nada que ver con las propiedades.

Por lo tanto, agregar soporte de inyección de campo, para propiedades en particular, no es un esfuerzo de ingeniería trivial ya que las partes del código que hacen una u otra están prácticamente separadas.

Esto no responde exactamente "¿Por qué?", pero creo que esta es una explicación más convincente de por qué Spring no ha agregado la inyección de dependencia de campo directa que las otras explicaciones que he escuchado. A menos que tengan algo fundamental en contra (lo cual dudo, considerando que quieren permitir la configuración de las clases de terceros, no sólo JavaBeans) existente, entonces es sólo una cuestión de esfuerzo de ingeniería para obtener la funcionalidad de.

1

se hace ayuda esto a través de JSR-250 @Resource anotación (en la primavera 3.0+)

Yo personalmente prefiero esto a la inyección setter, y tengo sentimientos encontrados con esto al considerar la inyección del constructor. Aquí están las consideraciones que he pensado sobre FWIW:

1) La inyección de constructor es una buena forma de auto-documentar sus dependencias de beans (pro) pero crea una gran cantidad de violaciones DRY: (a) campo privado, (b) argumento constructor, (c) código de constructor para establecer el campo del parámetro, (d) el código adicional en la configuración del bean (ya sea en @Configuration classes o en xml). Eso es MUCHA violación SECA solo por el bien de la pureza de la encapsulación, que ni siquiera me importa. Esto es apenas una violación de encapsulación - pero crea una gran dependencia de algunos contenedores inyección que respeta JSR-250 anotaciones (ver al lado)

2) crea una dependencia de JSR-250 contenedores compatible. Tengo sentimientos encontrados sobre esto. Cuando escuché por primera vez sobre @Resource lo escribí diciendo que dificultaría la prueba de mi sistema. Sin embargo, terminé usando primavera en mis pruebas de todos modos. Todavía puedo usar frijoles simulados como lo haría de todos modos, así que nunca fue realmente un problema. Y la pregunta está fuera de las pruebas, ¿cuándo querrías reutilizar esto? En mi opinión, si está diseñando el sistema para aprovechar un contenedor, abrácelo y úselo. ¿Las violaciones secas realmente valen la flexibilidad de no correr dentro de un contenedor? Al menos con las anotaciones JSR-250 puede ejecutar en cualquier entorno JEE6 y obtener la inyección como desee.

3) puede crear algunos no tan grandes escenarios de depuración si se instancia una parte exterior del contenedor: es decir, obtendrá excepciones de puntero nulo en lugar de algo agradable. Esto es una compensación. Personalmente, creo que no es horrible, quiero decir, si obtienes una NPE en una línea con @Resource, entonces te das cuenta rápidamente de que no fue inyectada.

+0

Las anotaciones de @Resource se autocablean por nombre, que no es lo que estaba buscando. – Nakedible

Cuestiones relacionadas