Documente Academic
Documente Profesional
Documente Cultură
ndice
Introduccin a Spring Declaracin y Configuracin de beans Excepciones de Persistencia Declaracin de DataSources Integracin con Hibernate 3 Gestin de Transacciones
Qu es Spring? (1)
http://www.springframework.org
Motivacin: Facilitar el desarrollo de aplicaciones Java EE, promoviendo buenas prcticas de diseo y programacin
Simplifica el uso de muchas de las APIs de Java EE Dispone de alternativas a algunas de las APIs de Java EE
Facilita el uso de patrones de diseo ampliamente reconocidos dentro de la industria del desarrollo de software (Factory, Abstract Factory, Builder, Decorator, Service Locator, etc.) Es modular: es posible usar algunos de los mdulos sin comprometerse con el uso del resto
Qu es Spring? (y 2)
Nosotros utilizaremos el soporte de Spring para implementar casos de uso a nivel de capa modelo
Puede actuar como una capa de integracin entre diferentes APIs (JDBC, JNDI, etc.) y frameworks
Inyeccin de dependencias (Dependency Injection, DI) Programacin orientada a aspectos (Aspect-Oriented Programming, AOP)
Inyeccin de Dependencias
Tradicionalmente cada objeto es responsable de obtener sus propias referencias a los objetos con los que colabora Cuando se aplica la Inyeccin de dependencias (DI), alguna entidad externa es la responsable de proporcionar a un objeto sus dependencias cuando se crea el objeto (las dependencias se inyectan en el objeto) Ventajas
Si un objeto conoce sus dependencias a travs de interfaces, es posible cambiar la implementacin de esas dependencias transparentemente para el objeto que las contiene
AOP: http://en.wikipedia.org/wiki/Aspect-oriented_programming
Algunos servicios son utilizados repetidas veces en diferentes componentes de un sistema, cuya responsabilidad principal es otra
E.g. servicios de logging o gestin de transacciones y seguridad Se conocen como aspectos transversales ("cross cutting concerns)
Un framework de AOP hace posible modularizar estos aspectos o servicios y aplicarlos declarativamente a los componentes que los precisen
Cada aspecto se implementa en un nico punto Declarativamente se especifican los mtodos que el framework tiene que interceptar para aplicarles el o los aspectos que el desarrollador desea Cada componente debe preocuparse nicamente de su funcionalidad principal sin preocuparse de los servicios del sistema que precise
Mdulos/Paquetes (1)
ORM
Spring Web MVC Framework Integration Struts WebWork Tapestry JSF Rich View Support JSPs Velocity FreeMarker PDF Jasper Reports Excel Spring Portlet MVC
Web
DAO
AOP
JEE
IoC Container
Core
Mdulos/Paquetes (2)
Core
Constituye la parte fundamental del framework y proporciona la caracterstica de Inyeccin de Dependencias (DI) / Inversin de Control (IoC) Proporciona una implementacin sofisticada del patrn Factora que permite desacoplar la configuracin y especificacin de dependencias de la lgica de la aplicacin Proporciona una capa de abstraccin sobre JDBC que elimina la necesidad de codificar y analizar los cdigos de error especficos de cada BBDD Tambin proporciona una manera de gestionar transacciones tanto programtica como declarativamente, no slo para clases que implementen ciertas interfaces, sino para cualquier objeto Java (POJO)
DAO
Mdulos/Paquetes (y 3)
ORM
Proporciona capas de integracin para las APIs de mapeadores objeto-relacionales ms populares: Hibernate, JPA, JDO, iBatis Utilizando este paquete es posible utilizar cualquiera de estos mapeadores objeto-relacionales en combinacin con las dems caractersticas que ofrece Spring (como por ejemplo con la gestin declarativa de transacciones) Proporciona una implementacin del paradigma de la programacin orientada a aspectos (conforme a la AOP Alliance), que es utilizada, transparentemente para el programador, por otros paquetes de Spring, pero que tambin puede ser usada directamente
AOP
El Contenedor (1)
Responsable de la creacin y configuracin de los Beans Nota: Un bean, en el contexto de Spring, es un POJO que es creado y manejado por el contenedor de IoC
El Contenedor (2)
Instanciacin
try {
ApplicationContext ctx = new ClassPathXmlApplicationContext( new String[] {SPRING_CONFIG_FILE, SPRING_CONFIG_TEST_FILE}); AccountService accountService = (AccountService) ctx.getBean("accountService"); ... } catch (Exception e) { e.printStackTrace(); }
El Contenedor (y 3)
ClassPathXmlApplicationContext
Permite declarar los objetos que componen la aplicacin, y las dependencias entre ellos en XML A partir de los metadatos de configuracin en XML es capaz de crear y configurar los objetos que componen la aplicacin A travs del mtodo getBean es posible obtener referencias a los objetos declarados, a partir de su nombre
Es equivalente a
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost/pojo" /> <property name="username" value="pojo" /> <property name="password" value="pojo" /> </bean> <bean id="accountService" class="es.udc.pojo.minibank.model.accountservice.AccountServiceImpl" <property name="accountDao" ref="accountDao" /> <property name="accountOperationDao" ref="accountOperationDao" /> </bean>
id: Nombre o identificador del bean class: Clase de implementacin del bean Permite inyectar valores u otros beans (a travs de referencias), invocando al mtodo set correspondiente del bean sobre el que se est realizando la inyeccin
Se indica el nombre de la propiedad que se desea inyectar y el valor que se le desea proporcionar
Es posible especificarlas
name: Nombre de la propiedad donde se desea inyectar el valor value: Para inyectar un valor constante ref: Para inyectar otro bean a partir de su nombre
p:nombrePropiedad: Para inyectar un valor constante en la propiedad indicada p:nombrePropiedad-ref: Para inyectar otro bean a partir de su nombre en la propiedad indicada
El bean se crea a partir de su constructor vaco y a continuacin se invocan los mtodos set con los valores adecuados
Spring proporciona un mecanismo para detectar automticamente clases anotadas y registrarlas como beans en el ApplicationContext
Se puede utilizar la anotacin genrica @Component sobre cualquier clase, o sus especializaciones @Repository, @Service y @Controller para anotar clases en las capas de persistencia, servicios y vista respectivamente
La documentacin de Spring aconseja utilizar las especializaciones porque otros frameworks o herramientas pueden aplicarles semntica especial y el propio Spring puede aadrsela en futuras versiones Si la anotacin contiene el atributo name, entonces el valor de ese atributo ser el que d nombre al bean; en otro caso, se le dar el mismo nombre que la clase pero empezando por minscula
Spring proporciona un mecanismo para detectar automticamente clases anotadas y registrarlas como beans en el ApplicationContext (cont)
Adems debe utilizarse el tag component-scan en el fichero de configuracin de Spring indicando en el atributo base-package el o los paquetes (separados por comas) que incluyen las clases anotadas (puede ser un paquete padre, no es necesario que sean los paquetes que contienen las clases directamente)
<beans xmlns="http://www.springframework.org/schema/beans" ... xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="... "> ... <context:component-scan base-package="es.udc.pojo.minibank.model"/> ... </beans>
Auto-inyeccin de Dependencias
El contenedor de Spring es capaz de auto-inyectar dependencias entre beans segn diferentes criterios
Cuando se usan metadatos de configuracin en XML, la autoinyeccin de dependencias se especifica a travs del atributo autowire de la etiqueta <bean/> Tambin es posible realizar auto-inyeccin de dependencias utilizando la anotacin @Autowired
Por tipo: Busca un bean con el mismo tipo que la propiedad Por nombre: Busca un bean con el mismo id que la propiedad Por constructor: Busca uno o ms beans cuyos tipos coincidan con los parmetros de uno de los constructores de ese bean
Ventajas
Desventajas
La cantidad de configuracin necesaria puede verse reducida significativamente, y tambin las necesidades de cambios en dicha configuracin segn avanza el desarrollo Las relaciones entre los beans dejan de estar documentadas explcitamente Si hay varios beans con el mismo tipo no puede utilizarse la autoinyeccin por tipo (que es la que suele utilizarse)
@Service("accountService") public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; @Autowired private AccountOperationDao accountOperationDao; ... }
La anotacin @Autowired puede ser aplicada a setters, pero tambin a mtodos con nombres arbitrarios y mltiples argumentos, a constructores y a propiedades Por defecto la auto-inyeccin se realiza por tipo y si no hay ningn bean que la satisfaga entonces se produce un error Para poder utilizar esta anotacin es necesario especificar la etiqueta annotation-config en el fichero de configuracin de Spring
<beans xmlns="http://www.springframework.org/schema/beans" ... xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="... "> ... <context:annotation-config/> ... </beans>
... </beans>
AccountServiceImpl.java
@Service("accountService") public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; @Autowired private AccountOperationDao accountOperationDao; ... }
AccountDaoHibernate.java y AccountOperationDaoHibernate.java
@Repository("accountDao") public class AccountDaoHibernate extends GenericDaoHibernate<Account, Long> implements AccountDao { ... }
@Repository("accountOperationDao") public class AccountOperationDaoHibernate extends GenericDaoHibernate<AccountOperation, Long> implements AccountOperationDao { ...
GenericDaoHibernate.java
public class GenericDaoHibernate<E, PK extends Serializable> implements GenericDao<E, PK> { private SessionFactory sessionFactory; ... @Autowired public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } ... }
Declaracin:
Un bean para cada DAO de Hibernate Un bean para la implementacin del servicio
Se declara un bean en XML para la SessionFactory (que usan los DAOs de Hibernate)
Inyeccin de dependencias
Las dependencias entre los beans se auto-inyectan mediante anotaciones La implementacin del servicio usa los DAOs
Se inyectan directamente sobre las propiedades privadas que los referencian y por tanto no son necesarios los setters Se inyecta en GenericDaoHibernate sobre el setter correspondiente (el setter es necesario ya que es invocado desde las clases de prueba)
accountService
AccountServiceImpl
- accountDao : AccountDao - accountOperationDao : AccountOperationDao
sessionFactory
accountOperationDao
AccountOperationDaoHibernate - sessionFactory : SessionFactory
El mbito de un bean se especifica a travs del atributo scope de la etiqueta bean o a travs de la anotacin @Scope (segn se use configuracin XML o basada en anotaciones) Algunos posibles valores son:
El contenedor usa siempre la misma instancia (ya sea cuando se le pide a travs de la API o cuando necesita inyectarlo) Indica que el contenedor debe crear una nueva instancia del bean cada vez que se precise una Puede ser necesario, por ejemplo, si el bean tiene estado
prototype
OJO con los beans de tipo singleton con dependencias con beans de tipo prototype
Los valores o referencias a otros beans se inyectan a travs de los argumentos de un constructor
Inyeccin de propiedades multivaluadas (arrays, listas, conjuntos, mapas) Inicializacin y liberacin de recursos de un bean a travs de mtodos que deben ser invocados justo despus de haberse creado y justo antes de ser destruido, respectivamente ...
En JDBC se lanza la excepcin java.sql.SQLException cuando se produce cualquier tipo de error en el acceso a los datos
Problema: Hay que capturarla siempre y analizarla para saber de qu tipo de error se trata
Algunos frameworks (e.g. Hibernate) ofrecen una jerarqua de excepciones ms descriptiva (una excepcin diferente para cada tipo de error)
Ventaja: Permite diferenciar entre qu tipos de errores capturar Problema: Son especficas del framework utilizado para realizar la persistencia de los datos
Excepciones de Persistencia (y 4)
Spring proporciona una jerarqua de excepciones de acceso a datos (heredan de DataAccessException) que resuelve ambos problemas
Cada excepcin representa un error concreto No son especficas del framework de persistencia de datos utilizado, que por tanto se oculta a las capas superiores Son excepciones unchecked
Spring proporciona mecanismos para realizar la conversin entre las excepciones nativas del framework de persistencia utilizado y la jerarqua propia
El mtodo convertHibernateAccessException de la clase SessionFactoryUtils realiza esta conversin para el caso de Hibernate Se ha creado un mtodo protegido en el DAO genrico para realizar la conversin En todos los mtodos de los DAOs (incluidos los del DAO genrico) se captura HibernateException y se lanza hacia las capas superiores el resultado de convertirla a una excepcin de la jerarqua de DataAccessException
DataSources
Independientemente del framework de persistencia utilizado probablemente se necesitar configurar una referencia a un DataSource Spring proporciona, entre otras, las siguientes opciones para configurar un bean de tipo DataSource
SingleConnectionDataSource DriverMangerDataSource Cualquier contenedor Java EE puede poner accesible va JNDI un DataSource (que normalmente implementar pool de conexiones)
SingleConnectionDataSource
Cuando se crea, solicita una conexin a la base de datos El mtodo getConnection siempre devuelve esa conexin (o un proxy)
Es usual especificar suppressClose="true" para que devuelva un proxy de la conexin cuyo mtodo close (el del proxy) no cierra la conexin Un DataSource con estas caractersticas es suficiente y eficiente para los tests de integracin
Suficiente: llega con disponer de una conexin Eficiente: la conexin a la base de datos slo se pide cuando se crea el DataSource El nombre de la clase del driver JDBC La URL de conexin a la BD El usuario para conectarse a la BD La contrasea del usuario indicado
DriverMangerDataSource
No implementa pool de conexiones El mtodo getConnection devuelve una conexin nueva cada vez que es invocado Vlido para entornos multi-thread
Un DataSource con estas caractersticas es suficientemente eficiente y seguro para probar una aplicacin Web a nivel de usuario Hay que indicar las siguientes propiedades
El nombre de la clase del driver JDBC La URL de conexin a la BD El usuario para conectarse a la BD La contrasea del usuario indicado
Familia de paquetes javax.naming (Java SE) Es una API que abstrae el acceso a un servicio de nombres y directorios (e.g. LDAP) Es posible registrar objetos mediante un nombre jerrquico
Los servidores de aplicaciones Java EE exponen diversos objetos mediante JNDI Los servidores de aplicaciones Java EE proporcionan implementaciones de DataSource
Cada objeto DataSource es accesible a partir de un nombre JNDI de la forma java:comp/env/XXX/YYY, donde XXX suele (recomendado) ser "jdbc" e YYY el nombre de un DataSource concreto Habitualmente estos objetos DataSource implementan la estrategia de pool de conexiones Requiere configuracin en el servidor (driver, URL, usuario, contrasea, parmetros especficos al pool, etc.)
El atributo jndiName se utiliza para indicar el nombre del recurso accesible va JNDI Si la aplicacin est ejecutndose dentro de un servidor de aplicaciones Java EE
El valor del atributo resourceRef debe ser true El nombre indicado en jndiName es relativo al contexto java:comp/env/
Los beans que implementan esta interfaz se definen igual que el resto de beans, pero cuando se referencian desde otros beans no se inyecta una instancia de ese tipo sino el objeto que devuelve su mtodo getObject El mtodo getObject de JndiObjectFactoryBean devuelve el objeto asociado al nombre JNDI especificado en la configuracin
Los DAOs implementados con Hibernate necesitan un objeto de tipo org.hibernate.SessionFactory del que obtener la sesin actual
La siguiente declaracin permite definir un bean para obtener un SessionFactory que utiliza las anotaciones de Hibernate en las entidades
Como veremos ms adelante, el gestor de transacciones de Hibernate tambin precisa un objeto de ese tipo
datasource: indica el nombre de un bean de tipo DataSource (para obtener las conexiones a la BD) configLocation: Indica donde est el fichero de configuracin de Hibernate (fichero llamado pojo-minibank-hibernateconfig.xml localizado en el classpath de ejecucin) AnnotationSessionFactoryBean es un FactoryBean (igual que la clase JndiObjectFactoryBean explicada en la transparencia anterior) cuyo mtodo getObject devuelve una instancia que implementa SessionFactory
Como ya hemos visto con anterioridad el bean sessionFactory se inyecta en el DAO genrico
public class GenericDaoHibernate<E, PK extends Serializable> implements GenericDao<E, PK> { private SessionFactory sessionFactory; ... @Autowired public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; } ... }
Transacciones
Spring no gestiona directamente las transacciones, sino que lo hace a travs de una abstraccin (patrn Strategy)
Interfaz
org.springframework.transaction.PlatformTransactionManager
Se puede trabajar con las transacciones independientemente de la implementacin del gestor de transacciones concreto que se est utilizando
Spring proporciona una serie de gestores de transacciones que delegan la responsabilidad de la gestin de las transacciones a implementaciones especficas de la plataforma utilizada Se puede trabajar con transacciones a travs de la API Java de Spring, pero tambin se pueden definir de forma declarativa haciendo uso del framework AOP de Spring, que incluye una implementacin del aspecto de transaccionalidad
Es posible hacerlo mediante XML o mediante anotaciones Java Utilizaremos la opcin de las anotaciones porque simplifica la declaracin de las transacciones
Si la capa de persistencia del modelo est implementada con Hibernate, entonces el gestor de transacciones a utilizar es el siguiente
A partir del objeto SessionFactory obtiene la sesin Hibernate A partir de la sesin Hibernate obtiene el objeto Transaction
API Transacciones (y 2)
TransactionException es una excepcin unchecked El mtodo getTransaction devuelve un objeto de tipo TransactionStatus en funcin de un parmetro de tipo TransactionDefinition
TransactionDefinition es una interfaz a travs de la cual se pueden especificar las caractersticas de la transaccin que se quiere obtener (propagacin, nivel de aislamiento, timeout, propiedad read-only)
La subinterfaz TransactionAttribute permite, adems, especificar la lista de excepciones que provocan o no un rollback Si se pasa null en getTransaction, se utilizan los valores por defecto
Los mtodos commit y rollback se utilizan para hacer un commit o un rollback de la transaccin que se les pasa
Iniciar transaccin
Finalizar transaccin
Iniciar transaccin
Finalizar transaccin
Iniciar transaccin
Finalizar transaccin
Iniciar la transaccin (con las propiedades adecuadas) a partir del gestor de transacciones utilizad Ejecutar la lgica propia del caso de uso Finalizar transaccin (commit o rollback) en funcin de si se ha producido alguna excepcin o no y de su tipo
El cdigo comn para la gestin de las transacciones puede eliminarse de la implementacin de todos los casos de uso que lo precisen, y declarativamente decir cundo debe ejecutarse
El gestor de transacciones a utilizar Que los mtodos createAccount, removeAccount, withdrawFromAccount y todos los dems de la clase AccountServiceImpl son transaccionales Spring intercepta las invocaciones a los mtodos declarados como transaccionales:
[Antes de que se ejecute el mtodo] Ejecuta el cdigo necesario para comenzar la transaccin
Declarativamente pueden indicarse parmetros como, por ejemplo, el nivel de aislamiento deseado para la transaccin
[Despus de que se ejecute el mtodo] Ejecuta el cdigo necesario para finalizar la transaccin (ya sea con un commit o un rollback)
AccountServiceImpl.java (1)
@Service("accountService") @Transactional public class AccountServiceImpl implements AccountService {
AccountServiceImpl.java (2)
public void withdrawFromAccount(Long accountId, double amount) throws InstanceNotFoundException, InsufficientBalanceException { /* Find account. */ Account account = accountDao.find(accountId); /* Modify balance. */ double currentBalance = account.getBalance(); if (currentBalance < amount) { throw new InsufficientBalanceException(accountId, currentBalance,amount); } account.setBalance(currentBalance - amount); /* Register account operation. */ accountOperationDao.save(new AccountOperation(account, Calendar.getInstance(), AccountOperation.Type.WITHDRAW, amount)); }
AccountServiceImpl.java (3)
@Transactional(readOnly = true) public AccountBlock findAccountsByUserId(Long userId, int startIndex, int count) { ... }
@Transactional(readOnly = true) public List<AccountOperation> findAccountOperationsByDate( Long accountId, Calendar startDate, Calendar endDate, int startIndex, int count) throws InstanceNotFoundException { ... }
}
Si se desean utilizar anotaciones para declarar las transacciones, el gestor de transacciones a utilizar se declara a travs de la etiqueta annotation-driven, perteneciente al espacio de nombres tx
<tx:annotation-driven transaction-manager="transactionManager" />
El valor del atributo transaction-manager indica el nombre del bean que debe ser utilizado como gestor de transacciones
Este atributo es opcional, y si no est presente toma el valor por defecto transactionManager Por tanto en nuestro caso podramos no haberlo especificado
Propiedades:
En una clase se aplica a todos los mtodos que contiene En un mtodo sobrescribe el comportamiento especificado para la clase a la que pertenece propagation:
PROPAGATION_REQUIRED (valor por defecto): El mtodo debe ejecutarse dentro de una transaccin; si ya existe una, se ejecuta en esa, y si no, se crea una nueva PROPAGATION_MANDATORY: Igual que el anterior pero la transaccin debe existir (si no se lanza una excepcin) PROPAGATION_REQUIRES_NEW: El mtodo debe ejecutarse dentro de una transaccin nueva (si ya existe una se suspende mientras) PROPAGATION_NESTED: El mtodo debe ejecutarse en una transaccin anidada si ya existe una transaccin en progreso. Si no existe se comporta igual que PROPAGATION_REQUIRED PROPAGATION_NEVER: El mtodo no debe ejecutarse dentro de una transaccin (si existe una en progreso se lanza una excepcin) PROPAGATION_NOT_SUPPORTED: Igual que el anterior pero si existe una transaccin se suspende en vez de lanzar una excepcin PROPAGATION_SUPPORTS: No es necesario que el mtodo se ejecute dentro de una transaccin pero si ya existe una se ejecuta dentro de ella
Propiedades (cont.):
ISOLATION_DEFAULT: El de la plataforma ISOLATION_READ_UNCOMMITED: Pueden ocurrir dirty reads, non-repeatable reads y phanton reads ISOLATION_READ_COMMITED: Pueden ocurrir nonrepeatable reads y phamton reads ISOLATION_REPETEABLE_READ: Pueden ocurrir phanton reads ISOLATION_SERIALIZABLE: Elimina todos los problemas de concurrencia
Importante indicarlo para poder hacer optimizaciones !!
Anotacin @Transactional (y 3)
Por defecto cualquier excepcin de tipo unchecked (hija de RuntimeException) provocar un rollback, y cualquier excepcin checked (hija de Exception) no
Ejemplos:
La utilizaremos en los servicios del modelo para declarar la transaccionalidad de sus mtodos
Es posible anotar la interfaz que declara las operaciones del servicio, pero es ms recomendable anotar la clase de implementacin del servicio
Clase Paquete es.udc.pojo.minibank.test.experiments.spring Ilustra cmo obtener una referencia a un servicio implementado con Spring y llamar a un caso de uso
... try { /* Get service object. */ ApplicationContext ctx = new ClassPathXmlApplicationContext( new String[] {SPRING_CONFIG_FILE, SPRING_CONFIG_TEST_FILE}); AccountService accountService = (AccountService) ctx .getBean("accountService"); accountService.withdrawFromAccount(accountId, amount); } catch (InstanceNotFoundException e) { e.printStackTrace(); } catch (InsufficientBalanceException e) { e.printStackTrace(); } ...
Los casos de uso se implementan en trminos de DAOs (tal como se vio en el apartado 3.2)
En los DAOs se inyecta un objeto de tipo org.hibernate.SessionFactory proporcionado por Spring Los DAOs se inyectan en la clase de implementacin de la Fachada Se utiliza un mecanismo proporcionado por Spring para convertir las excepciones de persistencia nativas a una jerarqua propia, independiente del framework utilizado para la persistencia
Implementacin de Servicios (y 2)
Para indicar la transaccionalidad de los casos de uso utilizamos la anotacin @Transactional sobre la clase de implementacin del Servicio
Se le inyecta el mismo SessionFactory que el creado para inyectar en los DAOs, para que pueda acceder a travs de l al gestor de transacciones de Hibernate Se le inyecta un DataSource
La implementacin de los Servicios es independiente del software utilizado para el acceso a datos (en nuestro caso Hibernate)