español jsf java-ee dependency-injection seam cdi

jsf - español - Comprender la necesidad de escribir seguridad en CDI



cdi java ee 7 (3)

No es necesario crear un @Qualifier personalizado cada vez. Es suficiente crear solo uno con algún parámetro. CDI tratará @CustomQualifier("myBean") y @CustomQualifier("yourBean") como calificadores diferentes.

Además, dicho calificador ya está en el paquete CDI - @Named

Pero a veces los calificadores estáticos son muy útiles, por ejemplo (vea @Alternative ):

@Alternative @Qualifier @Documented @Retention(value=RUNTIME) public @interface Mock

y puede marcar algunos beans como @Mock para usar solo para pruebas (no olvide habilitar alternative sección alternative en beans.xml en classpath de prueba).

Primero, debo aclarar que este post no pretende criticar el CDI, sino descubrir el pensamiento y las suposiciones detrás del diseño del CDI y que tendrá una influencia obvia en el diseño de cualquier aplicación web que use CDI.

Una de las características más destacadas de CDI (de Java EE 6) es seguridad tipo . Jboss Seam no era seguro en el tipo. Utiliza nombre para calificar cualquier instancia para inyectar. Como abajo:

@Name("myBean") public class MyBean implements Bean { ... } @Name("yourBean") public class YourBean implements Bean { ... }

Al inyectar MyBean uno puede hacer esto:

@In private Bean myBean; //myBean is injected @In private Bean yourBean; //yourBean is injected

Y versiones anteriores de Spring (antes de 3.0), este tipo de inyección sucedió como sigue:

Simplemente defina los beans en el archivo de configuración de beans:

<bean id="myBean" class="com.example.common.MyBean"> ... </bean> <bean id="yourBean" class="com.example.common.YourBean"> ... </bean>

Y use calificador con nombre, decidiendo cuál usar:

@Autowired @Qualifier("myBean") private Bean bean; @Autowired @Qualifier("yourBean") private Bean bean;

Pero ahora en CDI, primero debe definir una anotación de Qualifier personalizada para cualquier tipo específico de objeto. Luego usa esa anotación para calificar ese objeto. Al final del día, cuando miras tu código fuente, ves que desperdiciaste una cantidad de tiempo considerable para escribir muchas anotaciones personalizadas para la inyección de dependencias. La comunidad Java se está moviendo hacia las anotaciones, dejando atrás las configuraciones basadas en XML ( verbose XML ). ¿Hay algo que convenza a alguien a pensar esto (escriba seguridad con anotaciones personalizadas) no como anotaciones verbosas , sino como una característica excelente y distinguida de CDI?

Editar:

Puntos, empujados para ser resaltados

  1. Si utilizo el calificador personalizado para la seguridad del tipo por servicio o dao (por interfaz), entonces para una aplicación de gran tamaño, como tener 1000 o más clases de servicio o dao con múltiples implementaciones, será complicado. Luego, para aplicaciones de gran tamaño, ¿es factible usar inyección segura tipo?
  2. Si la respuesta a la pregunta anterior es "No", entonces, ¿de qué sirve usar seguridad tipo?
  3. Incluso si es factible escribir anotaciones para la seguridad del tipo, en aplicaciones grandes, ¿realmente vale la pena el esfuerzo solo por evitar la configuración verbosa xml ?
  4. ¿Cuándo realmente necesito seguridad de tipo en lugar de calificador de nombre de frijol?

Breve discusión sobre los puntos anteriores

  1. No hay demasiados casos en los que realmente necesite un tipo de inyección segura, especialmente cuando tiene una implementación de una interfaz, debe usar @Name como calificador. Entonces sí, en una aplicación de gran tamaño es factible usar seguridad tipo cuando realmente se necesita.
  2. Por supuesto, la seguridad del tipo es una de las características distintivas del CDI y en la respuesta aceptada hay una lista no exhaustiva de razones por las que puede optar por utilizar el tipo de seguridad.
  3. Como usted es un programador inteligente y sabe exactamente cuándo usar la seguridad del tipo, definitivamente vale la pena el esfuerzo, cuando realmente lo necesita.
  4. La mayoría de las partes de la respuesta aceptada realmente habla, cuando necesitamos seguridad de tipo y este artículo también es muy útil para entender.

Gracias y feliz codificación!


