Capa de logica de aplicacion en Java EE

Anuncio
Tema 5. Capa de lógica de aplicación en
Java EE
DAGSS – Diseño de Arquitecturas de Grandes Sistemas Software
4o Grado Ing. Informática
http://ccia.ei.uvigo.es/docencia/DAGSS
noviembre 2014
– FJRP – 2014 – DAGS –
5.1 Enterprise JavaBeans, versión 3.x
Componentes del lado del servidor que encapsulan la lógica de la
aplicación y que son gestionados por el contenedor de EJBs
Cada Enterprise JavaBean encapsula parte de la lógica de negocio
de la aplicación
Paquete javax.ejb.*: interfaces, anotaciones, excepciones, ...
Ventajas que aportan
Permiten la distribución de la lógica entre distintas máquinas manteniendo la transparencia
• Uso de directorios JNDI para nombrar y acceder a los componentes y recursos
Manejo de transacciones para controlar accesos concurrentes asegurando la integridad de los datos
• Controladas por el contenedor de EJB
Simplifican el desarrollo de aplicaciones que atiendan una gran variedad de clientes (clientes web o ”de escritorio” (stand-alone), cliente
locales o remotos, etc)
Diseño orientado a componentes → reusabilidad y abstracción
• El desarrollador se concentra en la lógica de negocio y utiliza los servicios
proporcionados por el contenedor
Servicios básicos a los que da soporte el contenedor EJB
1. Persistencia mediante JPA (Java Persistence API )
2. Invocación remota de métodos mediante RMI/IIOP
3. Procesamiento de transacciones y control de la concurrencia
Por defecto cada invocación de un método de un EJB forma parte de una
transacción
4. Procesamiento de eventos/mesajes ası́ncronos
(API Java Message Service
[JMS])
5. Servicios de directorio: JNDI (Java Naming and Directory Interface)
6. Seguridad: autenticación, control de acceso y privilegios, criptografı́a
(JAAS [Java Authentication and Authorization Service], JCA [Java Cryptography Architecture])
– FJRP – 2014 – DAGS –
1
7. Publicación de los métodos de negocio de los EJBs como servicios
Web SOAP (JAX-WS) o REST (JAX-RS)
– FJRP – 2014 – DAGS –
2
(a) Tipos de EJB
Message-Driven EJBs (EJBs dirigidos por mensajes)
Permiten el procesamiento de mensajes (operaciones) de forma
ası́ncrona (recepción y tratamiento de eventos JMS)
Actúan como listeners (escuchadores) de eventos JMS (Java
Message Service)
• Implementan el interfaz MessageListener
Se ”suscriben” a una cola (queue) quedando a la espera y se
activan cuando se recibe un mensaje dirigido a dicha cola
• Esos mensajes JMS pueden ser enviados por cualquier componente de una
aplicación Java EE (clientes, componentes Web, otros EJBs)
• Los ”clientes” de un Message-Driven EJB no invocan directamente sus
métodos, simplemente envı́an mensajes JMS
Session EJBs (EJBs de sesión)
Representan procesos de negocio (funcionalidades de la aplicación)
⇒ implementan un interfaz de negocio (bussines interface)
Gestionan la interacción con los ”clientes” (objetos/procesos que
hacen uso del componente) y encapsulan el flujo y manipulación
de la información en el servidor
• Proporcionan a los ”clientes” una ”fachada” de los servicios proporcionados
por otros componentes disponibles en el servidor (patrón de diseño Facade)
Ofrecen operaciones sı́ncronas (petición-respuesta)
• Procesos de negocio ejecutados en respuesta a una solicitud del cliente
Contenedor de EJBs crea e inicializa sus instancias (inyectándoles
las referencias necesarias) y las asigna a los ”clientes” a medida
que estos los van requiriendo (pool de EJBs)
En un instante de tiempo dado, sólo un ”cliente” tiene acceso a
los métodos de la instancia del EJB (control de concurrencia)
• Contenedor de EJBs garantiza que cada método del EJB se ejecuta dentro
de una transacción atómica
Nota: Desde EJB 3.1 se permite declarar métodos ası́ncronos dentro de EJBs de sesión marcándolos
con la anotación @Asyncronous
En el caso de que el método tenga valor de retorno T, el tipo del valor de retorno del método
ası́ncrono será Future<T>
public Future<EstadoPago> procesarPagoTarjeta(int cantidad, Tarjeta tarjeta){...}
Sobre los objetos Future<T> se puede ”consultar” la disponibilidad del valor vinculado.
– FJRP – 2014 – DAGS –
3
(b) EJBs de sesión
Tipos de EJB de sesión: en función la interacción con el ”cliente”
(quien realiza las llamadas)
Stateful EJB: tienen un estado conversacional dependiente del cliente
• Cada llamada está condicionada por las anteriores (importa el orden)
• Los valores de sus atributos se mantienen entre las distintas invocaciones de un mismo ”cliente”
◦ Representan el estado de la interacción con un cliente especı́fico
• Cada instancia de un stateful EJB ”atiende” invocaciones de un único ”cliente”
◦ En función del estado conversacional el resultado de las operaciones realizadas podrá variar
• Estado conversacional no es persistente (no se mantiene cuando el cliente ”libera” el EJB)
◦ Valores de atributos no premanentes: finalizada sesión el estado desaparece
◦ Por eficiencia el contenedor de EJBs puede decidir detener temporalmente la ejecución un
stateful EJB, almacenando temporalmente su estado en disco para recuperarlo al volver a
la ejecución
• Ejemplo: objeto ”carrito de la compra” en el cual se van añadiendo productos
Stateless EJB: no almacenan datos de los ”clientes” que los invocan
• Cada llamada es independiente de las demás
• Para el ”cliente” todos los stateless EJB son idénticos (e intercambiables)
◦ Estado de un stateless EJB no contiene información especı́fica de un cliente
no se garantiza que los valores de sus atributos se mantengan entre llamadas
◦ Stateless EJBs realizan tareas genéricas e idénticas para todos los clientes
• Pueden implementar Servicios Web (anotaciones @WebService y @WebMethod)
• Ejemplo: objeto ”gestor de artı́culos”: altas, bajas, modificaciones de artı́culos
Singleton EJB: contenedor garantiza que existe una única instancia del objeto,
compartida por todos los ”clientes” del EJB (estado global compartido).
• Por defecto el acceso concurrente lo gestiona el contenedor, pero puede controlarse por código
mediante anotaciones @Lock
• Uso tı́pico: gestión de datos globales de la aplicación (cachés, etc)
Interfaces en EJB de sesión
Interfaz de negocio de un EJB define el tipo de acceso permitido a
sus clientes (Define la visión del EJB que tiene el cliente)
Un EJB de de sesión puede tener varios interfaces de negocio con
distintos tipos de acceso
Clientes remotos: pueden ejecutarse en máquinas virtuales (JVM) distintas a la
del EJB que invocan
• Pueden ser: componentes web (servlet, JSP), clientes remotos u otros EJBs
• Para el cliente remoto la localización del EJB es transparente (sólo necesita acceso al
directorio JNDI)
Clientes locales: deben ejecutarse en la misma máquina virtual (JVM) que el
EJB que invocan
• Pueden ser: componentes web (servlet, JSP) u otros EJBs
• Para cliente local la localización del EJB no es transparente (debe estar en su misma JVM)
– FJRP – 2014 – DAGS –
4
(c) Definición de EJBs de sesión
(1) Definición del interfaz de negocio
• Interfaces de negocio → interfaces Java decorados con las anotaciones @Remote o @Local para indicar las restricciones de acceso
◦ El interfaz y todos los métodos definidos deben de ser public
◦ Mismas restriciones para tipos de parámetros y valores de retorno que Java RMI
• Si no se indica con una anotación en el interfaz o en el EJB se
asume todos los interfaces son locales por defecto
• Ejemplos:
@Local
@Remote
public interface GestorClientes {
public interface CarritoCompra {
public void alta(Cliente c);
public void inicializarCarrito(Cliente c);
public void baja(Cliente c);
public void anadirArticulo(Articulo a, int cant);
public void actualizar(Cliente c;
public void eliminarArticulo(Articulo a);
public cliente buscarDNI(String DNI);
public void vaciarCarrito();
};
};
• Nota: Llamadas remotas son mucho más costosas que locales
◦ En general se tiende a pasar parámetros ”grandes” ⇒ reducir núm. de
llamadas pasando mayor cantidad de información
(2) Implementación de EJBs de sesión
• Clases de implementación marcadas con las anotaciones
@Stateless, @Stateful ó @Singleton según del tipo de bean
• Se indican los interfaces de negocio implementados (implements)
@Stateless
public class GestorClientesBean implements GestorClientes { ... }
@Stateful
public class CarritoCompraBean implements CarritoCompra { ... }
◦ Opcionalmente se puede asociar un nombre JDNI al EJB para su acceso
(parámetro mappedName="...." de ambas anotaciones)
@Stateless(mappedName="gestor_clientes") ...
@Stateful(mappedName="carrito_compra") ...
Nota: si no se indica nada el contendor EJB asignará uno por defecto
◦ Opcionalmente se puede omitir la palabra clave implements explicitando el
nombre y el tipo de acceso de los interfaces de negocio soportados mediante
anotaciones.
@Stateless @Local(GestorClientes.class) ...
@Stateful @Remote(CarritoCompra.class) ...
◦ En EJB 3.1 (incluido EJB lite), no se requiere definir interfaz para EJBs
con acceso local (no-inteface view), basta marcar la clase con la anotación
@LocalBean o directamente no anotarlo.
– FJRP – 2014 – DAGS –
5
• Se pueden marcar métodos con anotaciones especiales para indicar que sean invocados por el contenedor EJBs en momentos
concretos del ciclo de vida del bean
◦ @PostConstruct: una vez que el contenedor instancia el EJB y le inyecta dependencias
(antes de recibir su primera invocación)
◦ @PreDestroy: justo antes de eliminar definitivamente el EJB
◦ @PrePassitivate: (sólo stateful) justo antes almacenar el estado en disco temporalmente
◦ @PostPassitivate (sólo stateful) justo de recuperar el estado almacenado en disco
temporalmente
• Método anotado con @Remove puede ser invocado por clientes para
provocar eliminación de una instancia del EJB del contenedor
(3) Empaquetado y despliegue
• Empaquetado de EJBs: Creación
de fichero JAR conteniendo:

 clases de implemementación de EJBs
interfaces de negocio
◦ Bytecode (ficheros .class) de

otras clases auxiliares necesarias
Ficheros .class estructurados en paquetes (subdirectorios) según corresponda
◦ Directorio META-INF conteniendo los ficheros de configuración exigidos por
el contenedor o/y otros que pudieran ser necesarios
descriptor de despliegue [ejb-jar.xml]
descriptor de persistencia JPA [persistence.xml]
MANIFEST.MF
otros: descriptor de despliegue especı́fico del contenedor
(glassfish-ejb-jar.xml) ...
◦ Librerı́as adicionales (ficheros JAR)
• Despliegue de EJBs: El fichero JAR resultante se puede desplegar directamente en el contenedor JEE o incluirlo como un
módudo EJB de una Aplicación JEE (paquetes EAR)
◦ Al ser deplegado un EJB en un contenedor se le asocia un nombre según lo
indicado en sus anotaciones o en su descriptor de despliegue
◦ Ese nombre se registra en el servidor de nombres del contendor EJB,
accesible por JNDI.
◦ Una vez desplegado los clientes pueden acceder al EJB usando ese nombre
e invocar sus métodos
– FJRP – 2014 – DAGS –
6
(c) Acceso e invocación de EJBs de sesión
Para utilizar métodos de un EJB desplegado es necesario que el
cliente cuente con una referencia a un representante (proxy o stub)
del mismo
• proxy gestiona las invocaciones, bien locales o bien sobre RMI/IIOP
Opción 1: Inyección de dependencias por parte del contenedor JEE
• Uso de la anotación @EJB acompañando al atributo donde se
mantendrá la referencia al EJB
◦ El tipo del atributo/referencia será el nombre del interfaz de negocio que se
desea invocar
◦ El contenedor usará el nombre por defecto del EJB (o el que se especifique
en el parámetro @EBJ(mappedName="...")) para consultar al servidor de
nombres JNDI, inyectando en ese atributo la referencia encontrada
• Inyección de referencias con @EJB sólo es posible dentro de un
entorno de ejecución JEE
◦ Contenedor de servlets: es posible inyectar EJBs para invocarlos desde
servlets, páginas JSP, Java beans o @ManagedBeans de JSF
◦ Contenedor de EJBs: posible inyectar EJBs para invocarlos desde otros EJBs
◦ Contenedor de clientes JEE: clientes Java stand-alone ejecutándose en el
contexto de un contenedor de clientes JEE pueden obtener e invocar EJBs
Es necesario disponer de las API de JEE 6 para inyectar estas dependencias
Ejemplo: appclient en GlassFish
Importante: Los contenedores de clientes JEE sólo pueden inyectar EJBs sobre atributos
de tipo static de la clase principal donde se incluya el método main()
• Ejemplo:
public class ClaseLlamadora {
@EJB // puede especificarse un nombre de EJB con mappedName (opcional)
private CarritoCompra carrito;
...
public void metodoLlamador(String args[]) {
...
carrito.inicializar(...);
carrito.anadir(...);
...
}
}
• También posible inyección de dependencias de CDI con @Inject
– FJRP – 2014 – DAGS –
7
Opción 2: Consulta JDNI empleando el nombre asignado al EJB
• Siempre es posible consultar el servicio JNDI presente en todos
los servidores aplicaciones JEE
◦ Se usa el API de JNDI → paquete javax.naming.*
◦ Interfaz InitialContext, métodos lookup(...)
• Se obtiene una referencia a un EJB a partir del nombre asignado
(en la anotación, en el descriptor de despliegue o del nombre por
defecto asignado por el contenedor)
◦ Este era el modo en que se hacı́a en las versiones de EJB 2.x y anteriores
◦ Antes de JEE 6: el nombre por defecto con el que se registraban los EJBs
no estaba estandarizado
cada servidor de aplicaciones organizaba el directorio de nombres JNDI de forma distinta
◦ JEE 6 especifica formato estandar para nombres por defecto de los EJBs
java:global[/<app-name>]/<module-name>/<bean-name>[!<fully-qualified-interface-name>]
• Consultas JNDI desde clientes stand-alone
◦ Es necesario configurar las properties de la máquina virtual cliente para
indicar la dirección y el puerto de escucha del servicio JNDI del contenedor
donde está desplegado el EJB (dependiente de cada contenedor)
Nota: con GlassFish las propiedades a especificar son:
org.omg.CORBA.ORBInitialHost → IP/nombre servidor aplicaciones JEE
org.omg.CORBA.ORBInitialPort → 3700
Pueden indicarse como parámetros de la JVM al arrancar (-Dparametro=valor) o con
System.setProperty(’’parametro’’, valor);
◦ Con esa información será posible obtener un InitialContex donde buscar
[lookup()] el nombre del EJB deseado
• Ejemplo:
import javax.naming.InitialContext;
public class EjemploCliente {
public static void main(String args[]) {
...
InitialContext ic = new InitialContext();
CarritoCompra carrito =
(CarritoCompra) ic.lookup("java:global/EjemploTiendaWeb/"+
"EjemploTiendaWeb-ejb/CarritoCompraBean");
carrito.inicializar(...);
carrito.anadir(...);
...
}
– FJRP – 2014 – DAGS –
8
Transacciones en EJBs
Contenedor de EJBs ofrece por defecto gestión de transacciones sobre
métodos de EJBs (container-managed transactions [CMT])
EJB también pueden gestionar desde código sus propias transacciones
(bean managed transactions [BMT])
Mediante el API JTA (Jave Transaction API ) [objetos javax.transaction.UserTransac
UserTransaction provee los métodos begin() , commit() y rollback()
EJBs con transacciones BMT se ”marcan” con la
@@TransactionManagement(TransactionManagementType.BEAN)
anotación
Se puede especificar que el contenedor de EJBs inyecte una instancia de
UserTransaction mediante la anotación @Resource
@Resource
UserTransaction utx;
También se pueden obtener con un consulta JNDI
En la gestión de transacciones se busca garantizar las propiedades ACID:
Atomicidad (Atomicity ): una transacción está compuesta de 1 o más unidades de trabajo
• o se ejecutan TODAS ellas [commit]
• o no se ejecuta NINGUNA [rollback], si ocurre algún error (exception) inesperado no recuperable
Consistencia (Consistence): al finalizar la transacción (éxito [commit] ó fracaso [rollback]) los
datos se dejan en un estado consistente
aIslamiento (Isolation): el estado intermedio de la transacción no es visible a los demás componentes de la aplicación (u a otras aplicaciones)
Durabilidad (Durability ): una vez la transacción se completa con éxtio [commit] los cambios
realizados sobre los datos son visibles al resto de aplicaciones
Por defecto todos los métodos de los EJBs que usan CBT se ejecutarán
dentro de una transacción (no es necesaria configuración explı́cita)
EJBs se consideran anotados por defecto con @TransactionManagement(TransactionMa
Pueden especificarse caracterı́sticas especı́ficas en la gestión de transacciones a nivel de
EJB o de método [anotación @TransactionAttribute(TransactionAttributeType.XXX)]
TransactionAttributeType.REQUIRED (valor por defecto)
– FJRP – 2014 – DAGS –
9
• Si el ”cliente” del EJB ya ha iniciado una transacción, el método del EJB se ejecuta formando
parte de ella.
• En otro caso, se inicia una transacción nueva para el método del EJB.
(el método del EJB se ejecuta siempre dentro de una transacción, propia o heredada)
TransactionAttributeType.REQUIRES NEW
• Se inicia siempre una nueva transacción para el EJB.
• Si el ”cliente” ya habı́a iniciado una transacción, esta se suspende y se reanuda cuando termine
el método del EJB.
(el método del EJB se ejecuta siempre dentro de una transacción propia)
TransactionAttributeType.SUPPORTS
• Si el ”cliente” ha iniciado una transacción, el método del EJB se ejecuta dentro de ella.
• En otro caso, se ejecuta el método del EJB sin iniciar una transacción nueva.
(se mantiene el estado transaccional del ”cliente”)
TransactionAttributeType.MANDATORY:
• Si el ”cliente” no ha iniciado una transacción, se lanza una excepción y se aborta la ejecución
del método del EJB.
• En otro caso, se utiliza la transacción iniciada por el ”cliente”.
(el método del EJB se ejecuta siempre dentro de una transacción proporcionada por el ”cliente”)
TransactionAttributeType.NOT SUPPORTED
• Si el ”cliente” ha iniciado una transacción, esta se suspende, se ejecuta el método del EJB sin
iniciar una transacción nueva y se reanuda la transacción del cliente.
(el método del EJB se ejecuta fuera de cualquier transacción)
TransactionAttributeType.NEVER:
• Si el ”cliente” ha iniciado una transacción, se lanza una excepción y esta se suspende y no se
ejecuta el método.
(sólo se ejecuta el método del EJB si no está dentro de una transacción)
– FJRP – 2014 – DAGS –
10
5.2 CDI: Context and Dependece Injection
Framework de inyección de dependencias y gestión de objetos, disponible
desde Java EE 6.
Simplifica el desarrollo de componentes Java EE haciendo uso del principio IoC
Inversion of Control
Servicios ofrecidos por CDI
1. Inyección de dependencias: inyección de referencias a objetos
”fuertemente tipada”
Favorece un bajo acoplamiento
• simplifica plantear un diseño donde se depende de abstracciones (interfaces)
en lugar de depende de implementaciones concretas
• CDI ”carga” con la responsabilidad de la creación de los objetos
• no es necesario especificar las clases concretas a utilizar ni sus respectivas
dependencias, CDI creará los objetos ”correctos” e inyectará esas referencias
Posibilidad de ”configurar” la inyección concreta en tiempo de despliegue
(descriptor beans.xml)
2. Gestión de contextos: ciclo de vida de los objetos CDI está vinculado
a contextos (scopes)
3. Gestión del ciclo de vida de los objetos CDI (beans CDI ): uso de
interceptores de llamadas a métodos, definición de decoradores y
gestión de eventos
4. Nombrado de los beans: permite el acceso a los beans CDI desde
el Expression Language(EL) de JSF y JSP (simplifica la integración de la
capa web y de la capa de lógica de aplicación)
– FJRP – 2014 – DAGS –
11
Beans CDI: un bean CDI puede ser casi cualquier objeto Java
No exige implementar interfaces concretos o heredar de determinadas clases :
POJOs [plain old Java objects]
Puede ser accedido desde objetos ”cliente” vı́a inyección de dependencias (@Inject)
o mediante un nombre desde el EL de JSF y JSP.
Componentes Java susceptibles de ser gestionados por CDI deben verificar:
1. no son una clase interna no estática
2. son una clase concreta o está anotada con @Decorator
3. tienen un constructor sin parámetros o uno de sus constructores está anotado con @Inject
A un bean CDI puede vinculársele:
1. un contexto (scope) [@ApplicationScoped, @SessionScoped, @RequestScoped, ...]
2. un nombre EL [Named]
3. un conjunto de cualificadores [@Qualifier] que delimitan el tipo de cliente que
podrá inyectarlos
4. un conjunto de interceptores de métodos [@ArroundInvoke, @ArroundConstructor,
@Interceptors]
5. puntos de gestión del ciclo de vida [@PostConstruct, @PreDestroy]
6. un conjunto de decoradores [@Decorator, @Delegate]
7. un conjunto de ”observadores” de eventos [Event<T>, @Observes]
Por defecto, se habilita CDI incluyendo un descriptor de despliegue beans.xml vacı́o
con la opción bean-discovery-mode="annotated" en los directorios de configuración
de la aplicación (WEB-INF en aplicaciones web, META-INF en el resto de casos)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="annotated">
</beans>
Framework CDI considera susceptibles de ser tipos válidos para los beans CDI todos
los interfaces, clases abstractas, clases concretas, arrays y tipos básicos incluidos
en la aplicación, excepto los paquetes o tipos marcados con la anotación @Vetoed
beans.xml se utiliza en tiempo de despliegue para
• seleccionar las alternativas a utilizar en el caso de tipos CDI con varias implementaciones
• habilitar/deshabilitar los decoradores y ”observadores” de eventos
– FJRP – 2014 – DAGS –
12
(a) Inyección de dependencias
Inyección de dependencias (DI : dependence injection) es un principio
de diseño que desacopla la creación de objetos dependientes
la responsabilidad de crear los objetos y obtener las dependencias
necesarias pasa al contenedor de DI (ejemplos: Spring, Google Guice, CDI)
en Java EE, DI presente en distintas especificaciones:
@ManagedBeans en JSF, @EJB, @PersistenceContext en EJBs,
inyección de recursos con @Resource (CDI unifica todas esas especificaciones)
Anotación @Inject
Define en el objeto ”cliente” (quien recibe la referencia de la dependencia
inyectada) un punto de inyección de una referencia a un bean CDI del
tipo especificado (con frecuencia un nombre de interfaz Java)
Puede estar acompañado de uno o más cualificadores
• usados en el caso de tipos de objetos CDI con varias implementaciones para
delimitar la que finalmente será inyectada
Puede especificarse un punto de inyección:
• en el propio atributo de la clase del bean CDI donde se inyectará la dependencia
@Inject
TipoBean atributo;
• en el método set() de ese atributo
@Inject
void setAtributo(TipoBean atributo) {
this.atributo = atributo;
}
• en un constructor
@Inject
public ClaseBean(TipoAtributo atributo) {
this.atributo = atributo;
}
– FJRP – 2014 – DAGS –
13
Cualificadores
Por defecto CDI asume que existe una única implementación del tipo de
bean especificado por @Inject.
En caso de que sólo exista una implementación se inyecta la correspondiente referencia (creando e inicializando el bean CDI si es necesario)
• Por defecto todos los beans CDI cuantan con el cualificador Default
• La anotación @Inject implı́citamente
En caso de que existan más implementaciones los cualificadores
(@Qualifier) permiten seleccionar cuál inyectar en cada caso
Las distintas implementaciones de un tipo CDI pueden estar vinculadas con anotaciones que ”hereden” de la anotación Qualifier
(pueden vincularse a una clase varios cualificadores y ”exigirse” varios cualificadores
en los @Inject)
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD})
public @interface Calificador1 { }
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD})
public @interface Calificador2 {
int
parametro();
Tipo tipo();
}
public enum Tipo {A,B,C}
public interface TipoBean;
@Default
public class Implementacion1 implements TipoBean {...}
@Calificador1
public class Implementacion2 implements TipoBean {...}
@Calificador2(parametro=25, tio=Tipo.A)
public class Implementacion3 implements TipoBean {...}
– FJRP – 2014 – DAGS –
14
public class ClienteBasico {
...
@Inject @Calificador1 TipoBean inyectado;
...
}
public class ClienteMejorado {
...
@Inject @Calificador2(parametro=25) TipoBean inyectado;
...
}
Si existen varias implementaciones, una de ellas debe estar marcada
con el cualificador @Default (si no es ası́ se genera una excepción)
• Por defecto todos los beans CDI que no incluyan un cualificador se anotan con
el cualificador Default
• Si no se especifican cualificadores acompañando a una anotación @Inject,
implı́citamente se ”exige” Default
En el fichero beans.xml se puede modificar en tiempo de despliegue
el comportamiento por defecto, seleccionando un cualificador por
defecto de entre los marcados con @Alternative
@Alternative @Default
public class Implementacion1 implements TipoBean {...}
@Alternative @Calificador1
public class Implementacion2 implements TipoBean {...}
...
<beans ...>
<alternatives>
<class>paquete.Implementacion2</class>
</alternatives>
</beans>
Por defecto las alternativas están deshabilitadas, deben indicarse
explı́citamente en beans.xml
– FJRP – 2014 – DAGS –
15
Productores (anotación @Produces)
Usando la anotación @Produces es posible especificar que el origen de
una dependencia a inyectar sea el resultado de la invocación de un
método en lugar de ser un objeto de una clase determinada (también es
posible vincular @Produces a los atributos de una clase)
El método anotado con @Produces podrá ir acompañado de los cualificadores que
correspondan.
En caso de que no exista una clase adecuada para la inyección (o se haya
especificado ası́ en beans.xml), el contenedor CDI se encargará de buscar un
método adecuado para la inyección entre los paquetes y clases de la aplicación. Si
lo encuentra, CDI lo invocará (creando una instancia si es necesario) e inyectará
su valor de retorno en el punto/s de inyección marcado por @Inject
La anotación @Produces permite inyectar arrays y tipos básicos
Cuando existen cualificadores y alternativas los métodos @Produces funcionan
como una especie de métodos factorı́a ”tipado”, gobernado por los cualificadores,
el beans.xml y la propia lógica del método.
En los casos en que la lógica del método @Produces requiera información sobre
el contexto donde se va a inyectar su resultado puede incluir como parámetro
adicional un objeto InjectionPoint con información del ”cliente” CDI.
public class Productor {
@Produces @Calificador2(parametro=25) TipoBean productor1() {
TipoBean instancia;
...
return instancia;
}
@Produces @Calificador1 TipoBean productor1(InjectionPoint ip) {
TipoBean instancia;
...
String claseReceptora = ip.getMember().getDeclaringClass().getName();
...
return instancia;
}
}
– FJRP – 2014 – DAGS –
16
(b) Contextos (scopes) en CDI
Los beans CDI ”viven” en un contexto (alcance) gestionado por el
contendor CDI
Contenedor crea y maneja los beans dentro de cada contexto, destruyéndolos al finalizar el contexto
Contenedor garantiza el mantenimiento del estado del bean dentro
del contexto
• Los ”clientes” donde se inyecta un bean CDI interactúan siempre con el mismo
objeto mientras el correspondiente contexto esté activo
• Contenedor garantiza que desde el punto de vista de los clientes y dentro de
un contexto dado existe una única instancia de cada tipo de bean inyectado
(CDI ofrece un especie singleton contextual)
Contexto de aplicación @ApplicationScoped
Se prolonga durante la vida de la aplicación
Los beans CDI se crea en el momento en que arranca la aplicación y
se destruye al finalizar
Existe una única instancia de los beans compartida por todos los
”clientes”
Usado para funciones complementarias, cachés, etc
Puede presentar problemas de concurrencia
Contexto de sesión @SessionScoped
Se prolonga durante el conjunto de peticiones HTTP que se corresponden con una sesión de usuario
Vinculado al objeto HttpSession de la capa web
Los beans CDI se crean en su primer uso por parte del usuario
y se destruyen al finalizar la sesión (bien por una invocación al método
invalidate() o por agotamiento de time-out de sesión)
Almacenan datos necesarios durante la sesión de usuario (credenciales
de acceso, selecciones y preferencias, etc)
– FJRP – 2014 – DAGS –
17
Contexto de petición @RequestScoped
Vinculado a una petición HTTP concreta
Vinculado al objeto HttpServletRequest de la capa web
Los beans CDI se crean al recibir la petición y se destruyen con el
envı́o de su respuesta
Mantienen datos que sólo son necesarios para la petición actual
Es el más utilizado (la anotación @Model combina las anotaciones
@RequestScoped y @Named)
Contexto de conversación @ConversationScoped
Contexto de duración variable, definida por la lógica del bean
Todos los beans @ConversationScoped deben de inyectar un objeto
Conversation a través del cuál se controla el contexto.
Existe desde que se invoca el método begin() del objeto
Conversation (normalmente en un método @PostContruct) hasta la invocación del método end()
Utilizado para gestionar datos que se mantienen entre invocaciones
sucesivas del cliente, pero con un alcance menor que la sesión, en el
caso de aplicaciones web
Contexto dependiente @Dependent
Contexto por defecto
El bean CDI ”’hereda” el contexto en el cuál se encontraba el
”cliente” al que fue inyectado
– FJRP – 2014 – DAGS –
18
(c) Ciclo de vida de beans en CDI
El contenedor CDI gestiona el ciclo de vida de los beans.
En la creación del bean, CDI se encarga (por este orden) de:
1. Crear el objeto invocando a su constructor vacı́o o al etiquetado con @Inyect
2. Realizar la inyección de las dependencias requeridas por las anotaciones
@Inyect del beanName
3. Invocar a los métodos anotados con @PostConstruct
En la destrución del bean, CDI invoca a los métodos anotados con
@PreDestroy justo antes de eliminar el bean de la JVM.
Opcionalmente, CDI puede invocar a los interceptores de los métodos
anotados con @ArroundInvoke o @ArroundContruct
Interceptores
Los interceptores CDI ofrecen funcionalidades basadas en los principios
de la programación orienta a aspectos (AOP: Aspect Oriented Programming )
Suelen utilizarse para implementar funcionalidades que son comunes a varios
componentes de la aplicación (loging, transacciones, controles de seguridad, etc)
Ofrecen un forma limita y muy especı́fica del patrón Cadena de responsabilidad
– FJRP – 2014 – DAGS –
19
1. Interceptores ”de clase”
En el propio bean se puede añadir un interceptor propio marcando un método del
bean con @ArroundInvoke o @ArroundsContruct
Ese método recibirá un objeto InvocationContext con el cuál acceder a información del método invocado
Mediante el método proceed() se indica que puede continuar su ejecución
public class ClienteService {
@Inject private EntityManager em;
@Inject private Logger logger;
public void crearCliente (Cliente cliente) {
em.persist(cliente);
}
...
@AroundInvoke private Object logMethod(InvocationContext ic) throws Exception {
Logger.log("Entrando en clase "+ic.getTarget().toString()+
" con metodo "+ic.getMethod().getName());
try {
return ic.proceed();
} finally {
Logger.log("Saliendo de clase "+ic.getTarget().toString()+
" con metodo "+ic.getMethod().getName());
}
}
}
– FJRP – 2014 – DAGS –
20
2. Interceptores generales
Lo más habitual es definir interceptores genéricos que podrán emplearse en varios
beans CDI
• marcando la clase que implementa los interceptores con @Interceptor
• definiendo una nueva anotación (derivada de @InterceptorBinding) con la que vincular esa
clase interceptora a los beans implicados
public class UsuarioService {
private @Inject EntityManager em;
@Transactional
public almacenarUsuario(Usuario u) {
em.persist(u);
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target( { ElementType.TYPE, ElementType.METHOD })
@InterceptorBinding
public @interface Transactional {}
@Interceptor @Transactional
public class TransactionalInterceptor {
private @Inject EntityManager em;
@AroundInvoke
public Object invoke(InvocationContext context) throws Exception{
EntityTransaction t =em.getTransaction();
try {
if(!t.isActive()) {
t.begin();
}
return context.proceed();
} catch(Exception e) {
t.rollback();
} finally {
if(t != null && t.isActive()) {
t.commit();
}
}
}
}
En ambos casos, para ser activados las clases que implementan los interceptores deberán ser declaradas en el fichero beans.xml dentro de etiquetas <interceptors>
– FJRP – 2014 – DAGS –
21
(d) Decoradores y eventos
CDI incluye soporte a la implementación del patrón Decorador
Decoradores similares en funcionamiento a los Interceptores (amplı́an las funcionalidades de un objeto gestionado)
A diferencia de los Interceptores los Decoradores ”conocen” el interfaz/comportamiento de la clase decorada
public interface TipoBasico {
... Metodos interfaz ...
}
@Decorator
public class MiDecorador implements TipoBasico {
@Inject @Delegate
private TipoBasico delegado;
... Metodos interfaz ...
}
public class MiClase implements TipoBasico {
... Metodos interfaz ...
}
<beans
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.
version="1.1" bean-discovery-mode="all">
<decorators>
<class>paquete.MiDecorador</class>
</decorators>
</beans>
– FJRP – 2014 – DAGS –
22
CDI incluye soporte a la implementación del patrón Observer
Permite vincular comportamiento a acciones/eventos que ocurran sobre los objetos
gestionados
public class BookService {
@Inject
private NumberGenerator numberGenerator;
@Inject
-> private Event<Book> bookAddedEvent;
public Book createBook(String title, Float price, String description) {
Book book = new Book(title, price, description);
book.setIsbn(numberGenerator.generateNumber());
->
bookAddedEvent.fire(book);
return book;
}
}
public class InventoryService {
@Inject
private Logger logger;
List<Book> inventory = new ArrayList<>();
-> public void addBook(@Observes Book book) {
logger.info("Adding book " + book.getTitle() + " to inventory");
inventory.add(book);
}
}
– FJRP – 2014 – DAGS –
23
Descargar