¿CDI es detallado? ¿Se necesitan calificadores?

  1. En primer lugar, no necesita calificadores cuando solo tiene una implementación de una interfaz.
  2. Si tiene múltiples implementaciones de una interfaz, pregúntese: ¿necesita diferenciarlas después de la implementación?

    • Si la respuesta es no, entonces considere usar alternativas.
    • Si la respuesta es sí, entonces aún no necesita calificadores. Este es el por qué:

      Para usar tu propio ejemplo:

      public class MyBean implements Bean { ... } public class YourBean implements Bean { ... }

      Entonces, simplemente haces:

      @Inject MyBean bean;

      o

      @Inject YourBean bean;

      Si no le gusta que las variables de su instancia sean de un tipo concreto y prefiera ver una interfaz, entonces haga lo siguiente:

      private Bean bean; @Inject public void setBean(MyBean bean) { this.bean = bean; }

      o

      private Bean bean; @Inject public void setBean(YourBean bean) { this.bean = bean; }

      En todos los casos anteriores, es completamente libre de calificadores, absolutamente seguro para tipos, y definitivamente no es detallado.

  3. Luego, para detallar el último punto, ¿necesita el desarrollador elegir la implementación adecuada o puede hacer la elección de forma problemática?

    • Si el desarrollador va a elegir, haz lo que se describe arriba en 2.
    • Si la elección puede hacerse problemáticamente, entonces use un productor:

      @Produces public Bean obtainTheAppropriateBean(InjectionPoint ip) { if (meetsConditionA(ip)) { return getBeanImplA(); } else if (meetsConditionB(ip)) { return getBeanImplB(); } else if (...) { ... } else { return getDefaultBeanImpl(); } }

      Aún calificador libre, seguro para tipos, y tal vez aún no detallado (opción comercial para la automatización).

    Vea este artículo para una excelente expansión de este punto e ideas sobre cómo usar la API de InjectionPoint .

¿Se necesitan calificadores en absoluto?

Puedo ver que esta pregunta surge después de los ejemplos anteriores. La respuesta es sí y aquí hay una lista no exhaustiva de razones por las que puede elegir usarlas:

  • En uno de los ejemplos anteriores mencioné la inyección de implementaciones específicas de una interfaz para evitar el uso de calificadores. Eso está completamente bien cuando el código es interno y los desarrolladores internos sabrán cuál es cuál. Pero, ¿qué pasa si el código es una biblioteca o marco y no desea exponer ninguna implementación en particular en la API pública? Defina algunos calificadores a continuación y documentarlos bien. ¿En qué se diferencia esto de XML detallado? A pesar de que usted como escritor de la biblioteca puede estar haciendo tanto trabajo detallado, sus usuarios no tendrán que hacerlo. En su lugar, escribirán una palabra sobre un punto de inyección y se alegrarán de que no les haya hecho escribir ningún XML; yo personalmente estaría muy muy feliz. :)
  • En el ejemplo de productor anterior, es posible que pueda cubrir la mayoría de los casos pero no todos con la lógica en el método de productor. O tal vez solo desea la capacidad de anular esa lógica en cualquier punto de inyección en particular. Luego, mantenga al productor, haga un calificador y anote alguna implementación específica con él. Luego use el calificador cuando no quiera que se ejecute la lógica del productor.
  • Imagine una situación en la que tiene múltiples interfaces y múltiples implementaciones. Las implementaciones particulares pueden tener rasgos comunes distinguibles y esos rasgos son comunes a todas sus interfaces. Como ejemplo, tomemos el Marco de Colection de Java, específicamente las interfaces List, Set y Map. Cada uno de ellos tiene implementaciones múltiples, pero hay rasgos comunes en todas o algunas interfaces. Por ejemplo, nodos vinculados (iteración rápida): piense LinkedList, LinkedHashSet, LinkedHashMap; ordenados (pedidos): piense en TreeSet, TreeMap; basada en tablas hash (inserción / eliminación rápida / contiene) - piense en HashSet, LinkedHashSet, HashMap, LinkedHashMap; concurente; acceso aleatorio; etc. Ahora, podría definir las @Linked , @Sorted y @Hash . Luego inyecta:

    @Inject @Linked @Hash private Map map; @Inject @Sorted private Map map; @Inject @Hash private Set set; @Inject @Hash private Set set;

    Ahora, ¿vale la pena hacer esto para un marco de colecciones? No lo haría, pero tengo un caso similar al que describo aquí en mi proyecto actual en el trabajo (lo siento, no puedo discutir).

  • Y, por último, puede usar calificadores para pasar parámetros a los productores junto con @Nonbinding . Continuando con el marco de colecciones anterior, defina:

    @Qualifier @Documented @Retention(RetentionPolicy.RUNTIME) public @interface Hash { @Nonbinding int capacity() default 16; @Nonbinding float loadFactor() default 0.75f; }

    De esta forma, puede pasar la capacidad y el factor de carga deseados al productor que devuelve cualquier tabla hash basada así:

    @Inject @Hash(capacity = 256, loadFactor = 0.85f) private Set set; @Inject @Hash private Set set; @Inject @Hash(capacity = 8, loadFactor = 0.65f) private Map map;

Espero que esto responda tu pregunta. Seguro que es parte de por qué amo CDI.


¿Cuándo deben usarse los calificadores personalizados?

Los calificadores personalizados de CDI solo necesitan ser utilizados cuando: (a) su código (no anotado) ha introducido deliberadamente ambigüedad en el tipo a usar; (b) desea inyectar un tipo de objeto más específico que el indicado por el código; (c) desea tener una selección y capacidad de configuración de tipo más flexible que la proporcionada por el calificador predefinido @Named.

Los calificadores desambiguan qué tipo debe ser creado / inyectado por CDI.
(a) y (b) ocurren cuando usa tipos de alto nivel en el código para proporcionar un potente algoritmo general que puede reutilizarse y ajustarse de manera flexible. Por ejemplo, a menudo es posible y se recomienda codificar un algoritmo completo contra una interfaz en lugar de una clase específica, como algoritmos contra Lista, Mapa o Cola. Pero a menudo estos algoritmos solo funcionan bien si nos comprometemos con implementaciones particulares de la interfaz como SortedList, TreeMap o PriorityQueue. Si CDI debe actuar como fábrica de objetos, llevar a cabo la gestión del ciclo de vida de los objetos, inicializar los objetos correctos para la administración de inyección y de dependencia, entonces necesita conocer todos los detalles.

Los calificadores personalizados son inteligentes y configurables en la forma en que eliminan la ambigüedad del tipo que CDI va a crear / inyectar. @Named es un instrumento mucho más blunter. Para (c), cuando menciono "selección de tipo" me refiero a que el desarrollador puede combinar múltiples calificadores para "seleccionar lógicamente" el tipo más apropiado, sin nombrar la clase precisa. @Named efectivamente requiere la nominación de la clase precisa: múltiples calificadores permitirán que CDI lo resuelva por usted. La configurabilidad se refiere a la capacidad de cambiar el comportamiento de CDI en el momento del despliegue mediante la modificación de beans.xml (no es necesario modificar código / anotaciones). es decir, puede sintonizar lógicamente lo que CDI está haciendo y puede hacerlo hasta el momento del despliegue, sin tocar el código O las anotaciones.

¿Cuándo se deben declarar los calificadores personalizados?

Los calificadores personalizados CDI no necesitan crearse para cada tipo individual que se pueda inyectar. Esto es lo más importante

Se pueden usar múltiples calificadores tanto en el punto de inyección como en las declaraciones de tipo. CDI coincide con el tipo de bean más los calificadores SET OF al determinar qué tipo inyectar. Para reducir el número de calificadores a declarar, es una buena idea clasificar los tipos en una serie de atributos / inquietudes que los describen. Entonces, en lugar de un calificador por tipo, puede tener un calificador por tipo de "atributo", incluso si dicho atributo tiene múltiples valores como Hash / Tree / Enum (ver el siguiente párrafo).

En segundo lugar, los calificadores deberían usar parámetros de forma inteligente para reducir aún más el número de declaraciones requeridas. En lugar de crear 10 calificadores, podría tener un calificador que tiene un parámetro que es una cadena o, preferiblemente, una enumeración que puede tomar 10 valores.

Combine estas dos ideas (calificadores = tipo de atributo PLUS use los parámetros del calificador) en un ejemplo para una biblioteca de colecciones: los tipos se pueden describir con los atributos que se ordenan / no ordenan? Hash / Tree / Array? Vinculado / Desvinculado? Si especifico una variable para ser

@Inject @Sorted @Organisation("hash") @Navigation("linked") Map myMap;

luego tengo una fuerte selección de tipos porque se garantiza que cada uno de estos aspectos estará satisfecho, tengo un mapa hash vinculado ordenado sin saber que esto necesita una clase muy particular en un paquete en particular.

Puntos no abordados aún:

Si uso alguna anotación con nombre de bean parametrizado, ¿cómo se escribe seguro?

Se garantiza la escritura fuerte porque debe haber una coincidencia completa con:

  • el tipo de frijol
  • el conjunto de calificadores
  • el conjunto de valores de parámetros

Ningún tipo inyectado puede violar cualquiera de estas cosas especificadas. En lugar de pensar en typesafety como la coincidencia de una sola cadena (nombredelpaquete.nombreclase), piense en ello como la coincidencia de múltiples cadenas / valores totalmente controlados por el desarrollador: estas mismas cadenas / valores se utilizan tanto en el lado de declaración como en el de inyección, proporcionando coincidencia segura. Además, en lugar de pensar que debe coincidir con la clase concreta de nivel más bajo, recuerde que puede hacer coincidir clases de nivel superior en la jerarquía de herencia y uso inteligente de calificadores @Default en sus tipos (recuerde Java EE 6 "configuración predeterminada inteligente sobre la configuración" ?) le llevará a los tipos concretos preferidos.

Si utilizo el calificador personalizado para la seguridad del tipo por servicio o dao (por interfaz), entonces para una aplicación de gran tamaño, como tener 1000 o más clases de servicio o dao con múltiples implementaciones, será complicado. Luego, para aplicaciones de gran tamaño, ¿es factible usar inyección segura tipo?

  • ¿1000 o más clases de servicio o dao? ¡¿¿De Verdad??! Eso a menudo marcaría un gran problema de diseño de inmediato. A menos que esta sea una aplicación de súper mega-tamaño-guiness-records-attempt para un negocio complejo como el departamento de impuestos o la NASA. Incluso entonces, sería normal dividirse en módulos más pequeños Java EE / servicios SOA con un número mucho menor de clases. Si esto es lo que tienes, tal vez quieras simplificar tu diseño: es posible que tengas problemas mucho más grandes que las definiciones de calificador de corte y pasado ...

  • Los calificadores solo son necesarios cuando hay ambigüedad de tipo, es decir, muchas clases ancestrales / descendientes en la misma jerarquía de tipos. A menudo ocurre que relativamente pocas clases de servicio o DAO son ambiguas.

  • Como se describió anteriormente, no use el patrón de un calificador por clase de implementación: en su lugar, refactorice para usar qualifier-per-type-aspect y también use patrones descriptivos de implmentación de parámetros por calificador

  • Es factible usar inyección segura de tipo con aplicaciones grandes.

Si la respuesta a la pregunta anterior es "No", entonces, ¿de qué sirve usar seguridad tipo?

  • la respuesta es sí"
  • el escenario propuesto (más de 1000 clases de servicio / DAO) es muy raro

Incluso si es factible escribir anotaciones para la seguridad del tipo, en aplicaciones grandes, ¿realmente vale la pena el esfuerzo solo por evitar la configuración verbosa xml?

El objetivo de CDI no es solo "evitar la configuración verbosa xml" CDI tiene los siguientes objetivos:

  • Compatibilidad con ámbitos (incluido el nuevo ámbito de conversación web) con la gestión del ciclo de vida de los objetos
  • Mecanismo de inyección de dependencia de tipo seguro, que incluye la capacidad de seleccionar dependencias en el tiempo de desarrollo o despliegue, sin configuración detallada
  • Compatibilidad con la modularidad Java EE y la arquitectura de componentes Java EE: la estructura modular de una aplicación Java EE se tiene en cuenta a la hora de resolver dependencias entre componentes Java EE
  • Integración con el Lenguaje de Expresión Unificado (EL), permitiendo que cualquier objeto contextual sea utilizado directamente en una página JSF o JSP
  • La capacidad de decorar objetos inyectados
  • La capacidad de asociar interceptores a objetos a través de enlaces de interceptor tipo seguro
  • Un modelo de notificación de eventos
  • Un contexto de conversación web además de los tres contextos web estándar definidos por la especificación Java Servlets
  • Un SPI que permite extensiones portátiles para integrarse limpiamente con el contenedor

Esto es MUY diferente y más útil que un archivo de configuración XML básico convertido en anotaciones, incluso las anotaciones iniciales de inyección de recursos de Java EE 5 eran diferentes y más útiles que las configuraciones XML convertidas en anotaciones.